fpcmmain.pp 56 KB

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