fpcmmain.pp 57 KB

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