fpcmmain.pp 59 KB

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