fppkg.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. program fppkg;
  2. {$mode objfpc}{$H+}{$macro on}
  3. {$if defined(VER2_2) and (FPC_PATCH<1)}
  4. {$fatal At least FPC 2.2.1 is required to compile fppkg}
  5. {$endif}
  6. {$ifndef package_version_major}
  7. {$define package_version_major:=0}
  8. {$endif}
  9. {$ifndef package_version_minor}
  10. {$define package_version_minor:=0}
  11. {$endif}
  12. {$ifndef package_version_micro}
  13. {$define package_version_micro:=0}
  14. {$endif}
  15. {$ifndef package_version_build}
  16. {$define package_version_build:=0}
  17. {$endif}
  18. uses
  19. // General
  20. {$ifdef unix}
  21. baseunix, cthreads,
  22. {$endif}
  23. Classes, SysUtils, TypInfo, custapp, inifiles,
  24. // Repository handler objects
  25. fprepos, fpxmlrep,
  26. pkgmessages, pkgglobals, pkgoptions, pkgrepos,
  27. // Package Handler components
  28. pkghandler,pkgmkconv, pkgdownload,
  29. pkgfpmake, pkgcommands,
  30. pkgPackagesStructure,
  31. fpmkunit
  32. // Downloaders
  33. {$if (defined(unix) and not defined(android)) or defined(windows)}
  34. ,pkgwget
  35. ,pkglnet
  36. ,pkgfphttp
  37. ,opensslsockets
  38. {$endif}
  39. ;
  40. const
  41. version_major = package_version_major;
  42. version_minor = package_version_minor;
  43. version_micro = package_version_micro;
  44. version_build = package_version_build;
  45. Type
  46. { TMakeTool }
  47. TMakeTool = Class(TCustomApplication)
  48. Private
  49. ParaAction : string;
  50. ParaPackages : TStringList;
  51. procedure HandleConfig;
  52. procedure MaybeCreateLocalDirs;
  53. procedure ShowUsage(const aErrorMsg : String = '');
  54. procedure ShowVersion;
  55. Public
  56. Constructor Create; overload;
  57. Constructor Create(aOwner : TComponent); overload; override;
  58. Destructor Destroy;override;
  59. Procedure LoadGlobalDefaults;
  60. Procedure ProcessCommandLine(FirstPass: boolean);
  61. Procedure DoRun; Override;
  62. end;
  63. EMakeToolError = Class(Exception);
  64. { TMakeTool }
  65. procedure TMakeTool.LoadGlobalDefaults;
  66. var
  67. i : integer;
  68. cfgfile : String;
  69. begin
  70. // Default verbosity
  71. LogLevels:=DefaultLogLevels;
  72. for i:=1 to ParamCount do
  73. begin
  74. if (ParamStr(i)='-d') or (ParamStr(i)='--debug') then
  75. begin
  76. LogLevels:=AllLogLevels+[llDebug];
  77. break;
  78. end;
  79. if (ParamStr(i)='-v') or (ParamStr(i)='--verbose') then
  80. begin
  81. LogLevels:=AllLogLevels+[llDebug];
  82. break;
  83. end;
  84. end;
  85. // First try config file from command line
  86. if HasOption('C','config-file') then
  87. cfgfile:=GetOptionValue('C','config-file')
  88. else
  89. cfgfile:='';
  90. GFPpkg.InitializeGlobalOptions(CfgFile);
  91. end;
  92. procedure TMakeTool.MaybeCreateLocalDirs;
  93. begin
  94. ForceDirectories(GFPpkg.Options.GlobalSection.BuildDir);
  95. ForceDirectories(GFPpkg.Options.GlobalSection.ArchivesDir);
  96. ForceDirectories(GFPpkg.Options.GlobalSection.CompilerConfigDir);
  97. end;
  98. procedure TMakeTool.ShowUsage(const aErrorMsg : String = '');
  99. begin
  100. if (aErrorMsg<>'') then
  101. Writeln(stdErr,'Error: ',aErrorMsg);
  102. Writeln('Usage: ',Paramstr(0),' [options] <action> <package>');
  103. Writeln('Options:');
  104. Writeln(' -C --config-file Specify the configuration file to use');
  105. Writeln(' -c --config Set compiler configuration to use');
  106. Writeln(' -g --global Prefer global configuration file over local configuration file.');
  107. Writeln(' -h --help This help');
  108. Writeln(' -V --version Show version and exit');
  109. Writeln(' -v --verbose Show more information');
  110. Writeln(' -d --debug Show debugging information');
  111. Writeln(' -f --force Force installation also if the package is already installed');
  112. Writeln(' -r --recovery Recovery mode, use always internal fpmkunit');
  113. Writeln(' -b --broken Do not stop on broken packages');
  114. Writeln(' -l --showlocation Show in which repository the the packages are installed');
  115. Writeln(' -o --options=value Pass extra options to the compiler');
  116. Writeln(' -n Do not read the default configuration files');
  117. Writeln(' -p --prefix=value Specify the prefix');
  118. Writeln(' -s --skipbroken Skip the rebuild of depending packages after installation');
  119. Writeln(' -i --installlocation Specify the repository to install packages into');
  120. Writeln(' --compiler=value Specify the compiler-executable');
  121. Writeln(' --cpu=value Specify the target cpu to compile for');
  122. Writeln(' --os=value Specify the target operating system to compile for');
  123. Writeln('Actions:');
  124. Writeln(' update Update packages list');
  125. Writeln(' list List available and installed packages');
  126. Writeln(' build Build package');
  127. Writeln(' compile Compile package');
  128. Writeln(' install Install package');
  129. Writeln(' uninstall Uninstall package');
  130. Writeln(' clean Clean package');
  131. Writeln(' archive Create archive of package');
  132. Writeln(' download Download package');
  133. Writeln(' convertmk Convert Makefile.fpc to fpmake.pp');
  134. Writeln(' info Show more information about a package');
  135. Writeln(' fixbroken Recompile all (broken) packages with changed dependencies');
  136. Writeln(' listsettings Show the values for all fppkg settings');
  137. Writeln(' config Get/Set configuration file values');
  138. // Writeln(' addconfig Add a compiler configuration for the supplied compiler');
  139. Writeln('Config commands:');
  140. Writeln(' config get a.b Get setting from config file, section a, key b');
  141. Writeln(' config get a b Get setting from config file, section a, key b');
  142. Writeln(' config set a.b c Set setting from config file, section a, key b to value c');
  143. Writeln(' config set a b c Set setting from config file, section a, key b to value c');
  144. Halt(Ord(aErrorMsg<>''));
  145. end;
  146. constructor TMakeTool.Create;
  147. begin
  148. Create(nil);
  149. end;
  150. constructor TMakeTool.Create(aOwner: TComponent);
  151. begin
  152. Inherited;
  153. ParaPackages:=TStringList.Create;
  154. end;
  155. destructor TMakeTool.Destroy;
  156. begin
  157. FreeAndNil(ParaPackages);
  158. inherited Destroy;
  159. end;
  160. procedure TMakeTool.ProcessCommandLine(FirstPass: boolean);
  161. Function CheckOption(Index : Integer;Short,Long : String): Boolean;
  162. var
  163. O : String;
  164. begin
  165. O:=Paramstr(Index);
  166. Result:=(O='-'+short) or (O='--'+long) or (copy(O,1,Length(Long)+3)=('--'+long+'='));
  167. end;
  168. Function OptionArg(Var Index : Integer) : String;
  169. Var
  170. P : Integer;
  171. begin
  172. if (Length(ParamStr(Index))>1) and (Paramstr(Index)[2]<>'-') then
  173. begin
  174. If Index<ParamCount then
  175. begin
  176. Inc(Index);
  177. Result:=Paramstr(Index);
  178. end
  179. else
  180. Error(SErrNeedArgument,[Index,ParamStr(Index)]);
  181. end
  182. else If length(ParamStr(Index))>2 then
  183. begin
  184. P:=Pos('=',Paramstr(Index));
  185. If (P=0) then
  186. Error(SErrNeedArgument,[Index,ParamStr(Index)])
  187. else
  188. begin
  189. Result:=Paramstr(Index);
  190. Delete(Result,1,P);
  191. end;
  192. end;
  193. end;
  194. function SplitSpaces(var SplitString: string) : string;
  195. var i : integer;
  196. begin
  197. i := pos(' ',SplitString);
  198. if i > 0 then
  199. begin
  200. result := copy(SplitString,1,i-1);
  201. delete(SplitString,1,i);
  202. end
  203. else
  204. begin
  205. result := SplitString;
  206. SplitString:='';
  207. end;
  208. end;
  209. Var
  210. I : Integer;
  211. HasAction : Boolean;
  212. OptString : String;
  213. begin
  214. I:=0;
  215. HasAction:=false;
  216. // We can't use the TCustomApplication option handling,
  217. // because they cannot handle [general opts] [command] [cmd-opts] [args]
  218. While (I<ParamCount) do
  219. begin
  220. Inc(I);
  221. // Check options.
  222. if CheckOption(I,'C','config-file') then
  223. begin
  224. // Do nothing, the config-file has already been read.
  225. OptionArg(I);
  226. end
  227. else if CheckOption(I,'c','config') then
  228. GFPpkg.Options.CommandLineSection.CompilerConfig:=OptionArg(I)
  229. else if CheckOption(I,'v','verbose') then
  230. LogLevels:=AllLogLevels
  231. else if CheckOption(I,'d','debug') then
  232. LogLevels:=AllLogLevels+[llDebug]
  233. else if CheckOption(I,'i','installrepository') then
  234. GFPpkg.Options.CommandLineSection.InstallRepository:=OptionArg(I)
  235. else if CheckOption(I,'r','recovery') then
  236. GFPpkg.Options.CommandLineSection.RecoveryMode:=true
  237. else if CheckOption(I,'n','') then
  238. GFPpkg.Options.CommandLineSection.SkipConfigurationFiles:=true
  239. else if CheckOption(I,'b','broken') then
  240. GFPpkg.Options.CommandLineSection.AllowBroken:=true
  241. else if CheckOption(I,'l','showlocation') then
  242. GFPpkg.Options.CommandLineSection.ShowLocation:=true
  243. else if CheckOption(I,'s','skipbroken') then
  244. GFPpkg.Options.CommandLineSection.SkipFixBrokenAfterInstall:=true
  245. else if CheckOption(I,'g','global') then
  246. GFPpkg.Options.PreferGlobal:=true
  247. else if CheckOption(I,'o','options') and FirstPass then
  248. begin
  249. OptString := OptionArg(I);
  250. while OptString <> '' do
  251. GFPpkg.CompilerOptions.Options.Add(SplitSpaces(OptString));
  252. end
  253. else if CheckOption(I,'p','prefix') then
  254. begin
  255. GFPpkg.CompilerOptions.GlobalPrefix := OptionArg(I);
  256. GFPpkg.CompilerOptions.LocalPrefix := OptionArg(I);
  257. GFPpkg.FPMakeCompilerOptions.GlobalPrefix := OptionArg(I);
  258. GFPpkg.FPMakeCompilerOptions.LocalPrefix := OptionArg(I);
  259. end
  260. else if CheckOption(I,'','compiler') then
  261. begin
  262. GFPpkg.CompilerOptions.Compiler := OptionArg(I);
  263. GFPpkg.FPMakeCompilerOptions.Compiler := OptionArg(I);
  264. end
  265. else if CheckOption(I,'','os') then
  266. GFPpkg.CompilerOptions.CompilerOS := StringToOS(OptionArg(I))
  267. else if CheckOption(I,'','cpu') then
  268. GFPpkg.CompilerOptions.CompilerCPU := StringToCPU(OptionArg(I))
  269. else if CheckOption(I,'h','help') then
  270. begin
  271. ShowUsage;
  272. halt(0);
  273. end
  274. else if CheckOption(I,'V','version') then
  275. begin
  276. ShowVersion;
  277. halt(0);
  278. end
  279. else if (Length(Paramstr(i))>0) and (Paramstr(I)[1]='-') then
  280. begin
  281. if FirstPass then
  282. Raise EMakeToolError.CreateFmt(SErrInvalidArgument,[I,ParamStr(i)])
  283. end
  284. else
  285. // It's a command or target.
  286. begin
  287. if HasAction then
  288. begin
  289. if FirstPass then
  290. ParaPackages.Add(Paramstr(i))
  291. end
  292. else
  293. begin
  294. ParaAction:=Paramstr(i);
  295. HasAction:=true;
  296. end;
  297. end;
  298. end;
  299. if not HasAction then
  300. ShowUsage('No action specified!');
  301. end;
  302. procedure TMakeTool.HandleConfig;
  303. Type
  304. TConfigMode = (cfUnknown,cfGet,cfSet);
  305. Const
  306. cCount : array[TConfigMode] of byte = (0,2,3);
  307. var
  308. aMode : TConfigMode;
  309. aIni : TMemIniFile;
  310. cfgFile,aSection,aKey,aValue : String;
  311. function GetSectionKey(getValue : boolean) : Boolean;
  312. var
  313. p,pValue : Integer;
  314. begin
  315. aSection:=ParaPackages[1];
  316. pValue:=2;
  317. P:=Pos('.',aSection);
  318. if P>0 then
  319. begin
  320. aKey:=Copy(aSection,P+1,Length(aSection));
  321. Delete(aSection,P,Length(aSection));
  322. end
  323. else
  324. begin
  325. if ParaPackages.Count>=3 then
  326. begin
  327. aKey:=ParaPackages[2];
  328. Inc(pValue);
  329. end;
  330. end;
  331. Result:=Not ((aSection='') or (aKey=''));
  332. if not Result then
  333. ShowUsage('Config: No section and key specified!')
  334. else if GetValue then
  335. if pValue<ParaPackages.Count then
  336. aValue:=ParaPackages[Pvalue]
  337. else
  338. ShowUsage('Config: No value specified!');
  339. Writeln('S: ',aSection,', K: ',aKey,', V: ',aValue);
  340. end;
  341. begin
  342. // We know there is at least 1 parapackage
  343. aIni:=Nil;
  344. aMode:=cfUnknown;
  345. //writeln('args: ',parapackages.text);
  346. if ParaPackages[0]='get' then
  347. aMode:=cfGet
  348. else if ParaPackages[0]='set' then
  349. aMode:=cfSet;
  350. if aMode=cfUnknown then
  351. ShowUsage('Config: Unknown config command : '+ParaPackages[0])
  352. else if Not (ParaPackages.Count in [cCount[aMode],cCount[aMode]+1]) then
  353. begin
  354. ShowUsage(Format('Config: Wrong amount of arguments. Expected %d, got %d.',[cCount[aMode],ParaPackages.Count]));
  355. aMode:=cfUnknown;
  356. end;
  357. if HasOption('C','config-file') then
  358. cfgfile:=GetOptionValue('C','config-file')
  359. else
  360. begin
  361. cfgfile:=GetFppkgConfigFile(GFPpkg.Options.PreferGlobal,false);
  362. if Not FileExists(cfgFile) then
  363. cfgfile:=GetFppkgConfigFile(GFPpkg.Options.PreferGlobal,false);
  364. end;
  365. Writeln('Getting from file : ',CfgFile);
  366. if aMode<>cfUnknown then
  367. aIni:=TMemIniFile.Create(cfgFile);
  368. Case aMode of
  369. cfGet :
  370. begin
  371. if GetSectionKey(False) then
  372. aValue:=aIni.ReadString(aSection,aKey,'')
  373. else
  374. exit;
  375. writeln(aValue);
  376. end;
  377. cfSet :
  378. begin
  379. if GetSectionKey(True) then
  380. begin
  381. aIni.WriteString(aSection,aKey,aValue);
  382. try
  383. aIni.UpdateFile;
  384. except
  385. On EIO: EInoutError do
  386. Writeln(stderr,'Failed to update file: ',cfgfile,'. Make sure you have sufficient rights to write this file.');
  387. end;
  388. end
  389. else
  390. exit;
  391. end;
  392. else
  393. ShowUsage;
  394. end;
  395. end;
  396. procedure TMakeTool.DoRun;
  397. var
  398. OldCurrDir : String;
  399. i : Integer;
  400. SL : TStringList;
  401. Repo: TFPRepository;
  402. InstPackages: TFPCurrentDirectoryPackagesStructure;
  403. ArchivePackages: TFPArchiveFilenamePackagesStructure;
  404. begin
  405. Terminate; // We run only once
  406. OldCurrDir:=GetCurrentDir;
  407. Try
  408. InitializeFppkg;
  409. LoadGlobalDefaults;
  410. ProcessCommandLine(true);
  411. SetLength(FPMKUnitDeps,FPMKUnitDepDefaultCount);
  412. for i := 0 to FPMKUnitDepDefaultCount-1 do
  413. FPMKUnitDeps[i]:=FPMKUnitDepsDefaults[i];
  414. MaybeCreateLocalDirs;
  415. if not GFPpkg.Options.CommandLineSection.SkipConfigurationFiles then
  416. begin
  417. GFPpkg.InitializeCompilerOptions;
  418. if GFPpkg.Options.GlobalSection.ConfigVersion = 4 then
  419. begin
  420. // This version did not have any repository configured, but used a
  421. // 'local' and 'global' compiler-setting.
  422. GFPpkg.Options.AddRepositoriesForCompilerSettings(GFPpkg.CompilerOptions);
  423. end;
  424. end
  425. else
  426. begin
  427. GFPpkg.FPMakeCompilerOptions.InitCompilerDefaults;
  428. GFPpkg.CompilerOptions.InitCompilerDefaults;
  429. end;
  430. // The command-line is parsed for the second time, to make it possible
  431. // to override the values in the compiler-configuration file. (like prefix)
  432. ProcessCommandLine(false);
  433. // Config command does not do anything except get/set values
  434. if (ParaAction = 'config') then
  435. begin
  436. If ParaPackages.Count=0 then
  437. ShowUsage('config command needs arguments')
  438. else
  439. HandleConfig;
  440. exit;
  441. end;
  442. // If CompilerVersion, CompilerOS or CompilerCPU is still empty, use the
  443. // compiler-executable to get them
  444. GFPpkg.FPMakeCompilerOptions.CheckCompilerValues;
  445. GFPpkg.CompilerOptions.CheckCompilerValues;
  446. LoadLocalAvailableMirrors;
  447. // Load local repository, update first if this is a new installation
  448. // errors will only be reported as warning. The user can be bootstrapping
  449. // and do an update later
  450. if not FileExists(GFPpkg.Options.GlobalSection.LocalPackagesFile) then
  451. begin
  452. try
  453. pkghandler.ExecuteAction('','update', GFPpkg);
  454. except
  455. on E: Exception do
  456. pkgglobals.Log(llWarning,E.Message);
  457. end;
  458. end;
  459. FindInstalledPackages(GFPpkg.FPMakeCompilerOptions,true);
  460. // Check for broken dependencies
  461. if not GFPpkg.Options.CommandLineSection.AllowBroken and
  462. (((ParaAction='fixbroken') and (ParaPackages.Count>0)) or
  463. (ParaAction='compile') or
  464. (ParaAction='build') or
  465. (ParaAction='install') or
  466. (ParaAction='archive')) then
  467. begin
  468. pkgglobals.Log(llDebug,SLogCheckBrokenDependenvies);
  469. SL:=TStringList.Create;
  470. if FindBrokenPackages(SL) then
  471. Error(SErrBrokenPackagesFound);
  472. FreeAndNil(SL);
  473. end;
  474. if (ParaAction='install') or (ParaAction='uninstall') or
  475. (ParaAction='fixbroken') then
  476. GFPpkg.ScanInstalledPackagesForAvailablePackages;
  477. if ParaPackages.Count=0 then
  478. begin
  479. // Do not add the fake-repository with the contents of the current directory
  480. // when a list of packages is shown. (The fake repository should not be shown)
  481. if ParaAction<>'list' then
  482. begin
  483. Repo := TFPRepository.Create(GFPpkg);
  484. GFPpkg.RepositoryList.Add(Repo);
  485. Repo.RepositoryType := fprtAvailable;
  486. Repo.RepositoryName := 'CurrentDirectory';
  487. Repo.Description := 'Package in current directory';
  488. InstPackages := TFPCurrentDirectoryPackagesStructure.Create(GFPpkg);
  489. InstPackages.InitializeWithOptions(nil, GFPpkg.Options, GFPpkg.CompilerOptions);
  490. InstPackages.Path := OldCurrDir;
  491. InstPackages.AddPackagesToRepository(Repo);
  492. Repo.DefaultPackagesStructure := InstPackages;
  493. end;
  494. pkghandler.ExecuteAction(CurrentDirPackageName,ParaAction,GFPpkg);
  495. end
  496. else
  497. begin
  498. // Process packages
  499. for i:=0 to ParaPackages.Count-1 do
  500. begin
  501. if sametext(ExtractFileExt(ParaPackages[i]),'.zip') and FileExists(ParaPackages[i]) then
  502. begin
  503. Repo := TFPRepository.Create(GFPpkg);
  504. GFPpkg.RepositoryList.Add(Repo);
  505. Repo.RepositoryType := fprtAvailable;
  506. Repo.RepositoryName := 'ArchiveFile';
  507. Repo.Description := 'Package in archive-file';
  508. ArchivePackages := TFPArchiveFilenamePackagesStructure.Create(GFPpkg);
  509. ArchivePackages.InitializeWithOptions(nil, GFPpkg.Options, GFPpkg.CompilerOptions);
  510. ArchivePackages.ArchiveFileName := ParaPackages[i];
  511. ArchivePackages.AddPackagesToRepository(Repo);
  512. Repo.DefaultPackagesStructure := ArchivePackages;
  513. pkgglobals.Log(llDebug,SLogCommandLineAction,['['+CmdLinePackageName+']',ParaAction]);
  514. pkghandler.ExecuteAction(CmdLinePackageName,ParaAction,GFPpkg);
  515. end
  516. else
  517. begin
  518. pkgglobals.Log(llDebug,SLogCommandLineAction,['['+ParaPackages[i]+']',ParaAction]);
  519. pkghandler.ExecuteAction(ParaPackages[i],ParaAction,GFPpkg);
  520. end;
  521. end;
  522. end;
  523. // Recompile all packages dependent on this package
  524. if (ParaAction='install') and not GFPpkg.Options.CommandLineSection.SkipFixBrokenAfterInstall then
  525. pkghandler.ExecuteAction('','fixbroken',GFPpkg);
  526. except
  527. On E : Exception do
  528. begin
  529. Writeln(StdErr,SErrException);
  530. Writeln(StdErr,E.Message);
  531. Halt(1);
  532. end;
  533. end;
  534. SetCurrentDir(OldCurrDir);
  535. end;
  536. procedure TMakeTool.ShowVersion;
  537. var
  538. Version: TFPVersion;
  539. begin
  540. Version := TFPVersion.Create;
  541. try
  542. Version.Major := version_major;
  543. Version.Minor := version_minor;
  544. Version.Micro := version_micro;
  545. Version.Build := version_build;
  546. Writeln('Version: ', Version.AsString);
  547. finally
  548. Version.Free;
  549. end;
  550. end;
  551. begin
  552. With TMakeTool.Create do
  553. try
  554. run;
  555. finally
  556. Free;
  557. end;
  558. end.