fpcmmain.pp 58 KB

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