fpcmmain.pp 52 KB

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