fpcmmain.pp 53 KB

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