fpcmmain.pp 53 KB

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