instantfptools.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. unit InstantFPTools;
  2. {$mode objfpc}{$H+}
  3. {$define UseFpExecV}
  4. {$ifdef WINDOWS}
  5. {$undef UseFpExecV}
  6. {$define HASEXEEXT}
  7. {$endif WINDOWS}
  8. {$ifdef go32v2}
  9. {$undef UseFpExecV}
  10. {$define HASEXEEXT}
  11. {$endif go32v2}
  12. interface
  13. uses
  14. {$IFDEF UseFpExecV}
  15. Unix,
  16. {$ENDIF}
  17. Classes, SysUtils, Process;
  18. procedure CheckSourceName(const Filename: string);
  19. procedure CommentShebang(Src: TStringList);
  20. function GetCacheDir: string;
  21. procedure SetCacheDir(AValue : string);
  22. function IsCacheValid(Src: TStringList;
  23. const CachedSrcFile, CachedExeFile: string): boolean;
  24. procedure Compile(const CacheFilename, OutputFilename: string);
  25. function GetCompiler: string;
  26. procedure SetCompiler(AValue : string);
  27. function GetCompilerParameters(const SrcFilename, OutputFilename: string): string;
  28. procedure Run(const Filename: string);
  29. implementation
  30. Var
  31. CmdCacheDir : String;
  32. CmdCompiler : String;
  33. procedure AddParam(p: string; var Line: string);
  34. begin
  35. if p='' then exit;
  36. if Line<>'' then Line:=Line+' ';
  37. if (p[1]<>'"') and (System.Pos(' ',p)>0) then
  38. p:='"'+p+'"';
  39. Line:=Line+p;
  40. end;
  41. procedure CheckSourceName(const Filename: string);
  42. var
  43. Ext: String;
  44. begin
  45. // avoid name clashes
  46. Ext:=lowercase(ExtractFileExt(Filename));
  47. if (Ext<>'') and (Ext<>'.pas') and (Ext<>'.pp') and (Ext<>'.p')
  48. and (Ext<>'.lpr') and (Ext<>'.txt') and (Ext<>'.sh') and (Ext<>'.cgi')
  49. then begin
  50. writeln('invalid source extension ',Ext);
  51. Halt(1);
  52. end;
  53. end;
  54. procedure CommentShebang(Src: TStringList);
  55. var
  56. Line: string;
  57. i: Integer;
  58. begin
  59. // comment shebang #!
  60. if (Src.Count=0) then exit;
  61. Line:=Src[0];
  62. i:=1;
  63. if copy(Line,1,3)=#$EF#$BB#$BF then
  64. inc(i,3);// UTF8 BOM
  65. if (i>length(Line)) or (Line[i]<>'#') then exit;
  66. Src[0]:=copy(Line,1,i-1)+'//'+copy(Line,i,length(Line));
  67. end;
  68. procedure SetCacheDir(AValue : string);
  69. begin
  70. CmdCacheDir:=AValue;
  71. end;
  72. function GetCacheDir: string;
  73. begin
  74. Result:=CmdCacheDir;
  75. if (Result='') then
  76. begin
  77. Result:=GetEnvironmentVariable('INSTANTFPCCACHE');
  78. if Result='' then
  79. begin
  80. Result:=GetEnvironmentVariable('HOME');
  81. if Result<>'' then
  82. Result:=IncludeTrailingPathDelimiter(Result)+'.cache'+PathDelim+'instantfpc';
  83. end;
  84. end;
  85. if Result='' then begin
  86. writeln('missing environment variable: HOME or INSTANTFPCCACHE');
  87. Halt(1);
  88. end;
  89. Result:=IncludeTrailingPathDelimiter(ExpandFileName(Result));
  90. if not ForceDirectories(Result) then begin
  91. writeln('unable to create cache directory "'+Result+'"');
  92. Halt(1);
  93. end;
  94. end;
  95. function IsCacheValid(Src: TStringList; const CachedSrcFile,
  96. CachedExeFile: string): boolean;
  97. var
  98. OldSrc: TStringList;
  99. i: Integer;
  100. p: String;
  101. begin
  102. Result:=false;
  103. for i:=1 to Paramcount do begin
  104. p:=ParamStr(i);
  105. if (p='') or (p[1]<>'-') then break;
  106. if p='-B' then exit; // always compile
  107. end;
  108. if not FileExists(CachedSrcFile) then exit;
  109. if not FileExists(CachedExeFile) then exit;
  110. OldSrc:=TStringList.Create;
  111. OldSrc.LoadFromFile(CachedSrcFile);
  112. Result:=Src.Equals(OldSrc);
  113. {$IFDEF IFFreeMem}
  114. OldSrc.Free;
  115. {$ENDIF}
  116. end;
  117. procedure SetCompiler(AValue : string);
  118. begin
  119. CmdCompiler:=AValue;
  120. end;
  121. function GetCompiler: string;
  122. var
  123. Path: String;
  124. p: Integer;
  125. StartPos: LongInt;
  126. Dir: String;
  127. CompFile: String;
  128. begin
  129. Result:=CmdCompiler;
  130. if (Result<>'') then
  131. begin
  132. Result:=ExpandFileName(Result);
  133. if not FileExists(Result) then
  134. begin
  135. writeln('Error: '+Result+' not found, check the --compiler parameter.');
  136. Halt(1);
  137. end;
  138. exit;
  139. end;
  140. {$IFDEF Windows}
  141. CompFile:='fpc.exe';
  142. {$ELSE}
  143. CompFile:='fpc';
  144. {$ENDIF}
  145. Path:=GetEnvironmentVariable('PATH');
  146. {$IFDEF VER2_4}
  147. if PATH<>'' then begin
  148. p:=1;
  149. while p<=length(Path) do begin
  150. StartPos:=p;
  151. while (p<=length(Path)) and (Path[p]<>':') do inc(p);
  152. if StartPos<p then begin
  153. Dir:=copy(Path,StartPos,p-StartPos);
  154. Result:=ExpandFileName(IncludeTrailingPathDelimiter(Dir))+CompFile;
  155. if FileExists(Result) then exit;
  156. end;
  157. inc(p);
  158. end;
  159. end;
  160. {$ELSE}
  161. Result:=ExeSearch(CompFile);
  162. {$ENDIF}
  163. if (Result='') then
  164. begin
  165. writeln('Error: '+CompFile+' not found in PATH');
  166. Halt(1);
  167. end;
  168. end;
  169. procedure Compile(const CacheFilename, OutputFilename: string);
  170. var
  171. Compiler: String;
  172. CompParams: String;
  173. Proc: TProcess;
  174. Count: Int64;
  175. ss: TStringStream;
  176. buf : Array[1..4096] of byte;
  177. begin
  178. Compiler:=GetCompiler;
  179. CompParams:=GetCompilerParameters(CacheFilename,OutputFilename);
  180. //writeln('Compiler=',Compiler,' Params=',CompParams);
  181. if FileExists(OutputFilename) and not DeleteFile(OutputFilename) then begin
  182. writeln('unable to delete ',OutputFilename);
  183. Halt(1);
  184. end;
  185. Proc:=TProcess.Create(nil);
  186. Proc.CommandLine:=Compiler+' '+CompParams;
  187. Proc.Options:= [poUsePipes, poStdErrToOutput];
  188. Proc.ShowWindow := swoHide;
  189. Proc.Execute;
  190. ss:=TStringStream.Create('');
  191. repeat
  192. Count:=Proc.Output.Read(Buf,4096);
  193. if Count>0 then
  194. ss.write(buf,count);
  195. until Count=0;
  196. if (not Proc.WaitOnExit) or (Proc.ExitStatus<>0) then begin
  197. write(ss.DataString);
  198. Halt(1);
  199. end;
  200. ss.Free;
  201. Proc.Free;
  202. end;
  203. function GetCompilerParameters(const SrcFilename, OutputFilename: string): string;
  204. { For example:
  205. /usr/bin/instantfpc -MObjFpc -Sh ./envvars.pas param1
  206. The shebang compile parameters: -MObjFpc -Sh
  207. }
  208. var
  209. p: String;
  210. i : integer;
  211. begin
  212. Result:='';
  213. I:=1;
  214. While (I<=ParamCount) and (Copy(ParamStr(i),1,1)='-') do
  215. begin
  216. p:=ParamStr(i);
  217. if (Copy(p,1,1)='-') and (copy(p,1,2)<>'--') then
  218. AddParam(P,Result);
  219. inc(I);
  220. end;
  221. AddParam('-o'+OutputFilename {$IFDEF HASEXEEXT} + '.exe' {$ENDIF},Result);
  222. AddParam(SrcFilename,Result);
  223. end;
  224. procedure Run(const Filename: string);
  225. var
  226. p: PPChar;
  227. begin
  228. p:=argv;
  229. inc(p);
  230. while (p<>nil) do begin
  231. if (p^<>nil) and (p^^<>'-') then begin
  232. break;
  233. end;
  234. inc(p);
  235. end;
  236. {$IFNDEF UseFpExecV}
  237. Inc(p); //lose the first command-line argument with the the script filename
  238. Halt(ExecuteProcess(Filename,[p^]));
  239. {$ELSE}
  240. Halt(FpExecV(Filename,p));
  241. {$ENDIF}
  242. end;
  243. end.