fpcmmain.pp 54 KB

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