fpcmkcfg.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. {$mode objfpc}
  2. {$H+}
  3. {$macro on}
  4. {
  5. This file is part of Free Pascal Build tools
  6. Copyright (c) 2005 by Michael Van Canneyt
  7. Create a configuration file
  8. See the file COPYING.FPC, included in this distribution,
  9. for details about the copyright.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. **********************************************************************}
  14. program fpcmkcfg;
  15. uses
  16. fpmkunit,
  17. SysUtils,
  18. Classes,
  19. {$ifdef unix}
  20. baseunix,
  21. {$endif}
  22. fpTemplate;
  23. {
  24. The inc files must be built from a template with the data2inc
  25. command.
  26. data2inc -b -s fpc.cft fpccfg.inc DefaultConfig
  27. data2inc -b -s fpinc.ini fpini.inc fpini
  28. data2inc -b -s fpinc.cfg fpcfg.inc fpcfg
  29. data2inc -b -s fppkg.cfg fppkg.inc fppkg
  30. data2inc -b -s default.cft default.inc fppkg_default
  31. }
  32. {$i fpccfg.inc}
  33. {$i fpini.inc}
  34. {$i fpcfg.inc}
  35. {$i fppkg.inc}
  36. {$i default.inc}
  37. {$ifndef package_version_major}
  38. {$define package_version_major:=0}
  39. {$endif}
  40. {$ifndef package_version_minor}
  41. {$define package_version_minor:=0}
  42. {$endif}
  43. {$ifndef package_version_micro}
  44. {$define package_version_micro:=0}
  45. {$endif}
  46. {$ifndef package_version_build}
  47. {$define package_version_build:=0}
  48. {$endif}
  49. Const
  50. BuildVersion={$I %FPCVERSION%};
  51. BuildTarget={$I %FPCTARGET%};
  52. BuildOSTarget={$I %FPCTARGETOS%};
  53. {$ifdef unix}
  54. ExeExt = '';
  55. {$else unix}
  56. ExeExt = '.exe';
  57. {$endif unix}
  58. version_major = package_version_major;
  59. version_minor = package_version_minor;
  60. version_micro = package_version_micro;
  61. version_build = package_version_build;
  62. Resourcestring
  63. SUsage00 = 'Usage: %s [options]';
  64. SUsage10 = 'Where options is one or more of';
  65. SUSage20 = ' -t filename Template file name. Default is built-in';
  66. SUSage30 = ' -o filename Set output file. Default is standard output.';
  67. SUsage40 = ' -d name=value define name=value pair.';
  68. SUsage50 = ' -h show this help and exit.';
  69. SUsage60 = ' -u name remove name from list of name/value pairs.';
  70. // SUsage70 = ' -l filename read name/value pairs from filename';
  71. SUsage70 = ' -m show builtin macros and exit.';
  72. SUsage80 = ' -b show builtin template and exit.';
  73. SUsage84 = ' -s skip the creation of a backup-file.';
  74. SUsage87 = ' -p force directory creation.';
  75. SUsage90 = ' -v be verbose.';
  76. SUsage95 = ' -V show version.';
  77. Susage100 = ' -0 use built in fpc.cfg template (default)';
  78. Susage110 = ' -1 use built in fp.cfg template';
  79. Susage120 = ' -2 use built in fp.ini template';
  80. Susage130 = ' -3 use built in fppkg.cfg template';
  81. Susage140 = ' -4 use built in fppkg default compiler template';
  82. Susage150 = ' -g add help section to fp.ini';
  83. SVersion = 'Version: %s';
  84. SErrUnknownOption = 'Error: Unknown option.';
  85. SErrArgExpected = 'Error: Option "%s" requires an argument.';
  86. SErrIncompletePair = 'Error: Incomplete name-value pair "%s".';
  87. SErrNoSuchFile = 'Error: File "%s" does not exist.';
  88. SErrNoSuchDirectory = 'Error: Directory of file "%s" does not exists. User -p to force creation.';
  89. SErrBackupFailed = 'Error: Backup of file "%s" to "%s" failed.';
  90. SErrDelBackupFailed = 'Error: Delete of old backup file "%s" failed.';
  91. SErrCreateDirFailed = 'Error: Could not create the directory for file "%s".';
  92. SErrDestDirectory = 'Error: The output file "%s" is a directory.';
  93. SWarnIgnoringFile = 'Warning: Ignoring non-existent file: ';
  94. SWarnIgnoringPair = 'Warning: Ignoring wrong name/value pair: ';
  95. SWarngccNotFound = 'Warning: Could not find gcc. Unable to determine the gcclib path.';
  96. SWarnCouldNotExecute= 'Warning: Could not execute command ''%s''';
  97. SBackupCreated = 'Saved old "%s" to "%s"';
  98. Var
  99. Verbose : Boolean;
  100. SkipBackup : Boolean;
  101. CreateDir: Boolean;
  102. Cfg : TStringList;
  103. TemplateParser: TTemplateParser;
  104. TemplateFileName,
  105. OutputFileName : String;
  106. IDEBuildin : Integer;
  107. function IsSuperUser:boolean;
  108. begin
  109. {$ifdef unix}
  110. result:=(fpGetUID=0);
  111. {$else unix}
  112. result:=false;
  113. {$endif unix}
  114. end;
  115. function GetDefaultLocalRepository: string;
  116. begin
  117. {$IFDEF Unix}
  118. result := '{UserDir}.fppkg'+PathDelim;
  119. {$ELSE Unix}
  120. result := '{AppConfigDir}';
  121. {$ENDIF Unix}
  122. end;
  123. function GetDefaultLocalBasepath: string;
  124. begin
  125. {$IFDEF Unix}
  126. result := '~/.fppkg'+PathDelim+'lib'+PathDelim+'fpc'+PathDelim+'$fpcversion';
  127. {$ELSE Unix}
  128. result := '$LOCAL_APPDATA'+PathDelim+'FreePascal'+PathDelim+'fppkg';
  129. {$ENDIF Unix}
  130. end;
  131. function GetDefaultCompilerConfigDir: string;
  132. begin
  133. {$IFDEF Unix}
  134. if IsSuperUser then
  135. result := '/etc/fppkg/'
  136. else
  137. {$ENDIF}
  138. result := '{LocalRepository}config/';
  139. end;
  140. function GetDefaultNeedCrossBinutilsIfdef: string;
  141. begin
  142. result := '';
  143. // On Darwin there is never a need for a crossbinutils prefix
  144. if SameText(BuildOSTarget,'Darwin') then
  145. result := '#IFNDEF ' + BuildOSTarget + LineEnding +
  146. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  147. '#ENDIF'
  148. else if (BuildTarget = 'i386') or (BuildTarget = 'x86_64') then
  149. begin
  150. // Cross-binutils are not needed to compile for i386 on an x86_64 system
  151. result := '#IFNDEF CPUI386' + LineEnding +
  152. '#IFNDEF CPUAMD64' + LineEnding +
  153. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  154. '#ENDIF' + LineEnding +
  155. '#ENDIF' + LineEnding +
  156. LineEnding +
  157. '#IFNDEF ' + BuildOSTarget + LineEnding +
  158. '#DEFINE NEEDCROSSBINUTILS' + LineEnding +
  159. '#ENDIF';
  160. end
  161. else
  162. result := '#DEFINE NEEDCROSSBINUTILS';
  163. end;
  164. function GetDefaultUserPathSuffix: string;
  165. begin
  166. if not (StringToOS(BuildOSTarget) in AllWindowsOSes) then
  167. Result := 'lib/fpc/{CompilerVersion}'
  168. else
  169. Result := '';
  170. end;
  171. function GetDefaultGCCDir: string;
  172. var
  173. OS: TOS;
  174. CPU: TCPU;
  175. s: string;
  176. procedure AddConditionalLinkerPath(const aCpuType: string; const ACPU: TCPU; var ConfigFileOption: string);
  177. var
  178. path: string;
  179. ErrS: string;
  180. begin
  181. path := GetDefaultLibGCCDir(ACPU, OS, ErrS);
  182. if ErrS<>'' then
  183. Writeln(StdErr, ErrS);
  184. if path <> '' then
  185. begin
  186. if ConfigFileOption<>'' then ConfigFileOption:=ConfigFileOption+LineEnding;
  187. ConfigFileOption := ConfigFileOption + '#ifdef ' + ACpuType + LineEnding + '-Fl' + Path + LineEnding + '#endif';
  188. end;
  189. end;
  190. begin
  191. CPU := StringToCPU(BuildTarget);
  192. OS := StringToOS(BuildOSTarget);
  193. result := '';
  194. case OS of
  195. freebsd, openbsd, netbsd :
  196. result := '-Fl'+GetDefaultLibGCCDir(CPU, OS, S);
  197. linux :
  198. begin
  199. if CPU in [i386, x86_64] then
  200. begin
  201. AddConditionalLinkerPath('cpui386', i386, result);
  202. AddConditionalLinkerPath('cpux86_64', x86_64, result);
  203. end
  204. else if CPU in [powerpc, powerpc64] then
  205. begin
  206. AddConditionalLinkerPath('cpupowerpc', powerpc, result);
  207. AddConditionalLinkerPath('cpupowerpc64', powerpc64, result);
  208. end
  209. else if CPU in [arm, aarch64] then
  210. begin
  211. AddConditionalLinkerPath('cpuarm', arm, result);
  212. AddConditionalLinkerPath('cpuaarch64', aarch64, result);
  213. end
  214. else
  215. result := '-Fl'+GetDefaultLibGCCDir(CPU, OS, S);
  216. end;
  217. darwin :
  218. begin
  219. AddConditionalLinkerPath('cpui386', i386, result);
  220. AddConditionalLinkerPath('cpux86_64', x86_64, result);
  221. AddConditionalLinkerPath('cpupowerpc', powerpc, result);
  222. AddConditionalLinkerPath('cpupowerpc64', powerpc64, result);
  223. AddConditionalLinkerPath('cpuaarch64', aarch64, result);
  224. { macOS 10.14 or later:
  225. 1) command line tools are installed under /Library/Developer/CommandLineTools
  226. 2) the system libraries still contain i386 code, but the 10.14 sdk doesn't
  227. (-> only use the 10.14 sdk when targeting x86_64 or unknown architectures )
  228. 3) crt1.o is no longer installed under /usr -> add its directory explicitly via
  229. -Fl
  230. We can't detect the macOS version inside fpc.cfg, unfortunately, so we can only
  231. insert this while generating the configuration file.
  232. This will stop working when macOS 10.15 is released without i386 support, but then
  233. users will be responsible for supplying their own i386 SDK anyway.
  234. }
  235. if DirectoryExists('/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk') then
  236. begin
  237. result:=result + LineEnding +
  238. '-FD/Library/Developer/CommandLineTools/usr/bin' + LineEnding +
  239. '#ifdef cpui386' + LineEnding +
  240. '-Fl/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib' + LineEnding +
  241. '#endif' + LineEnding +
  242. '#ifndef cpui386' + LineEnding +
  243. '#ifndef cpupowerpc' + LineEnding +
  244. '#ifndef cpupowerpc64' + LineEnding +
  245. '-XR/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' + LineEnding +
  246. '#endif' + LineEnding +
  247. '#endif' + LineEnding +
  248. '#endif';
  249. end
  250. else
  251. begin
  252. { add Xcode.app binutils to search path}
  253. result:=result + LineEnding +
  254. '-FD/Applications/Xcode.app/Contents/Developer/usr/bin';
  255. end;
  256. end;
  257. end; {case}
  258. { ifdef everything above related to the target OS otherwise host linker/clib paths can leak
  259. into the target while cross-ing, and cause nonworking executables (Darwin-x86_64 to ARM-Linux
  260. for example on my setup), and while it's advised to use -n when crosscompiling, it can
  261. cause hard to identify issues if -n is forgotten... (KB) }
  262. if result <> '' then
  263. result := '#ifdef ' + BuildOSTarget + LineEnding +
  264. result + LineEnding +
  265. '#endif' + LineEnding;
  266. end;
  267. procedure Init;
  268. begin
  269. Verbose:=False;
  270. IDEBuildIn:=0;
  271. TemplateParser := TTemplateParser.Create;
  272. TemplateParser.StartDelimiter:='%';
  273. TemplateParser.EndDelimiter:='%';
  274. TemplateParser.Values['FPCVERSION'] := BuildVersion;
  275. TemplateParser.Values['FPCTARGET'] := BuildTarget;
  276. TemplateParser.Values['FPCTARGETOS'] := BuildOSTarget;
  277. TemplateParser.Values['FPCBIN'] := 'fpc';
  278. TemplateParser.Values['PWD'] := GetCurrentDir;
  279. TemplateParser.Values['BUILDDATE'] := DateToStr(Date);
  280. TemplateParser.Values['BUILDTIME'] := TimeToStr(Time);
  281. TemplateParser.Values['LOCALREPOSITORY'] := GetDefaultLocalRepository;
  282. TemplateParser.Values['LOCALBASEPATH'] := GetDefaultLocalBasepath;
  283. TemplateParser.Values['COMPILERCONFIGDIR'] := GetDefaultCompilerConfigDir;
  284. TemplateParser.Values['NEEDCROSSBINUTILSIFDEF'] := GetDefaultNeedCrossBinutilsIfdef;
  285. TemplateParser.Values['GCCLIBPATH'] := GetDefaultGCCDIR;
  286. TemplateParser.Values['USERPATHSUFFIX'] := GetDefaultUserPathSuffix;
  287. Cfg:=TStringList.Create;
  288. Cfg.Text:=StrPas(Addr(DefaultConfig[0][1]));
  289. end;
  290. Procedure Done;
  291. begin
  292. FreeAndNil(Cfg);
  293. FreeAndNil(TemplateParser);
  294. end;
  295. Procedure Usage;
  296. begin
  297. Writeln(Format(SUsage00,[ExtractFileName(Paramstr(0))]));
  298. Writeln(SUsage10);
  299. Writeln(SUsage20);
  300. Writeln(SUsage30);
  301. Writeln(SUsage40);
  302. Writeln(SUsage50);
  303. Writeln(SUsage60);
  304. Writeln(SUsage70);
  305. Writeln(SUsage80);
  306. Writeln(SUsage84);
  307. Writeln(SUsage87);
  308. Writeln(SUsage90);
  309. Writeln(SUsage95);
  310. Writeln(SUsage100);
  311. Writeln(SUsage110);
  312. Writeln(SUsage120);
  313. Writeln(SUsage130);
  314. Writeln(SUsage140);
  315. Writeln(SUsage150);
  316. Halt(1);
  317. end;
  318. Procedure Version;
  319. var
  320. Version: string;
  321. begin
  322. Version := '';
  323. if version_major <> -1 then
  324. Version := Version + IntToStr(version_major);
  325. if version_minor <> -1 then
  326. Version := Version + '.' + IntToStr(version_minor);
  327. if version_micro <> -1 then
  328. Version := Version + '.' + IntToStr(version_micro);
  329. if version_build <> -1 then
  330. Version := Version + '-' + IntToStr(version_build);
  331. Writeln(Format(SVersion,[Version]));
  332. Halt(0);
  333. end;
  334. Procedure UnknownOption(Const S : String);
  335. begin
  336. Writeln(SErrUnknownOption,S);
  337. Usage;
  338. end;
  339. Procedure ShowBuiltIn;
  340. Var
  341. I : Integer;
  342. begin
  343. For I:=0 to Cfg.Count-1 do
  344. Writeln(Cfg[I]);
  345. end;
  346. Procedure ShowBuiltInMacros;
  347. Var
  348. I : Integer;
  349. begin
  350. For I:=0 to TemplateParser.ValueCount-1 do
  351. Writeln(TemplateParser.NamesByIndex[I]+'='+TemplateParser.ValuesByIndex[I]);
  352. end;
  353. Procedure ProcessCommandline;
  354. Var
  355. I : Integer;
  356. S : String;
  357. ShowBuiltinCommand : boolean;
  358. AppendHelp : boolean;
  359. Function GetOptArg : String;
  360. begin
  361. If I=ParamCount then
  362. begin
  363. Writeln(StdErr,Format(SErrArgExpected,[S]));
  364. Halt(1);
  365. end;
  366. inc(I);
  367. Result:=ParamStr(I);
  368. end;
  369. procedure AddPair(const Value: String);
  370. var P: integer;
  371. N,V: String;
  372. begin
  373. P:=Pos('=',Value);
  374. If p=0 then
  375. begin
  376. Writeln(StdErr,Format(SErrIncompletePair,[Value]));
  377. Halt(1);
  378. end;
  379. V:=Value;
  380. N:=Copy(V,1,P-1);
  381. Delete(V,1,P);
  382. TemplateParser.Values[N] := V;
  383. end;
  384. begin
  385. I:=1;
  386. ShowBuiltinCommand := False;
  387. SkipBackup := False;
  388. CreateDir := False;
  389. AppendHelp:=False;
  390. While( I<=ParamCount) do
  391. begin
  392. S:=Paramstr(i);
  393. If Length(S)<=1 then
  394. UnknownOption(S)
  395. else
  396. case S[2] of
  397. 'v' : Verbose:=True;
  398. 'V' : Version;
  399. 'h' : Usage;
  400. 'b' : ShowBuiltinCommand := true;
  401. 'm' : begin
  402. ShowBuiltinMacros;
  403. halt(0);
  404. end;
  405. 'g' : AppendHelp:=True;
  406. 't' : TemplateFileName:=GetOptArg;
  407. 'd' : AddPair(GetOptArg);
  408. 'u' : TemplateParser.Values[GetOptArg]:='';
  409. 'o' : OutputFileName:=GetoptArg;
  410. 's' : SkipBackup:=True;
  411. 'p' : CreateDir:=True;
  412. '0' : IDEBuildin:=0;
  413. '1' : IDEBuildin:=1;
  414. '2' : IDEBuildin:=2;
  415. '3' : IDEBuildin:=3;
  416. '4' : IDEBuildin:=4;
  417. else
  418. UnknownOption(S);
  419. end;
  420. Inc(I);
  421. end;
  422. If (TemplateFileName<>'') then
  423. begin
  424. If Not FileExists(TemplateFileName) then
  425. begin
  426. Writeln(StdErr,Format(SErrNoSuchFile,[TemplateFileName]));
  427. Halt(1);
  428. end;
  429. Cfg.LoadFromFile(TemplateFileName);
  430. TemplateParser.Values['TEMPLATEFILE'] := TemplateFileName;
  431. end
  432. else
  433. begin
  434. case IDEBuildin of
  435. 1:
  436. Cfg.Text:=StrPas(Addr(fpcfg[0][1]));
  437. 2:
  438. Cfg.Text:=StrPas(Addr(fpini[0][1]));
  439. 3:
  440. Cfg.Text:=StrPas(Addr(fppkg[0][1]));
  441. 4:
  442. Cfg.Text:=StrPas(Addr(fppkg_default[0][1]));
  443. end;
  444. TemplateParser.Values['TEMPLATEFILE'] := 'builtin';
  445. end;
  446. If AppendHelp Then
  447. Cfg.Text:=Cfg.Text+'[Help]'+LineEnding+'Files="%basepath%/help/toc.chm;%basepath%/help/fcl.chm;%basepath%/help/ref.chm;%basepath%/help/rtl.chm;%basepath%/help/prog.chm;%basepath%/help/user.chm;%basepath%/help/fclres.chm"'+LineEnding;
  448. if ShowBuiltinCommand then
  449. begin
  450. ShowBuiltIn;
  451. halt(0);
  452. end;
  453. end;
  454. Procedure CreateFile;
  455. Var
  456. Fout : Text;
  457. S,BFN,ODir : String;
  458. I : Integer;
  459. begin
  460. if (OutputFileName<>'') and
  461. DirectoryExists(OutputFileName) then
  462. begin
  463. Writeln(StdErr,Format(SErrDestDirectory,[OutputFileName]));
  464. Halt(1);
  465. end;
  466. If (OutputFileName<>'')
  467. and FileExists(OutputFileName)
  468. and not SkipBackup then
  469. begin
  470. BFN:=ChangeFileExt(OutputFileName,'.bak');
  471. If FileExists(BFN) and not DeleteFile(BFN) then
  472. begin
  473. Writeln(StdErr,Format(SErrDelBackupFailed,[BFN]));
  474. Halt(1);
  475. end;
  476. If not RenameFile(OutputFileName,BFN) then
  477. begin
  478. Writeln(StdErr,Format(SErrBackupFailed,[OutputFileName,BFN]));
  479. Halt(1);
  480. end
  481. else
  482. Writeln(Format(SBackupCreated,[ExtractFileName(OutputFileName),ExtractFileName(BFN)]));
  483. end;
  484. ODir:=ExtractFilePath(OutputFileName);
  485. if (OutputFileName<>'') and (ODir<>'') and not DirectoryExists(ODir) then
  486. begin
  487. if CreateDir then
  488. begin
  489. if not ForceDirectories(ExtractFilePath(OutputFileName)) then
  490. begin
  491. Writeln(StdErr,Format(SErrCreateDirFailed,[OutputFileName]));
  492. Halt(1);
  493. end;
  494. end
  495. else
  496. begin
  497. Writeln(StdErr,Format(SErrNoSuchDirectory,[OutputFileName]));
  498. Halt(1);
  499. end;
  500. end;
  501. Assign(Fout,OutputFileName);
  502. Rewrite(FOut);
  503. Try
  504. For I:=0 to Cfg.Count-1 do
  505. begin
  506. S:=Cfg[i];
  507. S := TemplateParser.ParseString(S);
  508. Writeln(FOut,S);
  509. end;
  510. Finally
  511. Close(Fout);
  512. end;
  513. end;
  514. begin
  515. Init;
  516. Try
  517. ProcessCommandLine;
  518. CreateFile;
  519. Finally
  520. Done;
  521. end;
  522. end.