fpcmkcfg.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  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. SWarnIgnoringFile = 'Warning: Ignoring non-existent file: ';
  73. SWarnIgnoringPair = 'Warning: Ignoring wrong name/value pair: ';
  74. SWarngccNotFound = 'Warning: Could not find gcc. Unable to determine the gcclib path.';
  75. SWarnCouldNotExecute= 'Warning: Could not execute command ''%s''';
  76. SBackupCreated = 'Saved old "%s" to "%s"';
  77. Var
  78. Verbose : Boolean;
  79. SkipBackup : Boolean;
  80. CreateDir: Boolean;
  81. Cfg : TStringList;
  82. TemplateParser: TTemplateParser;
  83. TemplateFileName,
  84. OutputFileName : String;
  85. IDEBuildin : Integer;
  86. function IsSuperUser:boolean;
  87. begin
  88. {$ifdef unix}
  89. result:=(fpGetUID=0);
  90. {$else unix}
  91. result:=false;
  92. {$endif unix}
  93. end;
  94. function GetDefaultLocalRepository: string;
  95. begin
  96. {$IFDEF Unix}
  97. result := '{UserDir}.fppkg'+PathDelim;
  98. {$ELSE Unix}
  99. result := '{AppConfigDir}';
  100. {$ENDIF Unix}
  101. end;
  102. function GetDefaultLocalBasepath: string;
  103. begin
  104. {$IFDEF Unix}
  105. result := '~/.fppkg'+PathDelim+'lib'+PathDelim+'fpc'+PathDelim+'$fpcversion';
  106. {$ELSE Unix}
  107. result := '$LOCAL_APPDATA'+PathDelim+'FreePascal'+PathDelim+'fppkg';
  108. {$ENDIF Unix}
  109. end;
  110. function GetDefaultCompilerConfigDir: string;
  111. begin
  112. {$IFDEF Unix}
  113. if IsSuperUser then
  114. result := '/etc/fppkg/'
  115. else
  116. {$ENDIF}
  117. result := '{LocalRepository}config/';
  118. end;
  119. function GetDefaultNeedCrossBinutilsIfdef: string;
  120. begin
  121. result := '';
  122. // On Darwin there is never a need for a crossbinutils prefix
  123. if SameText(BuildOSTarget,'Darwin') then
  124. result := '#IFNDEF ' + BuildOSTarget + LineEnding +
  125. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  126. '#ENDIF'
  127. else if (BuildTarget = 'i386') or (BuildTarget = 'x86_64') then
  128. begin
  129. // Cross-binutils are not needed to compile for i386 on an x86_64 system
  130. result := '#IFNDEF CPUI386' + LineEnding +
  131. '#IFNDEF CPUAMD64' + LineEnding +
  132. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  133. '#ENDIF' + LineEnding +
  134. '#ENDIF' + LineEnding +
  135. LineEnding +
  136. '#IFNDEF ' + BuildOSTarget + LineEnding +
  137. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  138. '#ENDIF';
  139. end
  140. else
  141. result := '#DEFINE NEEDCROSSBINUTILS';
  142. end;
  143. function GetDefaultGCCDir: string;
  144. var GccExecutable: string;
  145. function GetGccExecutable: string;
  146. begin
  147. if GccExecutable='' then
  148. begin
  149. GccExecutable := ExeSearch('gcc'+ExeExt,GetEnvironmentVariable('PATH'));
  150. if GccExecutable='' then
  151. begin
  152. Writeln(StdErr,SWarngccNotFound);
  153. GccExecutable:='-';
  154. end;
  155. end;
  156. if GccExecutable = '-' then
  157. result := ''
  158. else
  159. result := GccExecutable;
  160. end;
  161. function ExecuteProc(const CommandLine: string; ReadStdErr: boolean) : string;
  162. const BufSize=2048;
  163. var S: TProcess;
  164. buf: array[0..BufSize-1] of byte;
  165. count: integer;
  166. begin
  167. result := '';
  168. S:=TProcess.Create(Nil);
  169. try
  170. S.Commandline:=CommandLine;
  171. S.Options:=[poUsePipes,poWaitOnExit];
  172. try
  173. S.execute;
  174. Count:=s.output.read(buf,BufSize);
  175. if (count=0) and ReadStdErr then
  176. Count:=s.Stderr.read(buf,BufSize);
  177. setlength(result,count);
  178. move(buf[0],result[1],count);
  179. except
  180. Writeln(StdErr,Format(SWarnCouldNotExecute,[CommandLine]));
  181. end;
  182. finally
  183. S.Free;
  184. end;
  185. end;
  186. function Get4thWord(const AString: string): string;
  187. var p: pchar;
  188. spacecount: integer;
  189. StartWord: pchar;
  190. begin
  191. if length(AString)>6 then
  192. begin
  193. p := @AString[1];
  194. spacecount:=0;
  195. StartWord:=nil;
  196. while (not (p^ in [#0,#10,#13])) and ((p^<>' ') or (StartWord=nil)) do
  197. begin
  198. if p^=' ' then
  199. begin
  200. inc(spacecount);
  201. if spacecount=3 then StartWord:=p+1;
  202. end;
  203. inc(p);
  204. end;
  205. if StartWord<>nil then
  206. begin
  207. SetLength(result,p-StartWord);
  208. move(StartWord^,result[1],p-StartWord);
  209. end
  210. else
  211. result := '';
  212. end;
  213. end;
  214. function GetGccDirArch(const ACpuType, GCCParams: string) : string;
  215. var ExecResult: string;
  216. libgccFilename: string;
  217. gccDir: string;
  218. begin
  219. if FileExists(GetGccExecutable) then
  220. begin
  221. ExecResult:=ExecuteProc(GetGccExecutable+' -v '+GCCParams, True);
  222. libgccFilename:=Get4thWord(ExecResult);
  223. if libgccFilename='' then
  224. libgccFilename:=ExecuteProc(GetGccExecutable+' --print-libgcc-file-name '+GCCParams, False);
  225. gccDir := ExtractFileDir(libgccFilename);
  226. end
  227. else
  228. gccDir := '';
  229. if gccDir='' then
  230. result := ''
  231. else if ACpuType = '' then
  232. result := '-Fl'+gccDir
  233. else
  234. result := '#ifdef ' + ACpuType + LineEnding + '-Fl' + gccDir + LineEnding + '#endif';
  235. end;
  236. begin
  237. result := '';
  238. GccExecutable:='';
  239. if sametext(BuildOSTarget,'Freebsd') or sametext(BuildOSTarget,'Openbsd') then
  240. result := '-Fl/usr/local/lib'
  241. else if sametext(BuildOSTarget,'Netbsd') then
  242. result := '-Fl/usr/pkg/lib'
  243. else if sametext(BuildOSTarget,'Linux') then
  244. begin
  245. if (BuildTarget = 'i386') or (BuildTarget = 'x86_64') then
  246. result := GetGccDirArch('cpui386','-m32') + LineEnding +
  247. GetGccDirArch('cpux86_64','-m64')
  248. else if (BuildTarget = 'powerpc') or (BuildTarget = 'powerpc64') then
  249. result := GetGccDirArch('cpupowerpc','-m32') + LineEnding +
  250. GetGccDirArch('cpupowerpc64','-m64')
  251. end
  252. else if sametext(BuildOSTarget,'Darwin') then
  253. result := GetGccDirArch('cpupowerpc','-arch ppc') + LineEnding +
  254. GetGccDirArch('cpupowerpc64','-arch ppc64') + LineEnding +
  255. GetGccDirArch('cpui386','-arch i386') + LineEnding +
  256. GetGccDirArch('cpux86_64','-arch x86_64');
  257. end;
  258. procedure Init;
  259. begin
  260. Verbose:=False;
  261. IDEBuildIn:=0;
  262. TemplateParser := TTemplateParser.Create;
  263. TemplateParser.StartDelimiter:='%';
  264. TemplateParser.EndDelimiter:='%';
  265. TemplateParser.Values['FPCVERSION'] := BuildVersion;
  266. TemplateParser.Values['FPCTARGET'] := BuildTarget;
  267. TemplateParser.Values['FPCTARGETOS'] := BuildOSTarget;
  268. TemplateParser.Values['FPCBIN'] := 'fpc';
  269. TemplateParser.Values['PWD'] := GetCurrentDir;
  270. TemplateParser.Values['BUILDDATE'] := DateToStr(Date);
  271. TemplateParser.Values['BUILDTIME'] := TimeToStr(Time);
  272. TemplateParser.Values['LOCALREPOSITORY'] := GetDefaultLocalRepository;
  273. TemplateParser.Values['LOCALBASEPATH'] := GetDefaultLocalBasepath;
  274. TemplateParser.Values['COMPILERCONFIGDIR'] := GetDefaultCompilerConfigDir;
  275. TemplateParser.Values['NEEDCROSSBINUTILSIFDEF'] := GetDefaultNeedCrossBinutilsIfdef;
  276. TemplateParser.Values['GCCLIBPATH'] := GetDefaultGCCDIR;
  277. Cfg:=TStringList.Create;
  278. Cfg.Text:=StrPas(Addr(DefaultConfig[0][1]));
  279. end;
  280. Procedure Done;
  281. begin
  282. FreeAndNil(Cfg);
  283. FreeAndNil(TemplateParser);
  284. end;
  285. Procedure Usage;
  286. begin
  287. Writeln(Format(SUsage00,[ExtractFileName(Paramstr(0))]));
  288. Writeln(SUsage10);
  289. Writeln(SUsage20);
  290. Writeln(SUsage30);
  291. Writeln(SUsage40);
  292. Writeln(SUsage50);
  293. Writeln(SUsage60);
  294. Writeln(SUsage70);
  295. Writeln(SUsage80);
  296. Writeln(SUsage84);
  297. Writeln(SUsage87);
  298. Writeln(SUsage90);
  299. Writeln(SUsage100);
  300. Writeln(SUsage110);
  301. Writeln(SUsage120);
  302. Writeln(SUsage130);
  303. Writeln(SUsage140);
  304. Halt(1);
  305. end;
  306. Procedure UnknownOption(Const S : String);
  307. begin
  308. Writeln(SErrUnknownOption,S);
  309. Usage;
  310. end;
  311. Procedure ShowBuiltIn;
  312. Var
  313. I : Integer;
  314. begin
  315. For I:=0 to Cfg.Count-1 do
  316. Writeln(Cfg[I]);
  317. end;
  318. Procedure ShowBuiltInMacros;
  319. Var
  320. I : Integer;
  321. begin
  322. For I:=0 to TemplateParser.ValueCount-1 do
  323. Writeln(TemplateParser.NamesByIndex[I]+'='+TemplateParser.ValuesByIndex[I]);
  324. end;
  325. Procedure ProcessCommandline;
  326. Var
  327. I : Integer;
  328. S : String;
  329. ShowBuiltinCommand : boolean;
  330. Function GetOptArg : String;
  331. begin
  332. If I=ParamCount then
  333. begin
  334. Writeln(StdErr,Format(SErrArgExpected,[S]));
  335. Halt(1);
  336. end;
  337. inc(I);
  338. Result:=ParamStr(I);
  339. end;
  340. procedure AddPair(const Value: String);
  341. var P: integer;
  342. N,V: String;
  343. begin
  344. P:=Pos('=',Value);
  345. If p=0 then
  346. begin
  347. Writeln(StdErr,Format(SErrIncompletePair,[Value]));
  348. Halt(1);
  349. end;
  350. V:=Value;
  351. N:=Copy(V,1,P-1);
  352. Delete(V,1,P);
  353. TemplateParser.Values[N] := V;
  354. end;
  355. begin
  356. I:=1;
  357. ShowBuiltinCommand := False;
  358. SkipBackup := False;
  359. CreateDir := False;
  360. While( I<=ParamCount) do
  361. begin
  362. S:=Paramstr(i);
  363. If Length(S)<=1 then
  364. UnknownOption(S)
  365. else
  366. case S[2] of
  367. 'v' : Verbose:=True;
  368. 'h' : Usage;
  369. 'b' : ShowBuiltinCommand := true;
  370. 'm' : begin
  371. ShowBuiltinMacros;
  372. halt(0);
  373. end;
  374. 't' : TemplateFileName:=GetOptArg;
  375. 'd' : AddPair(GetOptArg);
  376. 'u' : TemplateParser.Values[GetOptArg]:='';
  377. 'o' : OutputFileName:=GetoptArg;
  378. 's' : SkipBackup:=True;
  379. 'p' : CreateDir:=True;
  380. '0' : IDEBuildin:=0;
  381. '1' : IDEBuildin:=1;
  382. '2' : IDEBuildin:=2;
  383. '3' : IDEBuildin:=3;
  384. '4' : IDEBuildin:=4;
  385. else
  386. UnknownOption(S);
  387. end;
  388. Inc(I);
  389. end;
  390. If (TemplateFileName<>'') then
  391. begin
  392. If Not FileExists(TemplateFileName) then
  393. begin
  394. Writeln(StdErr,Format(SErrNoSuchFile,[TemplateFileName]));
  395. Halt(1);
  396. end;
  397. Cfg.LoadFromFile(TemplateFileName);
  398. TemplateParser.Values['TEMPLATEFILE'] := TemplateFileName;
  399. end
  400. else
  401. begin
  402. case IDEBuildin of
  403. 1:
  404. Cfg.Text:=StrPas(Addr(fpcfg[0][1]));
  405. 2:
  406. Cfg.Text:=StrPas(Addr(fpini[0][1]));
  407. 3:
  408. Cfg.Text:=StrPas(Addr(fppkg[0][1]));
  409. 4:
  410. Cfg.Text:=StrPas(Addr(fppkg_default[0][1]));
  411. end;
  412. TemplateParser.Values['TEMPLATEFILE'] := 'builtin';
  413. end;
  414. if ShowBuiltinCommand then
  415. begin
  416. ShowBuiltIn;
  417. halt(0);
  418. end;
  419. end;
  420. Procedure CreateFile;
  421. Var
  422. Fout : Text;
  423. S,BFN : String;
  424. I : Integer;
  425. begin
  426. If (OutputFileName<>'')
  427. and FileExists(OutputFileName)
  428. and not SkipBackup then
  429. begin
  430. BFN:=ChangeFileExt(OutputFileName,'.bak');
  431. If FileExists(BFN) and not DeleteFile(BFN) then
  432. begin
  433. Writeln(StdErr,Format(SErrDelBackupFailed,[BFN]));
  434. Halt(1);
  435. end;
  436. If not RenameFile(OutputFileName,BFN) then
  437. begin
  438. Writeln(StdErr,Format(SErrBackupFailed,[OutputFileName,BFN]));
  439. Halt(1);
  440. end
  441. else
  442. Writeln(Format(SBackupCreated,[ExtractFileName(OutputFileName),ExtractFileName(BFN)]));
  443. end;
  444. if (OutputFileName<>'') and not DirectoryExists(ExtractFilePath(OutputFileName)) then
  445. begin
  446. if CreateDir then
  447. begin
  448. if not ForceDirectories(ExtractFilePath(OutputFileName)) then
  449. begin
  450. Writeln(StdErr,Format(SErrCreateDirFailed,[OutputFileName]));
  451. Halt(1);
  452. end;
  453. end
  454. else
  455. begin
  456. Writeln(StdErr,Format(SErrNoSuchDirectory,[OutputFileName]));
  457. Halt(1);
  458. end;
  459. end;
  460. Assign(Fout,OutputFileName);
  461. Rewrite(FOut);
  462. Try
  463. For I:=0 to Cfg.Count-1 do
  464. begin
  465. S:=Cfg[i];
  466. S := TemplateParser.ParseString(S);
  467. Writeln(FOut,S);
  468. end;
  469. Finally
  470. Close(Fout);
  471. end;
  472. end;
  473. begin
  474. Init;
  475. Try
  476. ProcessCommandLine;
  477. CreateFile;
  478. Finally
  479. Done;
  480. end;
  481. end.