fpcmmain.pp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. {
  2. Copyright (c) 2001 by Peter Vreman
  3. FPCMake - Main module
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {$ifdef fpc}{$mode objfpc}{$endif}
  11. {$H+}
  12. unit fpcmmain;
  13. interface
  14. uses
  15. dos,
  16. {$ifdef Unix}
  17. {$ifdef VER1_0}
  18. {$ifdef linux}
  19. {$ifndef BSD}
  20. linux,
  21. {$endif}
  22. {$endif}
  23. {$ifdef BSD}
  24. Linux,
  25. {$endif}
  26. {$else}
  27. baseunix,
  28. unix,
  29. {$endif}
  30. {$endif}
  31. sysutils,classes,
  32. fpcmdic;
  33. {$ifdef BEOS}
  34. {$define NO_UNIX_UNIT}
  35. {$endif}
  36. {$ifdef SOLARIS}
  37. {$define NO_UNIX_UNIT}
  38. {$endif}
  39. {$ifdef QNX}
  40. {$define NO_UNIX_UNIT}
  41. {$endif}
  42. {$ifdef OS2}
  43. {$define LIMIT83}
  44. {$endif}
  45. {$ifdef EMX}
  46. {$define LIMIT83}
  47. {$endif}
  48. {$ifdef GO32V2}
  49. {$define LIMIT83}
  50. {$endif}
  51. const
  52. Version='2.0.0';
  53. Title='FPCMake Version '+Version;
  54. TitleDate=Title+' ['+{$ifdef fpc}{$i %DATE}{$else}'n/a'{$endif}+']';
  55. type
  56. TCpu=(
  57. c_i386,c_m68k,c_powerpc,c_sparc,c_x86_64,c_arm,c_powerpc64
  58. );
  59. TOS=(
  60. o_linux,o_go32v2,o_win32,o_os2,o_freebsd,o_beos,o_netbsd,
  61. o_amiga,o_atari, o_solaris, o_qnx, o_netware, o_openbsd,o_wdosx,
  62. o_palmos,o_macos,o_darwin,o_emx,o_watcom,o_morphos,o_netwlibc,
  63. o_win64,o_wince,o_gba
  64. );
  65. TTargetSet=array[tcpu,tos] of boolean;
  66. const
  67. CpuStr : array[TCpu] of string=(
  68. 'i386','m68k','powerpc','sparc','x86_64','arm','powerpc64'
  69. );
  70. CpuSuffix : array[TCpu] of string=(
  71. '_i386','_m68k','_powerpc','_sparc','_x86_64','_arm','_powerpc64'
  72. );
  73. OSStr : array[TOS] of string=(
  74. 'linux','go32v2','win32','os2','freebsd','beos','netbsd',
  75. 'amiga','atari','solaris', 'qnx', 'netware','openbsd','wdosx',
  76. 'palmos','macos','darwin','emx','watcom','morphos','netwlibc',
  77. 'win64','wince','gba'
  78. );
  79. OSSuffix : array[TOS] of string=(
  80. '_linux','_go32v2','_win32','_os2','_freebsd','_beos','_netbsd',
  81. '_amiga','_atari','_solaris', '_qnx', '_netware','_openbsd','_wdosx',
  82. '_palmos','_macos','_darwin','_emx','_watcom','_morphos','_netwlibc',
  83. '_win64','_wince','_gba'
  84. );
  85. { This table is kept OS,Cpu because it is easier to maintain (PFV) }
  86. OSCpuPossible : array[TOS,TCpu] of boolean = (
  87. { os i386 m68k ppc sparc x86_64 arm ppc64}
  88. { linux } ( true, true, true, true, true, true, true),
  89. { go32v2 } ( true, false, false, false, false, false, false),
  90. { win32 } ( true, false, false, false, false, false, false),
  91. { os2 } ( true, false, false, false, false, false, false),
  92. { freebsd } ( true, true, false, false, true, false, false),
  93. { beos } ( true, false, false, false, false, false, false),
  94. { netbsd } ( true, true, true, true, false, false, false),
  95. { amiga } ( false, true, true, false, false, false, false),
  96. { atari } ( false, true, false, false, false, false, false),
  97. { solaris } ( true, false, false, true, false, false, false),
  98. { qnx } ( true, false, false, false, false, false, false),
  99. { netware } ( true, false, false, false, false, false, false),
  100. { openbsd } ( true, true, false, false, false, false, false),
  101. { wdosx } ( true, false, false, false, false, false, false),
  102. { palmos } ( false, true, false, false, false, true, false),
  103. { macos } ( false, false, true, false, false, false, false),
  104. { darwin } ( true, false, true, false, false, false, false),
  105. { emx } ( true, false, false, false, false, false, false),
  106. { watcom } ( true, false, false, false ,false, false, false),
  107. { morphos } ( false, false, true, false ,false, false, false),
  108. { netwlibc }( true, false, false, false, false, false, false),
  109. { win64 } ( false, false, false, false, true, false, false),
  110. { wince }( true, false, false, false, false, true, false),
  111. { gba } ( false, false, false, false, false, true, false)
  112. );
  113. type
  114. TKeyValueItem = class(TDictionaryItem)
  115. private
  116. FValue : string;
  117. public
  118. constructor Create(const k,v:string);
  119. property Value:string read FValue write FValue;
  120. end;
  121. TKeyValue = class(TDictionary)
  122. private
  123. function GetKey(const k:string):string;
  124. public
  125. procedure Add(const k,v:String);
  126. property Key[const s:string]:string read GetKey write Add;default;
  127. end;
  128. TFPCMakeSection = class(TDictionaryItem)
  129. private
  130. FList : TStringList;
  131. FDictionary : TKeyValue;
  132. procedure BuildIniDic(p:TDictionaryItem);
  133. procedure BuildMakefileDic(p:TDictionaryItem);
  134. function GetKey(const k:string):string;
  135. public
  136. constructor Create(const n:string);
  137. constructor CreateKeyValue(const n:string);
  138. destructor Destroy;override;
  139. procedure AddLine(const s:string);
  140. procedure AddKey(const k,v:string);
  141. procedure Clear;
  142. procedure ParseIni;
  143. procedure BuildIni;
  144. procedure BuildMakefile;
  145. property Key[const s:string]:string read GetKey;default;
  146. property List:TStringList read FList;
  147. property Dictionary:TKeyValue read FDictionary;
  148. end;
  149. TTargetRequireList = array[tcpu,tos] of TStringList;
  150. TFPCMakeVerbose = (FPCMakeError, FPCMakeInfo, FPCMakeDebug);
  151. TFPCMake = class
  152. private
  153. FStream : TStream;
  154. FFileName : string;
  155. FCommentChars : TSysCharSet;
  156. FEmptyLines : boolean;
  157. FSections : TDictionary;
  158. FPackageSec,
  159. FExportSec : TFPCMakeSection;
  160. FUsesLCL,
  161. FIsPackage : boolean;
  162. FPackageName,
  163. FPackageVersion : string;
  164. FRequireList : TTargetRequireList;
  165. FVariables : TKeyValue;
  166. FIncludeTargets : TTargetSet;
  167. procedure Init;
  168. procedure ParseSec(p:TDictionaryItem);
  169. procedure PrintSec(p:TDictionaryItem);
  170. procedure PrintDic(p:TDictionaryItem);
  171. function GetSec(const AName:string):TDictionaryItem;
  172. procedure LoadRequiredPackage(c:TCpu;t:TOS;const ReqName,ReqVersion:string);
  173. procedure LoadRequiredDir(c:TCpu;t:TOS;const MainPack,currdir,subdir:string);
  174. procedure LoadRequires(c:TCpu;t:TOS;FromFPCMake:TFPCMake);
  175. function CopySection(Sec:TFPCMakeSection;Secname:string):TFPCMakeSection;
  176. protected
  177. VerboseIdent : string;
  178. public
  179. constructor Create(const AFileName:string);
  180. constructor CreateFromStream(s:TStream;const AFileName:string);
  181. destructor Destroy;override;
  182. procedure Verbose(lvl:TFPCMakeVerbose;const s:string);virtual;
  183. procedure SetTargets(const s:string);
  184. procedure LoadSections;
  185. procedure LoadMakefileFPC;
  186. procedure LoadPackageSection;
  187. procedure LoadRequireSection;
  188. function GetTargetRequires(c:TCpu;t:TOS):TStringList;
  189. function CheckLibcRequire:boolean;
  190. procedure CreateExportSection;
  191. procedure AddFPCDefaultVariables;
  192. procedure AddLCLDefaultVariables;
  193. function SubstVariables(const s:string):string;
  194. function GetVariable(const inivar:string;dosubst:boolean):string;
  195. function GetTargetVariable(c:TCPU;t:TOS;const inivar:string;dosubst:boolean):string;
  196. function HasVariable(const inivar:string):boolean;
  197. function HasTargetVariable(const inivar:string):boolean;
  198. function SetVariable(const inivar,value:string;add:boolean):string;
  199. procedure Print;
  200. property Section[const s:string]:TDictionaryItem read GetSec;default;
  201. property RequireList:TTargetRequireList read FRequireList;
  202. property Variables:TKeyValue read FVariables;
  203. property UsesLCL:boolean read FUsesLCL;
  204. property IsPackage:boolean read FIsPackage;
  205. property PackageName:string read FPackageName;
  206. property PackageVersion:string read FPackageVersion;
  207. property PackageSec:TFPCMakeSection read FPackageSec;
  208. property ExportSec:TFPCMakeSection read FExportSec;
  209. property CommentChars:TSysCharSet read FCommentChars write FCommentChars;
  210. property EmptyLines:Boolean read FEmptyLines write FEmptyLines;
  211. property IncludeTargets:TTargetSet read FIncludeTargets write FIncludeTargets;
  212. end;
  213. function posidx(const substr,s : string;idx:integer):integer;
  214. function GetToken(var s:string;sep:char):string;
  215. procedure AddToken(var s:string;const tok:string;sep:char);
  216. procedure AddTokenNoDup(var s:string;const s2:string;sep:char);
  217. implementation
  218. resourcestring
  219. s_not_list_sec='Not a list section "%s"';
  220. s_not_key_value_sec='Not a key-value section "%s"';
  221. s_err_section_start='%s:%d: Wrong section start';
  222. s_err_not_key_value='Parse error key=value excepted: "%s"';
  223. s_err_no_section='%s:%d: Entries without section';
  224. s_no_package_name='No package name set';
  225. s_no_package_version='No package version set';
  226. s_err_require_format='Wrong require format "%s"';
  227. s_wrong_package_name='Package name "%s" expected, but "%s" found';
  228. s_wrong_package_version='Package version "%s" expected, but version "%s" found';
  229. s_directory_not_found='Directory "%s" not found';
  230. s_makefilefpc_not_found='No Makefile.fpc found in directory "%s"';
  231. s_package_not_found='Target "%s", package "%s" not found';
  232. s_fpcmake_version_required='FPCMake version "%s" is required';
  233. s_no_targets_set='No targets set';
  234. s_targets_info='Targets: "%s"';
  235. s_globals='Globals:';
  236. {****************************************************************************
  237. Helpers
  238. ****************************************************************************}
  239. Function PathExists ( F : String) : Boolean;
  240. Var
  241. Info : TSearchRec;
  242. begin
  243. if F[Length(f)] in ['/','\'] then
  244. Delete(f,length(f),1);
  245. PathExists:=(findfirst(F,faAnyFile,info)=0) and
  246. ((info.attr and fadirectory)=fadirectory);
  247. findclose(Info);
  248. end;
  249. Function PathOrFileExists ( F : String) : Boolean;
  250. Var
  251. Info : Dos.SearchRec;
  252. begin
  253. if F[Length(f)] in ['/','\'] then
  254. Delete(f,length(f),1);
  255. dos.findfirst(f,fareadonly+faarchive+fahidden+fadirectory,info);
  256. PathOrFileExists:=(Doserror=0);
  257. dos.findclose(Info);
  258. end;
  259. function posidx(const substr,s : string;idx:integer):integer;
  260. var
  261. i,j : integer;
  262. e : boolean;
  263. begin
  264. i:=idx;
  265. j:=0;
  266. e:=(length(SubStr)>0);
  267. while e and (i<=Length(s)-Length(SubStr)) do
  268. begin
  269. inc(i);
  270. if (SubStr[1]=s[i]) and (Substr=Copy(s,i,Length(SubStr))) then
  271. begin
  272. j:=i;
  273. e:=false;
  274. end;
  275. end;
  276. PosIdx:=j;
  277. end;
  278. function GetToken(var s:string;sep:char):string;
  279. var
  280. i : integer;
  281. begin
  282. s:=Trim(s);
  283. i:=pos(sep,s);
  284. if i=0 then
  285. begin
  286. Result:=s;
  287. s:='';
  288. end
  289. else
  290. begin
  291. Result:=Copy(s,1,i-1);
  292. Delete(s,1,i);
  293. end;
  294. end;
  295. procedure AddToken(var s:string;const tok:string;sep:char);
  296. begin
  297. if tok='' then
  298. exit;
  299. if s<>'' then
  300. s:=s+sep+tok
  301. else
  302. s:=tok;
  303. end;
  304. procedure AddTokenNoDup(var s:string;const s2:string;sep:char);
  305. var
  306. i,idx : integer;
  307. again,add : boolean;
  308. begin
  309. add:=false;
  310. idx:=0;
  311. repeat
  312. again:=false;
  313. i:=posidx(s2,s,idx);
  314. if (i=0) then
  315. add:=true
  316. else
  317. if (i=1) then
  318. begin
  319. if (length(s)>length(s2)) and
  320. (s[length(s2)+1]<>sep) then
  321. add:=true;
  322. end
  323. else
  324. if (i>1) and
  325. ((s[i-1]<>sep) or
  326. ((length(s)>=i+length(s2)) and (s[i+length(s2)]<>sep))) then
  327. begin
  328. idx:=i+length(s2);
  329. again:=true;
  330. end;
  331. until not again;
  332. if add then
  333. begin
  334. if s='' then
  335. s:=s2
  336. else
  337. s:=s+sep+s2;
  338. end;
  339. end;
  340. {****************************************************************************
  341. TKeyValueItem
  342. ****************************************************************************}
  343. constructor TKeyValueItem.Create(const k,v:string);
  344. begin
  345. inherited Create(k);
  346. value:=v;
  347. end;
  348. {****************************************************************************
  349. TKeyValue
  350. ****************************************************************************}
  351. function TKeyValue.GetKey(const k:string):string;
  352. var
  353. p : TKeyValueItem;
  354. begin
  355. p:=TKeyValueItem(Search(k));
  356. if p=nil then
  357. GetKey:=''
  358. else
  359. GetKey:=p.Value;
  360. end;
  361. procedure TKeyValue.Add(const k,v:string);
  362. var
  363. p : TKeyValueItem;
  364. begin
  365. p:=TKeyValueItem(Search(k));
  366. if p=nil then
  367. begin
  368. p:=TKeyValueItem.Create(k,v);
  369. Insert(p);
  370. end
  371. else
  372. p.Value:=v;
  373. end;
  374. {****************************************************************************
  375. TFPCMakeSection
  376. ****************************************************************************}
  377. constructor TFPCMakeSection.Create(const n:string);
  378. begin
  379. inherited Create(n);
  380. FList:=TStringList.Create;
  381. FDictionary:=nil;
  382. end;
  383. constructor TFPCMakeSection.CreateKeyValue(const n:string);
  384. begin
  385. inherited Create(n);
  386. FList:=nil;
  387. FDictionary:=TKeyValue.Create;
  388. end;
  389. destructor TFPCMakeSection.Destroy;
  390. begin
  391. inherited Destroy;
  392. FList.Free;
  393. FDictionary.Free;
  394. end;
  395. procedure TFPCMakeSection.Clear;
  396. begin
  397. FList.Free;
  398. FList:=TStringList.Create;
  399. FDictionary.Free;
  400. FDictionary:=nil;
  401. end;
  402. procedure TFPCMakeSection.AddLine(const s:string);
  403. begin
  404. if FList=nil then
  405. raise Exception.Create(Format(s_not_list_sec,[Name]));
  406. FList.Add(s);
  407. end;
  408. procedure TFPCMakeSection.AddKey(const k,v:string);
  409. begin
  410. if FDictionary=nil then
  411. raise Exception.Create(Format(s_not_key_value_sec,[Name]));
  412. { Don't add empty values }
  413. if v<>'' then
  414. FDictionary.Add(k,v);
  415. end;
  416. function TFPCMakeSection.GetKey(const k:string):string;
  417. begin
  418. if FDictionary=nil then
  419. raise Exception.Create(Format(s_not_key_value_sec,[Name]));
  420. GetKey:=FDictionary[k];
  421. end;
  422. procedure TFPCMakeSection.ParseIni;
  423. var
  424. p : TKeyValueItem;
  425. i,j,len,maxi : integer;
  426. s,newkey,value : string;
  427. begin
  428. { If already processed skip }
  429. if assigned(FDictionary) then
  430. exit;
  431. { Don't process rules section }
  432. if (Name='prerules') or (Name='rules') then
  433. exit;
  434. { Parse the section }
  435. FDictionary:=TKeyValue.Create;
  436. { Parse the list }
  437. maxi:=FList.Count;
  438. i:=0;
  439. while (i<maxi) do
  440. begin
  441. s:=Trim(FList[i]);
  442. len:=Length(s);
  443. { Concat lines ending with \ }
  444. while s[len]='\' do
  445. begin
  446. Delete(s,len,1);
  447. if i+1<maxi then
  448. begin
  449. inc(i);
  450. s:=s+Trim(FList[i]);
  451. len:=Length(s);
  452. end;
  453. end;
  454. { Parse key=value line }
  455. j:=0;
  456. while (j<len) and (s[j+1] in ['A'..'Z','a'..'z','0'..'9','_']) do
  457. inc(j);
  458. NewKey:=Copy(s,1,j);
  459. While (j<len) and (s[j+1] in [' ',#9]) do
  460. inc(j);
  461. inc(j);
  462. if s[j]<>'=' then
  463. Raise Exception.Create(Format(s_err_not_key_value,[s]));
  464. While (j<len) and (s[j+1] in [' ',#9]) do
  465. inc(j);
  466. Value:=Copy(s,j+1,len-j);
  467. p:=TKeyValueItem(FDictionary.Search(NewKey));
  468. { Concat values if key already exists }
  469. if assigned(p) then
  470. AddToken(p.FValue,Value,' ')
  471. else
  472. FDictionary.Add(NewKey,Value);
  473. inc(i);
  474. end;
  475. { List is not used anymore }
  476. FList.Free;
  477. FList:=nil;
  478. end;
  479. procedure TFPCMakeSection.BuildIniDic(p:TDictionaryItem);
  480. begin
  481. with TKeyValueItem(p) do
  482. begin
  483. FList.Add(Name+'='+Value);
  484. end;
  485. end;
  486. procedure TFPCMakeSection.BuildIni;
  487. begin
  488. if assigned(FList) then
  489. exit;
  490. FList:=TStringList.Create;
  491. FDictionary.Foreach(@BuildIniDic);
  492. FDictionary.Free;
  493. FDictionary:=nil;
  494. end;
  495. procedure TFPCMakeSection.BuildMakefileDic(p:TDictionaryItem);
  496. begin
  497. FList.Add(Uppercase(Name+'_'+TKeyValueItem(p).Name)+'='+TKeyValueItem(p).Value);
  498. end;
  499. procedure TFPCMakeSection.BuildMakefile;
  500. begin
  501. if assigned(FList) then
  502. exit;
  503. FList:=TStringList.Create;
  504. FDictionary.Foreach(@BuildMakefileDic);
  505. FDictionary.Free;
  506. FDictionary:=nil;
  507. end;
  508. {****************************************************************************
  509. TFPCMake
  510. ****************************************************************************}
  511. constructor TFPCMake.Create(const AFileName:string);
  512. begin
  513. FFileName:=AFileName;
  514. FStream:=nil;
  515. Init;
  516. end;
  517. constructor TFPCMake.CreateFromStream(s:TStream;const AFileName:string);
  518. begin
  519. FFileName:=AFileName;
  520. FStream:=s;
  521. Init;
  522. end;
  523. procedure TFPCMake.Init;
  524. var
  525. t : tos;
  526. c : tcpu;
  527. begin
  528. FSections:=TDictionary.Create;
  529. for c:=low(tcpu) to high(tcpu) do
  530. for t:=low(tos) to high(tos) do
  531. FRequireList[c,t]:=TStringList.Create;
  532. FVariables:=TKeyValue.Create;
  533. FCommentChars:=[';','#'];
  534. FEmptyLines:=false;
  535. FIsPackage:=false;
  536. FPackageName:='';
  537. FPackageVersion:='';
  538. FPackageSec:=nil;
  539. FExportSec:=nil;
  540. FillChar(FIncludeTargets,sizeof(FIncludeTargets),true);
  541. VerboseIdent:='';
  542. FUsesLCL:=false;
  543. end;
  544. destructor TFPCMake.Destroy;
  545. var
  546. t : tos;
  547. c : tcpu;
  548. begin
  549. FSections.Free;
  550. for c:=low(tcpu) to high(tcpu) do
  551. for t:=low(tos) to high(tos) do
  552. FRequireList[c,t].Free;
  553. FVariables.Free;
  554. end;
  555. procedure TFPCMake.LoadSections;
  556. var
  557. SLInput : TStringList;
  558. i,j,n : integer;
  559. s,
  560. SecName : string;
  561. CurrSec : TFPCMakeSection;
  562. begin
  563. try
  564. CurrSec:=nil;
  565. SLInput:=TStringList.Create;
  566. if assigned(FStream) then
  567. SLInput.LoadFromStream(FStream)
  568. else
  569. SLInput.LoadFromFile(FFileName);
  570. { Load Input into sections list }
  571. n:=SLInput.Count;
  572. i:=0;
  573. while (i<n) do
  574. begin
  575. s:=Trim(SLInput[i]);
  576. if (EmptyLines and (s='')) or
  577. ((s<>'') and not(s[1] in FCommentChars)) then
  578. begin
  579. { section start? }
  580. if (s<>'') and (s[1]='[') then
  581. begin
  582. j:=pos(']',s);
  583. if j=0 then
  584. raise Exception.Create(Format(s_err_section_start,[FFileName,i+1]));
  585. SecName:=Copy(s,2,j-2);
  586. CurrSec:=TFPCMakeSection(FSections[SecName]);
  587. if CurrSec=nil then
  588. CurrSec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.Create(SecName)));
  589. end
  590. else
  591. begin
  592. if CurrSec=nil then
  593. raise Exception.Create(Format(s_err_no_section,[FFileName,i+1]));
  594. { Insert string without spaces stripped }
  595. CurrSec.AddLine(SLInput[i]);
  596. end;
  597. end;
  598. inc(i);
  599. end;
  600. finally
  601. SLInput.Free;
  602. end;
  603. end;
  604. function TFPCMake.CopySection(Sec:TFPCMakeSection;Secname:string):TFPCMakeSection;
  605. begin
  606. Result:=TFPCMakeSection(FSections[SecName]);
  607. if Sec=Nil then
  608. exit;
  609. { Clear old section or if not existing create new }
  610. if assigned(Result) then
  611. Result.Clear
  612. else
  613. Result:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.Create(SecName)));
  614. Sec.BuildIni;
  615. Result.List.AddStrings(Sec.List);
  616. Result.ParseIni;
  617. Sec.ParseIni;
  618. end;
  619. procedure TFPCMake.LoadMakefileFPC;
  620. var
  621. s : string;
  622. begin
  623. LoadSections;
  624. { Parse all sections }
  625. FSections.Foreach(@ParseSec);
  626. { Load package section }
  627. LoadPackageSection;
  628. { Add some default variables like FPCDIR, UNITSDIR }
  629. AddFPCDefaultVariables;
  630. { Load LCL code ? }
  631. {$ifdef SupportLCL}
  632. s:=GetVariable('require_packages',true);
  633. if (pos('lcl',s)>0) or (PackageName='lcl') then
  634. begin
  635. FUsesLCL:=true;
  636. AddLCLDefaultVariables;
  637. end;
  638. {$endif SupportLCL}
  639. { Show globals }
  640. Verbose(FPCMakeDebug,s_globals);
  641. Variables.Foreach(@PrintDic);
  642. { Load required packages }
  643. LoadRequireSection;
  644. end;
  645. procedure TFPCMake.Verbose(lvl:TFPCMakeVerbose;const s:string);
  646. begin
  647. writeln(VerboseIdent,s);
  648. end;
  649. procedure TFPCMake.SetTargets(const s:string);
  650. var
  651. hslst : string;
  652. hs : string;
  653. hcpu,htarget : string;
  654. t : TOs;
  655. c : TCpu;
  656. i : integer;
  657. targetset : boolean;
  658. begin
  659. FillChar(FIncludeTargets,sizeof(FIncludeTargets),0);
  660. targetset:=false;
  661. hslst:=s;
  662. repeat
  663. hs:=LowerCase(GetToken(hslst,','));
  664. if hs='' then
  665. break;
  666. { target 'all' includes all targets }
  667. if hs='all' then
  668. begin
  669. for c:=low(TCpu) to high(TCpu) do
  670. for t:=low(TOs) to high(TOs) do
  671. if OSCpuPossible[t,c] then
  672. FIncludeTargets[c,t]:=true;
  673. targetset:=true;
  674. break;
  675. end;
  676. { full cpu-target specified? }
  677. i:=pos('-',hs);
  678. if i>0 then
  679. begin
  680. hcpu:=copy(hs,1,i-1);
  681. htarget:=copy(hs,i+1,length(hs)-i);
  682. for c:=low(TCpu) to high(TCpu) do
  683. begin
  684. if hcpu=CpuStr[c] then
  685. begin
  686. for t:=low(TOs) to high(TOs) do
  687. begin
  688. if htarget=OSStr[t] then
  689. begin
  690. if OSCpuPossible[t,c] then
  691. begin
  692. FIncludeTargets[c,t]:=true;
  693. targetset:=true;
  694. end;
  695. break;
  696. end;
  697. end;
  698. break;
  699. end;
  700. end;
  701. end
  702. else
  703. begin
  704. for c:=low(TCpu) to high(TCpu) do
  705. begin
  706. for t:=low(TOS) to high(TOS) do
  707. begin
  708. if hs=OSStr[t] then
  709. begin
  710. if OSCpuPossible[t,c] then
  711. begin
  712. FIncludeTargets[c,t]:=true;
  713. targetset:=true;
  714. end;
  715. break;
  716. end;
  717. end;
  718. end;
  719. end;
  720. until false;
  721. if not targetset then
  722. raise Exception.Create(s_no_targets_set)
  723. else
  724. begin
  725. hs:='';
  726. for c:=low(TCpu) to high(TCpu) do
  727. for t:=low(TOs) to high(TOs) do
  728. if FIncludeTargets[c,t] then
  729. AddToken(hs,CpuStr[c]+'-'+OSStr[t],' ');
  730. Verbose(FPCMakeDebug,Format(s_targets_info,[hs]));
  731. end;
  732. end;
  733. procedure TFPCMake.LoadPackageSection;
  734. var
  735. hs,s : string;
  736. t : TOS;
  737. begin
  738. { Get package info from package section }
  739. FPackageSec:=TFPCMakeSection(FSections['package']);
  740. if FPackageSec=nil then
  741. exit;
  742. { Parse the section to key=value pairs }
  743. FPackageSec.ParseIni;
  744. { Are we a subpart of a package, then load that package }
  745. s:=FPackageSec['main'];
  746. if s<>'' then
  747. begin
  748. SetVariable('package_name',s,false);
  749. FPackageName:=s;
  750. end
  751. else
  752. begin
  753. { mandatory name }
  754. FPackageName:=FPackageSec['name'];
  755. if FPackageName='' then
  756. Raise Exception.Create(s_no_package_name);
  757. { mandatory version }
  758. FPackageVersion:=FPackageSec['version'];
  759. if FPackageVersion='' then
  760. Raise Exception.Create(s_no_package_version);
  761. FIsPackage:=true;
  762. { Set the ExportSec }
  763. FExportSec:=TFPCMakeSection(FSections[Lowercase(FPackageName)]);
  764. end;
  765. end;
  766. procedure TFPCMake.CreateExportSection;
  767. var
  768. t : TOS;
  769. c : TCpu;
  770. begin
  771. { Don't create a section twice }
  772. if FExportSec<>nil then
  773. exit;
  774. { Look if we've already an own section, else create a new
  775. key-value section }
  776. FExportSec:=TFPCMakeSection(FSections[LowerCase(FPackageName)]);
  777. if FExportSec=nil then
  778. FExportSec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.CreateKeyValue(LowerCase(FPackageName))));
  779. { Add default the values to the export section }
  780. FExportSec.AddKey('name',FPackageName);
  781. FExportSec.AddKey('version',FPackageVersion);
  782. { Add required packages }
  783. for c:=low(TCpu) to high(TCpu) do
  784. for t:=low(TOS) to high(TOS) do
  785. FExportSec.AddKey('require'+CpuSuffix[c]+OSSuffix[t],FPackageSec['require'+CpuSuffix[c]+OSSuffix[t]]);
  786. { Unit dir }
  787. {FExportSec.AddKey('unitdir','$(UNITSDIR)/'+Lowercase(PackageName));}
  788. end;
  789. procedure TFPCMake.LoadRequiredPackage(c:TCpu;t:TOS;const ReqName,ReqVersion:string);
  790. function TryFile(const fn:string):boolean;
  791. var
  792. ReqFPCMake : TFPCMake;
  793. begin
  794. TryFile:=false;
  795. if FileExists(fn) then
  796. begin
  797. VerboseIdent:=VerboseIdent+' ';
  798. Verbose(FPCMakeDebug,'Package '+ReqName+': '+fn);
  799. ReqFPCMake:=TFPCMake.Create(fn);
  800. ReqFPCMake.LoadSections;
  801. ReqFPCMake.LoadPackageSection;
  802. { Check package name and version }
  803. if LowerCase(ReqFPCMake.PackageName)<>ExtractFileName(ReqName) then
  804. raise Exception.Create(Format(s_wrong_package_name,[ReqName,LowerCase(ReqFPCMake.PackageName)]));
  805. if (ReqVersion<>'') and (ReqFPCMake.PackageVersion<ReqVersion) then
  806. raise Exception.Create(Format(s_wrong_package_version,[ReqVersion,ReqFPCMake.PackageVersion]));
  807. { First load the requirements of this package }
  808. LoadRequires(c,t,ReqFPCMake);
  809. { Get a copy of the package section }
  810. CopySection(ReqFPCMake.PackageSec,ReqName+'_package');
  811. { Get a copy of the export section }
  812. CopySection(ReqFPCMake.ExportSec,ReqName);
  813. { Get a copy of the require section }
  814. CopySection(TFPCMakeSection(ReqFPCMake['require']),ReqName+'_require');
  815. { Free }
  816. ReqFPCMake.Free;
  817. Delete(VerboseIdent,1,2);
  818. TryFile:=true;
  819. end;
  820. end;
  821. var
  822. s : string;
  823. begin
  824. { Force the current target }
  825. SetVariable('OSTARGET',OSStr[t],false);
  826. SetVariable('CPUTARGET',CpuStr[c],false);
  827. {$ifdef LIMIT83}
  828. SetVariable('FULLTARGET',OSStr[t],false);
  829. {$else}
  830. SetVariable('FULLTARGET',CpuStr[c]+'-'+OSStr[t],false);
  831. {$endif}
  832. { Check for Makefile.fpc }
  833. s:=SubstVariables('$(addsuffix /'+ReqName+'/Makefile.fpc,$(FPCDIR)) $(addsuffix /'+ReqName+'/Makefile.fpc,$(PACKAGESDIR)) $(addsuffix /'+ReqName+'/Makefile.fpc,$(REQUIRE_PACKAGESDIR))');
  834. Verbose(FPCMakeDebug,'Package "'+ReqName+'": Looking for Makefile.fpc: "'+s+'"');
  835. s:=SubstVariables('$(firstword $(wildcard '+s+'))');
  836. if TryFile(s) then
  837. exit;
  838. { Check for Package.fpc }
  839. s:=SubstVariables('$(addsuffix /'+ReqName+'/Package.fpc,$(FPCDIR)) $(addsuffix /'+ReqName+'/Package.fpc,$(UNITSDIR)) $(addsuffix /'+ReqName+'/Package.fpc,$(REQUIRE_UNITSDIR))');
  840. Verbose(FPCMakeDebug,'Package "'+ReqName+'": Looking for Package.fpc: "'+s+'"');
  841. s:=SubstVariables('$(firstword $(wildcard '+s+'))');
  842. if TryFile(s) then
  843. exit;
  844. Raise Exception.Create(Format(s_package_not_found,[OSStr[t],Reqname]));
  845. end;
  846. procedure TFPCMake.LoadRequiredDir(c:TCpu;t:TOS;const MainPack,currdir,subdir:string);
  847. var
  848. ReqFPCMake : TFPCMake;
  849. s : string;
  850. begin
  851. VerboseIdent:=VerboseIdent+' ';
  852. s:=currdir+subdir;
  853. Verbose(FPCMakeDebug,'Subdir: '+s+'/Makefile.fpc');
  854. if not FileExists(s+'/Makefile.fpc') then
  855. begin
  856. { give better error what is wrong }
  857. if not PathExists(s) then
  858. Raise Exception.Create(Format(s_directory_not_found,[s]))
  859. else
  860. Raise Exception.Create(Format(s_makefilefpc_not_found,[s]));
  861. end;
  862. { Process Makefile.fpc }
  863. ReqFPCMake:=TFPCMake.Create(currdir+subdir+'/Makefile.fpc');
  864. ReqFPCMake.LoadSections;
  865. ReqFPCMake.LoadPackageSection;
  866. { Are we a subpackage? }
  867. if (ReqFPCMake.GetVariable('package_name',false)<>MainPack) then
  868. begin
  869. ReqFPCMake.Free;
  870. Delete(VerboseIdent,1,2);
  871. exit;
  872. end;
  873. { Load the requirements of this package }
  874. LoadRequires(c,t,ReqFPCMake);
  875. { Add the current requirements to our parents requirements }
  876. s:=ReqFPCMake.GetTargetVariable(c,t,'require_packages',true);
  877. SetVariable('require_packages'+OSSuffix[t]+cpusuffix[c],s,true);
  878. if ReqFPCMake.GetVariable('require_libc',false)<>'' then
  879. SetVariable('require_libc','y',false);
  880. { Free }
  881. ReqFPCMake.Free;
  882. Delete(VerboseIdent,1,2);
  883. end;
  884. procedure TFPCMake.LoadRequires(c:TCpu;t:TOS;FromFPCMake:TFPCMake);
  885. var
  886. s,
  887. ReqDir,
  888. ReqName,
  889. ReqVersion : string;
  890. i,j : integer;
  891. begin
  892. { packages }
  893. s:=FromFPCMake.GetTargetVariable(c,t,'require_packages',true);
  894. Verbose(FPCMakeDebug,'Required packages for '+OSStr[t]+'-'+CpuStr[c]+': '+s);
  895. repeat
  896. reqname:=GetToken(s,' ');
  897. if reqname='' then
  898. break;
  899. i:=Pos('(',ReqName);
  900. if i>0 then
  901. begin
  902. j:=Pos(')',ReqName);
  903. if (i=1) or (j=0) then
  904. Raise Exception.Create(Format(s_err_require_format,[ReqName]));
  905. ReqVersion:=Copy(ReqName,i+1,j-i-1);
  906. ReqName:=Copy(ReqName,1,i-1);
  907. end
  908. else
  909. ReqVersion:='';
  910. { We only use lowercase names }
  911. ReqName:=Lowercase(ReqName);
  912. { Already loaded ? }
  913. if (RequireList[c,t].IndexOf(ReqName)=-1) then
  914. begin
  915. LoadRequiredPackage(c,t,ReqName,ReqVersion);
  916. RequireList[c,t].Add(ReqName);
  917. end;
  918. until false;
  919. { sub dirs }
  920. s:=FromFPCMake.GetTargetVariable(c,t,'target_dirs',true);
  921. Verbose(FPCMakeDebug,'Required dirs for '+CpuStr[c]+'-'+OSStr[t]+': '+s);
  922. repeat
  923. reqdir:=GetToken(s,' ');
  924. if reqdir='' then
  925. break;
  926. LoadRequiredDir(c,t,FromFPCMake.FPackageName,ExtractFilePath(FromFPCMake.FFileName),ReqDir)
  927. until false;
  928. end;
  929. procedure TFPCMake.LoadRequireSection;
  930. var
  931. s : string;
  932. t : tos;
  933. c : tcpu;
  934. begin
  935. { Check FPCMake version }
  936. s:=GetVariable('require_fpcmake',false);
  937. if (s>version) then
  938. raise Exception.Create(Format(s_fpcmake_version_required,[s]));
  939. { Maybe add an implicit rtl dependency if there is something
  940. to compile }
  941. s:=GetVariable('require_packages',false);
  942. if (GetVariable('require_nortl',false)='') and
  943. (HasTargetVariable('target_programs') or
  944. HasTargetVariable('target_units') or
  945. HasTargetVariable('target_examples')) and
  946. (Pos('rtl(',s)=0) and (getvariable('package_name',false)<>'rtl') then
  947. begin
  948. s:='rtl '+s;
  949. SetVariable('require_packages',s,false);
  950. end;
  951. { Load recursively all required packages starting with this Makefile.fpc }
  952. for c:=low(TCpu) to high(TCpu) do
  953. for t:=low(Tos) to high(Tos) do
  954. if FIncludeTargets[c,t] then
  955. LoadRequires(c,t,self);
  956. end;
  957. function TFPCMake.GetTargetRequires(c:TCpu;t:Tos):TStringList;
  958. var
  959. ReqSec : TFPCMakeSection;
  960. ReqList : TStringList;
  961. procedure AddReqSec(c:TCpu;t:Tos;Sec:TFPCMakeSection);
  962. var
  963. s,
  964. ReqName : string;
  965. RSec : TFPCMakeSection;
  966. i : integer;
  967. begin
  968. s:=Sec['packages']+' '+
  969. Sec['packages'+CpuSuffix[c]]+' '+
  970. Sec['packages'+OSSuffix[t]]+' '+
  971. Sec['packages'+OSSuffix[t]+CpuSuffix[c]];
  972. repeat
  973. ReqName:=GetToken(s,' ');
  974. if ReqName='' then
  975. break;
  976. i:=Pos('(',ReqName);
  977. if i>0 then
  978. ReqName:=Copy(ReqName,1,i-1);
  979. { We only use lowercase names }
  980. ReqName:=Lowercase(ReqName);
  981. { Already loaded ? }
  982. if (ReqList.IndexOf(ReqName)=-1) then
  983. begin
  984. RSec:=TFPCMakeSection(FSections[ReqName+'_require']);
  985. if assigned(RSec) then
  986. AddReqSec(c,t,RSec);
  987. ReqList.Add(ReqName);
  988. end;
  989. until false;
  990. end;
  991. begin
  992. ReqList:=TStringList.Create;
  993. ReqSec:=TFPCMakeSection(FSections['require']);
  994. if assigned(ReqSec) then
  995. AddReqSec(c,t,ReqSec);
  996. GetTargetRequires:=ReqList;
  997. end;
  998. function TFPCMake.CheckLibcRequire:boolean;
  999. var
  1000. i : integer;
  1001. RSec : TFPCMakeSection;
  1002. t : tos;
  1003. c : tcpu;
  1004. begin
  1005. Result:=false;
  1006. if GetVariable('require_libc',false)<>'' then
  1007. begin
  1008. Result:=true;
  1009. exit;
  1010. end;
  1011. for c:=low(tcpu) to high(tcpu) do
  1012. for t:=low(tos) to high(tos) do
  1013. if FIncludeTargets[c,t] then
  1014. begin
  1015. for i:=0 to RequireList[c,t].Count-1 do
  1016. begin
  1017. RSec:=TFPCMakeSection(FSections[RequireList[c,t][i]+'_require']);
  1018. if assigned(RSec) then
  1019. begin
  1020. if RSec['libc']<>'' then
  1021. begin
  1022. Result:=true;
  1023. exit;
  1024. end;
  1025. end;
  1026. end;
  1027. end;
  1028. end;
  1029. procedure TFPCMake.AddFPCDefaultVariables;
  1030. var
  1031. hs,s : string;
  1032. begin
  1033. { Already set FPCDIR }
  1034. hs:='';
  1035. s:=GetVariable('FPCDIR',false);
  1036. if s<>'' then
  1037. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1038. { Load from environment }
  1039. if hs='' then
  1040. begin
  1041. s:=GetEnv('FPCDIR');
  1042. if s<>'' then
  1043. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1044. end;
  1045. { default_fpcdir }
  1046. if hs='' then
  1047. begin
  1048. s:=GetVariable('default_fpcdir',true);
  1049. { add the current subdir to relative paths }
  1050. if s<>'' then
  1051. begin
  1052. {$ifdef UNIX}
  1053. if (s[1]<>'/') then
  1054. {$else}
  1055. if ((length(s)>2) and (s[2]<>':')) or (length(s)<=2) then
  1056. {$endif}
  1057. s:=ExtractFilePath(FFileName)+s;
  1058. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1059. end
  1060. end;
  1061. { OS defaults }
  1062. if hs='' then
  1063. begin
  1064. {$ifdef UNIX}
  1065. {$ifndef NO_UNIX_UNIT}
  1066. if FileExists('/usr/local/bin/ppc386') then
  1067. begin
  1068. s:=ExtractFilePath({$ifdef ver1_0}ReadLink{$else}fpReadlink{$endif}('/usr/local/bin/ppc386'));
  1069. if s<>'' then
  1070. begin
  1071. if s[length(s)]='/' then
  1072. delete(s,length(s),1);
  1073. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1074. end;
  1075. end;
  1076. if hs='' then
  1077. begin
  1078. if FileExists('/usr/bin/ppc386') then
  1079. begin
  1080. s:=ExtractFilePath({$ifdef ver1_0}ReadLink{$else}fpReadLink{$endif}('/usr/bin/ppc386'));
  1081. if s<>'' then
  1082. begin
  1083. if s[length(s)]='/' then
  1084. delete(s,length(s),1);
  1085. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1086. end;
  1087. end;
  1088. end;
  1089. {$endif}
  1090. {$else UNIX}
  1091. hs:=ExtractFilePath(FSearch('ppc386.exe',getenv('PATH')));
  1092. if hs<>'' then
  1093. begin
  1094. s:=hs+'/..';
  1095. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1096. if hs='' then
  1097. begin
  1098. s:=s+'/..';
  1099. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  1100. end;
  1101. end;
  1102. if hs='' then
  1103. s:='c:/pp';
  1104. {$endif UNIX}
  1105. end;
  1106. SetVariable('FPCDIR',s,false);
  1107. { PACKAGESDIR }
  1108. if GetVariable('PACKAGESDIR',false)='' then
  1109. SetVariable('PACKAGESDIR','$(FPCDIR)/packages/base $(FPCDIR)/packages/extra',false);
  1110. { UNITSDIR }
  1111. if GetVariable('UNITSDIR',false)='' then
  1112. SetVariable('UNITSDIR','$(FPCDIR)/units/$(FULLTARGET)',false);
  1113. { BASEDIR }
  1114. SetVariable('BASEDIR',GetCurrentDir,false);
  1115. end;
  1116. procedure TFPCMake.AddLCLDefaultVariables;
  1117. var
  1118. hs,s : string;
  1119. begin
  1120. { Already set LCLDIR }
  1121. hs:=SubstVariables('$(wildcard $(LCLDIR)/units)');
  1122. { Load from environment }
  1123. if hs='' then
  1124. begin
  1125. SetVariable('LCLDIR',GetEnv('LCLDIR'),false);
  1126. hs:=SubstVariables('$(wildcard $(LCLDIR)/units)');
  1127. end;
  1128. { default_lcldir }
  1129. if hs='' then
  1130. begin
  1131. s:=GetVariable('default_lcldir',true);
  1132. { add the current subdir to relative paths }
  1133. if s<>'' then
  1134. begin
  1135. {$ifdef UNIX}
  1136. if (s[1]<>'/') then
  1137. {$else}
  1138. if ((length(s)>2) and (s[2]<>':')) or (length(s)<=2) then
  1139. {$endif}
  1140. s:=ExtractFilePath(FFileName)+s;
  1141. SetVariable('LCLDIR',s,false);
  1142. hs:=SubstVariables('$(wildcard $(LCLDIR)/units)');
  1143. end
  1144. end;
  1145. { OS defaults }
  1146. if hs='' then
  1147. begin
  1148. hs:=SubstVariables('$(subst /units,,$(firstword $(wildcard $(addsuffix /units,$(BASEDIR)/lcl $(BASEDIR)))))');
  1149. if hs<>'' then
  1150. SetVariable('LCLDIR',hs,false);
  1151. end;
  1152. {$ifdef UNIX}
  1153. if hs='' then
  1154. begin
  1155. hs:=SubstVariables('$(subst /units,,$(firstword $(wildcard $(addsuffix /lib/lazarus/units,/usr/local /usr))))');
  1156. if hs<>'' then
  1157. SetVariable('LCLDIR',hs,false);
  1158. end;
  1159. {$endif UNIX}
  1160. { Add components to PACKAGESDIR }
  1161. SetVariable('PACKAGESDIR',SubstVariables('$(wildcard $(LCLDIR)/.. $(LCLDIR)/../components $(LCLDIR)/components)'),true);
  1162. end;
  1163. function TFPCMake.SubstVariables(const s:string):string;
  1164. function Expect(var s:string;c:char):boolean;
  1165. begin
  1166. if (s<>'') and (s[1]=c) then
  1167. begin
  1168. Delete(s,1,1);
  1169. Result:=true;
  1170. end
  1171. else
  1172. begin
  1173. Verbose(FPCMakeError,'Error "'+c+'" expected');
  1174. Result:=false;
  1175. end;
  1176. end;
  1177. function GetVar(var s:string;untilc:char):string;
  1178. var
  1179. i,j,k : integer;
  1180. first : boolean;
  1181. func,
  1182. tok,s1,s2,s3 : string;
  1183. Sec : TFPCMakeSection;
  1184. begin
  1185. Result:='';
  1186. repeat
  1187. j:=Pos(untilc,s);
  1188. if j=0 then
  1189. j:=Length(s)+1;
  1190. i:=Pos('$(',s);
  1191. if (j<i) or (i=0) then
  1192. break;
  1193. Result:=Result+Copy(s,1,i-1);
  1194. Delete(s,1,i+1);
  1195. { Maybe Function ? }
  1196. j:=Pos(')',s);
  1197. if j=0 then
  1198. j:=Length(s)+1;
  1199. i:=Pos(' ',s);
  1200. if i=0 then
  1201. i:=Length(s)+1;
  1202. if i<j then
  1203. begin
  1204. { It's a function }
  1205. Func:=Copy(s,1,i-1);
  1206. //writeln('func: ',func);
  1207. { $(wildcard <list>) }
  1208. if Func='wildcard' then
  1209. begin
  1210. Delete(s,1,9);
  1211. s1:=GetVar(s,')');
  1212. Expect(s,')');
  1213. first:=true;
  1214. repeat
  1215. tok:=GetToken(s1,' ');
  1216. if tok='' then
  1217. break;
  1218. if PathOrFileExists(tok) then
  1219. begin
  1220. if not first then
  1221. Result:=Result+' '
  1222. else
  1223. first:=false;
  1224. Result:=Result+tok;
  1225. end;
  1226. until false;
  1227. end
  1228. { $(addprefix <suffix>,<list>) }
  1229. else if Func='addprefix' then
  1230. begin
  1231. Delete(s,1,10);
  1232. s1:=GetVar(s,',');
  1233. if Expect(s,',') then
  1234. begin
  1235. s2:=GetVar(s,')');
  1236. Expect(s,')');
  1237. end;
  1238. first:=true;
  1239. repeat
  1240. tok:=GetToken(s2,' ');
  1241. if tok='' then
  1242. break;
  1243. if not first then
  1244. Result:=Result+' '
  1245. else
  1246. first:=false;
  1247. Result:=Result+s1+tok;
  1248. until false;
  1249. end
  1250. { $(addsuffix <suffix>,<list>) }
  1251. else if Func='addsuffix' then
  1252. begin
  1253. Delete(s,1,10);
  1254. s1:=GetVar(s,',');
  1255. if Expect(s,',') then
  1256. begin
  1257. s2:=GetVar(s,')');
  1258. Expect(s,')');
  1259. end;
  1260. first:=true;
  1261. repeat
  1262. tok:=GetToken(s2,' ');
  1263. if tok='' then
  1264. break;
  1265. if not first then
  1266. Result:=Result+' '
  1267. else
  1268. first:=false;
  1269. Result:=Result+tok+s1;
  1270. until false;
  1271. end
  1272. { $(firstword <list>) }
  1273. else if Func='firstword' then
  1274. begin
  1275. Delete(s,1,10);
  1276. s1:=GetVar(s,')');
  1277. Expect(s,')');
  1278. Result:=GetToken(s1,' ');
  1279. end
  1280. { $(subst <oldpat>,<newpat>,<string>) }
  1281. else if Func='subst' then
  1282. begin
  1283. Delete(s,1,6);
  1284. s1:=GetVar(s,',');
  1285. if Expect(s,',') then
  1286. begin
  1287. s2:=GetVar(s,',');
  1288. if Expect(s,',') then
  1289. begin
  1290. s3:=GetVar(s,')');
  1291. Expect(s,')');
  1292. end;
  1293. end;
  1294. Result:=StringReplace(s3,s1,s2,[rfReplaceAll]);
  1295. end;
  1296. end
  1297. else
  1298. begin
  1299. s2:=Copy(s,1,j-1);
  1300. Delete(s,1,j);
  1301. k:=pos('_',s2);
  1302. if k>0 then
  1303. begin
  1304. s3:=LowerCase(Copy(s2,k+1,Length(s2)-k));
  1305. s2:=LowerCase(Copy(s2,1,k-1));
  1306. Sec:=TFPCMakeSection(Section[s2]);
  1307. if assigned(Sec) then
  1308. s2:=Sec[s3]
  1309. else
  1310. s2:='';
  1311. end
  1312. else
  1313. s2:=Variables[s2];
  1314. Insert(s2,s,1);
  1315. end;
  1316. until false;
  1317. Result:=Result+Copy(s,1,j-1);
  1318. Delete(s,1,j-1);
  1319. end;
  1320. var
  1321. s1 : string;
  1322. begin
  1323. //writeln('S: ',s);
  1324. s1:=s;
  1325. Result:=GetVar(s1,#0);
  1326. //writeln('R: ',result);
  1327. end;
  1328. function TFPCMake.GetVariable(const inivar:string;dosubst:boolean):string;
  1329. var
  1330. Sec : TFPCMakeSection;
  1331. Dic : TKeyValue;
  1332. i : integer;
  1333. begin
  1334. Result:='';
  1335. i:=Pos('_',inivar);
  1336. if i<>0 then
  1337. begin
  1338. Sec:=TFPCMakeSection(FSections[Copy(Inivar,1,i-1)]);
  1339. if assigned(Sec) then
  1340. begin
  1341. if not assigned(Sec.Dictionary) then
  1342. Sec.ParseIni;
  1343. Dic:=TKeyValue(Sec.Dictionary);
  1344. Result:=Dic[Copy(IniVar,i+1,Length(IniVar)-i)];
  1345. end
  1346. else
  1347. exit;
  1348. end
  1349. else
  1350. Result:=Variables[IniVar];
  1351. { Substition asked ? }
  1352. if dosubst then
  1353. Result:=SubstVariables(Result);
  1354. end;
  1355. function TFPCMake.GetTargetVariable(c:TCPU;t:TOS;const inivar:string;dosubst:boolean):string;
  1356. begin
  1357. result:=Trim(GetVariable(inivar,dosubst)+' '+
  1358. GetVariable(inivar+cpusuffix[c],dosubst)+' '+
  1359. GetVariable(inivar+OSSuffix[t],dosubst)+' '+
  1360. GetVariable(inivar+CpuSuffix[c]+OSSuffix[t],dosubst)+' '+
  1361. GetVariable(inivar+OSSuffix[t]+cpusuffix[c],dosubst));
  1362. end;
  1363. function TFPCMake.HasVariable(const inivar:string):boolean;
  1364. begin
  1365. Result:=(GetVariable(IniVar,false)<>'');
  1366. end;
  1367. function TFPCMake.HasTargetVariable(const inivar:string):boolean;
  1368. var
  1369. c:TCPU;
  1370. t:TOS;
  1371. begin
  1372. result:=false;
  1373. for c:=low(tcpu) to high(tcpu) do
  1374. for t:=low(tos) to high(tos) do
  1375. if FIncludeTargets[c,t] then
  1376. begin
  1377. if (GetVariable(inivar,false)<>'') or
  1378. (GetVariable(inivar+cpusuffix[c],false)<>'') or
  1379. (GetVariable(inivar+OSSuffix[t],false)<>'') or
  1380. (GetVariable(inivar+CpuSuffix[c]+OSSuffix[t],false)<>'') or
  1381. (GetVariable(inivar+OSSuffix[t]+cpusuffix[c],false)<>'') then
  1382. begin
  1383. result:=true;
  1384. exit;
  1385. end;
  1386. end;
  1387. end;
  1388. function TFPCMake.SetVariable(const inivar,value:string;add:boolean):string;
  1389. var
  1390. Sec : TFPCMakeSection;
  1391. P : TKeyValueItem;
  1392. i : integer;
  1393. tempval,key : string;
  1394. begin
  1395. Result:='';
  1396. i:=Pos('_',inivar);
  1397. if i<>0 then
  1398. begin
  1399. Sec:=TFPCMakeSection(FSections[Copy(Inivar,1,i-1)]);
  1400. if Sec=nil then
  1401. Sec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.CreateKeyValue(Copy(Inivar,1,i-1))));
  1402. key:=Copy(IniVar,i+1,Length(IniVar)-i);
  1403. p:=TKeyValueItem(Sec.Dictionary.Search(Key));
  1404. if assigned(p) then
  1405. begin
  1406. if Add then
  1407. AddToken(p.FValue,Value,' ')
  1408. else
  1409. p.Value:=Value;
  1410. end
  1411. else
  1412. TKeyValue(Sec.Dictionary).Add(key,value);
  1413. end
  1414. else
  1415. begin
  1416. if Add then
  1417. begin
  1418. tempval:=Variables[IniVar];
  1419. AddToken(tempval,Value,' ');
  1420. Variables[IniVar]:=tempval;
  1421. end
  1422. else
  1423. Variables[IniVar]:=Value;
  1424. end;
  1425. end;
  1426. procedure TFPCMake.ParseSec(p:TDictionaryItem);
  1427. begin
  1428. TFPCMakeSection(p).ParseIni;
  1429. end;
  1430. procedure TFPCMake.PrintSec(p:TDictionaryItem);
  1431. var
  1432. i : integer;
  1433. begin
  1434. with TFPCMakeSection(p) do
  1435. begin
  1436. Verbose(FPCMakeDebug,'['+Name+']');
  1437. if assigned(FList) then
  1438. begin
  1439. Verbose(FPCMakeDebug,' List:');
  1440. for i:=0 to FList.Count-1 do
  1441. Verbose(FPCMakeDebug,' "'+FList[i]+'"');
  1442. if assigned(FDictionary) then
  1443. Verbose(FPCMakeDebug,'');
  1444. end;
  1445. if assigned(FDictionary) then
  1446. begin
  1447. Verbose(FPCMakeDebug,' Dictionary:');
  1448. FDictionary.Foreach(@PrintDic);
  1449. end;
  1450. end;
  1451. end;
  1452. procedure TFPCMake.PrintDic(p:TDictionaryItem);
  1453. begin
  1454. with TKeyValueItem(p) do
  1455. begin
  1456. Verbose(FPCMakeDebug,' '+name+' = "'+value+'"');
  1457. end;
  1458. end;
  1459. procedure TFPCMake.Print;
  1460. begin
  1461. { global variables }
  1462. Verbose(FPCMakeDebug,'[global variables]');
  1463. Verbose(FPCMakeDebug,' Dictionary:');
  1464. Variables.Foreach(@PrintDic);
  1465. { sections }
  1466. FSections.Foreach(@PrintSec);
  1467. end;
  1468. function TFPCMake.GetSec(const AName:string):TDictionaryItem;
  1469. begin
  1470. GetSec:=FSections.Search(AName);
  1471. end;
  1472. end.