fpcmmain.pp 56 KB

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