Hi all,
I would like to execute small pascal programs like normal unix scripts. For example:
#!/usr/bin/instantfpc
begin
writeln('shebang executed');
end.Has anyone already written a tool, that compiles and runs the program?
Mattias

Mailing lists / fpc-pascal / 2011-03
44 messages · 12 people · –
Reply by email Subscribe to this list Your subscription
Read-only archive. Use j and k to move between messages.
Hi all,
I would like to execute small pascal programs like normal unix scripts. For example:
#!/usr/bin/instantfpc
begin
writeln('shebang executed');
end.Has anyone already written a tool, that compiles and runs the program?
Mattias
Using an idea I saw somewhere else, I've done this:
ls -l
total 4
-rwxr-xr-x 1 hcv hcv 148 Mar 24 16:38 fpc_script.pp
cat fpc_script.pp
// 2>/dev/null; fpc fpc_script.pp &> /dev/null && exec ./fpc_script "$@"; exit
program fpc_script;
begin
writeln('hi there, from a script!');
end../fpc_script.pp
hi there, from a script!
It would be nice to have #!fpc --run
Henry
Or even:
// 2>/dev/null; fpc fpc_script.pp &> build.log && exec ./fpc_script
"$@" || cat build.log; exitNice idea. I didn't know the // 2>/dev/null trick. It does not work with tcsh.
The working directory must be '.'.
The temp files should go to another directory.
Mattias
I would like to execute small pascal programs like normal unix scripts. For example:…
As far as I know, the "!/something just means, start /something, and pass current filename as a commandline param ?
So all your tool has to do is write all except the first line to a temp file, compile temp file and execute it ?
Michael.
Almost.
- It should return the exit code.
- If compile fails it should print out the fpc output.
- It should give nice error messages.
- Handle spaces in parameters correctly.
- Preferably it recompiles only if source changed.
Mattias
As far as I know, the "!/something just means, start /something, and pass current…
In general, "pass all parameters" allowing that unix doctrine expects an implicit zeroeth parameter which is the name of the current program.
So all your tool has to do is write all except the first line to a temp file, compile temp file and execute it ?
If I recall correctly, Rexx has special handling for the first line which- intentionally or otherwise- facilitates shebang handling. Also I'm reminded of a Perl option which causes it to throw away anything before valid scripting.
Would it be feasible to modify FPC so that it discarded everything preceding "program", "unit" etc?
It wouldn't be Pascal then :)
In general, "pass all parameters" allowing that unix doctrine expects an implicit zeroeth…
Mark Morgan Lloyd wroteWould it be feasible to modify FPC so that it discarded everything preceding "program", "unit" etc?
Feasable, yes, but it is probably easier to make a pre-processor which removes this line, simply.
Commenting is better. Then the reported error line numbers are still valid.
Mattias
Commenting is better. Then the reported error line numbers are still valid.
Yes, but then bash won't recognize it, I suppose
> Commenting is better. Then the reported error line numbers are still > valid. Yes, but then bash won't recognize it, I suppose
I meant:
Original file:
#!/usr/bin/instantfpc
begin
end.bash is happy.
Preprocessor creates:
//#!/usr/bin/instantfpc
begin
end.fpc is happy.
Mattias
#!/usr/bin/env instantfpc
That's the cross platform way to do it for all unicses.
LINESIP - Opening the source for communication
http://www.linesip.com
http://www.linesip.co.il
On Thu, 24 Mar 2011 22:40:22 +0100 Commenting is better. Then the reported error line…
Fpc treats # as // if (and only if) found as the first character of a file? Character position in this line won't be relevant since it's not going to be compilable source.
Also I notice that tcc (Tiny C Compiler) has a -run parameter which runs the compiled program after compilation.
With things as they stand, particularly in view of the requirement that the program is only recompiled if it's changed, I think the easiest way would be a wrapper: since that could be of adequate complexity and would have no restrictions on temporary files etc. it could excise/comment without restriction.
On Thu, 24 Mar 2011 23:10:08 +0100 I meant: Original file: #!/usr/bin/instantfpc begin…
Indeed it works:
fsb: >cat testscript
#!/home/michael/fpcscript
program testscript;
Var
I : Integer;
begin
Write('Testscript called as ');
For I:=0 to ParamCount do
begin
if I>0 then
Write(' ');
Write(paramstr(i));
end;
writeln;
end.
fsb: >./testscript
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
Testscript called as /tmp/fpc00000.tmp
fsb: >And here is fpcscript (50 lines only):
$mode objfpc}{$h+}
program fpcscript;
uses sysutils,classes,baseunix, unix;
Const
BufSize = 10 * 1024;
Comment = '//';
Type
TStringArray = Array of string;
Var
F1,F2 : TFileStream;
FN : String;
Args : TStringArray;
E,I : integer;
begin
E:=127;
F1:=TFileStream.Create(ParamStr(1),fmOpenRead);
try
FN:=GetTempFileName(GetTempDir,'fpc')+'.pp';
F2:=TFileStream.Create(FN,fmCreate);
try
F2.WriteBuffer(Comment[1],Length(Comment));
F2.CopyFrom(F1,F1.Size);
finally
F2.Free;
end;
if (ExecuteProcess('/usr/local/bin/fpc',[FN])=0) then
begin
If FileExists(ChangeFileExt(FN,'.o')) then
DeleteFIle(ChangeFileExt(FN,'.o'));
SetLength(Args,ParamCount-1);
For I:=2 to ParamCount do
Args[I-1]:=Paramstr(i);
E:=ExecuteProcess(ChangeFileExt(FN,''),Args);
If FileExists(ChangeFileExt(FN,'')) then
DeleteFile(ChangeFileExt(FN,''));
If FileExists(FN) then
DeleteFile(Fn);
end;
Finally
F1.Free;
end;
Halt(E);
end.Now we need to get rid of the ugly ld message and you're all set.
Michael.
Mattias Gaertner wroteI wrote a little tool: http://wiki.lazarus.freepascal.org/InstantFPC
Nice job. OK to include in FPC as one of the utils ?
Michael.
Yes.
Mattias
Thank you. But please, fix a couple of bugs first:
1. I get a stream read error each time I execute a script the first time (i.e. it's not yet in the cache) : home: >./helloworld.pas
An unhandled exception occurred at $0000000000425556 : EReadError : Stream read error
$0000000000425556
$00000000004256FA
$0000000000469E8A
$0000000000400A25
home: >./helloworld.pas
Hello world 2
2. passing options on the commandline does not work on my Kubuntu 10.04:
home: >./envvars.pas
/usr/bin/env: instantfpc -Mobjfpc -Sh: No such file or directory
Michael.
It works here.
Fixed.
Mattias
I assume you use FPC 2.4.x. In FPC 2.5.1 your code
repeat
Count:=ss.CopyFrom(Proc.Output,4096);
until Count=0;Fails.
This is because CopyFrom assumes that at least 4096 bytes are present. If it doesn't get 4096 bytes, an exception is thrown.
(see recent change in trunk:
r16992 | sergei | 2011-02-24 04:25:40 +0100 (Thu, 24 Feb 2011) | 5 lines
* Rework TStream.CopyFrom (see Mantis #17980):
a) Use significantly larger buffer (128k instead of 1k) b) When Count=0, do not try to determine the source size. Just copy until the source can be read. This should improve performance with limited seek capability sources (e.g. a decompression stream will be decompressed once rather than twice). c) Use ReadBuffer/WriteBuffer, so an exception is raised when something goes wrong. This conforms to Delphi behavior. )
My FPC outputs nothing, so CopyFrom does not get 4096 bytes and therefor raises an exception:
Breakpoint 1, 0x000000000041396c in fpc_raiseexception () (gdb) bt
#0 0x000000000041396c in fpc_raiseexception ()
#1 0x0000000000425565 in CLASSES_TSTREAM_$__READBUFFER$formal$LONGINT () #2 0x00000000004256fa in CLASSES_TSTREAM_$__COPYFROM$TSTREAM$INT64$$INT64 () #3 0x0000000000469e8a in INSTANTFPTOOLS_COMPILE$ANSISTRING$ANSISTRING () #4 0x0000000000400a25 in main ()
since the compilation is already done, the binary is in the cache, and the second run works.
Changing the code to
buf : Array[1..4096] of byte;
begin
// snip
ss:=TStringStream.Create('');
repeat
Count:=Proc.Output.Read(Buf,4096);
if Count>0 then
ss.write(buf,count);
until Count=0;it works always. Will you change it, or do I change it once it is in FPC ?
This change may affect other things in Lazarus.
Sergei (hope you are reading this), I think you should put this in the user_changes wiki page.
Michael.
I changed it on sf.
This change may affect other things in Lazarus. Sergei (hope you are reading this), I think you should put this in the user_changes wiki page.
Mattias
I created a patch to make instantFPC run on win32. To make the app run I implemented TProcess as windows does not understands fpexecv. Even better would be to use TProcess for all platforms, thus removing the need of defines and we could drop the unix and windows units.
The only script that did not work for me was exitcode.pas. I will have to look more in detail how to support this.
Regards, Darius
Darius Blaszyk wroteI created a patch to make instantFPC run on win32. To make the app run I implemented TProcess as windows does not understands fpexecv. Even better would be to use TProcess for all platforms, thus removing the need of defines and we could drop the unix and windows units.
I doubt it since execv actually replaces the current running process with the indicated binary (i.e. the binary you just compiled).
The only script that did not work for me was exitcode.pas. I will have to look more in detail how to support this.
That will require an alternative to fpexecv.
Michael.
I dont believe it was a fpexecv problem. The issue was that the command line option was not well be that I'm misinterpreting the intention of how to use it.
I did a: instantpfc exitcode.pas 1
Assuming that the compiler options come from the shebang line and the "1" provided on the command line is passed as parameter to exitcode.exe. But as said the "1" never reached the executable. I will test mac later today to see how it should work.
Regards, Darius
I had a look at your patch.
Do yourself a favour, and use ExecuteProcess from the sysutils unit for your Exec routine. It has all you need. The use of pipes is not needed when running the program, it will only seriously slow the running of the program.
But do not replace the unix fpexecv call; There will always be a difference between Windows and Unix.
The use of TProcess for the compile command is so the output of the compiler can be hidden. For running the actual process, this is not needed.
Michael.
This simplifies things quite a bit. Thanks for the hint. Here's a new patch, I also added a fix for the command line options issue. Should be alright now. All examples work fine on win32.
Darius
Hi,
Darius Blaszyk wroteThis simplifies things quite a bit. Thanks for the hint. Here's a new patch, I also added a fix for the command line options issue. Should be alright now. All examples work fine on win32. Darius
I haven't tried to run that yet, but, why not
...
{$IFNDEF MSWINDOWS}
Halt(FpExecV(Filename,p));
{$ELSE}
Halt(ExecuteProcess(Filename,CollectArgs(p)));
{$ENDIF}
...and then in another function, CollectArgs:
function CollectArgs(p: PPChar):string;
begin
// Result := '';
Inc(p); //lose the first command-line argument with the the script filename
while (p^ nil) do
begin
Result := Result + ' ' + p^^;
inc(p);
end;
end;Reason : there is no error checking nor any try/except construct involved/needed in this code, and it seems more, hm... readable when split ?
Lukasz
This simplifies things quite a bit. Thanks for the hint. Here's a new patch, I also added…
Ok, last request:
Please use the ExecuteProcess variant with an array of string as second parameter.
Reason: as soon as a command-line argument with spaces in it is passed to the script, your code will mangle the arguments.
Michael.
Here it is.
Darius
[THIS IS OFF TOPIC :) ]
linux init scripts in Pascal, anyone :)
L.
Here it is. Darius
Thanks. I applied it to lazarus-ccr.
Mattias
Nice job. OK to include in FPC as one of the utils ? Michael.
Are there any outstanding issues?
Mattias
Nice tool.
2 requests:
- I would like to select the compiler. I'm typically working with the svn version of compiler but have in my PATH a stable version of the compiler.
From the sources I can see that the compiler is searched for only in the
PATH. A simple solution would be to look first for the compiler in the path specified in the shebang and, if not found, then in the PATH.
- I was going to ask for the possibility of passing parameters to the program. Scripts without the possiblity to pass parameters are not really usefull. And we do't want to put parameters in the program neither since it would be re-compiled everytime the parameters change. Looking at the source I figured out that the compiler parameters stop at the first parameter not starting with a "-" and passes the rest on to the program. Great,needs to be added to the documentation, but... The cache is compared to the complete script, including the shebang. So if a program parameter is changed, the program is re-compiled... The shebang is commented out but program parameters should be removed before compare. Recompiling if compiler parameters change is OK.
Ludo
One solution:
PATH=YourDirectory:$PATH; script.pas
Another solution:
A parameter could be added to instantfpc to specify the compiler.
- I was going to ask for the possibility of passing parameters to the program.
Just like a normal script:
script.pas param1
Scripts without the possiblity to pass parameters are not really usefull.
Indeed, that would be total crap.
And we do't want to put parameters in the program neither since it would be re-compiled everytime the parameters change. Looking at the source I figured out that the compiler parameters stop at the first parameter not starting with a "-"
Yes, that parameter is the "script.pas".
and passes the rest on to the program. Great,needs to be added to the documentation,
This is is how shebang works internally. I didn't know the internals of shebang until I wrote instantfpc. That's why I think a user does not need that information.
The shebang contains the parameters for the compiler, not for the script.
Mattias
On Sun, 27 Mar 2011 19:05:12 +0200 (CEST) Are there any outstanding issues? Mattias
Lack of time :-)
I added it to FPC's subversion repository.
Michael.
Thanks. :)
I hope it does not take too many years until it comes preinstalled with the distributed packages.
Mattias
It should be in the next release from trunk ?
In each case, I have already a small collection of scripts locally, I can't deny it's usefullness. Bash scripting is on it's way out for me :-)
Michael.
On Sat, 23 Apr 2011 20:27:37 +0200 One solution: PATH=YourDirectory:$PATH; script.pas…
One solution: PATH=YourDirectory:$PATH; script.pas
I prefer to keep the PATH intact. I'll use:
bash -c "PATH=YourDirectory:$PATH; script.pas"
Another solution: A parameter could be added to instantfpc to specify the compiler.
Would be the most elegant solution. I understand the "search in the directory of calling program first" is not very Linux though.
This is is how shebang works internally. I didn't know the internals of
shebang until I wrote
instantfpc. That's why I think a user does not need that information.
Learned also something new.
Thanks, Ludo
That's the same. To change the PATH you must use
export PATH=
in bash.
I added the option --compiler=. It's already in the fpc version of instantfpc.
Mattias
I added the option --compiler=. It's already in the fpc version of
instantfpc.
Great, thanks.
Ludo
On Mon, 25 Apr 2011 10:24:58 +0200 That's the same. To change the PATH you must use…
Instantfpc seems to work fine under FreeBSD/32-bit too.
On Sun, 27 Mar 2011 19:05:12 +0200 (CEST) Are there any outstanding issues? Mattias
Yes I have one,
I would like to request adding a pascal script executable extension to the tool. Someting like .ppx for instance and have the installers for fpc and lazarus modified to register this extension also with instantfpc.
Regards, Darius
If no-one objects I could make a trivial patch to add .ppx or any other extension as "pascal executable" to instantfpc. I can also make a bug report. Is it ok to register .ppx to instantfpc with the windows installers? I don;t know how to do this for the other platforms. So perhaps we need two bugreports?
Regards, Darius