fpcmmain.pp 59 KB

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