fpcmmain.pp 41 KB

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