fpcmmain.pp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405
  1. {
  2. $Id$
  3. Copyright (c) 2001 by Peter Vreman
  4. FPCMake - Main module
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$ifdef fpc}{$mode objfpc}{$endif}
  12. {$H+}
  13. unit fpcmmain;
  14. interface
  15. uses
  16. dos,
  17. {$ifdef Unix}
  18. {$ifdef VER1_0}
  19. {$ifdef linux}
  20. linux,
  21. {$endif}
  22. {$else}
  23. unix,
  24. {$endif}
  25. {$endif}
  26. sysutils,classes,
  27. fpcmdic;
  28. {$ifdef BEOS}
  29. {$define NO_UNIX_UNIT}
  30. {$endif}
  31. {$ifdef SUNOS}
  32. {$define NO_UNIX_UNIT}
  33. {$endif}
  34. {$ifdef QNX}
  35. {$define NO_UNIX_UNIT}
  36. {$endif}
  37. const
  38. Version='1.1';
  39. Title='FPCMake Version '+Version;
  40. TitleDate=Title+' ['+{$ifdef fpc}{$i %DATE}{$else}'n/a'{$endif}+']';
  41. type
  42. TTarget=(
  43. t_linux,t_go32v2,t_win32,t_os2,t_freebsd,t_beos,t_netbsd,
  44. t_amiga,t_atari, t_sunos, t_qnx
  45. );
  46. TTargetSet=set of TTarget;
  47. const
  48. TargetStr : array[TTarget] of string=(
  49. 'linux','go32v2','win32','os2','freebsd','beos','netbsd',
  50. 'amiga','atari','sunos', 'qnx'
  51. );
  52. TargetSuffix : array[TTarget] of string=(
  53. '_linux','_go32v2','_win32','_os2','_freebsd','_beos','_netbsd',
  54. '_amiga','_atari','_sunos', '_qnx'
  55. );
  56. type
  57. TKeyValueItem = class(TDictionaryItem)
  58. private
  59. FValue : string;
  60. public
  61. constructor Create(const k,v:string);
  62. property Value:string read FValue write FValue;
  63. end;
  64. TKeyValue = class(TDictionary)
  65. private
  66. function GetKey(const k:string):string;
  67. public
  68. procedure Add(const k,v:String);
  69. property Key[const s:string]:string read GetKey write Add;default;
  70. end;
  71. TFPCMakeSection = class(TDictionaryItem)
  72. private
  73. FList : TStringList;
  74. FDictionary : TKeyValue;
  75. procedure BuildIniDic(p:TDictionaryItem);
  76. procedure BuildMakefileDic(p:TDictionaryItem);
  77. function GetKey(const k:string):string;
  78. public
  79. constructor Create(const n:string);
  80. constructor CreateKeyValue(const n:string);
  81. destructor Destroy;override;
  82. procedure AddLine(const s:string);
  83. procedure AddKey(const k,v:string);
  84. procedure Clear;
  85. procedure ParseIni;
  86. procedure BuildIni;
  87. procedure BuildMakefile;
  88. property Key[const s:string]:string read GetKey;default;
  89. property List:TStringList read FList;
  90. property Dictionary:TKeyValue read FDictionary;
  91. end;
  92. TTargetRequireList = array[ttarget] of TStringList;
  93. TFPCMakeVerbose = (FPCMakeError, FPCMakeInfo, FPCMakeDebug);
  94. TFPCMake = class
  95. private
  96. FStream : TStream;
  97. FFileName : string;
  98. FCommentChars : TSysCharSet;
  99. FEmptyLines : boolean;
  100. FSections : TDictionary;
  101. FPackageSec,
  102. FExportSec : TFPCMakeSection;
  103. FIsPackage : boolean;
  104. FPackageName,
  105. FPackageVersion,
  106. FPackageTargets : string;
  107. FRequireList : TTargetRequireList;
  108. FVariables : TKeyValue;
  109. FIncludeTargets : TTargetSet;
  110. procedure Init;
  111. procedure ParseSec(p:TDictionaryItem);
  112. procedure PrintSec(p:TDictionaryItem);
  113. procedure PrintDic(p:TDictionaryItem);
  114. function GetSec(const AName:string):TDictionaryItem;
  115. procedure LoadRequiredPackage(t:TTarget;const ReqName,ReqVersion:string);
  116. procedure LoadRequiredDir(t:TTarget;const MainPack,currdir,subdir:string);
  117. procedure LoadRequires(t:Ttarget;FromFPCMake:TFPCMake);
  118. function CopySection(Sec:TFPCMakeSection;Secname:string):TFPCMakeSection;
  119. protected
  120. VerboseIdent : string;
  121. public
  122. constructor Create(const AFileName:string);
  123. constructor CreateFromStream(s:TStream;const AFileName:string);
  124. destructor Destroy;override;
  125. procedure Verbose(lvl:TFPCMakeVerbose;const s:string);virtual;
  126. procedure SetTargets(const s:string);
  127. procedure LoadSections;
  128. procedure LoadMakefileFPC;
  129. procedure LoadPackageSection;
  130. procedure LoadRequireSection;
  131. function GetTargetRequires(t:TTarget):TStringList;
  132. function CheckLibcRequire:boolean;
  133. procedure CreateExportSection;
  134. procedure AddDefaultVariables;
  135. function SubstVariables(const s:string):string;
  136. function GetVariable(const inivar:string;dosubst:boolean):string;
  137. function SetVariable(const inivar,value:string;add:boolean):string;
  138. procedure Print;
  139. property Section[const s:string]:TDictionaryItem read GetSec;default;
  140. property RequireList:TTargetRequireList read FRequireList;
  141. property Variables:TKeyValue read FVariables;
  142. property IsPackage:boolean read FIsPackage;
  143. property PackageName:string read FPackageName;
  144. property PackageVersion:string read FPackageVersion;
  145. property PackageSec:TFPCMakeSection read FPackageSec;
  146. property ExportSec:TFPCMakeSection read FExportSec;
  147. property CommentChars:TSysCharSet read FCommentChars write FCommentChars;
  148. property EmptyLines:Boolean read FEmptyLines write FEmptyLines;
  149. property IncludeTargets:TTargetSet read FIncludeTargets write FIncludeTargets;
  150. end;
  151. function posidx(const substr,s : string;idx:integer):integer;
  152. function GetToken(var s:string;sep:char):string;
  153. procedure AddToken(var s:string;const tok:string;sep:char);
  154. implementation
  155. resourcestring
  156. s_not_list_sec='Not a list section "%s"';
  157. s_not_key_value_sec='Not a key-value section "%s"';
  158. s_err_section_start='%s:%d: Wrong section start';
  159. s_err_not_key_value='Parse error key=value excepted: "%s"';
  160. s_err_no_section='%s:%d: Entries without section';
  161. s_no_package_name='No package name set';
  162. s_no_package_version='No package version set';
  163. s_err_require_format='Wrong require format "%s"';
  164. s_wrong_package_name='Package name "%s" expected, but "%s" found';
  165. s_wrong_package_version='Package version "%s" expected, but version "%s" found';
  166. s_directory_not_found='Directory "%s" not found';
  167. s_makefilefpc_not_found='No Makefile.fpc found in directory "%s"';
  168. s_package_not_found='Target "%s", package "%s" not found';
  169. s_fpcmake_version_required='FPCMake version "%s" is required';
  170. s_no_targets_set='No targets set';
  171. s_targets_info='Targets: "%s"';
  172. s_globals='Globals:';
  173. {****************************************************************************
  174. Helpers
  175. ****************************************************************************}
  176. Function PathExists ( F : String) : Boolean;
  177. Var
  178. Info : TSearchRec;
  179. begin
  180. if F[Length(f)] in ['/','\'] then
  181. Delete(f,length(f),1);
  182. PathExists:=(findfirst(F,faAnyFile,info)=0) and
  183. ((info.attr and fadirectory)=fadirectory);
  184. findclose(Info);
  185. end;
  186. Function PathOrFileExists ( F : String) : Boolean;
  187. Var
  188. Info : Dos.SearchRec;
  189. begin
  190. if F[Length(f)] in ['/','\'] then
  191. Delete(f,length(f),1);
  192. dos.findfirst(f,fareadonly+faarchive+fahidden+fadirectory,info);
  193. PathOrFileExists:=(Doserror=0);
  194. dos.findclose(Info);
  195. end;
  196. function posidx(const substr,s : string;idx:integer):integer;
  197. var
  198. i,j : integer;
  199. e : boolean;
  200. begin
  201. i:=idx;
  202. j:=0;
  203. e:=(length(SubStr)>0);
  204. while e and (i<=Length(s)-Length(SubStr)) do
  205. begin
  206. inc(i);
  207. if (SubStr[1]=s[i]) and (Substr=Copy(s,i,Length(SubStr))) then
  208. begin
  209. j:=i;
  210. e:=false;
  211. end;
  212. end;
  213. PosIdx:=j;
  214. end;
  215. function GetToken(var s:string;sep:char):string;
  216. var
  217. i : integer;
  218. begin
  219. s:=Trim(s);
  220. i:=pos(sep,s);
  221. if i=0 then
  222. begin
  223. Result:=s;
  224. s:='';
  225. end
  226. else
  227. begin
  228. Result:=Copy(s,1,i-1);
  229. Delete(s,1,i);
  230. end;
  231. end;
  232. procedure AddToken(var s:string;const tok:string;sep:char);
  233. begin
  234. if tok='' then
  235. exit;
  236. if s<>'' then
  237. s:=s+sep+tok
  238. else
  239. s:=tok;
  240. end;
  241. {****************************************************************************
  242. TKeyValueItem
  243. ****************************************************************************}
  244. constructor TKeyValueItem.Create(const k,v:string);
  245. begin
  246. inherited Create(k);
  247. value:=v;
  248. end;
  249. {****************************************************************************
  250. TKeyValue
  251. ****************************************************************************}
  252. function TKeyValue.GetKey(const k:string):string;
  253. var
  254. p : TKeyValueItem;
  255. begin
  256. p:=TKeyValueItem(Search(k));
  257. if p=nil then
  258. GetKey:=''
  259. else
  260. GetKey:=p.Value;
  261. end;
  262. procedure TKeyValue.Add(const k,v:string);
  263. var
  264. p : TKeyValueItem;
  265. begin
  266. p:=TKeyValueItem(Search(k));
  267. if p=nil then
  268. begin
  269. p:=TKeyValueItem.Create(k,v);
  270. Insert(p);
  271. end
  272. else
  273. p.Value:=v;
  274. end;
  275. {****************************************************************************
  276. TFPCMakeSection
  277. ****************************************************************************}
  278. constructor TFPCMakeSection.Create(const n:string);
  279. begin
  280. inherited Create(n);
  281. FList:=TStringList.Create;
  282. FDictionary:=nil;
  283. end;
  284. constructor TFPCMakeSection.CreateKeyValue(const n:string);
  285. begin
  286. inherited Create(n);
  287. FList:=nil;
  288. FDictionary:=TKeyValue.Create;
  289. end;
  290. destructor TFPCMakeSection.Destroy;
  291. begin
  292. inherited Destroy;
  293. FList.Free;
  294. FDictionary.Free;
  295. end;
  296. procedure TFPCMakeSection.Clear;
  297. begin
  298. FList.Free;
  299. FList:=TStringList.Create;
  300. FDictionary.Free;
  301. FDictionary:=nil;
  302. end;
  303. procedure TFPCMakeSection.AddLine(const s:string);
  304. begin
  305. if FList=nil then
  306. raise Exception.Create(Format(s_not_list_sec,[Name]));
  307. FList.Add(s);
  308. end;
  309. procedure TFPCMakeSection.AddKey(const k,v:string);
  310. begin
  311. if FDictionary=nil then
  312. raise Exception.Create(Format(s_not_key_value_sec,[Name]));
  313. { Don't add empty values }
  314. if v<>'' then
  315. FDictionary.Add(k,v);
  316. end;
  317. function TFPCMakeSection.GetKey(const k:string):string;
  318. begin
  319. if FDictionary=nil then
  320. raise Exception.Create(Format(s_not_key_value_sec,[Name]));
  321. GetKey:=FDictionary[k];
  322. end;
  323. procedure TFPCMakeSection.ParseIni;
  324. var
  325. p : TKeyValueItem;
  326. i,j,len,maxi : integer;
  327. s,newkey,value : string;
  328. begin
  329. { If already processed skip }
  330. if assigned(FDictionary) then
  331. exit;
  332. { Don't process rules section }
  333. if (Name='prerules') or (Name='rules') then
  334. exit;
  335. { Parse the section }
  336. FDictionary:=TKeyValue.Create;
  337. { Parse the list }
  338. maxi:=FList.Count;
  339. i:=0;
  340. while (i<maxi) do
  341. begin
  342. s:=Trim(FList[i]);
  343. len:=Length(s);
  344. { Concat lines ending with \ }
  345. while s[len]='\' do
  346. begin
  347. Delete(s,len,1);
  348. if i+1<maxi then
  349. begin
  350. inc(i);
  351. s:=s+Trim(FList[i]);
  352. len:=Length(s);
  353. end;
  354. end;
  355. { Parse key=value line }
  356. j:=0;
  357. while (j<len) and (s[j+1] in ['A'..'Z','a'..'z','0'..'9','_']) do
  358. inc(j);
  359. NewKey:=Copy(s,1,j);
  360. While (j<len) and (s[j+1] in [' ',#9]) do
  361. inc(j);
  362. inc(j);
  363. if s[j]<>'=' then
  364. Raise Exception.Create(Format(s_err_not_key_value,[s]));
  365. While (j<len) and (s[j+1] in [' ',#9]) do
  366. inc(j);
  367. Value:=Copy(s,j+1,len-j);
  368. p:=TKeyValueItem(FDictionary.Search(NewKey));
  369. { Concat values if key already exists }
  370. if assigned(p) then
  371. AddToken(p.FValue,Value,' ')
  372. else
  373. FDictionary.Add(NewKey,Value);
  374. inc(i);
  375. end;
  376. { List is not used anymore }
  377. FList.Free;
  378. FList:=nil;
  379. end;
  380. procedure TFPCMakeSection.BuildIniDic(p:TDictionaryItem);
  381. begin
  382. with TKeyValueItem(p) do
  383. begin
  384. FList.Add(Name+'='+Value);
  385. end;
  386. end;
  387. procedure TFPCMakeSection.BuildIni;
  388. begin
  389. if assigned(FList) then
  390. exit;
  391. FList:=TStringList.Create;
  392. FDictionary.Foreach(@BuildIniDic);
  393. FDictionary.Free;
  394. FDictionary:=nil;
  395. end;
  396. procedure TFPCMakeSection.BuildMakefileDic(p:TDictionaryItem);
  397. begin
  398. FList.Add(Uppercase(Name+'_'+TKeyValueItem(p).Name)+'='+TKeyValueItem(p).Value);
  399. end;
  400. procedure TFPCMakeSection.BuildMakefile;
  401. begin
  402. if assigned(FList) then
  403. exit;
  404. FList:=TStringList.Create;
  405. FDictionary.Foreach(@BuildMakefileDic);
  406. FDictionary.Free;
  407. FDictionary:=nil;
  408. end;
  409. {****************************************************************************
  410. TFPCMake
  411. ****************************************************************************}
  412. constructor TFPCMake.Create(const AFileName:string);
  413. begin
  414. FFileName:=AFileName;
  415. FStream:=nil;
  416. Init;
  417. end;
  418. constructor TFPCMake.CreateFromStream(s:TStream;const AFileName:string);
  419. begin
  420. FFileName:=AFileName;
  421. FStream:=s;
  422. Init;
  423. end;
  424. procedure TFPCMake.Init;
  425. var
  426. t : ttarget;
  427. begin
  428. FSections:=TDictionary.Create;
  429. for t:=low(ttarget) to high(ttarget) do
  430. FRequireList[t]:=TStringList.Create;
  431. FVariables:=TKeyValue.Create;
  432. FCommentChars:=[';','#'];
  433. FEmptyLines:=false;
  434. FIsPackage:=false;
  435. FPackageName:='';
  436. FPackageVersion:='';
  437. FPackageSec:=nil;
  438. FExportSec:=nil;
  439. FIncludeTargets:=[low(TTarget)..high(TTarget)];
  440. VerboseIdent:='';
  441. end;
  442. destructor TFPCMake.Destroy;
  443. var
  444. t : ttarget;
  445. begin
  446. FSections.Free;
  447. for t:=low(ttarget) to high(ttarget) do
  448. FRequireList[t].Free;
  449. FVariables.Free;
  450. end;
  451. procedure TFPCMake.LoadSections;
  452. var
  453. SLInput : TStringList;
  454. i,j,n : integer;
  455. s,
  456. SecName : string;
  457. CurrSec : TFPCMakeSection;
  458. begin
  459. try
  460. CurrSec:=nil;
  461. SLInput:=TStringList.Create;
  462. if assigned(FStream) then
  463. SLInput.LoadFromStream(FStream)
  464. else
  465. SLInput.LoadFromFile(FFileName);
  466. { Load Input into sections list }
  467. n:=SLInput.Count;
  468. i:=0;
  469. while (i<n) do
  470. begin
  471. s:=Trim(SLInput[i]);
  472. if (EmptyLines and (s='')) or
  473. ((s<>'') and not(s[1] in FCommentChars)) then
  474. begin
  475. { section start? }
  476. if (s<>'') and (s[1]='[') then
  477. begin
  478. j:=pos(']',s);
  479. if j=0 then
  480. raise Exception.Create(Format(s_err_section_start,[FFileName,i+1]));
  481. SecName:=Copy(s,2,j-2);
  482. CurrSec:=TFPCMakeSection(FSections[SecName]);
  483. if CurrSec=nil then
  484. CurrSec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.Create(SecName)));
  485. end
  486. else
  487. begin
  488. if CurrSec=nil then
  489. raise Exception.Create(Format(s_err_no_section,[FFileName,i+1]));
  490. { Insert string without spaces stripped }
  491. CurrSec.AddLine(SLInput[i]);
  492. end;
  493. end;
  494. inc(i);
  495. end;
  496. finally
  497. SLInput.Free;
  498. end;
  499. end;
  500. function TFPCMake.CopySection(Sec:TFPCMakeSection;Secname:string):TFPCMakeSection;
  501. begin
  502. Result:=TFPCMakeSection(FSections[SecName]);
  503. if Sec=Nil then
  504. exit;
  505. { Clear old section or if not existing create new }
  506. if assigned(Result) then
  507. Result.Clear
  508. else
  509. Result:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.Create(SecName)));
  510. Sec.BuildIni;
  511. Result.List.AddStrings(Sec.List);
  512. Result.ParseIni;
  513. Sec.ParseIni;
  514. end;
  515. procedure TFPCMake.LoadMakefileFPC;
  516. begin
  517. LoadSections;
  518. { Parse all sections }
  519. FSections.Foreach(@ParseSec);
  520. { Add some default variables like FPCDIR, UNITSDIR }
  521. AddDefaultVariables;
  522. { Load package section }
  523. LoadPackageSection;
  524. LoadRequireSection;
  525. end;
  526. procedure TFPCMake.Verbose(lvl:TFPCMakeVerbose;const s:string);
  527. begin
  528. writeln(VerboseIdent,s);
  529. end;
  530. procedure TFPCMake.SetTargets(const s:string);
  531. var
  532. hslst : string;
  533. hs : string;
  534. t : TTarget;
  535. begin
  536. FIncludeTargets:=[];
  537. hslst:=s;
  538. repeat
  539. hs:=LowerCase(GetToken(hslst,','));
  540. if hs='' then
  541. break;
  542. for t:=low(TTarget) to high(TTarget) do
  543. if hs=TargetStr[t] then
  544. include(FIncludeTargets,t);
  545. until false;
  546. if FIncludeTargets=[] then
  547. raise Exception.Create(s_no_targets_set)
  548. else
  549. begin
  550. hs:='';
  551. for t:=low(TTarget) to high(TTarget) do
  552. if t in FIncludeTargets then
  553. AddToken(hs,TargetStr[t],' ');
  554. Verbose(FPCMakeDebug,Format(s_targets_info,[hs]));
  555. end;
  556. end;
  557. procedure TFPCMake.LoadPackageSection;
  558. var
  559. hs,s : string;
  560. t : TTarget;
  561. begin
  562. { Get package info from package section }
  563. FPackageSec:=TFPCMakeSection(FSections['package']);
  564. if FPackageSec=nil then
  565. exit;
  566. { Parse the section to key=value pairs }
  567. FPackageSec.ParseIni;
  568. { Are we a subpart of a package, then load that package }
  569. s:=FPackageSec['main'];
  570. if s<>'' then
  571. begin
  572. SetVariable('package_name',s,false);
  573. FPackageName:=s;
  574. end
  575. else
  576. begin
  577. { mandatory name }
  578. FPackageName:=FPackageSec['name'];
  579. if FPackageName='' then
  580. Raise Exception.Create(s_no_package_name);
  581. { mandatory version }
  582. FPackageVersion:=FPackageSec['version'];
  583. if FPackageVersion='' then
  584. Raise Exception.Create(s_no_package_version);
  585. FIsPackage:=true;
  586. { optional targets }
  587. FPackageTargets:='';
  588. s:=LowerCase(FPackageSec['targets']);
  589. repeat
  590. hs:=GetToken(s,' ');
  591. if hs='' then
  592. break;
  593. for t:=low(TTarget) to high(TTarget) do
  594. if hs=TargetStr[t] then
  595. begin
  596. AddToken(FPackageTargets,hs,' ');
  597. break;
  598. end;
  599. until false;
  600. { Set the ExportSec }
  601. FExportSec:=TFPCMakeSection(FSections[Lowercase(FPackageName)]);
  602. end;
  603. end;
  604. procedure TFPCMake.CreateExportSection;
  605. var
  606. t : TTarget;
  607. begin
  608. { Don't create a section twice }
  609. if FExportSec<>nil then
  610. exit;
  611. { Look if we've already an own section, else create a new
  612. key-value section }
  613. FExportSec:=TFPCMakeSection(FSections[LowerCase(FPackageName)]);
  614. if FExportSec=nil then
  615. FExportSec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.CreateKeyValue(LowerCase(FPackageName))));
  616. { Add default the values to the export section }
  617. FExportSec.AddKey('name',FPackageName);
  618. FExportSec.AddKey('version',FPackageVersion);
  619. { Add required packages }
  620. for t:=low(TTarget) to high(TTarget) do
  621. FExportSec.AddKey('require'+TargetSuffix[t],FPackageSec['require'+TargetSuffix[t]]);
  622. { Unit dir }
  623. {FExportSec.AddKey('unitdir','$(UNITSDIR)/'+Lowercase(PackageName));}
  624. end;
  625. procedure TFPCMake.LoadRequiredPackage(t:TTarget;const ReqName,ReqVersion:string);
  626. function TryFile(const fn:string):boolean;
  627. var
  628. ReqFPCMake : TFPCMake;
  629. begin
  630. TryFile:=false;
  631. if FileExists(fn) then
  632. begin
  633. VerboseIdent:=VerboseIdent+' ';
  634. Verbose(FPCMakeDebug,'Package '+ReqName+': '+fn);
  635. ReqFPCMake:=TFPCMake.Create(fn);
  636. ReqFPCMake.LoadSections;
  637. ReqFPCMake.LoadPackageSection;
  638. { Check package name and version }
  639. if LowerCase(ReqFPCMake.PackageName)<>ReqName then
  640. raise Exception.Create(Format(s_wrong_package_name,[ReqName,LowerCase(ReqFPCMake.PackageName)]));
  641. if (ReqVersion<>'') and (ReqFPCMake.PackageVersion<ReqVersion) then
  642. raise Exception.Create(Format(s_wrong_package_version,[ReqVersion,ReqFPCMake.PackageVersion]));
  643. { First load the requirements of this package }
  644. LoadRequires(t,ReqFPCMake);
  645. { Get a copy of the package section }
  646. CopySection(ReqFPCMake.PackageSec,ReqName+'_package');
  647. { Get a copy of the export section }
  648. CopySection(ReqFPCMake.ExportSec,ReqName);
  649. { Get a copy of the require section }
  650. CopySection(TFPCMakeSection(ReqFPCMake['require']),ReqName+'_require');
  651. { Free }
  652. ReqFPCMake.Free;
  653. Delete(VerboseIdent,1,2);
  654. TryFile:=true;
  655. end;
  656. end;
  657. var
  658. s : string;
  659. begin
  660. { Force the current target }
  661. SetVariable('TARGET',TargetStr[t],false);
  662. { Check for Makefile.fpc }
  663. s:=SubstVariables('$(addsuffix /'+ReqName+'/Makefile.fpc,$(FPCDIR)) $(addsuffix /'+ReqName+'/Makefile.fpc,$(PACKAGESDIR)) $(addsuffix /'+ReqName+'/Makefile.fpc,$(REQUIRE_PACKAGESDIR))');
  664. Verbose(FPCMakeDebug,'Looking for Makefile.fpc: "'+s+'"');
  665. s:=SubstVariables('$(firstword $(wildcard '+s+'))');
  666. if TryFile(s) then
  667. exit;
  668. { Check for Package.fpc }
  669. s:=SubstVariables('$(addsuffix /'+ReqName+'/Package.fpc,$(FPCDIR)) $(addsuffix /'+ReqName+'/Package.fpc,$(UNITSDIR)) $(addsuffix /'+ReqName+'/Package.fpc,$(REQUIRE_UNITSDIR))');
  670. Verbose(FPCMakeDebug,'Looking for Package.fpc: "'+s+'"');
  671. s:=SubstVariables('$(firstword $(wildcard '+s+'))');
  672. if TryFile(s) then
  673. exit;
  674. Raise Exception.Create(Format(s_package_not_found,[TargetStr[t],Reqname]));
  675. end;
  676. procedure TFPCMake.LoadRequiredDir(t:TTarget;const MainPack,currdir,subdir:string);
  677. var
  678. ReqFPCMake : TFPCMake;
  679. s : string;
  680. begin
  681. VerboseIdent:=VerboseIdent+' ';
  682. s:=currdir+subdir;
  683. Verbose(FPCMakeDebug,'Subdir: '+s+'/Makefile.fpc');
  684. if not FileExists(s+'/Makefile.fpc') then
  685. begin
  686. { give better error what is wrong }
  687. if not PathExists(s) then
  688. Raise Exception.Create(Format(s_directory_not_found,[s]))
  689. else
  690. Raise Exception.Create(Format(s_makefilefpc_not_found,[s]));
  691. end;
  692. { Process Makefile.fpc }
  693. ReqFPCMake:=TFPCMake.Create(currdir+subdir+'/Makefile.fpc');
  694. ReqFPCMake.LoadSections;
  695. ReqFPCMake.LoadPackageSection;
  696. { Are we a subpackage? }
  697. if (ReqFPCMake.GetVariable('package_name',false)<>MainPack) then
  698. begin
  699. ReqFPCMake.Free;
  700. Delete(VerboseIdent,1,2);
  701. exit;
  702. end;
  703. { Load the requirements of this package }
  704. LoadRequires(t,ReqFPCMake);
  705. { Add the current requirements to our parents requirements }
  706. s:=Trim(ReqFPCMake.GetVariable('require_packages',true)+' '+ReqFPCMake.GetVariable('require_packages'+targetsuffix[t],true));
  707. SetVariable('require_packages'+targetsuffix[t],s,true);
  708. if ReqFPCMake.GetVariable('require_libc',false)<>'' then
  709. SetVariable('require_libc','y',false);
  710. { Free }
  711. ReqFPCMake.Free;
  712. Delete(VerboseIdent,1,2);
  713. end;
  714. procedure TFPCMake.LoadRequires(t:Ttarget;FromFPCMake:TFPCMake);
  715. var
  716. s,
  717. ReqDir,
  718. ReqName,
  719. ReqVersion : string;
  720. i,j : integer;
  721. begin
  722. { packages }
  723. s:=Trim(FromFPCMake.GetVariable('require_packages',true)+' '+FromFPCMake.GetVariable('require_packages'+TargetSuffix[t],true));
  724. Verbose(FPCMakeDebug,'Required packages for '+TargetStr[t]+': '+s);
  725. repeat
  726. reqname:=GetToken(s,' ');
  727. if reqname='' then
  728. break;
  729. i:=Pos('(',ReqName);
  730. if i>0 then
  731. begin
  732. j:=Pos(')',ReqName);
  733. if (i=1) or (j=0) then
  734. Raise Exception.Create(Format(s_err_require_format,[ReqName]));
  735. ReqVersion:=Copy(ReqName,i+1,j-i-1);
  736. ReqName:=Copy(ReqName,1,i-1);
  737. end
  738. else
  739. ReqVersion:='';
  740. { We only use lowercase names }
  741. ReqName:=Lowercase(ReqName);
  742. { Already loaded ? }
  743. if (RequireList[t].IndexOf(ReqName)=-1) then
  744. begin
  745. LoadRequiredPackage(t,ReqName,ReqVersion);
  746. RequireList[t].Add(ReqName);
  747. end;
  748. until false;
  749. { sub dirs }
  750. s:=FromFPCMake.GetVariable('target_dirs',true)+' '+FromFPCMake.GetVariable('target_dirs'+TargetSuffix[t],true);
  751. Verbose(FPCMakeDebug,'Required dirs for '+TargetStr[t]+': '+s);
  752. repeat
  753. reqdir:=GetToken(s,' ');
  754. if reqdir='' then
  755. break;
  756. LoadRequiredDir(t,FromFPCMake.FPackageName,ExtractFilePath(FromFPCMake.FFileName),ReqDir)
  757. until false;
  758. end;
  759. procedure TFPCMake.LoadRequireSection;
  760. function CheckVar(const s:string):boolean;
  761. var
  762. t : ttarget;
  763. begin
  764. result:=false;
  765. if GetVariable(s,false)<>'' then
  766. begin
  767. result:=true;
  768. exit;
  769. end;
  770. for t:=low(ttarget) to high(ttarget) do
  771. if t in FIncludeTargets then
  772. begin
  773. if GetVariable(s+targetsuffix[t],false)<>'' then
  774. begin
  775. result:=true;
  776. exit;
  777. end;
  778. end;
  779. end;
  780. var
  781. s : string;
  782. t : ttarget;
  783. begin
  784. { Check FPCMake version }
  785. s:=GetVariable('require_fpcmake',false);
  786. if (s>version) then
  787. raise Exception.Create(Format(s_fpcmake_version_required,[s]));
  788. { Maybe add an implicit rtl dependency if there is something
  789. to compile }
  790. s:=GetVariable('require_packages',false);
  791. if (GetVariable('require_nortl',false)='') and
  792. (CheckVar('target_programs') or
  793. CheckVar('target_units') or
  794. CheckVar('target_examples')) and
  795. (Pos('rtl(',s)=0) then
  796. begin
  797. s:='rtl '+s;
  798. SetVariable('require_packages',s,false);
  799. end;
  800. { Load recursively all required packages starting with this Makefile.fpc }
  801. for t:=low(TTarget) to high(TTarget) do
  802. if t in FIncludeTargets then
  803. LoadRequires(t,self);
  804. end;
  805. function TFPCMake.GetTargetRequires(t:TTarget):TStringList;
  806. var
  807. ReqSec : TFPCMakeSection;
  808. ReqList : TStringList;
  809. procedure AddReqSec(t:TTarget;Sec:TFPCMakeSection);
  810. var
  811. s,
  812. ReqName : string;
  813. RSec : TFPCMakeSection;
  814. i : integer;
  815. begin
  816. s:=Sec['packages']+' '+Sec['packages'+TargetSuffix[t]];
  817. repeat
  818. ReqName:=GetToken(s,' ');
  819. if ReqName='' then
  820. break;
  821. i:=Pos('(',ReqName);
  822. if i>0 then
  823. ReqName:=Copy(ReqName,1,i-1);
  824. { We only use lowercase names }
  825. ReqName:=Lowercase(ReqName);
  826. { Already loaded ? }
  827. if (ReqList.IndexOf(ReqName)=-1) then
  828. begin
  829. RSec:=TFPCMakeSection(FSections[ReqName+'_require']);
  830. if assigned(RSec) then
  831. AddReqSec(t,RSec);
  832. ReqList.Add(ReqName);
  833. end;
  834. until false;
  835. end;
  836. begin
  837. ReqList:=TStringList.Create;
  838. ReqSec:=TFPCMakeSection(FSections['require']);
  839. if assigned(ReqSec) then
  840. AddReqSec(t,ReqSec);
  841. GetTargetRequires:=ReqList;
  842. end;
  843. function TFPCMake.CheckLibcRequire:boolean;
  844. var
  845. i : integer;
  846. RSec : TFPCMakeSection;
  847. t : ttarget;
  848. begin
  849. Result:=false;
  850. if GetVariable('require_libc',false)<>'' then
  851. begin
  852. Result:=true;
  853. exit;
  854. end;
  855. for t:=low(ttarget) to high(ttarget) do
  856. if t in FIncludeTargets then
  857. begin
  858. for i:=0 to RequireList[t].Count-1 do
  859. begin
  860. RSec:=TFPCMakeSection(FSections[RequireList[t][i]+'_require']);
  861. if assigned(RSec) then
  862. begin
  863. if RSec['libc']<>'' then
  864. begin
  865. Result:=true;
  866. exit;
  867. end;
  868. end;
  869. end;
  870. end;
  871. end;
  872. procedure TFPCMake.AddDefaultVariables;
  873. var
  874. hs,s : string;
  875. begin
  876. { Already set FPCDIR }
  877. hs:='';
  878. s:=GetVariable('FPCDIR',false);
  879. if s<>'' then
  880. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  881. { Load from environment }
  882. if hs='' then
  883. begin
  884. s:=GetEnv('FPCDIR');
  885. if s<>'' then
  886. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  887. end;
  888. { default_fpcdir }
  889. if hs='' then
  890. begin
  891. s:=GetVariable('default_fpcdir',true);
  892. { add the current subdir to relative paths }
  893. if s<>'' then
  894. begin
  895. {$ifdef UNIX}
  896. if (s[1]<>'/') then
  897. {$else}
  898. if (length(s)>2) and (s[2]<>':') then
  899. {$endif}
  900. s:=ExtractFilePath(FFileName)+s;
  901. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  902. end
  903. end;
  904. { OS defaults }
  905. if hs='' then
  906. begin
  907. {$ifdef UNIX}
  908. {$ifndef NO_UNIX_UNIT}
  909. if FileExists('/usr/local/bin/ppc386') then
  910. begin
  911. s:=ExtractFilePath(ReadLink('/usr/local/bin/ppc386'));
  912. if s<>'' then
  913. begin
  914. if s[length(s)]='/' then
  915. delete(s,length(s),1);
  916. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  917. end;
  918. end;
  919. if hs='' then
  920. begin
  921. if FileExists('/usr/bin/ppc386') then
  922. begin
  923. s:=ExtractFilePath(ReadLink('/usr/bin/ppc386'));
  924. if s<>'' then
  925. begin
  926. if s[length(s)]='/' then
  927. delete(s,length(s),1);
  928. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  929. end;
  930. end;
  931. end;
  932. {$endif}
  933. {$else UNIX}
  934. hs:=ExtractFilePath(FSearch('ppc386.exe',getenv('PATH')));
  935. if hs<>'' then
  936. begin
  937. s:=hs+'/..';
  938. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  939. if hs='' then
  940. begin
  941. s:=s+'/..';
  942. hs:=SubstVariables('$(wildcard $(addprefix '+s+'/,rtl units))');
  943. end;
  944. end;
  945. if hs='' then
  946. s:='c:/pp';
  947. {$endif UNIX}
  948. end;
  949. SetVariable('FPCDIR',s,false);
  950. { PACKAGESDIR }
  951. if GetVariable('PACKAGESDIR',false)='' then
  952. SetVariable('PACKAGESDIR','$(FPCDIR)/packages',false);
  953. { UNITSDIR }
  954. if GetVariable('UNITSDIR',false)='' then
  955. SetVariable('UNITSDIR','$(FPCDIR)/units/$(TARGET)',false);
  956. Verbose(FPCMakeDebug,s_globals);
  957. Variables.Foreach(@PrintDic);
  958. end;
  959. function TFPCMake.SubstVariables(const s:string):string;
  960. function Expect(var s:string;c:char):boolean;
  961. begin
  962. if (s<>'') and (s[1]=c) then
  963. begin
  964. Delete(s,1,1);
  965. Result:=true;
  966. end
  967. else
  968. begin
  969. Verbose(FPCMakeError,'Error "'+c+'" expected');
  970. Result:=false;
  971. end;
  972. end;
  973. function GetVar(var s:string;untilc:char):string;
  974. var
  975. i,j,k : integer;
  976. first : boolean;
  977. func,
  978. tok,s1,s2,s3 : string;
  979. Sec : TFPCMakeSection;
  980. begin
  981. Result:='';
  982. repeat
  983. j:=Pos(untilc,s);
  984. if j=0 then
  985. j:=Length(s)+1;
  986. i:=Pos('$(',s);
  987. if (j<i) or (i=0) then
  988. break;
  989. Result:=Result+Copy(s,1,i-1);
  990. Delete(s,1,i+1);
  991. { Maybe Function ? }
  992. j:=Pos(')',s);
  993. if j=0 then
  994. j:=Length(s)+1;
  995. i:=Pos(' ',s);
  996. if i=0 then
  997. i:=Length(s)+1;
  998. if i<j then
  999. begin
  1000. { It's a function }
  1001. Func:=Copy(s,1,i-1);
  1002. //writeln('func: ',func);
  1003. { $(wildcard <list>) }
  1004. if Func='wildcard' then
  1005. begin
  1006. Delete(s,1,9);
  1007. s1:=GetVar(s,')');
  1008. Expect(s,')');
  1009. first:=true;
  1010. repeat
  1011. tok:=GetToken(s1,' ');
  1012. if tok='' then
  1013. break;
  1014. if PathOrFileExists(tok) then
  1015. begin
  1016. if not first then
  1017. Result:=Result+' '
  1018. else
  1019. first:=false;
  1020. Result:=Result+tok;
  1021. end;
  1022. until false;
  1023. end
  1024. { $(addprefix <suffix>,<list>) }
  1025. else if Func='addprefix' then
  1026. begin
  1027. Delete(s,1,10);
  1028. s1:=GetVar(s,',');
  1029. if Expect(s,',') then
  1030. begin
  1031. s2:=GetVar(s,')');
  1032. Expect(s,')');
  1033. end;
  1034. first:=true;
  1035. repeat
  1036. tok:=GetToken(s2,' ');
  1037. if tok='' then
  1038. break;
  1039. if not first then
  1040. Result:=Result+' '
  1041. else
  1042. first:=false;
  1043. Result:=Result+s1+tok;
  1044. until false;
  1045. end
  1046. { $(addsuffix <suffix>,<list>) }
  1047. else if Func='addsuffix' then
  1048. begin
  1049. Delete(s,1,10);
  1050. s1:=GetVar(s,',');
  1051. if Expect(s,',') then
  1052. begin
  1053. s2:=GetVar(s,')');
  1054. Expect(s,')');
  1055. end;
  1056. first:=true;
  1057. repeat
  1058. tok:=GetToken(s2,' ');
  1059. if tok='' then
  1060. break;
  1061. if not first then
  1062. Result:=Result+' '
  1063. else
  1064. first:=false;
  1065. Result:=Result+tok+s1;
  1066. until false;
  1067. end
  1068. { $(firstword <list>) }
  1069. else if Func='firstword' then
  1070. begin
  1071. Delete(s,1,10);
  1072. s1:=GetVar(s,')');
  1073. Expect(s,')');
  1074. Result:=GetToken(s1,' ');
  1075. end
  1076. end
  1077. else
  1078. begin
  1079. s2:=Copy(s,1,j-1);
  1080. Delete(s,1,j);
  1081. k:=pos('_',s2);
  1082. if k>0 then
  1083. begin
  1084. s3:=LowerCase(Copy(s2,k+1,Length(s2)-k));
  1085. s2:=LowerCase(Copy(s2,1,k-1));
  1086. Sec:=TFPCMakeSection(Section[s2]);
  1087. if assigned(Sec) then
  1088. s2:=Sec[s3]
  1089. else
  1090. s2:='';
  1091. end
  1092. else
  1093. s2:=Variables[s2];
  1094. Insert(s2,s,1);
  1095. end;
  1096. until false;
  1097. Result:=Result+Copy(s,1,j-1);
  1098. Delete(s,1,j-1);
  1099. end;
  1100. var
  1101. s1 : string;
  1102. begin
  1103. //writeln('S: ',s);
  1104. s1:=s;
  1105. Result:=GetVar(s1,#0);
  1106. //writeln('R: ',result);
  1107. end;
  1108. function TFPCMake.GetVariable(const inivar:string;dosubst:boolean):string;
  1109. var
  1110. Sec : TFPCMakeSection;
  1111. Dic : TKeyValue;
  1112. i : integer;
  1113. begin
  1114. Result:='';
  1115. i:=Pos('_',inivar);
  1116. if i<>0 then
  1117. begin
  1118. Sec:=TFPCMakeSection(FSections[Copy(Inivar,1,i-1)]);
  1119. if assigned(Sec) then
  1120. begin
  1121. if not assigned(Sec.Dictionary) then
  1122. Sec.ParseIni;
  1123. Dic:=TKeyValue(Sec.Dictionary);
  1124. Result:=Dic[Copy(IniVar,i+1,Length(IniVar)-i)];
  1125. end
  1126. else
  1127. exit;
  1128. end
  1129. else
  1130. Result:=Variables[IniVar];
  1131. { Substition asked ? }
  1132. if dosubst then
  1133. Result:=SubstVariables(Result);
  1134. end;
  1135. function TFPCMake.SetVariable(const inivar,value:string;add:boolean):string;
  1136. var
  1137. Sec : TFPCMakeSection;
  1138. P : TKeyValueItem;
  1139. i : integer;
  1140. key : string;
  1141. begin
  1142. Result:='';
  1143. i:=Pos('_',inivar);
  1144. if i<>0 then
  1145. begin
  1146. Sec:=TFPCMakeSection(FSections[Copy(Inivar,1,i-1)]);
  1147. if Sec=nil then
  1148. Sec:=TFPCMakeSection(FSections.Insert(TFPCMakeSection.CreateKeyValue(Copy(Inivar,1,i-1))));
  1149. key:=Copy(IniVar,i+1,Length(IniVar)-i);
  1150. p:=TKeyValueItem(Sec.Dictionary.Search(Key));
  1151. if assigned(p) then
  1152. begin
  1153. if Add then
  1154. AddToken(p.FValue,Value,' ')
  1155. else
  1156. p.Value:=Value;
  1157. end
  1158. else
  1159. TKeyValue(Sec.Dictionary).Add(key,value);
  1160. end
  1161. else
  1162. Variables[IniVar]:=value;
  1163. end;
  1164. procedure TFPCMake.ParseSec(p:TDictionaryItem);
  1165. begin
  1166. TFPCMakeSection(p).ParseIni;
  1167. end;
  1168. procedure TFPCMake.PrintSec(p:TDictionaryItem);
  1169. var
  1170. i : integer;
  1171. begin
  1172. with TFPCMakeSection(p) do
  1173. begin
  1174. Verbose(FPCMakeDebug,'['+Name+']');
  1175. if assigned(FList) then
  1176. begin
  1177. Verbose(FPCMakeDebug,' List:');
  1178. for i:=0 to FList.Count-1 do
  1179. Verbose(FPCMakeDebug,' "'+FList[i]+'"');
  1180. if assigned(FDictionary) then
  1181. Verbose(FPCMakeDebug,'');
  1182. end;
  1183. if assigned(FDictionary) then
  1184. begin
  1185. Verbose(FPCMakeDebug,' Dictionary:');
  1186. FDictionary.Foreach(@PrintDic);
  1187. end;
  1188. end;
  1189. end;
  1190. procedure TFPCMake.PrintDic(p:TDictionaryItem);
  1191. begin
  1192. with TKeyValueItem(p) do
  1193. begin
  1194. Verbose(FPCMakeDebug,' '+name+' = "'+value+'"');
  1195. end;
  1196. end;
  1197. procedure TFPCMake.Print;
  1198. begin
  1199. { global variables }
  1200. Verbose(FPCMakeDebug,'[global variables]');
  1201. Verbose(FPCMakeDebug,' Dictionary:');
  1202. Variables.Foreach(@PrintDic);
  1203. { sections }
  1204. FSections.Foreach(@PrintSec);
  1205. end;
  1206. function TFPCMake.GetSec(const AName:string):TDictionaryItem;
  1207. begin
  1208. GetSec:=FSections.Search(AName);
  1209. end;
  1210. end.
  1211. {
  1212. $Log$
  1213. Revision 1.17 2001-12-15 04:16:57 carl
  1214. + clean support for QNX / BeOS and SunOS targets
  1215. Revision 1.16 2001/12/11 23:01:56 carl
  1216. + Added SunOS and QNX targets
  1217. Revision 1.15 2001/10/14 21:38:32 peter
  1218. * cross compiling support
  1219. Revision 1.14 2001/09/29 19:47:50 carl
  1220. * make it work for BeOS
  1221. Revision 1.13 2001/08/22 20:45:19 peter
  1222. * firstword added
  1223. * pathexist fix to include sysfile
  1224. Revision 1.12 2001/08/10 10:28:55 pierre
  1225. + netbsd target added
  1226. Revision 1.11 2001/08/02 20:50:29 peter
  1227. * -T<target> support
  1228. * better error reporting for not found dirs
  1229. * some cleanups and nicer strings
  1230. Revision 1.10 2001/07/31 22:02:32 peter
  1231. * install Package.fpc
  1232. Revision 1.9 2001/07/24 09:06:40 pierre
  1233. + added amiga and atari targets
  1234. Revision 1.8 2001/07/13 21:01:59 peter
  1235. * cygdrive support
  1236. * fixed cygwin detection
  1237. * fixed some duplicate and extraeous spaces
  1238. Revision 1.7 2001/06/04 21:42:57 peter
  1239. * Arguments added
  1240. * Start of Package.fpc creation
  1241. Revision 1.6 2001/06/02 19:20:24 peter
  1242. * beos target added
  1243. Revision 1.5 2001/02/22 21:11:24 peter
  1244. * fpcdir detection added
  1245. * fixed loading of variables in fpcmake itself
  1246. Revision 1.4 2001/02/05 20:44:56 peter
  1247. * variable substition like GNU Make. wildcard,addprefix,addsuffix
  1248. already implemented
  1249. Revision 1.3 2001/02/01 22:00:10 peter
  1250. * default.fpcdir is back
  1251. * subdir requirement checking works, but not very optimal yet as
  1252. it can load the same Makefile.fpc multiple times
  1253. Revision 1.2 2001/01/29 21:49:10 peter
  1254. * lot of updates
  1255. Revision 1.1 2001/01/24 21:59:36 peter
  1256. * first commit of new fpcmake
  1257. }