fpcmmain.pp 61 KB

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