fpcmmain.pp 50 KB

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