fpcmmain.pp 52 KB

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