make.pas 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. //https://castle-engine.io/modern_pascal
  2. program Make;
  3. {$mode objfpc}{$H+}
  4. uses
  5. Classes,
  6. SysUtils,
  7. StrUtils,
  8. FileUtil,
  9. Zipper,
  10. fphttpclient,
  11. RegExpr,
  12. openssl,
  13. LazUTF8,
  14. opensslsockets,
  15. eventlog,
  16. Process;
  17. function OutLog(const Knd: TEventType; const Msg: string): string;
  18. begin
  19. case Knd of
  20. etError: Result := #27'[91m%s'#27'[0m';
  21. etInfo: Result := #27'[32m%s'#27'[0m';
  22. etDebug: Result := #27'[33m%s'#27'[0m';
  23. end;
  24. Writeln(stderr, UTF8ToConsole(Result.Format([Msg])));
  25. end;
  26. function AddPackage(const Path: string): string;
  27. begin
  28. if RunCommand('lazbuild', ['--add-package-link', Path], Result, [poStderrToOutPut]) then
  29. OutLog(etDebug, 'Add package:'#9 + Path)
  30. else
  31. begin
  32. ExitCode += 1;
  33. OutLog(etError, Result);
  34. end;
  35. end;
  36. function SelectString(const Input, Reg: string): string;
  37. var
  38. Line: string;
  39. begin
  40. Result := EmptyStr;
  41. with TRegExpr.Create do
  42. begin
  43. Expression := Reg;
  44. for Line in Input.Split(LineEnding) do
  45. if Exec(Line) then
  46. begin
  47. if Result <> EmptyStr then
  48. Result += LineEnding;
  49. Result += Line;
  50. end;
  51. Free;
  52. end;
  53. end;
  54. function RunTest(const Path: String): string;
  55. begin
  56. OutLog(etDebug, #9'run:'#9 + Path);
  57. if RunCommand(Path, ['--all', '--format=plain'], Result, [poStderrToOutPut]) then
  58. OutLog(etInfo, #9'success!')
  59. else
  60. begin
  61. ExitCode += 1;
  62. OutLog(etError, Result);
  63. end;
  64. end;
  65. function AddDDL(const Path: String): string;
  66. const
  67. LibPath: string = '/usr/lib/';
  68. var
  69. List: array of string;
  70. Last: integer;
  71. begin
  72. OutLog(etDebug, #9'add:'#9 + Path);
  73. List := Path.Split(DirectorySeparator);
  74. Last := High(List);
  75. if not FileExists(LibPath + List[Last]) then
  76. if RunCommand('sudo', ['bash', '-c', 'cp %s %s; ldconfig --verbose'.Format([Path, LibPath])], Result, [poStderrToOutPut]) then
  77. OutLog(etInfo, #9'success!')
  78. else
  79. begin
  80. ExitCode += 1;
  81. OutLog(etError, Result);
  82. end;
  83. end;
  84. function BuildProject(const Path: string): string;
  85. var
  86. Text: string;
  87. begin
  88. OutLog(etDebug, 'Build from:'#9 + Path);
  89. if RunCommand('lazbuild',
  90. ['--build-all', '--recursive', '--no-write-project', Path], Result, [poStderrToOutPut]) then
  91. begin
  92. Result := SelectString(Result, 'Linking').Split(' ')[2].Replace(LineEnding, EmptyStr);
  93. OutLog(etInfo, #9'to:'#9 + Result);
  94. Text := ReadFileToString(Path.Replace('.lpi', '.lpr'));
  95. if Text.Contains('program') and Text.Contains('consoletestrunner') then
  96. RunTest(Result)
  97. else if Text.Contains('library') and Text.Contains('exports') then
  98. AddDDL(Result)
  99. end
  100. else
  101. begin
  102. ExitCode += 1;
  103. OutLog(etError, SelectString(Result, '(Fatal|Error|/ld(\.[a-z]+)?):'));
  104. end;
  105. end;
  106. function DownloadFile(const Uri: string): string;
  107. var
  108. OutFile: TStream;
  109. begin
  110. InitSSLInterface;
  111. Result := GetTempFileName;
  112. OutFile := TFileStream.Create(Result, fmCreate or fmOpenWrite);
  113. with TFPHttpClient.Create(nil) do
  114. begin
  115. try
  116. AddHeader('User-Agent', 'Mozilla/5.0 (compatible; fpweb)');
  117. AllowRedirect := True;
  118. Get(Uri, OutFile);
  119. OutLog(etDebug, 'Download from %s to %s'.Format([Uri, Result]));
  120. finally
  121. Free;
  122. OutFile.Free;
  123. end;
  124. end;
  125. end;
  126. procedure UnZip(const ZipFile, ZipPath: string);
  127. begin
  128. with TUnZipper.Create do
  129. begin
  130. try
  131. FileName := ZipFile;
  132. OutputPath := ZipPath;
  133. Examine;
  134. UnZipAllFiles;
  135. OutLog(etDebug, 'Unzip from'#9 + ZipFile + #9'to'#9 + ZipPath);
  136. DeleteFile(ZipFile);
  137. finally
  138. Free;
  139. end;
  140. end;
  141. end;
  142. function InstallOPM(const Path: string): string;
  143. begin
  144. Result :=
  145. {$IFDEF MSWINDOWS}
  146. GetEnvironmentVariable('APPDATA') + '\.lazarus\onlinepackagemanager\packages\'
  147. {$ELSE}
  148. GetEnvironmentVariable('HOME') + '/.lazarus/onlinepackagemanager/packages/'
  149. {$ENDIF}
  150. + Path;
  151. if not DirectoryExists(Result) then
  152. begin
  153. if ForceDirectories(Result) then
  154. UnZip(DownloadFile('https://packages.lazarus-ide.org/%s.zip'.Format([Path])), Result);
  155. end;
  156. end;
  157. procedure RetrieveSubmodules;
  158. var CommandOutput: string;
  159. begin
  160. if FileExists('.gitmodules') then
  161. if RunCommand('git', ['submodule', 'update', '--init',
  162. '--force', '--remote'], CommandOutput, [poStderrToOutPut]) then
  163. OutLog(etInfo, CommandOutput)
  164. else
  165. begin
  166. ExitCode += 1;
  167. OutLog(etError, CommandOutput);
  168. end;
  169. end;
  170. function BuildAll(const Target: string; const Dependencies: array of string): string;
  171. var
  172. List: TStringList;
  173. DT: TDateTime;
  174. begin
  175. DT := Time;
  176. // GitHub already retrieves submodules
  177. List := FindAllFiles(GetCurrentDir, '*.lpk');
  178. try
  179. for Result in Dependencies do
  180. List.AddStrings(FindAllFiles(InstallOPM(Result), '*.lpk'));
  181. for Result in List do
  182. AddPackage(Result);
  183. List := FindAllFiles(Target, '*.lpi');
  184. List.Sort;
  185. for Result in List do
  186. if not Result.Contains('backup') and not Result.Contains('/use/') then
  187. BuildProject(Result);
  188. finally
  189. List.Free;
  190. end;
  191. if not RunCommand('delp', ['-r', GetCurrentDir], Result, [poStderrToOutPut]) then
  192. OutLog(etError, Result);
  193. OutLog(etDebug, 'Duration:'#9 + FormatDateTime('hh:nn:ss', Time - DT));
  194. end;
  195. begin
  196. try
  197. BuildAll('.', ['UEControls']);
  198. OutLog(etDebug, '------------');
  199. case ExitCode of
  200. 0: OutLog(etInfo, 'No Errors 😊');
  201. else
  202. OutLog(etError, 'Errors:'#9 + ExitCode.ToString);
  203. end;
  204. OutLog(etDebug, '------------');
  205. except
  206. on E: Exception do
  207. Writeln(E.ClassName, #9, E.Message);
  208. end;
  209. end.