fpcmmain.pp 37 KB

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