fpcmmain.pp 59 KB

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