fpcmmain.pp 51 KB

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