fpcmmain.pp 56 KB

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