fpcmmain.pp 54 KB

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