fpcmmain.pp 50 KB

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