fpcmmain.pp 61 KB

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