fpcmmain.pp 54 KB

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