fpcmmain.pp 58 KB

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