fpcmmain.pp 50 KB

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