fpcmkcfg.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. {$mode objfpc}
  2. {$H+}
  3. {
  4. This file is part of Free Pascal Build tools
  5. Copyright (c) 2005 by Michael Van Canneyt
  6. Create a configuration file
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. program fpcmkcfg;
  14. uses
  15. SysUtils,
  16. Classes,
  17. {$ifdef unix}
  18. baseunix,
  19. {$endif}
  20. fpTemplate,
  21. process;
  22. {
  23. The inc files must be built from a template with the data2inc
  24. command.
  25. data2inc -b -s fpc.cft fpccfg.inc DefaultConfig
  26. data2inc -b -s fpinc.ini fpini.inc fpini
  27. data2inc -b -s fpinc.cfg fpcfg.inc fpcfg
  28. data2inc -b -s fppkg.cfg fppkg.inc fppkg
  29. data2inc -b -s default.cft default.inc fppkg_default
  30. }
  31. {$i fpccfg.inc}
  32. {$i fpini.inc}
  33. {$i fpcfg.inc}
  34. {$i fppkg.inc}
  35. {$i default.inc}
  36. Const
  37. BuildVersion={$I %FPCVERSION%};
  38. BuildTarget={$I %FPCTARGET%};
  39. BuildOSTarget={$I %FPCTARGETOS%};
  40. {$ifdef unix}
  41. ExeExt = '';
  42. {$else unix}
  43. ExeExt = '.exe';
  44. {$endif unix}
  45. Resourcestring
  46. SUsage00 = 'Usage: %s [options]';
  47. SUsage10 = 'Where options is one or more of';
  48. SUSage20 = ' -t filename Template file name. Default is built-in';
  49. SUSage30 = ' -o filename Set output file. Default is standard output.';
  50. SUsage40 = ' -d name=value define name=value pair.';
  51. SUsage50 = ' -h show this help and exit.';
  52. SUsage60 = ' -u name remove name from list of name/value pairs.';
  53. // SUsage70 = ' -l filename read name/value pairs from filename';
  54. SUsage70 = ' -m show builtin macros and exit.';
  55. SUsage80 = ' -b show builtin template and exit.';
  56. SUsage84 = ' -s skip the creation of a backup-file.';
  57. SUsage87 = ' -p force directory creation.';
  58. SUsage90 = ' -v be verbose.';
  59. Susage100 = ' -0 use built in fpc.cfg template (default)';
  60. Susage110 = ' -1 use built in fp.cfg template';
  61. Susage120 = ' -2 use built in fp.ini template';
  62. Susage130 = ' -3 use built in fppkg.cfg template';
  63. Susage140 = ' -4 use built in fppkg default compiler template';
  64. SErrUnknownOption = 'Error: Unknown option.';
  65. SErrArgExpected = 'Error: Option "%s" requires an argument.';
  66. SErrIncompletePair = 'Error: Incomplete name-value pair "%s".';
  67. SErrNoSuchFile = 'Error: File "%s" does not exist.';
  68. SErrNoSuchDirectory = 'Error: Directory of file "%s" does not exists. User -p to force creation.';
  69. SErrBackupFailed = 'Error: Backup of file "%s" to "%s" failed.';
  70. SErrDelBackupFailed = 'Error: Delete of old backup file "%s" failed.';
  71. SErrCreateDirFailed = 'Error: Could not create the directory for file "%s".';
  72. SErrDestDirectory = 'Error: The output file "%s" is a directory.';
  73. SWarnIgnoringFile = 'Warning: Ignoring non-existent file: ';
  74. SWarnIgnoringPair = 'Warning: Ignoring wrong name/value pair: ';
  75. SWarngccNotFound = 'Warning: Could not find gcc. Unable to determine the gcclib path.';
  76. SWarnCouldNotExecute= 'Warning: Could not execute command ''%s''';
  77. SBackupCreated = 'Saved old "%s" to "%s"';
  78. Var
  79. Verbose : Boolean;
  80. SkipBackup : Boolean;
  81. CreateDir: Boolean;
  82. Cfg : TStringList;
  83. TemplateParser: TTemplateParser;
  84. TemplateFileName,
  85. OutputFileName : String;
  86. IDEBuildin : Integer;
  87. function IsSuperUser:boolean;
  88. begin
  89. {$ifdef unix}
  90. result:=(fpGetUID=0);
  91. {$else unix}
  92. result:=false;
  93. {$endif unix}
  94. end;
  95. function GetDefaultLocalRepository: string;
  96. begin
  97. {$IFDEF Unix}
  98. result := '{UserDir}.fppkg'+PathDelim;
  99. {$ELSE Unix}
  100. result := '{AppConfigDir}';
  101. {$ENDIF Unix}
  102. end;
  103. function GetDefaultLocalBasepath: string;
  104. begin
  105. {$IFDEF Unix}
  106. result := '~/.fppkg'+PathDelim+'lib'+PathDelim+'fpc'+PathDelim+'$fpcversion';
  107. {$ELSE Unix}
  108. result := '$LOCAL_APPDATA'+PathDelim+'FreePascal'+PathDelim+'fppkg';
  109. {$ENDIF Unix}
  110. end;
  111. function GetDefaultCompilerConfigDir: string;
  112. begin
  113. {$IFDEF Unix}
  114. if IsSuperUser then
  115. result := '/etc/fppkg/'
  116. else
  117. {$ENDIF}
  118. result := '{LocalRepository}config/';
  119. end;
  120. function GetDefaultNeedCrossBinutilsIfdef: string;
  121. begin
  122. result := '';
  123. // On Darwin there is never a need for a crossbinutils prefix
  124. if SameText(BuildOSTarget,'Darwin') then
  125. result := '#IFNDEF ' + BuildOSTarget + LineEnding +
  126. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  127. '#ENDIF'
  128. else if (BuildTarget = 'i386') or (BuildTarget = 'x86_64') then
  129. begin
  130. // Cross-binutils are not needed to compile for i386 on an x86_64 system
  131. result := '#IFNDEF CPUI386' + LineEnding +
  132. '#IFNDEF CPUAMD64' + LineEnding +
  133. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  134. '#ENDIF' + LineEnding +
  135. '#ENDIF' + LineEnding +
  136. LineEnding +
  137. '#IFNDEF ' + BuildOSTarget + LineEnding +
  138. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  139. '#ENDIF';
  140. end
  141. else
  142. result := '#DEFINE NEEDCROSSBINUTILS';
  143. end;
  144. function GetDefaultGCCDir: string;
  145. var GccExecutable: string;
  146. function GetGccExecutable: string;
  147. begin
  148. if GccExecutable='' then
  149. begin
  150. GccExecutable := ExeSearch('gcc'+ExeExt,GetEnvironmentVariable('PATH'));
  151. if GccExecutable='' then
  152. begin
  153. Writeln(StdErr,SWarngccNotFound);
  154. GccExecutable:='-';
  155. end;
  156. end;
  157. if GccExecutable = '-' then
  158. result := ''
  159. else
  160. result := GccExecutable;
  161. end;
  162. function ExecuteProc(const CommandLine: string; ReadStdErr: boolean) : string;
  163. const BufSize=2048;
  164. var S: TProcess;
  165. buf: array[0..BufSize-1] of byte;
  166. count: integer;
  167. begin
  168. result := '';
  169. S:=TProcess.Create(Nil);
  170. try
  171. S.Commandline:=CommandLine;
  172. S.Options:=[poUsePipes,poWaitOnExit];
  173. try
  174. S.execute;
  175. Count:=s.output.read(buf,BufSize);
  176. if (count=0) and ReadStdErr then
  177. Count:=s.Stderr.read(buf,BufSize);
  178. setlength(result,count);
  179. move(buf[0],result[1],count);
  180. except
  181. Writeln(StdErr,Format(SWarnCouldNotExecute,[CommandLine]));
  182. end;
  183. finally
  184. S.Free;
  185. end;
  186. end;
  187. function Get4thWord(const AString: string): string;
  188. var p: pchar;
  189. spacecount: integer;
  190. StartWord: pchar;
  191. begin
  192. if length(AString)>6 then
  193. begin
  194. p := @AString[1];
  195. spacecount:=0;
  196. StartWord:=nil;
  197. while (not (p^ in [#0,#10,#13])) and ((p^<>' ') or (StartWord=nil)) do
  198. begin
  199. if p^=' ' then
  200. begin
  201. inc(spacecount);
  202. if spacecount=3 then StartWord:=p+1;
  203. end;
  204. inc(p);
  205. end;
  206. if StartWord<>nil then
  207. begin
  208. SetLength(result,p-StartWord);
  209. move(StartWord^,result[1],p-StartWord);
  210. end
  211. else
  212. result := '';
  213. end;
  214. end;
  215. function GetGccDirArch(const ACpuType, GCCParams: string) : string;
  216. var ExecResult: string;
  217. libgccFilename: string;
  218. gccDir: string;
  219. begin
  220. if FileExists(GetGccExecutable) then
  221. begin
  222. ExecResult:=ExecuteProc(GetGccExecutable+' -v '+GCCParams, True);
  223. libgccFilename:=Get4thWord(ExecResult);
  224. if libgccFilename='' then
  225. libgccFilename:=ExecuteProc(GetGccExecutable+' --print-libgcc-file-name '+GCCParams, False);
  226. gccDir := ExtractFileDir(libgccFilename);
  227. end
  228. else
  229. gccDir := '';
  230. if gccDir='' then
  231. result := ''
  232. else if ACpuType = '' then
  233. result := '-Fl'+gccDir
  234. else
  235. result := '#ifdef ' + ACpuType + LineEnding + '-Fl' + gccDir + LineEnding + '#endif';
  236. end;
  237. begin
  238. result := '';
  239. GccExecutable:='';
  240. if sametext(BuildOSTarget,'Freebsd') or sametext(BuildOSTarget,'Openbsd') then
  241. result := '-Fl/usr/local/lib'
  242. else if sametext(BuildOSTarget,'Netbsd') then
  243. result := '-Fl/usr/pkg/lib'
  244. else if sametext(BuildOSTarget,'Linux') then
  245. begin
  246. if (BuildTarget = 'i386') or (BuildTarget = 'x86_64') then
  247. result := GetGccDirArch('cpui386','-m32') + LineEnding +
  248. GetGccDirArch('cpux86_64','-m64')
  249. else if (BuildTarget = 'powerpc') or (BuildTarget = 'powerpc64') then
  250. result := GetGccDirArch('cpupowerpc','-m32') + LineEnding +
  251. GetGccDirArch('cpupowerpc64','-m64')
  252. end
  253. else if sametext(BuildOSTarget,'Darwin') then
  254. result := GetGccDirArch('cpupowerpc','-arch ppc') + LineEnding +
  255. GetGccDirArch('cpupowerpc64','-arch ppc64') + LineEnding +
  256. GetGccDirArch('cpui386','-arch i386') + LineEnding +
  257. GetGccDirArch('cpux86_64','-arch x86_64');
  258. end;
  259. procedure Init;
  260. begin
  261. Verbose:=False;
  262. IDEBuildIn:=0;
  263. TemplateParser := TTemplateParser.Create;
  264. TemplateParser.StartDelimiter:='%';
  265. TemplateParser.EndDelimiter:='%';
  266. TemplateParser.Values['FPCVERSION'] := BuildVersion;
  267. TemplateParser.Values['FPCTARGET'] := BuildTarget;
  268. TemplateParser.Values['FPCTARGETOS'] := BuildOSTarget;
  269. TemplateParser.Values['FPCBIN'] := 'fpc';
  270. TemplateParser.Values['PWD'] := GetCurrentDir;
  271. TemplateParser.Values['BUILDDATE'] := DateToStr(Date);
  272. TemplateParser.Values['BUILDTIME'] := TimeToStr(Time);
  273. TemplateParser.Values['LOCALREPOSITORY'] := GetDefaultLocalRepository;
  274. TemplateParser.Values['LOCALBASEPATH'] := GetDefaultLocalBasepath;
  275. TemplateParser.Values['COMPILERCONFIGDIR'] := GetDefaultCompilerConfigDir;
  276. TemplateParser.Values['NEEDCROSSBINUTILSIFDEF'] := GetDefaultNeedCrossBinutilsIfdef;
  277. TemplateParser.Values['GCCLIBPATH'] := GetDefaultGCCDIR;
  278. Cfg:=TStringList.Create;
  279. Cfg.Text:=StrPas(Addr(DefaultConfig[0][1]));
  280. end;
  281. Procedure Done;
  282. begin
  283. FreeAndNil(Cfg);
  284. FreeAndNil(TemplateParser);
  285. end;
  286. Procedure Usage;
  287. begin
  288. Writeln(Format(SUsage00,[ExtractFileName(Paramstr(0))]));
  289. Writeln(SUsage10);
  290. Writeln(SUsage20);
  291. Writeln(SUsage30);
  292. Writeln(SUsage40);
  293. Writeln(SUsage50);
  294. Writeln(SUsage60);
  295. Writeln(SUsage70);
  296. Writeln(SUsage80);
  297. Writeln(SUsage84);
  298. Writeln(SUsage87);
  299. Writeln(SUsage90);
  300. Writeln(SUsage100);
  301. Writeln(SUsage110);
  302. Writeln(SUsage120);
  303. Writeln(SUsage130);
  304. Writeln(SUsage140);
  305. Halt(1);
  306. end;
  307. Procedure UnknownOption(Const S : String);
  308. begin
  309. Writeln(SErrUnknownOption,S);
  310. Usage;
  311. end;
  312. Procedure ShowBuiltIn;
  313. Var
  314. I : Integer;
  315. begin
  316. For I:=0 to Cfg.Count-1 do
  317. Writeln(Cfg[I]);
  318. end;
  319. Procedure ShowBuiltInMacros;
  320. Var
  321. I : Integer;
  322. begin
  323. For I:=0 to TemplateParser.ValueCount-1 do
  324. Writeln(TemplateParser.NamesByIndex[I]+'='+TemplateParser.ValuesByIndex[I]);
  325. end;
  326. Procedure ProcessCommandline;
  327. Var
  328. I : Integer;
  329. S : String;
  330. ShowBuiltinCommand : boolean;
  331. Function GetOptArg : String;
  332. begin
  333. If I=ParamCount then
  334. begin
  335. Writeln(StdErr,Format(SErrArgExpected,[S]));
  336. Halt(1);
  337. end;
  338. inc(I);
  339. Result:=ParamStr(I);
  340. end;
  341. procedure AddPair(const Value: String);
  342. var P: integer;
  343. N,V: String;
  344. begin
  345. P:=Pos('=',Value);
  346. If p=0 then
  347. begin
  348. Writeln(StdErr,Format(SErrIncompletePair,[Value]));
  349. Halt(1);
  350. end;
  351. V:=Value;
  352. N:=Copy(V,1,P-1);
  353. Delete(V,1,P);
  354. TemplateParser.Values[N] := V;
  355. end;
  356. begin
  357. I:=1;
  358. ShowBuiltinCommand := False;
  359. SkipBackup := False;
  360. CreateDir := False;
  361. While( I<=ParamCount) do
  362. begin
  363. S:=Paramstr(i);
  364. If Length(S)<=1 then
  365. UnknownOption(S)
  366. else
  367. case S[2] of
  368. 'v' : Verbose:=True;
  369. 'h' : Usage;
  370. 'b' : ShowBuiltinCommand := true;
  371. 'm' : begin
  372. ShowBuiltinMacros;
  373. halt(0);
  374. end;
  375. 't' : TemplateFileName:=GetOptArg;
  376. 'd' : AddPair(GetOptArg);
  377. 'u' : TemplateParser.Values[GetOptArg]:='';
  378. 'o' : OutputFileName:=GetoptArg;
  379. 's' : SkipBackup:=True;
  380. 'p' : CreateDir:=True;
  381. '0' : IDEBuildin:=0;
  382. '1' : IDEBuildin:=1;
  383. '2' : IDEBuildin:=2;
  384. '3' : IDEBuildin:=3;
  385. '4' : IDEBuildin:=4;
  386. else
  387. UnknownOption(S);
  388. end;
  389. Inc(I);
  390. end;
  391. If (TemplateFileName<>'') then
  392. begin
  393. If Not FileExists(TemplateFileName) then
  394. begin
  395. Writeln(StdErr,Format(SErrNoSuchFile,[TemplateFileName]));
  396. Halt(1);
  397. end;
  398. Cfg.LoadFromFile(TemplateFileName);
  399. TemplateParser.Values['TEMPLATEFILE'] := TemplateFileName;
  400. end
  401. else
  402. begin
  403. case IDEBuildin of
  404. 1:
  405. Cfg.Text:=StrPas(Addr(fpcfg[0][1]));
  406. 2:
  407. Cfg.Text:=StrPas(Addr(fpini[0][1]));
  408. 3:
  409. Cfg.Text:=StrPas(Addr(fppkg[0][1]));
  410. 4:
  411. Cfg.Text:=StrPas(Addr(fppkg_default[0][1]));
  412. end;
  413. TemplateParser.Values['TEMPLATEFILE'] := 'builtin';
  414. end;
  415. if ShowBuiltinCommand then
  416. begin
  417. ShowBuiltIn;
  418. halt(0);
  419. end;
  420. end;
  421. Procedure CreateFile;
  422. Var
  423. Fout : Text;
  424. S,BFN : String;
  425. I : Integer;
  426. begin
  427. if (OutputFileName<>'') and
  428. DirectoryExists(OutputFileName) then
  429. begin
  430. Writeln(StdErr,Format(SErrDestDirectory,[OutputFileName]));
  431. Halt(1);
  432. end;
  433. If (OutputFileName<>'')
  434. and FileExists(OutputFileName)
  435. and not SkipBackup then
  436. begin
  437. BFN:=ChangeFileExt(OutputFileName,'.bak');
  438. If FileExists(BFN) and not DeleteFile(BFN) then
  439. begin
  440. Writeln(StdErr,Format(SErrDelBackupFailed,[BFN]));
  441. Halt(1);
  442. end;
  443. If not RenameFile(OutputFileName,BFN) then
  444. begin
  445. Writeln(StdErr,Format(SErrBackupFailed,[OutputFileName,BFN]));
  446. Halt(1);
  447. end
  448. else
  449. Writeln(Format(SBackupCreated,[ExtractFileName(OutputFileName),ExtractFileName(BFN)]));
  450. end;
  451. if (OutputFileName<>'') and not DirectoryExists(ExtractFilePath(OutputFileName)) then
  452. begin
  453. if CreateDir then
  454. begin
  455. if not ForceDirectories(ExtractFilePath(OutputFileName)) then
  456. begin
  457. Writeln(StdErr,Format(SErrCreateDirFailed,[OutputFileName]));
  458. Halt(1);
  459. end;
  460. end
  461. else
  462. begin
  463. Writeln(StdErr,Format(SErrNoSuchDirectory,[OutputFileName]));
  464. Halt(1);
  465. end;
  466. end;
  467. Assign(Fout,OutputFileName);
  468. Rewrite(FOut);
  469. Try
  470. For I:=0 to Cfg.Count-1 do
  471. begin
  472. S:=Cfg[i];
  473. S := TemplateParser.ParseString(S);
  474. Writeln(FOut,S);
  475. end;
  476. Finally
  477. Close(Fout);
  478. end;
  479. end;
  480. begin
  481. Init;
  482. Try
  483. ProcessCommandLine;
  484. CreateFile;
  485. Finally
  486. Done;
  487. end;
  488. end.