fpcmkcfg.pp 16 KB

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