fpcmmain.pp 60 KB

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