fpcmmain.pp 53 KB

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