{ Compile and run a pascal program. Copyright (C) 2011 Mattias Gaertner mattias@freepascal.org This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GNU General Public License is available on the World Wide Web at . You can also obtain it by writing to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. } program instantfpc; {$mode objfpc}{$H+} uses Classes, SysUtils, InstantFPTools; const Version = '1.3'; // 1.3 compile in a separate directory, so that parallel invocations do not overwrite link.res files Procedure Usage(Err : string); begin if (Err<>'') then Writeln('Error : ',Err); writeln('instantfpc '+Version); writeln; writeln('Run pascal source files as scripts.'); writeln('Normal usage is to add to a program source file a first line'); writeln('("shebang") "#!/usr/bin/instantfpc".'); writeln('Then you can execute the source directly in the terminal/console.'); writeln; writeln('instantfpc -h'); writeln(' Print this help message and exit.'); writeln; writeln('instantfpc -v'); writeln(' Print version and exit.'); writeln; writeln('instantfpc [compiler options] [program parameters]'); writeln(' Compiles source and runs program.'); writeln(' Source is compared with the cache. If cache is not valid then'); writeln(' source is copied to cache with the shebang line commented and'); writeln(' cached source is compiled.'); writeln(' If compilation fails the fpc output is written to stdout and'); writeln(' instantfpc exits with error code 1.'); writeln(' If compilation was successful the program is executed.'); writeln(' If the compiler options contains -B the program is always'); writeln(' compiled.'); writeln(' If the environment option INSTANTFPCOPTIONS is set it is'); writeln(' passed to the compiler as first parameters.'); writeln; writeln('instantfpc --get-cache'); writeln(' Prints current cache directory and exit.'); writeln; writeln('Options:'); writeln; writeln(' --'); Writeln(' Read program from standard input'); writeln(' --set-cache='); writeln(' Set the cache to be used. Otherwise using environment variable'); writeln(' INSTANTFPCCACHE.'); writeln; writeln(' --compiler='); writeln(' Normally fpc is searched in PATH and used as compiler.'); writeln; writeln(' --skip-run'); writeln(' Do not execute the program. Useful to test if script compiles.'); writeln(' You probably want to combine it with -B.'); writeln; writeln(' -B'); writeln(' Always recompile.'); Halt(Ord(Err<>'')); end; Procedure DisplayCache; begin write(GetCacheDir); Halt(0); end ; var i,j: Integer; p: String; Filename: String; Src: TStringList; CacheDir: String; CacheFilename: String; OutputFilename: String; S,E : String; DeleteCache : Boolean = False; RunIt: boolean = true; // Return true if filename found. Function InterpretParam(p : String) : boolean; begin Result:=False; if (P='') then exit; if p='-v' then begin writeln('instantfpc '+Version); Halt(1); end else if p='-h' then usage('') else if p='--get-cache' then DisplayCache else if copy(p,1,11)='--compiler=' then begin delete(P,1,11); SetCompiler(p); end else if copy(p,1,12)='--set-cache=' then begin delete(P,1,12); SetCacheDir(p); end else if p='--skip-run' then begin RunIt:=false; end else if (P<>'') and (p[1]<>'-') then begin Filename:=p; Result:=True; end else if (p='--') then begin Filename:='--'; Result:=True; end; end; begin Filename:=''; { For example: /usr/bin/instantfpc -MObjFpc -Sh ./envvars.pas param1 } for i:=1 to Paramcount do begin p:=ParamStr(i); if p='' then continue else begin if (I<>1) then begin if InterpretParam(p) then Break; end else begin // The linux kernel passes the whole shebang line as 1 argument. // We must parse and split it ourselves. Repeat J:=Pos(' ',P); if (J=0) then J:=Length(P)+1; if InterpretParam(Copy(P,1,J-1)) then Break; Delete(P,1,J); Until (P=''); if (FileName<>'') then Break; end; end; end; if (Filename='') then Usage('Missing source file'); CheckSourceName(Filename); Src:=TStringList.Create; try if FileName<>'--' then Src.LoadFromFile(Filename) else begin While not EOF do begin Readln(S); Src.Add(S); end; FileName:=ChangeFileExt(GetTempFileName,'.pp'); DeleteCache:=true; end; CommentShebang(Src); CacheDir:=GetCacheDir; // check cache CacheFilename:=CacheDir+ExtractFileName(Filename); E:=LowerCase(ExtractFileExt(CacheFileName)); if (E<>'.pp') and (E<>'.pas') and (E<>'.lpr') then CacheFileName:=CacheFileName+'.pas'; OutputFilename:=CacheDir+ChangeFileExt(ExtractFileName(Filename),''); if not IsCacheValid(Src,CacheFilename,OutputFilename) then begin // save source in cache to find out next time if something changed Src.SaveToFile(CacheFilename); Compile(Filename,CacheFilename,OutputFilename); if deleteCache then DeleteFile(CacheFileName); end; // run if RunIt then Run(OutputFilename); if DeleteCache then DeleteFile(OutputFileName); finally // memory is freed by OS, but for debugging puposes you can do it manually {$IFDEF IFFreeMem} Proc.Free; Src.Free; {$ENDIF} end; end.