fpcmmain.pp 36 KB

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