fpcmmain.pp 55 KB

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