pas2fpm.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. program pas2fpm;
  2. {$mode objfpc}{$H+}
  3. uses
  4. {$IFDEF UNIX}{$IFDEF UseCThreads}
  5. cthreads,
  6. {$ENDIF}{$ENDIF}
  7. Classes, SysUtils, CustApp, passrcutil;
  8. type
  9. { TUnitEntry }
  10. TUnitEntry = Class(TCollectionItem)
  11. private
  12. FIntfDeps: TStrings;
  13. FImplDeps: TStrings;
  14. FDone: Boolean;
  15. FErr: String;
  16. FFileName : String;
  17. FName: String;
  18. FProcessing: Boolean;
  19. Fres: Boolean;
  20. function GetName: String;
  21. Public
  22. constructor Create(ACollection: TCollection); override;
  23. destructor Destroy; override;
  24. Procedure CleanIntfDependencies(Verbose : Boolean);
  25. Procedure CleanImplDependencies(Verbose : Boolean);
  26. Procedure OrderDependencies(Order : TStrings);
  27. Function Nodependencies : Boolean;
  28. Property FileName : String Read FFileName Write FFileName;
  29. Property Name : String Read GetName;
  30. Property IntfDependencies : TStrings Read FIntfDeps;
  31. Property ImplDependencies : TStrings Read FImplDeps;
  32. Property Resources : Boolean Read Fres Write Fres;
  33. Property Err : String Read FErr Write Ferr;
  34. Property Done : Boolean Read FDone Write FDone;
  35. Property Processing : Boolean Read FProcessing Write FProcessing;
  36. end;
  37. { TUnitEntries }
  38. TUnitEntries = Class(TCollection)
  39. private
  40. function GetE(AIndex : Integer): TUnitEntry;
  41. public
  42. Function IndexOfEntry(Const AName : String) : Integer;
  43. Function FindEntry(Const AName : string) : TUnitEntry;
  44. Function AddEntry(Const AFileName : String) : TUnitEntry;
  45. Property Units[AIndex : Integer] : TUnitEntry Read GetE; default;
  46. end;
  47. { TPas2FPMakeApp }
  48. TPas2FPMakeApp = class(TCustomApplication)
  49. private
  50. procedure AddLine(const ALine: String);
  51. function CheckParams : boolean;
  52. procedure CreateSources;
  53. Procedure ProcessUnits;
  54. function GetUnitProps(const FN: String; out Res: Boolean; UIn,UIm: TStrings; Out Err : string): Boolean;
  55. Function SimulateCompile(E,EFrom: TUnitEntry) : Boolean;
  56. procedure WriteProgEnd;
  57. procedure WriteProgStart;
  58. procedure WriteSources;
  59. protected
  60. FVerbose : Boolean;
  61. FFiles : TUnitEntries;
  62. FSrc,
  63. FUnits: TStrings;
  64. InterfaceUnitsOnly : Boolean;
  65. FPackageName : string;
  66. FOutputFile : string;
  67. procedure DoRun; override;
  68. public
  69. constructor Create(TheOwner: TComponent); override;
  70. destructor Destroy; override;
  71. procedure WriteHelp; virtual;
  72. end;
  73. { TUnitEntries }
  74. function TUnitEntries.GetE(AIndex : Integer): TUnitEntry;
  75. begin
  76. Result:=Items[AIndex] as TUnitEntry;
  77. end;
  78. function TUnitEntries.IndexOfEntry(const AName: String): Integer;
  79. begin
  80. Result:=Count-1;
  81. While (Result>=0) and (CompareText(GetE(Result).Name,AName)<>0) do
  82. Dec(Result);
  83. end;
  84. function TUnitEntries.FindEntry(const AName: string): TUnitEntry;
  85. Var
  86. I:Integer;
  87. begin
  88. I:=IndexofEntry(Aname);
  89. If (I<>-1) then
  90. Result:=GetE(I)
  91. else
  92. Result:=Nil;
  93. end;
  94. function TUnitEntries.AddEntry(Const AFileName: String): TUnitEntry;
  95. begin
  96. Result:=Add as TunitEntry;
  97. Result.FileName:=AFileName;
  98. end;
  99. { TUnitEntry }
  100. function TUnitEntry.GetName: String;
  101. begin
  102. Result:=ChangeFileExt(ExtractFileName(FileName),'');
  103. end;
  104. constructor TUnitEntry.Create(ACollection: TCollection);
  105. begin
  106. inherited Create(ACollection);
  107. FIntfDeps:=TStringList.Create;
  108. FImplDeps:=TStringList.Create;
  109. end;
  110. destructor TUnitEntry.Destroy;
  111. begin
  112. FreeAndNil(FIntfDeps);
  113. FreeAndNil(FImplDeps);
  114. inherited Destroy;
  115. end;
  116. procedure TUnitEntry.CleanIntfDependencies(Verbose : Boolean);
  117. Var
  118. I,J : Integer;
  119. U : TUnitEntry;
  120. begin
  121. For I:=FintfDeps.Count-1 downto 0 do
  122. begin
  123. U:=FIntfDeps.Objects[i] as TUnitEntry;
  124. J:=U.ImplDependencies.IndexOf(Name);
  125. if J<>-1 then
  126. begin
  127. U.ImplDependencies.Delete(J);
  128. If Verbose then
  129. Writeln(StdErr,'Removing interdependency of ',Name,' from ',U.Name);
  130. end;
  131. end;
  132. end;
  133. procedure TUnitEntry.CleanImplDependencies(Verbose : Boolean);
  134. Var
  135. I,J : Integer;
  136. U : TUnitEntry;
  137. begin
  138. For I:=FImplDeps.Count-1 downto 0 do
  139. begin
  140. U:=FImplDeps.Objects[i] as TUnitEntry;
  141. J:=U.ImplDependencies.IndexOf(Name);
  142. if J<>-1 then
  143. begin
  144. U.ImplDependencies.Delete(J);
  145. If Verbose then
  146. Writeln(StdErr,'Removing interdependency of ',Name,' from ',U.Name);
  147. end;
  148. end;
  149. end;
  150. procedure TUnitEntry.OrderDependencies(Order: TStrings);
  151. Var
  152. L : TStringList;
  153. I,CC : integer;
  154. begin
  155. L:=TstringList.Create;
  156. try
  157. L.Assign(FintfDeps);
  158. L.Sorted:=True;
  159. CC:=L.Count;
  160. FintfDeps.Clear;
  161. For I:=0 to Order.Count-1 do
  162. if L.Indexof(Order[i])<>-1 then
  163. FIntfDeps.Add(Order[i]);
  164. If FintfDeps.Count<>CC then
  165. Writeln('Internal error 1');
  166. L.Sorted:=False;
  167. L.Assign(FimplDeps);
  168. CC:=L.Count;
  169. L.Sorted:=True;
  170. FImplDeps.Clear;
  171. For I:=0 to Order.Count-1 do
  172. if L.Indexof(Order[i])<>-1 then
  173. FImplDeps.Add(Order[i]);
  174. If FImplDeps.Count<>CC then
  175. Writeln('Internal error 2');
  176. finally
  177. L.free;
  178. end;
  179. end;
  180. function TUnitEntry.Nodependencies: Boolean;
  181. begin
  182. Result:=(FIntfDeps.Count=0) and (FImplDeps.Count=0);
  183. end;
  184. { TPas2FPMakeApp }
  185. Function TPas2FPMakeApp.CheckParams : Boolean;
  186. Procedure AddFileMask(S : String);
  187. Var
  188. Info : TSearchRec;
  189. D : String;
  190. begin
  191. D:=ExtractFilePath(S);
  192. If FindFirst(S,0,Info)=0 then
  193. try
  194. Repeat
  195. FFiles.AddEntry(D+Info.Name);
  196. FUnits.Add(ChangeFileExt(ExtractFileName(info.name),''));
  197. until (FindNext(Info)<>0);
  198. finally
  199. FindClose(Info);
  200. end;
  201. end;
  202. Var
  203. I : Integer;
  204. S : String;
  205. begin
  206. Result:=True;
  207. I:=1;
  208. While I<=ParamCount do
  209. begin
  210. S:=Paramstr(i);
  211. if (S<>'') then
  212. begin
  213. if S[1]<>'-' then
  214. begin
  215. If (Pos('?',S)<>0) or (Pos('*',S)<>0) then
  216. AddFileMask(S)
  217. else if comparetext(ChangeFileExt(extractfilename(s),''),'fpmake')<>0 then
  218. begin
  219. FFiles.AddEntry(S);
  220. FUnits.Add(ChangeFileExt(ExtractFileName(S),''));
  221. end;
  222. end
  223. else
  224. begin
  225. If (s='o') then
  226. begin
  227. inc(I);
  228. FoutputFile:=ParamStr(i);
  229. end
  230. else If (s='-i') then
  231. InterfaceUnitsOnly:=True
  232. else If (s='-v') then
  233. FVerbose:=True
  234. else if (s='-p') then
  235. begin
  236. Inc(i);
  237. FPackageName:=ParamStr(i);
  238. end
  239. else
  240. begin
  241. Result:=False;
  242. exit;
  243. end;
  244. end;
  245. end;
  246. Inc(i);
  247. end;
  248. Result:=(FFiles.Count>0);
  249. end;
  250. procedure TPas2FPMakeApp.AddLine(Const ALine : String);
  251. begin
  252. FSrc.Add(ALine);
  253. end;
  254. Function TPas2FPMakeApp.GetUnitProps(Const FN : String; Out Res : Boolean; UIn,UIm : TStrings; Out Err : string) : Boolean;
  255. Var
  256. I,J : Integer;
  257. A : TPasSrcAnalysis;
  258. begin
  259. Result:=False;
  260. try
  261. If FVerbose then
  262. Writeln(StdErr,'Analysing unit ',FN);
  263. A:=TPasSrcAnalysis.Create(Self);
  264. try
  265. A.FileName:=FN;
  266. Res:=A.HasResourcestrings;
  267. A.GetInterfaceUnits(Uin);
  268. if Not InterfaceUnitsOnly then
  269. A.GetImplementationUnits(Uim);
  270. For I:=Uin.Count-1 downto 0 do
  271. begin
  272. J:=FUnits.IndexOf(UIN[i]);
  273. if (j=-1) then
  274. Uin.Delete(i)
  275. else
  276. Uin.Objects[i]:=FUnits.Objects[J];
  277. end;
  278. For I:=Uim.Count-1 downto 0 do
  279. begin
  280. J:=FUnits.IndexOf(UIm[i]);
  281. if (j=-1) then
  282. Uim.Delete(i)
  283. else
  284. Uim.Objects[i]:=FUnits.Objects[J];
  285. end;
  286. finally
  287. A.Free;
  288. end;
  289. Result:=True;
  290. except
  291. On E : Exception do
  292. Err:=E.Message;
  293. // Ignore
  294. end;
  295. end;
  296. procedure TPas2FPMakeApp.WriteProgStart;
  297. begin
  298. AddLine('program fpmake;');
  299. AddLine('');
  300. AddLine('uses fpmkunit;');
  301. AddLine('');
  302. AddLine('Var');
  303. AddLine(' T : TTarget;');
  304. AddLine(' P : TPackage;');
  305. AddLine('begin');
  306. AddLine(' With Installer do');
  307. AddLine(' begin');
  308. AddLine(' P:=AddPackage('''+FPackageName+''');');
  309. AddLine(' P.Version:=''0.0'';');
  310. // AddLine(' P.Dependencies.Add('fcl-base');
  311. AddLine(' P.Author := ''Your name'';');
  312. AddLine(' P.License := ''LGPL with modification'';');
  313. AddLine(' P.HomepageURL := ''www.yourcompany.com'';');
  314. AddLine(' P.Email := ''[email protected]'';');
  315. AddLine(' P.Description := ''Your very nice program'';');
  316. AddLine(' // P.NeedLibC:= false;');
  317. end;
  318. procedure TPas2FPMakeApp.WriteProgEnd;
  319. begin
  320. AddLine(' Run;');
  321. AddLine(' end;');
  322. AddLine('end.');
  323. end;
  324. procedure TPas2FPMakeApp.CreateSources;
  325. Var
  326. I,j : Integer;
  327. U : TStrings;
  328. F : TUnitEntry;
  329. FN : String;
  330. begin
  331. WriteProgStart;
  332. For I:=0 to FUnits.Count-1 do
  333. begin
  334. F:=FFiles.FindEntry(FUnits[i]);
  335. FN:=F.FileName;
  336. AddLine(' T:=P.Targets.AddUnit('''+FN+''');');
  337. if F.Err<>'' then
  338. AddLine(' // Failed to analyse unit "'+Fn+'". Error: "'+F.Err+'"')
  339. else
  340. begin
  341. if F.Resources then
  342. AddLine(' T.ResourceStrings := True;');
  343. U:=TStringList.Create;
  344. try
  345. U.AddStrings(F.IntfDependencies);
  346. U.AddStrings(F.ImplDependencies);
  347. if (U.Count>0) then
  348. begin
  349. AddLine(' with T.Dependencies do');
  350. AddLine(' begin');
  351. For J:=0 to U.Count-1 do
  352. AddLine(' AddUnit('''+U[j]+''');');
  353. AddLine(' end;');
  354. end;
  355. finally
  356. U.Free;
  357. end;
  358. end;
  359. end;
  360. WriteProgEnd;
  361. end;
  362. function TPas2FPMakeApp.SimulateCompile(E,EFrom: TUnitEntry): Boolean;
  363. Var
  364. I : Integer;
  365. begin
  366. Result:=True;
  367. if E.Done then
  368. begin
  369. Result:=Not E.Processing;
  370. if FVerbose then
  371. if Not Result then
  372. Writeln(StdErr,'Detected circular reference ',E.Name,' coming from ',EFrom.Name)
  373. else if Assigned(EFrom) then
  374. Writeln(StdErr,'Attempt to recompile ',E.Name,' coming from ',EFrom.Name)
  375. else
  376. Writeln(StdErr,'Attempt to recompile ',E.Name);
  377. exit;
  378. end;
  379. E.Done:=True;
  380. E.Processing:=True;
  381. For I:=0 to E.IntfDependencies.Count-1 do
  382. SimulateCompile(E.IntfDependencies.Objects[I] as TUnitEntry,E);
  383. For I:=0 to E.ImplDependencies.Count-1 do
  384. SimulateCompile(E.ImplDependencies.Objects[I] as TUnitEntry,E);
  385. E.Processing:=False;
  386. FUnits.Add(E.Name);
  387. end;
  388. procedure TPas2FPMakeApp.ProcessUnits;
  389. Var
  390. I,J,k : integer;
  391. Err : String;
  392. F : TUnitEntry;
  393. R : Boolean;
  394. begin
  395. For I:=0 to Funits.Count-1 do
  396. begin
  397. J:=FFiles.IndexOfEntry(FUnits[i]);
  398. Funits.Objects[i]:=FFiles[J];
  399. end;
  400. TStringList(FUnits).Sorted:=True;
  401. For I:=0 to FFiles.Count-1 do
  402. begin
  403. F:=FFiles[i];
  404. if not GetUnitProps(F.FileName,R,F.IntfDependencies,F.ImplDependencies,Err) then
  405. F.Err:=Err
  406. else
  407. F.Resources:=R;
  408. end;
  409. For I:=0 to FFiles.Count-1 do
  410. FFiles[i].CleanIntfDependencies(FVerbose);
  411. For I:=0 to FFiles.Count-1 do
  412. FFiles[i].CleanImplDependencies(FVerbose);
  413. TStringList(FUnits).Sorted:=False;
  414. FUnits.Clear;
  415. For I:=0 to FFiles.Count-1 do
  416. if FFiles[i].NoDependencies then
  417. begin
  418. FUnits.Add(FFiles[i].Name);
  419. FFiles[i].Done:=True;
  420. end;
  421. For I:=0 to FFiles.Count-1 do
  422. SimulateCompile(FFiles[i],Nil);
  423. // At this point, FUnits is in the order that the compiler should compile them.
  424. // Now we order the dependencies.
  425. For I:=0 to FFiles.Count-1 do
  426. FFiles[i].OrderDependencies(FUnits);
  427. end;
  428. procedure TPas2FPMakeApp.WriteSources;
  429. Var
  430. F : Text;
  431. begin
  432. AssignFile(F,FOutputFile);
  433. Rewrite(F);
  434. try
  435. Write(F,FSrc.Text);
  436. finally
  437. CloseFile(F);
  438. end;
  439. end;
  440. procedure TPas2FPMakeApp.DoRun;
  441. var
  442. ErrorMsg: String;
  443. begin
  444. // parse parameters
  445. if HasOption('h','help') or Not CheckParams then
  446. begin
  447. WriteHelp;
  448. Terminate;
  449. exit;
  450. end;
  451. ProcessUnits;
  452. CreateSources;
  453. WriteSources;
  454. // stop program loop
  455. Terminate;
  456. end;
  457. constructor TPas2FPMakeApp.Create(TheOwner: TComponent);
  458. begin
  459. inherited Create(TheOwner);
  460. StopOnException:=True;
  461. FFiles:=TUnitEntries.Create(TUnitEntry);
  462. FSrc:=TStringList.Create;
  463. FUnits:=TStringList.Create;
  464. FPackageName:='Your package name here';
  465. end;
  466. destructor TPas2FPMakeApp.Destroy;
  467. begin
  468. FreeAndNil(FFiles);
  469. FreeAndNil(FSrc);
  470. FreeAndNil(FUnits);
  471. inherited Destroy;
  472. end;
  473. procedure TPas2FPMakeApp.WriteHelp;
  474. begin
  475. { add your help code here }
  476. writeln('Usage: ',ExeName,' [options] file1 .. filen');
  477. Writeln('Where [options] is one or more of');
  478. Writeln(' -h This help');
  479. Writeln(' -p packagename Set package name');
  480. Writeln(' -i Use interface units only for checking dependencies');
  481. Writeln(' -o outputfile Set output filename (default is standard output)');
  482. Writeln(' -v Write diagnostic output to stderr');
  483. end;
  484. var
  485. Application: TPas2FPMakeApp;
  486. begin
  487. Application:=TPas2FPMakeApp.Create(nil);
  488. Application.Title:='Pascal to FPMake application';
  489. Application.Run;
  490. Application.Free;
  491. end.