fpcmmain.pp 56 KB

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