fpcmmain.pp 53 KB

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