fpcmmain.pp 53 KB

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