fpcmmain.pp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224
  1. {
  2. $Id$
  3. Copyright (c) 2001 by Peter Vreman
  4. FPCMake - Main module
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$ifdef fpc}{$mode objfpc}{$endif}
  12. {$H+}
  13. unit fpcmmain;
  14. interface
  15. uses
  16. dos,
  17. {$ifdef Unix}
  18. {$ifdef VER1_0}
  19. linux,
  20. {$else}
  21. unix,
  22. {$endif}
  23. {$endif}
  24. sysutils,classes,
  25. fpcmdic;
  26. const
  27. Version='v1.99.0';
  28. Title='fpcmake '+Version;
  29. TitleDate=Title+' ['+{$ifdef fpc}{$i %DATE}{$else}'n/a'{$endif}+']';
  30. type
  31. TTarget=(
  32. t_linux,t_go32v2,t_win32,t_os2,t_freebsd
  33. );
  34. const
  35. TargetStr : array[TTarget] of string=(
  36. 'linux','go32v2','win32','os2','freebsd'
  37. );
  38. TargetSuffix : array[TTarget] of string=(
  39. '_linux','_go32v2','_win32','_os2','_freebsd'
  40. );
  41. type
  42. TKeyValueItem = class(TDictionaryItem)
  43. private
  44. FValue : string;
  45. public
  46. constructor Create(const k,v:string);
  47. property Value:string read FValue write FValue;
  48. end;
  49. TKeyValue = class(TDictionary)
  50. private
  51. function GetKey(const k:string):string;
  52. public
  53. procedure Add(const k,v:String);
  54. property Key[const s:string]:string read GetKey write Add;default;
  55. end;
  56. TFPCMakeSection = class(TDictionaryItem)
  57. private
  58. FList : TStringList;
  59. FDictionary : TKeyValue;
  60. procedure BuildIniDic(p:TDictionaryItem);
  61. procedure BuildMakefileDic(p:TDictionaryItem);
  62. function GetKey(const k:string):string;
  63. public
  64. constructor Create(const n:string);
  65. constructor CreateKeyValue(const n:string);
  66. destructor Destroy;override;
  67. procedure AddLine(const s:string);
  68. procedure AddKey(const k,v:string);
  69. procedure ParseIni;
  70. procedure BuildIni;
  71. procedure BuildMakefile;
  72. property Key[const s:string]:string read GetKey;default;
  73. property List:TStringList read FList;
  74. property Dictionary:TKeyValue read FDictionary;
  75. end;
  76. TTargetRequireList = array[ttarget] of TStringList;
  77. TFPCMakeVerbose = (FPCMakeError, FPCMakeInfo, FPCMakeDebug);
  78. TFPCMake = class
  79. private
  80. FStream : TStream;
  81. FFileName : string;
  82. FCommentChars : TSysCharSet;
  83. FEmptyLines : boolean;
  84. FSections : TDictionary;
  85. FPackageSec,
  86. FExportSec : TFPCMakeSection;
  87. FIsPackage : boolean;
  88. FPackageName,
  89. FPackageVersion : string;
  90. FRequireList : TTargetRequireList;
  91. FVariables : TKeyValue;
  92. procedure Init;
  93. procedure ParseSec(p:TDictionaryItem);
  94. procedure PrintSec(p:TDictionaryItem);
  95. procedure PrintDic(p:TDictionaryItem);
  96. function GetSec(const AName:string):TDictionaryItem;
  97. procedure LoadRequiredPackage(t:TTarget;const ReqName,ReqVersion:string);
  98. procedure LoadRequiredDir(t:TTarget;const MainPack,currdir,subdir:string);
  99. procedure LoadRequires(t:Ttarget;FromFPCMake:TFPCMake);
  100. function CopySection(Sec:TFPCMakeSection;Secname:string):TFPCMakeSection;
  101. protected
  102. VerboseIdent : string;
  103. procedure Verbose(lvl:TFPCMakeVerbose;const s:string);
  104. public
  105. constructor Create(const AFileName:string);
  106. constructor CreateFromStream(s:TStream;const AFileName:string);
  107. destructor Destroy;override;
  108. procedure LoadSections;
  109. procedure LoadMakefileFPC;
  110. procedure LoadPackageSection;
  111. procedure LoadRequireSection;
  112. function GetTargetRequires(t:TTarget):TStringList;
  113. function CheckLibcRequire:boolean;
  114. procedure CreateExportSection;
  115. procedure AddDefaultVariables;
  116. function SubstVariables(const s:string):string;
  117. function GetVariable(const inivar:string;dosubst:boolean):string;
  118. function SetVariable(const inivar,value:string;add:boolean):string;
  119. procedure Print;
  120. property Section[const s:string]:TDictionaryItem read GetSec;default;
  121. property RequireList:TTargetRequireList read FRequireList;
  122. property Variables:TKeyValue read FVariables;
  123. property IsPackage:boolean read FIsPackage;
  124. property PackageName:string read FPackageName;
  125. property PackageVersion:string read FPackageVersion;
  126. property PackageSec:TFPCMakeSection read FPackageSec;
  127. property ExportSec:TFPCMakeSection read FExportSec;
  128. property CommentChars:TSysCharSet read FCommentChars write FCommentChars;
  129. property EmptyLines:Boolean read FEmptyLines write FEmptyLines;
  130. end;
  131. function posidx(const substr,s : string;idx:integer):integer;
  132. function GetToken(var s:string):string;
  133. implementation
  134. resourcestring
  135. s_not_list_sec='Not a list section "%s"';
  136. s_not_key_value_sec='Not a key-value section "%s"';
  137. s_err_section_start='%s:%d: Wrong section start';
  138. s_err_not_key_value='Parse error key=value excepted: "%s"';
  139. s_err_no_section='%s:%d: Entries without section';
  140. s_no_package_name='No package name set';
  141. s_no_package_version='No package version set';
  142. s_err_require_format='Wrong require format "%s"';
  143. {****************************************************************************
  144. Helpers
  145. ****************************************************************************}
  146. Function PathExists ( F : String) : Boolean;
  147. Var
  148. Info : TSearchRec;
  149. begin
  150. if F[Length(f)] in ['/','\'] then
  151. Delete(f,length(f),1);
  152. PathExists:=(findfirst(F,fareadonly+faarchive+fahidden+fadirectory,info)=0) and
  153. ((info.attr and fadirectory)=fadirectory);
  154. findclose(Info);
  155. end;
  156. Function PathOrFileExists ( F : String) : Boolean;
  157. Var
  158. Info : Dos.SearchRec;
  159. begin
  160. if F[Length(f)] in ['/','\'] then
  161. Delete(f,length(f),1);
  162. dos.findfirst(f,fareadonly+faarchive+fahidden+fadirectory,info);
  163. PathOrFileExists:=(Doserror=0);
  164. dos.findclose(Info);
  165. end;
  166. function posidx(const substr,s : string;idx:integer):integer;
  167. var
  168. i,j : integer;
  169. e : boolean;
  170. begin
  171. i:=idx;
  172. j:=0;
  173. e:=(length(SubStr)>0);
  174. while e and (i<=Length(s)-Length(SubStr)) do
  175. begin
  176. inc(i);
  177. if (SubStr[1]=s[i]) and (Substr=Copy(s,i,Length(SubStr))) then
  178. begin
  179. j:=i;
  180. e:=false;
  181. end;
  182. end;
  183. PosIdx:=j;
  184. end;
  185. function GetToken(var s:string):string;
  186. var
  187. i : integer;
  188. begin
  189. s:=Trim(s);
  190. i:=pos(' ',s);
  191. if i=0 then
  192. begin
  193. Result:=s;
  194. s:='';
  195. end
  196. else
  197. begin
  198. Result:=Copy(s,1,i-1);
  199. Delete(s,1,i);
  200. end;
  201. end;
  202. {****************************************************************************
  203. TKeyValueItem
  204. ****************************************************************************}
  205. constructor TKeyValueItem.Create(const k,v:string);
  206. begin
  207. inherited Create(k);
  208. value:=v;
  209. end;
  210. {****************************************************************************
  211. TKeyValue
  212. ****************************************************************************}
  213. function TKeyValue.GetKey(const k:string):string;
  214. var
  215. p : TKeyValueItem;
  216. begin
  217. p:=TKeyValueItem(Search(k));
  218. if p=nil then
  219. GetKey:=''
  220. else
  221. GetKey:=p.Value;
  222. end;
  223. procedure TKeyValue.Add(const k,v:string);
  224. var
  225. p : TKeyValueItem;
  226. begin
  227. p:=TKeyValueItem(Search(k));
  228. if p=nil then
  229. begin
  230. p:=TKeyValueItem.Create(k,v);
  231. Insert(p);
  232. end
  233. else
  234. p.Value:=v;
  235. end;
  236. {****************************************************************************
  237. TFPCMakeSection
  238. ****************************************************************************}
  239. constructor TFPCMakeSection.Create(const n:string);
  240. begin
  241. inherited Create(n);
  242. FList:=TStringList.Create;
  243. FDictionary:=nil;
  244. end;
  245. constructor TFPCMakeSection.CreateKeyValue(const n:string);
  246. begin
  247. inherited Create(n);
  248. FList:=nil;
  249. FDictionary:=TKeyValue.Create;
  250. end;
  251. destructor TFPCMakeSection.Destroy;
  252. begin
  253. inherited Destroy;
  254. FList.Free;
  255. FDictionary.Free;
  256. end;
  257. procedure TFPCMakeSection.AddLine(const s:string);
  258. begin
  259. if FList=nil then
  260. raise Exception.Create(Format(s_not_list_sec,[Name]));
  261. FList.Add(s);
  262. end;
  263. procedure TFPCMakeSection.AddKey(const k,v:string);
  264. begin
  265. if FDictionary=nil then
  266. raise Exception.Create(Format(s_not_key_value_sec,[Name]));
  267. { Don't add empty values }
  268. if v<>'' then
  269. FDictionary.Add(k,v);
  270. end;
  271. function TFPCMakeSection.GetKey(const k:string):string;
  272. begin
  273. if FDictionary=nil then
  274. raise Exception.Create(Format(s_not_key_value_sec,[Name]));
  275. GetKey:=FDictionary[k];
  276. end;
  277. procedure TFPCMakeSection.ParseIni;
  278. var
  279. p : TKeyValueItem;
  280. i,j,len,maxi : integer;
  281. s,newkey,value : string;
  282. begin
  283. { If already processed skip }
  284. if assigned(FDictionary) then
  285. exit;
  286. { Don't process rules section }
  287. if (Name='prerules') or (Name='rules') then
  288. exit;
  289. { Parse the section }
  290. FDictionary:=TKeyValue.Create;
  291. { Parse the list }
  292. maxi:=FList.Count;
  293. i:=0;
  294. while (i<maxi) do
  295. begin
  296. s:=Trim(FList[i]);
  297. len:=Length(s);
  298. { Concat lines ending with \ }
  299. while s[len]='\' do
  300. begin
  301. Delete(s,len,1);
  302. if i+1<maxi then
  303. begin
  304. inc(i);
  305. s:=s+Trim(FList[i]);
  306. len:=Length(s);
  307. end;
  308. end;
  309. { Parse key=value line }
  310. j:=0;
  311. while (j<len) and (s[j+1] in ['A'..'Z','a'..'z','0'..'9','_']) do
  312. inc(j);
  313. NewKey:=Copy(s,1,j);
  314. While (j<len) and (s[j+1] in [' ',#9]) do
  315. inc(j);
  316. inc(j);
  317. if s[j]<>'=' then
  318. Raise Exception.Create(Format(s_err_not_key_value,[s]));
  319. While (j<len) and (s[j+1] in [' ',#9]) do
  320. inc(j);
  321. if j=len then
  322. Raise Exception.Create(Format(s_err_not_key_value,[s]));
  323. Value:=Copy(s,j+1,len-j);
  324. p:=TKeyValueItem(FDictionary.Search(NewKey));
  325. { Concat values if key already exists }
  326. if assigned(p) then
  327. p.Value:=p.Value+' '+Value
  328. else
  329. FDictionary.Add(NewKey,Value);
  330. inc(i);
  331. end;
  332. { List is not used anymore }
  333. FList.Free;
  334. FList:=nil;
  335. end;
  336. procedure TFPCMakeSection.BuildIniDic(p:TDictionaryItem);
  337. begin
  338. with TKeyValueItem(p) do
  339. begin
  340. FList.Add(Name+'='+Value);
  341. end;
  342. end;
  343. procedure TFPCMakeSection.BuildIni;
  344. begin
  345. if assigned(FList) then
  346. exit;
  347. FList:=TStringList.Create;
  348. FDictionary.Foreach(@BuildIniDic);
  349. FDictionary.Free;
  350. FDictionary:=nil;
  351. end;
  352. procedure TFPCMakeSection.BuildMakefileDic(p:TDictionaryItem);
  353. begin
  354. FList.Add(Uppercase(Name+'_'+TKeyValueItem(p).Name)+'='+TKeyValueItem(p).Value);
  355. end;
  356. procedure TFPCMakeSection.BuildMakefile;
  357. begin
  358. if assigned(FList) then
  359. exit;
  360. FList:=TStringList.Create;
  361. FDictionary.Foreach(@BuildMakefileDic);
  362. FDictionary.Free;
  363. FDictionary:=nil;
  364. end;
  365. {****************************************************************************
  366. TFPCMake
  367. ****************************************************************************}
  368. constructor TFPCMake.Create(const AFileName:string);
  369. begin
  370. FFileName:=AFileName;
  371. FStream:=nil;
  372. Init;
  373. end;
  374. constructor TFPCMake.CreateFromStream(s:TStream;const AFileName:string);
  375. begin
  376. FFileName:=AFileName;
  377. FStream:=s;
  378. Init;
  379. end;
  380. procedure TFPCMake.Init;
  381. var
  382. t : ttarget;
  383. begin
  384. FSections:=TDictionary.Create;
  385. for t:=low(ttarget) to high(ttarget) do
  386. FRequireList[t]:=TStringList.Create;
  387. FVariables:=TKeyValue.Create;
  388. FCommentChars:=[';','#'];
  389. FEmptyLines:=false;
  390. FIsPackage:=false;
  391. FPackageName:='';
  392. FPackageVersion:='';
  393. FPackageSec:=nil;
  394. FExportSec:=nil;
  395. VerboseIdent:='';
  396. end;
  397. destructor TFPCMake.Destroy;
  398. var
  399. t : ttarget;
  400. begin
  401. FSections.Free;
  402. for t:=low(ttarget) to high(ttarget) do
  403. FRequireList[t].Free;
  404. FVariables.Free;
  405. end;
  406. procedure TFPCMake.LoadSections;
  407. var
  408. SLInput : TStringList;
  409. i,j,n : integer;
  410. s,
  411. SecName : string;
  412. CurrSec : TFPCMakeSection;
  413. begin
  414. try
  415. SLInput:=TStringList.Create;
  416. if assigned(FStream) then
  417. SLInput.LoadFromStream(FStream)
  418. else
  419. SLInput.LoadFromFile(FFileName);
  420. { Load Input into sections list }
  421. n:=SLInput.Count;
  422. i:=0;
  423. while (i<n) do
  424. begin
  425. s:=Trim(SLInput[i]);
  426. if (EmptyLines and (s='')) or
  427. ((s<>'') and not(s[1] in FCommentChars)) then
  428. begin
  429. { section start? }
  430. if (s<>'') and (s[1]='[') then
  431. begin
  432. j:=pos(']',s);
  433. if j=0 then
  434. raise Exception.Create(Format(s_err_section_start,[FFileName,i]));
  435. SecName:=Copy(s,2,j-2);
  436. CurrSec:=TFPCMakeSection(FSections[SecName]);
  437. if CurrSec=nil then
  438. CurrSec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.Create(SecName)));
  439. end
  440. else
  441. begin
  442. if CurrSec=nil then
  443. raise Exception.Create(Format(s_err_no_section,[FFileName,i]));
  444. { Insert string without spaces stripped }
  445. CurrSec.AddLine(SLInput[i]);
  446. end;
  447. end;
  448. inc(i);
  449. end;
  450. finally
  451. SLInput.Free;
  452. end;
  453. end;
  454. function TFPCMake.CopySection(Sec:TFPCMakeSection;Secname:string):TFPCMakeSection;
  455. begin
  456. Result:=TFPCMakeSection(FSections[SecName]);
  457. if Sec=Nil then
  458. exit;
  459. if assigned(Result) then
  460. Result.BuildIni
  461. else
  462. Result:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.Create(SecName)));
  463. Sec.BuildIni;
  464. Result.List.AddStrings(Sec.List);
  465. Result.ParseIni;
  466. Sec.ParseIni;
  467. end;
  468. procedure TFPCMake.LoadMakefileFPC;
  469. begin
  470. LoadSections;
  471. { Parse all sections }
  472. FSections.Foreach(@ParseSec);
  473. { Add some default variables like FPCDIR, UNITSDIR }
  474. AddDefaultVariables;
  475. { Load package section }
  476. LoadPackageSection;
  477. LoadRequireSection;
  478. end;
  479. procedure TFPCMake.Verbose(lvl:TFPCMakeVerbose;const s:string);
  480. begin
  481. writeln(VerboseIdent,s);
  482. end;
  483. procedure TFPCMake.LoadPackageSection;
  484. var
  485. s : string;
  486. begin
  487. { Get package info from package section }
  488. FPackageSec:=TFPCMakeSection(FSections['package']);
  489. if FPackageSec=nil then
  490. exit;
  491. { Parse the section to key=value pairs }
  492. FPackageSec.ParseIni;
  493. { Are we a subpart of a package, then load that package }
  494. s:=FPackageSec['main'];
  495. if s<>'' then
  496. begin
  497. SetVariable('package_name',s,false);
  498. FPackageName:=s;
  499. end
  500. else
  501. begin
  502. { mandatory name }
  503. FPackageName:=FPackageSec['name'];
  504. if FPackageName='' then
  505. Raise Exception.Create(s_no_package_name);
  506. { mandatory version }
  507. FPackageVersion:=FPackageSec['version'];
  508. if FPackageVersion='' then
  509. Raise Exception.Create(s_no_package_version);
  510. FIsPackage:=true;
  511. { Set the ExportSec }
  512. FExportSec:=TFPCMakeSection(FSections[Lowercase(FPackageName)]);
  513. end;
  514. end;
  515. procedure TFPCMake.CreateExportSection;
  516. var
  517. t : TTarget;
  518. begin
  519. { Don't create a section twice }
  520. if FExportSec<>nil then
  521. exit;
  522. { Look if we've already an own section, else create a new
  523. key-value section }
  524. FExportSec:=TFPCMakeSection(FSections[LowerCase(FPackageName)]);
  525. if FExportSec=nil then
  526. FExportSec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.CreateKeyValue(LowerCase(FPackageName))));
  527. { Add default the values to the export section }
  528. FExportSec.AddKey('name',FPackageName);
  529. FExportSec.AddKey('version',FPackageVersion);
  530. { Add required packages }
  531. for t:=low(TTarget) to high(TTarget) do
  532. FExportSec.AddKey('require'+TargetSuffix[t],FPackageSec['require'+TargetSuffix[t]]);
  533. { Unit dir }
  534. {FExportSec.AddKey('unitdir','$(UNITSDIR)/'+Lowercase(PackageName));}
  535. end;
  536. procedure TFPCMake.LoadRequiredPackage(t:TTarget;const ReqName,ReqVersion:string);
  537. function TryFile(const fn:string):boolean;
  538. var
  539. ReqFPCMake : TFPCMake;
  540. begin
  541. TryFile:=false;
  542. if FileExists(fn) then
  543. begin
  544. VerboseIdent:=VerboseIdent+' ';
  545. Verbose(FPCMakeDebug,'Package '+ReqName+': '+fn);
  546. ReqFPCMake:=TFPCMake.Create(fn);
  547. ReqFPCMake.LoadSections;
  548. ReqFPCMake.LoadPackageSection;
  549. { Check package name and version }
  550. if LowerCase(ReqFPCMake.PackageName)<>ReqName then
  551. raise Exception.Create('s_wrong_package_name');
  552. if (ReqVersion<>'') and (ReqFPCMake.PackageVersion<ReqVersion) then
  553. raise Exception.Create('s_wrong_package_version');
  554. { First load the requirements of this package }
  555. LoadRequires(t,ReqFPCMake);
  556. { Get a copy of the package section }
  557. CopySection(ReqFPCMake.PackageSec,ReqName+'_package');
  558. { Get a copy of the export section }
  559. CopySection(ReqFPCMake.ExportSec,ReqName);
  560. { Get a copy of the require section }
  561. CopySection(TFPCMakeSection(ReqFPCMake['require']),ReqName+'_require');
  562. { Free }
  563. ReqFPCMake.Free;
  564. Delete(VerboseIdent,1,2);
  565. TryFile:=true;
  566. end;
  567. end;
  568. var
  569. s : string;
  570. begin
  571. s:=SubstVariables('$(wildcard $(addsuffix /'+ReqName+'/Makefile.fpc,$(FPCDIR)) $(addsuffix /'+ReqName+'/Makefile.fpc,$(PACKAGESDIR)) $(addsuffix /'+ReqName+'/Makefile.fpc,$(REQUIRE_PACKAGESDIR)))');
  572. if TryFile(s) then
  573. exit;
  574. Raise Exception.Create('s_package_not_found '+Reqname);
  575. end;
  576. procedure TFPCMake.LoadRequiredDir(t:TTarget;const MainPack,currdir,subdir:string);
  577. var
  578. ReqFPCMake : TFPCMake;
  579. s : string;
  580. begin
  581. VerboseIdent:=VerboseIdent+' ';
  582. Verbose(FPCMakeDebug,'Subdir: '+currdir+subdir+'/Makefile.fpc');
  583. if not FileExists(currdir+subdir+'/Makefile.fpc') then
  584. Raise Exception.Create('s_no_makefile.fpc_found');
  585. { Process Makefile.fpc }
  586. ReqFPCMake:=TFPCMake.Create(currdir+subdir+'/Makefile.fpc');
  587. ReqFPCMake.LoadSections;
  588. ReqFPCMake.LoadPackageSection;
  589. { Are we a subpackage? }
  590. if (ReqFPCMake.GetVariable('package_name',false)<>MainPack) then
  591. begin
  592. ReqFPCMake.Free;
  593. Delete(VerboseIdent,1,2);
  594. exit;
  595. end;
  596. { Load the requirements of this package }
  597. LoadRequires(t,ReqFPCMake);
  598. { Add the current requirements to our parents requirements }
  599. s:=ReqFPCMake.GetVariable('require_packages',true)+' '+ReqFPCMake.GetVariable('require_packages'+targetsuffix[t],true);
  600. SetVariable('require_packages'+targetsuffix[t],s,true);
  601. if ReqFPCMake.GetVariable('require_libc',false)<>'' then
  602. SetVariable('require_libc','y',false);
  603. { Free }
  604. ReqFPCMake.Free;
  605. Delete(VerboseIdent,1,2);
  606. end;
  607. procedure TFPCMake.LoadRequires(t:Ttarget;FromFPCMake:TFPCMake);
  608. var
  609. s,
  610. ReqDir,
  611. ReqName,
  612. ReqVersion : string;
  613. i,j : integer;
  614. begin
  615. { packages }
  616. s:=FromFPCMake.GetVariable('require_packages',true)+' '+FromFPCMake.GetVariable('require_packages'+TargetSuffix[t],true);
  617. Verbose(FPCMakeDebug,'Required packages for '+TargetStr[t]+': '+s);
  618. repeat
  619. reqname:=GetToken(s);
  620. if reqname='' then
  621. break;
  622. i:=Pos('(',ReqName);
  623. if i>0 then
  624. begin
  625. j:=Pos(')',ReqName);
  626. if (i=1) or (j=0) then
  627. Raise Exception.Create(Format(s_err_require_format,[ReqName]));
  628. ReqVersion:=Copy(ReqName,i+1,j-i-1);
  629. ReqName:=Copy(ReqName,1,i-1);
  630. end
  631. else
  632. ReqVersion:='';
  633. { We only use lowercase names }
  634. ReqName:=Lowercase(ReqName);
  635. { Already loaded ? }
  636. if (RequireList[t].IndexOf(ReqName)=-1) then
  637. begin
  638. LoadRequiredPackage(t,ReqName,ReqVersion);
  639. RequireList[t].Add(ReqName);
  640. end;
  641. until false;
  642. { sub dirs }
  643. s:=FromFPCMake.GetVariable('target_dirs',true)+' '+FromFPCMake.GetVariable('target_dirs'+TargetSuffix[t],true);
  644. Verbose(FPCMakeDebug,'Required dirs for '+TargetStr[t]+': '+s);
  645. repeat
  646. reqdir:=GetToken(s);
  647. if reqdir='' then
  648. break;
  649. LoadRequiredDir(t,FromFPCMake.FPackageName,ExtractFilePath(FromFPCMake.FFileName),ReqDir)
  650. until false;
  651. end;
  652. procedure TFPCMake.LoadRequireSection;
  653. function CheckVar(const s:string):boolean;
  654. var
  655. t : ttarget;
  656. begin
  657. result:=false;
  658. if GetVariable(s,false)<>'' then
  659. begin
  660. result:=true;
  661. exit;
  662. end;
  663. for t:=low(ttarget) to high(ttarget) do
  664. begin
  665. if GetVariable(s+targetsuffix[t],false)<>'' then
  666. begin
  667. result:=true;
  668. exit;
  669. end;
  670. end;
  671. end;
  672. var
  673. s : string;
  674. t : ttarget;
  675. begin
  676. { Maybe add an implicit rtl dependency if there is something
  677. to compile }
  678. s:=GetVariable('require_packages',false);
  679. if (GetVariable('require_nortl',false)='') and
  680. (CheckVar('target_programs') or
  681. CheckVar('target_units') or
  682. CheckVar('target_examples')) and
  683. (Pos('rtl(',s)=0) then
  684. begin
  685. s:='rtl '+s;
  686. SetVariable('require_packages',s,false);
  687. end;
  688. { Load recursively all required packages starting with this Makefile.fpc }
  689. for t:=low(TTarget) to high(TTarget) do
  690. LoadRequires(t,self);
  691. end;
  692. function TFPCMake.GetTargetRequires(t:TTarget):TStringList;
  693. var
  694. ReqSec : TFPCMakeSection;
  695. ReqList : TStringList;
  696. procedure AddReqSec(t:TTarget;Sec:TFPCMakeSection);
  697. var
  698. s,
  699. ReqName : string;
  700. RSec : TFPCMakeSection;
  701. i : integer;
  702. begin
  703. s:=Sec['packages']+' '+Sec['packages'+TargetSuffix[t]];
  704. repeat
  705. ReqName:=GetToken(s);
  706. if ReqName='' then
  707. break;
  708. i:=Pos('(',ReqName);
  709. if i>0 then
  710. ReqName:=Copy(ReqName,1,i-1);
  711. { We only use lowercase names }
  712. ReqName:=Lowercase(ReqName);
  713. { Already loaded ? }
  714. if (ReqList.IndexOf(ReqName)=-1) then
  715. begin
  716. RSec:=TFPCMakeSection(FSections[ReqName+'_require']);
  717. if assigned(RSec) then
  718. AddReqSec(t,RSec);
  719. ReqList.Add(ReqName);
  720. end;
  721. until false;
  722. end;
  723. begin
  724. ReqList:=TStringList.Create;
  725. ReqSec:=TFPCMakeSection(FSections['require']);
  726. if assigned(ReqSec) then
  727. AddReqSec(t,ReqSec);
  728. Verbose(FPCMakeInfo,TargetStr[t]+' requires: '+ReqList.CommaText);
  729. GetTargetRequires:=ReqList;
  730. end;
  731. function TFPCMake.CheckLibcRequire:boolean;
  732. var
  733. i : integer;
  734. RSec : TFPCMakeSection;
  735. t : ttarget;
  736. begin
  737. Result:=false;
  738. if GetVariable('require_libc',false)<>'' then
  739. begin
  740. Result:=true;
  741. exit;
  742. end;
  743. for t:=low(ttarget) to high(ttarget) do
  744. begin
  745. for i:=0 to RequireList[t].Count-1 do
  746. begin
  747. RSec:=TFPCMakeSection(FSections[RequireList[t][i]+'_require']);
  748. if assigned(RSec) then
  749. begin
  750. if RSec['libc']<>'' then
  751. begin
  752. Result:=true;
  753. exit;
  754. end;
  755. end;
  756. end;
  757. end;
  758. end;
  759. procedure TFPCMake.AddDefaultVariables;
  760. var
  761. hs,s : string;
  762. begin
  763. { Already set FPCDIR }
  764. hs:='';
  765. s:=GetVariable('FPCDIR',false);
  766. if s<>'' then
  767. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  768. { Load from environment }
  769. if hs='' then
  770. begin
  771. s:=GetEnv('FPCDIR');
  772. if s<>'' then
  773. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  774. end;
  775. { default_fpcdir }
  776. if hs='' then
  777. begin
  778. s:=GetVariable('default_fpcdir',true);
  779. if s<>'' then
  780. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  781. end;
  782. { OS defaults }
  783. if hs='' then
  784. begin
  785. {$ifdef UNIX}
  786. if FileExists('/usr/local/bin/ppc386') then
  787. begin
  788. s:=ExtractFilePath(ReadLink('/usr/local/bin/ppc386'));
  789. if s<>'' then
  790. begin
  791. if s[length(s)]='/' then
  792. delete(s,length(s),1);
  793. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  794. end;
  795. end;
  796. if hs='' then
  797. begin
  798. if FileExists('/usr/bin/ppc386') then
  799. begin
  800. s:=ExtractFilePath(ReadLink('/usr/bin/ppc386'));
  801. if s<>'' then
  802. begin
  803. if s[length(s)]='/' then
  804. delete(s,length(s),1);
  805. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  806. end;
  807. end;
  808. end;
  809. {$else UNIX}
  810. hs:=ExtractFilePath(FSearch('ppc386.exe',getenv('PATH')));
  811. if hs<>'' then
  812. begin
  813. s:=hs+'/..';
  814. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  815. if hs='' then
  816. begin
  817. s:=s+'/..';
  818. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  819. end;
  820. end;
  821. if hs='' then
  822. s:='c:/pp';
  823. {$endif UNIX}
  824. end;
  825. SetVariable('FPCDIR',s,false);
  826. { PACKAGESDIR }
  827. if GetVariable('PACKAGESDIR',false)='' then
  828. SetVariable('PACKAGESDIR','$(FPCDIR)/packages',false);
  829. { UNITSDIR }
  830. if GetVariable('UNITSDIR',false)='' then
  831. SetVariable('UNITSDIR','$(FPCDIR)/units',false);
  832. Verbose(FPCMakeDebug,'Globals:');
  833. Variables.Foreach(@PrintDic);
  834. end;
  835. function TFPCMake.SubstVariables(const s:string):string;
  836. function Expect(var s:string;c:char):boolean;
  837. begin
  838. if (s<>'') and (s[1]=c) then
  839. begin
  840. Delete(s,1,1);
  841. Result:=true;
  842. end
  843. else
  844. begin
  845. Verbose(FPCMakeError,'Error "'+c+'" expected');
  846. Result:=false;
  847. end;
  848. end;
  849. function GetVar(var s:string;untilc:char):string;
  850. var
  851. i,j,k : integer;
  852. first : boolean;
  853. func,
  854. tok,s1,s2,s3 : string;
  855. Sec : TFPCMakeSection;
  856. begin
  857. Result:='';
  858. repeat
  859. j:=Pos(untilc,s);
  860. if j=0 then
  861. j:=Length(s)+1;
  862. i:=Pos('$(',s);
  863. if (j<i) or (i=0) then
  864. break;
  865. Result:=Result+Copy(s,1,i-1);
  866. Delete(s,1,i+1);
  867. { Maybe Function ? }
  868. j:=Pos(')',s);
  869. if j=0 then
  870. j:=Length(s)+1;
  871. i:=Pos(' ',s);
  872. if i=0 then
  873. i:=Length(s)+1;
  874. if i<j then
  875. begin
  876. { It's a function }
  877. Func:=Copy(s,1,i-1);
  878. //writeln('func: ',func);
  879. { $(wildcard(<list>) }
  880. if Func='wildcard' then
  881. begin
  882. Delete(s,1,9);
  883. s1:=GetVar(s,')');
  884. Expect(s,')');
  885. first:=true;
  886. repeat
  887. tok:=GetToken(s1);
  888. if tok='' then
  889. break;
  890. if PathOrFileExists(tok) then
  891. begin
  892. if not first then
  893. Result:=Result+' '
  894. else
  895. first:=false;
  896. Result:=Result+tok;
  897. end;
  898. until false;
  899. end
  900. { $(addprefix(<suffix>,<list>) }
  901. else if Func='addprefix' then
  902. begin
  903. Delete(s,1,10);
  904. s1:=GetVar(s,',');
  905. if Expect(s,',') then
  906. begin
  907. s2:=GetVar(s,')');
  908. Expect(s,')');
  909. end;
  910. first:=true;
  911. repeat
  912. tok:=GetToken(s2);
  913. if tok='' then
  914. break;
  915. if not first then
  916. Result:=Result+' '
  917. else
  918. first:=false;
  919. Result:=Result+s1+tok;
  920. until false;
  921. end
  922. { $(addsuffix(<suffix>,<list>) }
  923. else if Func='addsuffix' then
  924. begin
  925. Delete(s,1,10);
  926. s1:=GetVar(s,',');
  927. if Expect(s,',') then
  928. begin
  929. s2:=GetVar(s,')');
  930. Expect(s,')');
  931. end;
  932. first:=true;
  933. repeat
  934. tok:=GetToken(s2);
  935. if tok='' then
  936. break;
  937. if not first then
  938. Result:=Result+' '
  939. else
  940. first:=false;
  941. Result:=Result+tok+s1;
  942. until false;
  943. end;
  944. end
  945. else
  946. begin
  947. s2:=Copy(s,1,j-1);
  948. Delete(s,1,j);
  949. k:=pos('_',s2);
  950. if k>0 then
  951. begin
  952. s3:=LowerCase(Copy(s2,k+1,Length(s2)-k));
  953. s2:=LowerCase(Copy(s2,1,k-1));
  954. Sec:=TFPCMakeSection(Section[s2]);
  955. if assigned(Sec) then
  956. s2:=Sec[s3]
  957. else
  958. s2:='';
  959. end
  960. else
  961. s2:=Variables[s2];
  962. Insert(s2,s,1);
  963. end;
  964. until false;
  965. Result:=Result+Copy(s,1,j-1);
  966. Delete(s,1,j-1);
  967. end;
  968. var
  969. s1 : string;
  970. begin
  971. //writeln('S: ',s);
  972. s1:=s;
  973. Result:=GetVar(s1,#0);
  974. //writeln('R: ',result);
  975. end;
  976. function TFPCMake.GetVariable(const inivar:string;dosubst:boolean):string;
  977. var
  978. Sec : TFPCMakeSection;
  979. Dic : TKeyValue;
  980. i : integer;
  981. begin
  982. Result:='';
  983. i:=Pos('_',inivar);
  984. if i<>0 then
  985. begin
  986. Sec:=TFPCMakeSection(FSections[Copy(Inivar,1,i-1)]);
  987. if assigned(Sec) then
  988. begin
  989. if not assigned(Sec.Dictionary) then
  990. Sec.ParseIni;
  991. Dic:=TKeyValue(Sec.Dictionary);
  992. Result:=Dic[Copy(IniVar,i+1,Length(IniVar)-i)];
  993. end
  994. else
  995. exit;
  996. end
  997. else
  998. Result:=Variables[IniVar];
  999. { Substition asked ? }
  1000. if dosubst then
  1001. Result:=SubstVariables(Result);
  1002. end;
  1003. function TFPCMake.SetVariable(const inivar,value:string;add:boolean):string;
  1004. var
  1005. Sec : TFPCMakeSection;
  1006. P : TKeyValueItem;
  1007. i : integer;
  1008. key : string;
  1009. begin
  1010. Result:='';
  1011. i:=Pos('_',inivar);
  1012. if i<>0 then
  1013. begin
  1014. Sec:=TFPCMakeSection(FSections[Copy(Inivar,1,i-1)]);
  1015. if Sec=nil then
  1016. Sec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.CreateKeyValue(Copy(Inivar,1,i-1))));
  1017. key:=Copy(IniVar,i+1,Length(IniVar)-i);
  1018. p:=TKeyValueItem(Sec.Dictionary.Search(Key));
  1019. if assigned(p) then
  1020. begin
  1021. if Add then
  1022. p.Value:=p.Value+' '+Value
  1023. else
  1024. p.Value:=Value;
  1025. end
  1026. else
  1027. TKeyValue(Sec.Dictionary).Add(key,value);
  1028. end
  1029. else
  1030. Variables[IniVar]:=value;
  1031. end;
  1032. procedure TFPCMake.ParseSec(p:TDictionaryItem);
  1033. begin
  1034. TFPCMakeSection(p).ParseIni;
  1035. end;
  1036. procedure TFPCMake.PrintSec(p:TDictionaryItem);
  1037. var
  1038. i : integer;
  1039. begin
  1040. with TFPCMakeSection(p) do
  1041. begin
  1042. Verbose(FPCMakeDebug,'['+Name+']');
  1043. if assigned(FList) then
  1044. begin
  1045. Verbose(FPCMakeDebug,' List:');
  1046. for i:=0 to FList.Count-1 do
  1047. Verbose(FPCMakeDebug,' "'+FList[i]+'"');
  1048. if assigned(FDictionary) then
  1049. Verbose(FPCMakeDebug,'');
  1050. end;
  1051. if assigned(FDictionary) then
  1052. begin
  1053. Verbose(FPCMakeDebug,' Dictionary:');
  1054. FDictionary.Foreach(@PrintDic);
  1055. end;
  1056. end;
  1057. end;
  1058. procedure TFPCMake.PrintDic(p:TDictionaryItem);
  1059. begin
  1060. with TKeyValueItem(p) do
  1061. begin
  1062. Verbose(FPCMakeDebug,' '+name+' = "'+value+'"');
  1063. end;
  1064. end;
  1065. procedure TFPCMake.Print;
  1066. begin
  1067. { global variables }
  1068. Verbose(FPCMakeDebug,'[global variables]');
  1069. Verbose(FPCMakeDebug,' Dictionary:');
  1070. Variables.Foreach(@PrintDic);
  1071. { sections }
  1072. FSections.Foreach(@PrintSec);
  1073. end;
  1074. function TFPCMake.GetSec(const AName:string):TDictionaryItem;
  1075. begin
  1076. GetSec:=FSections.Search(AName);
  1077. end;
  1078. end.
  1079. {
  1080. $Log$
  1081. Revision 1.5 2001-02-22 21:11:24 peter
  1082. * fpcdir detection added
  1083. * fixed loading of variables in fpcmake itself
  1084. Revision 1.4 2001/02/05 20:44:56 peter
  1085. * variable substition like GNU Make. wildcard,addprefix,addsuffix
  1086. already implemented
  1087. Revision 1.3 2001/02/01 22:00:10 peter
  1088. * default.fpcdir is back
  1089. * subdir requirement checking works, but not very optimal yet as
  1090. it can load the same Makefile.fpc multiple times
  1091. Revision 1.2 2001/01/29 21:49:10 peter
  1092. * lot of updates
  1093. Revision 1.1 2001/01/24 21:59:36 peter
  1094. * first commit of new fpcmake
  1095. }