fpcmmain.pp 58 KB

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