fpcmmain.pp 50 KB

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