fpcmmain.pp 55 KB

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