fpcmkcfg.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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. end;
  209. darwin :
  210. begin
  211. AddConditionalLinkerPath('cpui386', i386, result);
  212. AddConditionalLinkerPath('cpux86_64', x86_64, result);
  213. AddConditionalLinkerPath('cpupowerpc', powerpc, result);
  214. AddConditionalLinkerPath('cpupowerpc64', powerpc64, result);
  215. { macOS 10.14 or later:
  216. 1) command line tools are installed under /Library/Developer/CommandLineTools
  217. 2) the system libraries still contain i386 code, but the 10.14 sdk doesn't
  218. (-> only use the 10.14 sdk when targeting x86_64 or unknown architectures )
  219. 3) crt1.o is no longer installed under /usr -> add its directory explicitly via
  220. -Fl
  221. We can't detect the macOS version inside fpc.cfg, unfortunately, so we can only
  222. insert this while generating the configuration file.
  223. This will stop working when macOS 10.15 is released without i386 support, but then
  224. users will be responsible for supplying their own i386 SDK anyway.
  225. }
  226. if DirectoryExists('/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk') then
  227. begin
  228. result:=result + LineEnding +
  229. '-FD/Library/Developer/CommandLineTools/usr/bin' + LineEnding +
  230. '#ifdef cpui386' + LineEnding +
  231. '-Fl/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib' + LineEnding +
  232. '#endif' + LineEnding +
  233. '#ifndef cpui386' + LineEnding +
  234. '#ifndef cpupowerpc' + LineEnding +
  235. '#ifndef cpupowerpc64' + LineEnding +
  236. '-XR/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' + LineEnding +
  237. '#endif' + LineEnding +
  238. '#endif' + LineEnding +
  239. '#endif';
  240. end
  241. else
  242. begin
  243. { add Xcode.app binutils to search path}
  244. result:=result + LineEnding +
  245. '-FD/Applications/Xcode.app/Contents/Developer/usr/bin';
  246. end;
  247. end;
  248. end; {case}
  249. { ifdef everything above related to the target OS otherwise host linker/clib paths can leak
  250. into the target while cross-ing, and cause nonworking executables (Darwin-x86_64 to ARM-Linux
  251. for example on my setup), and while it's advised to use -n when crosscompiling, it can
  252. cause hard to identify issues if -n is forgotten... (KB) }
  253. if result <> '' then
  254. result := '#ifdef ' + BuildOSTarget + LineEnding +
  255. result + LineEnding +
  256. '#endif' + LineEnding;
  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. TemplateParser.Values['USERPATHSUFFIX'] := GetDefaultUserPathSuffix;
  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(SUsage95);
  301. Writeln(SUsage100);
  302. Writeln(SUsage110);
  303. Writeln(SUsage120);
  304. Writeln(SUsage130);
  305. Writeln(SUsage140);
  306. Halt(1);
  307. end;
  308. Procedure Version;
  309. var
  310. Version: string;
  311. begin
  312. Version := '';
  313. if version_major <> -1 then
  314. Version := Version + IntToStr(version_major);
  315. if version_minor <> -1 then
  316. Version := Version + '.' + IntToStr(version_minor);
  317. if version_micro <> -1 then
  318. Version := Version + '.' + IntToStr(version_micro);
  319. if version_build <> -1 then
  320. Version := Version + '-' + IntToStr(version_build);
  321. Writeln(Format(SVersion,[Version]));
  322. Halt(0);
  323. end;
  324. Procedure UnknownOption(Const S : String);
  325. begin
  326. Writeln(SErrUnknownOption,S);
  327. Usage;
  328. end;
  329. Procedure ShowBuiltIn;
  330. Var
  331. I : Integer;
  332. begin
  333. For I:=0 to Cfg.Count-1 do
  334. Writeln(Cfg[I]);
  335. end;
  336. Procedure ShowBuiltInMacros;
  337. Var
  338. I : Integer;
  339. begin
  340. For I:=0 to TemplateParser.ValueCount-1 do
  341. Writeln(TemplateParser.NamesByIndex[I]+'='+TemplateParser.ValuesByIndex[I]);
  342. end;
  343. Procedure ProcessCommandline;
  344. Var
  345. I : Integer;
  346. S : String;
  347. ShowBuiltinCommand : boolean;
  348. Function GetOptArg : String;
  349. begin
  350. If I=ParamCount then
  351. begin
  352. Writeln(StdErr,Format(SErrArgExpected,[S]));
  353. Halt(1);
  354. end;
  355. inc(I);
  356. Result:=ParamStr(I);
  357. end;
  358. procedure AddPair(const Value: String);
  359. var P: integer;
  360. N,V: String;
  361. begin
  362. P:=Pos('=',Value);
  363. If p=0 then
  364. begin
  365. Writeln(StdErr,Format(SErrIncompletePair,[Value]));
  366. Halt(1);
  367. end;
  368. V:=Value;
  369. N:=Copy(V,1,P-1);
  370. Delete(V,1,P);
  371. TemplateParser.Values[N] := V;
  372. end;
  373. begin
  374. I:=1;
  375. ShowBuiltinCommand := False;
  376. SkipBackup := False;
  377. CreateDir := False;
  378. While( I<=ParamCount) do
  379. begin
  380. S:=Paramstr(i);
  381. If Length(S)<=1 then
  382. UnknownOption(S)
  383. else
  384. case S[2] of
  385. 'v' : Verbose:=True;
  386. 'V' : Version;
  387. 'h' : Usage;
  388. 'b' : ShowBuiltinCommand := true;
  389. 'm' : begin
  390. ShowBuiltinMacros;
  391. halt(0);
  392. end;
  393. 't' : TemplateFileName:=GetOptArg;
  394. 'd' : AddPair(GetOptArg);
  395. 'u' : TemplateParser.Values[GetOptArg]:='';
  396. 'o' : OutputFileName:=GetoptArg;
  397. 's' : SkipBackup:=True;
  398. 'p' : CreateDir:=True;
  399. '0' : IDEBuildin:=0;
  400. '1' : IDEBuildin:=1;
  401. '2' : IDEBuildin:=2;
  402. '3' : IDEBuildin:=3;
  403. '4' : IDEBuildin:=4;
  404. else
  405. UnknownOption(S);
  406. end;
  407. Inc(I);
  408. end;
  409. If (TemplateFileName<>'') then
  410. begin
  411. If Not FileExists(TemplateFileName) then
  412. begin
  413. Writeln(StdErr,Format(SErrNoSuchFile,[TemplateFileName]));
  414. Halt(1);
  415. end;
  416. Cfg.LoadFromFile(TemplateFileName);
  417. TemplateParser.Values['TEMPLATEFILE'] := TemplateFileName;
  418. end
  419. else
  420. begin
  421. case IDEBuildin of
  422. 1:
  423. Cfg.Text:=StrPas(Addr(fpcfg[0][1]));
  424. 2:
  425. Cfg.Text:=StrPas(Addr(fpini[0][1]));
  426. 3:
  427. Cfg.Text:=StrPas(Addr(fppkg[0][1]));
  428. 4:
  429. Cfg.Text:=StrPas(Addr(fppkg_default[0][1]));
  430. end;
  431. TemplateParser.Values['TEMPLATEFILE'] := 'builtin';
  432. end;
  433. if ShowBuiltinCommand then
  434. begin
  435. ShowBuiltIn;
  436. halt(0);
  437. end;
  438. end;
  439. Procedure CreateFile;
  440. Var
  441. Fout : Text;
  442. S,BFN,ODir : String;
  443. I : Integer;
  444. begin
  445. if (OutputFileName<>'') and
  446. DirectoryExists(OutputFileName) then
  447. begin
  448. Writeln(StdErr,Format(SErrDestDirectory,[OutputFileName]));
  449. Halt(1);
  450. end;
  451. If (OutputFileName<>'')
  452. and FileExists(OutputFileName)
  453. and not SkipBackup then
  454. begin
  455. BFN:=ChangeFileExt(OutputFileName,'.bak');
  456. If FileExists(BFN) and not DeleteFile(BFN) then
  457. begin
  458. Writeln(StdErr,Format(SErrDelBackupFailed,[BFN]));
  459. Halt(1);
  460. end;
  461. If not RenameFile(OutputFileName,BFN) then
  462. begin
  463. Writeln(StdErr,Format(SErrBackupFailed,[OutputFileName,BFN]));
  464. Halt(1);
  465. end
  466. else
  467. Writeln(Format(SBackupCreated,[ExtractFileName(OutputFileName),ExtractFileName(BFN)]));
  468. end;
  469. ODir:=ExtractFilePath(OutputFileName);
  470. if (OutputFileName<>'') and (ODir<>'') and not DirectoryExists(ODir) then
  471. begin
  472. if CreateDir then
  473. begin
  474. if not ForceDirectories(ExtractFilePath(OutputFileName)) then
  475. begin
  476. Writeln(StdErr,Format(SErrCreateDirFailed,[OutputFileName]));
  477. Halt(1);
  478. end;
  479. end
  480. else
  481. begin
  482. Writeln(StdErr,Format(SErrNoSuchDirectory,[OutputFileName]));
  483. Halt(1);
  484. end;
  485. end;
  486. Assign(Fout,OutputFileName);
  487. Rewrite(FOut);
  488. Try
  489. For I:=0 to Cfg.Count-1 do
  490. begin
  491. S:=Cfg[i];
  492. S := TemplateParser.ParseString(S);
  493. Writeln(FOut,S);
  494. end;
  495. Finally
  496. Close(Fout);
  497. end;
  498. end;
  499. begin
  500. Init;
  501. Try
  502. ProcessCommandLine;
  503. CreateFile;
  504. Finally
  505. Done;
  506. end;
  507. end.