fpcmmain.pp 51 KB

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