fpcmmain.pp 52 KB

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