fpcmmain.pp 50 KB

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