fpcmmain.pp 50 KB

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