fpc/utils/instantfpc/instantfpc.pas
2019-09-17 10:35:36 +00:00

226 lines
6.2 KiB
ObjectPascal

{ 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 <http://www.gnu.org/copyleft/gpl.html>. 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] <source file> [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=<path to cache>');
writeln(' Set the cache to be used. Otherwise using environment variable');
writeln(' INSTANTFPCCACHE.');
writeln;
writeln(' --compiler=<path to 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.