instantfpc.pas 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. { Compile and run a pascal program.
  2. Copyright (C) 2011 Mattias Gaertner [email protected]
  3. This source is free software; you can redistribute it and/or modify it under
  4. the terms of the GNU General Public License as published by the Free
  5. Software Foundation; either version 2 of the License, or (at your option)
  6. any later version.
  7. This code is distributed in the hope that it will be useful, but WITHOUT ANY
  8. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. details.
  11. A copy of the GNU General Public License is available on the World Wide Web
  12. at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  13. to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  14. MA 02110-1301, USA.
  15. }
  16. program instantfpc;
  17. {$mode objfpc}{$H+}
  18. uses
  19. Classes, SysUtils, InstantFPTools;
  20. const
  21. Version = '1.3';
  22. // 1.3 compile in a separate directory, so that parallel invocations do not overwrite link.res files
  23. Procedure Usage(Err : string);
  24. begin
  25. if (Err<>'') then
  26. Writeln('Error : ',Err);
  27. writeln('instantfpc '+Version);
  28. writeln;
  29. writeln('Run pascal source files as scripts.');
  30. writeln('Normal usage is to add to a program source file a first line');
  31. writeln('("shebang") "#!/usr/bin/instantfpc".');
  32. writeln('Then you can execute the source directly in the terminal/console.');
  33. writeln;
  34. writeln('instantfpc -h');
  35. writeln(' Print this help message and exit.');
  36. writeln;
  37. writeln('instantfpc -v');
  38. writeln(' Print version and exit.');
  39. writeln;
  40. writeln('instantfpc [compiler options] <source file> [program parameters]');
  41. writeln(' Compiles source and runs program.');
  42. writeln(' Source is compared with the cache. If cache is not valid then');
  43. writeln(' source is copied to cache with the shebang line commented and');
  44. writeln(' cached source is compiled.');
  45. writeln(' If compilation fails the fpc output is written to stdout and');
  46. writeln(' instantfpc exits with error code 1.');
  47. writeln(' If compilation was successful the program is executed.');
  48. writeln(' If the compiler options contains -B the program is always');
  49. writeln(' compiled.');
  50. writeln(' If the environment option INSTANTFPCOPTIONS is set it is');
  51. writeln(' passed to the compiler as first parameters.');
  52. writeln;
  53. writeln('instantfpc --get-cache');
  54. writeln(' Prints current cache directory and exit.');
  55. writeln;
  56. writeln('Options:');
  57. writeln;
  58. writeln(' --');
  59. Writeln(' Read program from standard input');
  60. writeln(' --set-cache=<path to cache>');
  61. writeln(' Set the cache to be used. Otherwise using environment variable');
  62. writeln(' INSTANTFPCCACHE.');
  63. writeln;
  64. writeln(' --compiler=<path to compiler>');
  65. writeln(' Normally fpc is searched in PATH and used as compiler.');
  66. writeln;
  67. writeln(' --skip-run');
  68. writeln(' Do not execute the program. Useful to test if script compiles.');
  69. writeln(' You probably want to combine it with -B.');
  70. writeln;
  71. writeln(' -B');
  72. writeln(' Always recompile.');
  73. Halt(Ord(Err<>''));
  74. end;
  75. Procedure DisplayCache;
  76. begin
  77. write(GetCacheDir);
  78. Halt(0);
  79. end ;
  80. var
  81. i,j: Integer;
  82. p: String;
  83. Filename: String;
  84. Src: TStringList;
  85. CacheDir: String;
  86. CacheFilename: String;
  87. OutputFilename: String;
  88. S,E : String;
  89. DeleteCache : Boolean = False;
  90. RunIt: boolean = true;
  91. // Return true if filename found.
  92. Function InterpretParam(p : String) : boolean;
  93. begin
  94. Result:=False;
  95. if (P='') then exit;
  96. if p='-v' then
  97. begin
  98. writeln('instantfpc '+Version);
  99. Halt(1);
  100. end
  101. else if p='-h' then
  102. usage('')
  103. else if p='--get-cache' then
  104. DisplayCache
  105. else if copy(p,1,11)='--compiler=' then
  106. begin
  107. delete(P,1,11);
  108. SetCompiler(p);
  109. end
  110. else if copy(p,1,12)='--set-cache=' then
  111. begin
  112. delete(P,1,12);
  113. SetCacheDir(p);
  114. end
  115. else if p='--skip-run' then
  116. begin
  117. RunIt:=false;
  118. end
  119. else if (P<>'') and (p[1]<>'-') then
  120. begin
  121. Filename:=p;
  122. Result:=True;
  123. end
  124. else if (p='--') then
  125. begin
  126. Filename:='--';
  127. Result:=True;
  128. end;
  129. end;
  130. begin
  131. Filename:='';
  132. { For example:
  133. /usr/bin/instantfpc -MObjFpc -Sh ./envvars.pas param1
  134. }
  135. for i:=1 to Paramcount do
  136. begin
  137. p:=ParamStr(i);
  138. if p='' then
  139. continue
  140. else
  141. begin
  142. if (I<>1) then
  143. begin
  144. if InterpretParam(p) then
  145. Break;
  146. end
  147. else
  148. begin
  149. // The linux kernel passes the whole shebang line as 1 argument.
  150. // We must parse and split it ourselves.
  151. Repeat
  152. J:=Pos(' ',P);
  153. if (J=0) then
  154. J:=Length(P)+1;
  155. if InterpretParam(Copy(P,1,J-1)) then
  156. Break;
  157. Delete(P,1,J);
  158. Until (P='');
  159. if (FileName<>'') then
  160. Break;
  161. end;
  162. end;
  163. end;
  164. if (Filename='') then
  165. Usage('Missing source file');
  166. CheckSourceName(Filename);
  167. Src:=TStringList.Create;
  168. try
  169. if FileName<>'--' then
  170. Src.LoadFromFile(Filename)
  171. else
  172. begin
  173. While not EOF do
  174. begin
  175. Readln(S);
  176. Src.Add(S);
  177. end;
  178. FileName:=ChangeFileExt(GetTempFileName,'.pp');
  179. DeleteCache:=true;
  180. end;
  181. CommentShebang(Src);
  182. CacheDir:=GetCacheDir;
  183. // check cache
  184. CacheFilename:=CacheDir+ExtractFileName(Filename);
  185. E:=LowerCase(ExtractFileExt(CacheFileName));
  186. if (E<>'.pp') and (E<>'.pas') and (E<>'.lpr') then
  187. CacheFileName:=CacheFileName+'.pas';
  188. OutputFilename:=CacheDir+ChangeFileExt(ExtractFileName(Filename),'');
  189. if not IsCacheValid(Src,CacheFilename,OutputFilename) then begin
  190. // save source in cache to find out next time if something changed
  191. Src.SaveToFile(CacheFilename);
  192. Compile(Filename,CacheFilename,OutputFilename);
  193. if deleteCache then
  194. DeleteFile(CacheFileName);
  195. end;
  196. // run
  197. if RunIt then
  198. Run(OutputFilename);
  199. if DeleteCache then
  200. DeleteFile(OutputFileName);
  201. finally
  202. // memory is freed by OS, but for debugging puposes you can do it manually
  203. {$IFDEF IFFreeMem}
  204. Proc.Free;
  205. Src.Free;
  206. {$ENDIF}
  207. end;
  208. end.