pkgrepos.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. unit pkgrepos;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. SysUtils,Classes,
  6. fprepos,pkgoptions;
  7. function GetRemoteRepositoryURL(const AFileName:string):string;
  8. procedure LoadLocalAvailableMirrors;
  9. procedure LoadLocalAvailableRepository;
  10. procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String);
  11. function LoadManifestFromFile(const AManifestFN:string):TFPPackage;
  12. procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
  13. function PackageIsBroken(APackage:TFPPackage):boolean;
  14. function FindBrokenPackages(SL:TStrings):Boolean;
  15. procedure CheckFPMakeDependencies;
  16. function PackageInstalledVersionStr(const AName:String):string;
  17. function PackageAvailableVersionStr(const AName:String):string;
  18. procedure ListAvailablePackages;
  19. procedure ListPackages;
  20. procedure ListRemoteRepository;
  21. procedure RebuildRemoteRepository;
  22. procedure SaveRemoteRepository;
  23. var
  24. AvailableMirrors : TFPMirrors;
  25. AvailableRepository,
  26. InstalledRepository : TFPRepository;
  27. implementation
  28. uses
  29. zipper,
  30. fpxmlrep,
  31. pkgglobals,
  32. pkgmessages;
  33. {*****************************************************************************
  34. Mirror Selection
  35. *****************************************************************************}
  36. var
  37. CurrentRemoteRepositoryURL : String;
  38. procedure LoadLocalAvailableMirrors;
  39. var
  40. S : String;
  41. X : TFPXMLMirrorHandler;
  42. begin
  43. if assigned(AvailableMirrors) then
  44. AvailableMirrors.Free;
  45. AvailableMirrors:=TFPMirrors.Create(TFPMirror);
  46. // Repository
  47. S:=GlobalOptions.LocalMirrorsFile;
  48. Log(vlDebug,SLogLoadingMirrorsFile,[S]);
  49. if not FileExists(S) then
  50. exit;
  51. try
  52. X:=TFPXMLMirrorHandler.Create;
  53. With X do
  54. try
  55. LoadFromXml(AvailableMirrors,S);
  56. finally
  57. Free;
  58. end;
  59. except
  60. on E : Exception do
  61. begin
  62. Log(vlError,E.Message);
  63. Error(SErrCorruptMirrorsFile,[S]);
  64. end;
  65. end;
  66. end;
  67. function SelectRemoteMirror:string;
  68. var
  69. i,j : Integer;
  70. Bucket,
  71. BucketCnt : Integer;
  72. M : TFPMirror;
  73. begin
  74. Result:='';
  75. M:=nil;
  76. if assigned(AvailableMirrors) then
  77. begin
  78. // Create array for selection
  79. BucketCnt:=0;
  80. for i:=0 to AvailableMirrors.Count-1 do
  81. inc(BucketCnt,AvailableMirrors[i].Weight);
  82. // Select random entry
  83. Bucket:=Random(BucketCnt);
  84. M:=nil;
  85. for i:=0 to AvailableMirrors.Count-1 do
  86. begin
  87. for j:=0 to AvailableMirrors[i].Weight-1 do
  88. begin
  89. if Bucket=0 then
  90. begin
  91. M:=AvailableMirrors[i];
  92. break;
  93. end;
  94. Dec(Bucket);
  95. end;
  96. if assigned(M) then
  97. break;
  98. end;
  99. end;
  100. if assigned(M) then
  101. begin
  102. Log(vlInfo,SLogSelectedMirror,[M.Name]);
  103. Result:=M.URL;
  104. end
  105. else
  106. Error(SErrFailedToSelectMirror);
  107. end;
  108. function GetRemoteRepositoryURL(const AFileName:string):string;
  109. begin
  110. if CurrentRemoteRepositoryURL='' then
  111. begin
  112. if GlobalOptions.RemoteRepository='auto' then
  113. CurrentRemoteRepositoryURL:=SelectRemoteMirror
  114. else
  115. CurrentRemoteRepositoryURL:=GlobalOptions.RemoteRepository;
  116. end;
  117. Result:=CurrentRemoteRepositoryURL+AFileName;
  118. end;
  119. {*****************************************************************************
  120. Local Repository
  121. *****************************************************************************}
  122. procedure ReadIniFile(Const AFileName: String;L:TStrings);
  123. Var
  124. F : TFileStream;
  125. Line : String;
  126. I,P,PC : Integer;
  127. begin
  128. F:=TFileStream.Create(AFileName,fmOpenRead);
  129. Try
  130. L.LoadFromStream(F);
  131. // Fix lines.
  132. For I:=L.Count-1 downto 0 do
  133. begin
  134. Line:=L[I];
  135. P:=Pos('=',Line);
  136. PC:=Pos(';',Line); // Comment line.
  137. If (P=0) or ((PC<>0) and (PC<P)) then
  138. L.Delete(I)
  139. else
  140. L[i]:=Trim(System.Copy(Line,1,P-1)+'='+Trim(System.Copy(Line,P+1,Length(Line)-P)));
  141. end;
  142. Finally
  143. F.Free;
  144. end;
  145. end;
  146. function LoadManifestFromFile(const AManifestFN:string):TFPPackage;
  147. var
  148. X : TFPXMLRepositoryHandler;
  149. NewPackages : TFPPackages;
  150. NewP,P : TFPPackage;
  151. begin
  152. result:=nil;
  153. NewPackages:=TFPPackages.Create(TFPPackage);
  154. X:=TFPXMLRepositoryHandler.Create;
  155. try
  156. X.LoadFromXml(NewPackages,AManifestFN);
  157. // Update or Add packages to repository
  158. if NewPackages.Count=1 then
  159. begin
  160. NewP:=NewPackages[0];
  161. // Prevent duplicate names
  162. { P:=InstalledRepository.FindPackage(NewP.Name);
  163. if not assigned(P) then
  164. P:=InstalledRepository.AddPackage(NewP.Name); }
  165. result:=TFPPackage.Create(nil);
  166. // Copy contents
  167. result.Assign(NewP);
  168. end
  169. else
  170. Error(SErrManifestNoSinglePackage,[AManifestFN]);
  171. finally
  172. X.Free;
  173. NewPackages.Free;
  174. end;
  175. end;
  176. procedure LoadUnitConfigFromFile(APackage:TFPPackage;const AFileName: String);
  177. Var
  178. L,DepSL : TStrings;
  179. DepName,
  180. V : String;
  181. DepChecksum : Cardinal;
  182. i,j,k : integer;
  183. D : TFPDependency;
  184. begin
  185. L:=TStringList.Create;
  186. Try
  187. ReadIniFile(AFileName,L);
  188. {$warning TODO Maybe check also CPU-OS}
  189. // Read fpunits.conf
  190. V:=L.Values['version'];
  191. APackage.Version.AsString:=V;
  192. V:=L.Values['checksum'];
  193. if V<>'' then
  194. APackage.Checksum:=StrToInt(V)
  195. else
  196. APackage.Checksum:=$ffffffff;
  197. // Load dependencies
  198. V:=L.Values['depends'];
  199. DepSL:=TStringList.Create;
  200. DepSL.CommaText:=V;
  201. for i:=0 to DepSL.Count-1 do
  202. begin
  203. DepName:=DepSL[i];
  204. k:=Pos('|',DepName);
  205. if k>0 then
  206. begin
  207. DepChecksum:=StrToInt(Copy(DepName,k+1,Length(DepName)-k));
  208. DepName:=Copy(DepName,1,k-1);
  209. end
  210. else
  211. DepChecksum:=$ffffffff;
  212. D:=nil;
  213. for j:=0 to APackage.Dependencies.Count-1 do
  214. begin
  215. D:=APackage.Dependencies[j];
  216. if D.PackageName=DepName then
  217. break;
  218. D:=nil;
  219. end;
  220. if not assigned(D) then
  221. D:=APackage.AddDependency(DepName,'');
  222. D.RequireChecksum:=DepChecksum;
  223. end;
  224. DepSL.Free;
  225. Finally
  226. L.Free;
  227. end;
  228. end;
  229. procedure FindInstalledPackages(ACompilerOptions:TCompilerOptions;showdups:boolean=true);
  230. function AddInstalledPackage(const AName,AFileName: String):TFPPackage;
  231. begin
  232. result:=InstalledRepository.FindPackage(AName);
  233. if not assigned(result) then
  234. result:=InstalledRepository.AddPackage(AName)
  235. else
  236. begin
  237. // Log packages found in multiple locations (local and global) ?
  238. if showdups then
  239. Log(vlDebug,SDbgPackageMultipleLocations,[result.Name,ExtractFilePath(AFileName)]);
  240. end;
  241. end;
  242. procedure LoadPackagefpcFromFile(APackage:TFPPackage;const AFileName: String);
  243. Var
  244. L : TStrings;
  245. V : String;
  246. begin
  247. L:=TStringList.Create;
  248. Try
  249. ReadIniFile(AFileName,L);
  250. V:=L.Values['version'];
  251. APackage.Version.AsString:=V;
  252. Finally
  253. L.Free;
  254. end;
  255. end;
  256. function CheckUnitDir(const AUnitDir:string):boolean;
  257. var
  258. SR : TSearchRec;
  259. P : TFPPackage;
  260. UD,UF : String;
  261. begin
  262. Result:=false;
  263. if FindFirst(IncludeTrailingPathDelimiter(AUnitDir)+AllFiles,faDirectory,SR)=0 then
  264. begin
  265. Log(vlDebug,SLogFindInstalledPackages,[AUnitDir]);
  266. repeat
  267. if ((SR.Attr and faDirectory)=faDirectory) and (SR.Name<>'.') and (SR.Name<>'..') then
  268. begin
  269. UD:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(AUnitDir)+SR.Name);
  270. // Try new fpunits.conf
  271. UF:=UD+UnitConfigFileName;
  272. if FileExistsLog(UF) then
  273. begin
  274. P:=AddInstalledPackage(SR.Name,UF);
  275. LoadUnitConfigFromFile(P,UF)
  276. end
  277. else
  278. begin
  279. // Try Old style Package.fpc
  280. UF:=UD+'Package.fpc';
  281. if FileExistsLog(UF) then
  282. begin
  283. P:=AddInstalledPackage(SR.Name,UF);
  284. LoadPackagefpcFromFile(P,UF);
  285. end;
  286. end;
  287. end;
  288. until FindNext(SR)<>0;
  289. end;
  290. end;
  291. begin
  292. if assigned(InstalledRepository) then
  293. InstalledRepository.Free;
  294. InstalledRepository:=TFPRepository.Create(nil);
  295. // First scan the global directory
  296. // The local directory will overwrite the versions
  297. if ACompilerOptions.GlobalUnitDir<>'' then
  298. CheckUnitDir(ACompilerOptions.GlobalUnitDir);
  299. if ACompilerOptions.LocalUnitDir<>'' then
  300. CheckUnitDir(ACompilerOptions.LocalUnitDir);
  301. end;
  302. function PackageIsBroken(APackage:TFPPackage):boolean;
  303. var
  304. j : integer;
  305. D : TFPDependency;
  306. DepPackage : TFPPackage;
  307. begin
  308. result:=false;
  309. for j:=0 to APackage.Dependencies.Count-1 do
  310. begin
  311. D:=APackage.Dependencies[j];
  312. if (CompilerOptions.CompilerOS in D.OSes) and
  313. (CompilerOptions.CompilerCPU in D.CPUs) then
  314. begin
  315. DepPackage:=InstalledRepository.FindPackage(D.PackageName);
  316. // Don't stop on missing dependencies
  317. if assigned(DepPackage) then
  318. begin
  319. if (DepPackage.Checksum<>D.RequireChecksum) then
  320. begin
  321. Log(vlInfo,SLogPackageChecksumChanged,[APackage.Name,D.PackageName]);
  322. result:=true;
  323. exit;
  324. end;
  325. end
  326. else
  327. Log(vlDebug,SDbgObsoleteDependency,[D.PackageName]);
  328. end;
  329. end;
  330. end;
  331. function FindBrokenPackages(SL:TStrings):Boolean;
  332. var
  333. i : integer;
  334. P : TFPPackage;
  335. begin
  336. SL.Clear;
  337. for i:=0 to InstalledRepository.PackageCount-1 do
  338. begin
  339. P:=InstalledRepository.Packages[i];
  340. if PackageIsBroken(P) then
  341. SL.Add(P.Name);
  342. end;
  343. Result:=(SL.Count>0);
  344. end;
  345. procedure CheckFPMakeDependencies;
  346. var
  347. i : Integer;
  348. P,AvailP : TFPPackage;
  349. AvailVerStr : string;
  350. ReqVer : TFPVersion;
  351. begin
  352. // Reset availability
  353. for i:=1 to FPMKUnitDepCount do
  354. FPMKUnitDepAvailable[i]:=false;
  355. // Not version check needed in Recovery mode, we always need to use
  356. // the internal bootstrap procedure
  357. if GlobalOptions.RecoveryMode then
  358. exit;
  359. // Check for fpmkunit dependencies
  360. for i:=1 to FPMKUnitDepCount do
  361. begin
  362. P:=InstalledRepository.FindPackage(FPMKUnitDeps[i].package);
  363. if P<>nil then
  364. begin
  365. AvailP:=AvailableRepository.FindPackage(FPMKUnitDeps[i].package);
  366. if AvailP<>nil then
  367. AvailVerStr:=AvailP.Version.AsString
  368. else
  369. AvailVerStr:='<not available>';
  370. ReqVer:=TFPVersion.Create;
  371. ReqVer.AsString:=FPMKUnitDeps[i].ReqVer;
  372. Log(vlDebug,SLogFPMKUnitDepVersion,[P.Name,ReqVer.AsString,P.Version.AsString,AvailVerStr]);
  373. if ReqVer.CompareVersion(P.Version)<=0 then
  374. FPMKUnitDepAvailable[i]:=true
  375. else
  376. Log(vlDebug,SLogFPMKUnitDepTooOld,[FPMKUnitDeps[i].package]);
  377. end
  378. else
  379. Log(vlDebug,SLogFPMKUnitDepTooOld,[FPMKUnitDeps[i].package]);
  380. end;
  381. end;
  382. {*****************************************************************************
  383. Local Available Repository
  384. *****************************************************************************}
  385. procedure LoadLocalAvailableRepository;
  386. var
  387. S : String;
  388. X : TFPXMLRepositoryHandler;
  389. begin
  390. if assigned(AvailableRepository) then
  391. AvailableRepository.Free;
  392. AvailableRepository:=TFPRepository.Create(Nil);
  393. // Repository
  394. S:=GlobalOptions.LocalPackagesFile;
  395. Log(vlDebug,SLogLoadingPackagesFile,[S]);
  396. if not FileExists(S) then
  397. exit;
  398. try
  399. X:=TFPXMLRepositoryHandler.Create;
  400. With X do
  401. try
  402. LoadFromXml(AvailableRepository,S);
  403. finally
  404. Free;
  405. end;
  406. except
  407. on E : Exception do
  408. begin
  409. Log(vlError,E.Message);
  410. Error(SErrCorruptPackagesFile,[S]);
  411. end;
  412. end;
  413. end;
  414. function PackageAvailableVersionStr(const AName:String):string;
  415. var
  416. P : TFPPackage;
  417. begin
  418. P:=AvailableRepository.FindPackage(AName);
  419. if P<>nil then
  420. result:=P.Version.AsString
  421. else
  422. result:='-';
  423. end;
  424. function PackageInstalledVersionStr(const AName:String):string;
  425. var
  426. P : TFPPackage;
  427. begin
  428. P:=InstalledRepository.FindPackage(AName);
  429. if P<>nil then
  430. result:=P.Version.AsString
  431. else
  432. result:='-';
  433. end;
  434. procedure ListAvailablePackages;
  435. var
  436. InstalledP,
  437. AvailP : TFPPackage;
  438. i : integer;
  439. SL : TStringList;
  440. begin
  441. SL:=TStringList.Create;
  442. SL.Sorted:=true;
  443. for i:=0 to AvailableRepository.PackageCount-1 do
  444. begin
  445. AvailP:=AvailableRepository.Packages[i];
  446. InstalledP:=InstalledRepository.FindPackage(AvailP.Name);
  447. if not assigned(InstalledP) or
  448. (AvailP.Version.CompareVersion(InstalledP.Version)>0) then
  449. SL.Add(Format('%-20s %-12s %-12s',[AvailP.Name,PackageInstalledVersionStr(AvailP.Name),AvailP.Version.AsString]));
  450. end;
  451. Writeln(Format('%-20s %-12s %-12s',['Name','Installed','Available']));
  452. for i:=0 to SL.Count-1 do
  453. Writeln(SL[i]);
  454. FreeAndNil(SL);
  455. end;
  456. procedure ListPackages;
  457. var
  458. i : integer;
  459. SL : TStringList;
  460. PackageName : String;
  461. begin
  462. SL:=TStringList.Create;
  463. SL.Sorted:=true;
  464. SL.Duplicates:=dupIgnore;
  465. for i:=0 to AvailableRepository.PackageCount-1 do
  466. SL.Add(AvailableRepository.Packages[i].Name);
  467. for i:=0 to InstalledRepository.PackageCount-1 do
  468. SL.Add(InstalledRepository.Packages[i].Name);
  469. Writeln(Format('%-20s %-12s %-12s',['Name','Installed','Available']));
  470. for i:=0 to SL.Count-1 do
  471. begin
  472. PackageName:=SL[i];
  473. if (PackageName<>CmdLinePackageName) and (PackageName<>CurrentDirPackageName) then
  474. Writeln(Format('%-20s %-12s %-12s',[PackageName,PackageInstalledVersionStr(PackageName),PackageAvailableVersionStr(PackageName)]));
  475. end;
  476. FreeAndNil(SL);
  477. end;
  478. {*****************************************************************************
  479. Remote Repository
  480. *****************************************************************************}
  481. procedure ListRemoteRepository;
  482. var
  483. P : TFPPackage;
  484. i : integer;
  485. SL : TStringList;
  486. begin
  487. SL:=TStringList.Create;
  488. SL.Sorted:=true;
  489. for i:=0 to InstalledRepository.PackageCount-1 do
  490. begin
  491. P:=InstalledRepository.Packages[i];
  492. SL.Add(Format('%-20s %-12s %-20s',[P.Name,P.Version.AsString,P.FileName]));
  493. end;
  494. Writeln(Format('%-20s %-12s %-20s',['Name','Available','FileName']));
  495. for i:=0 to SL.Count-1 do
  496. Writeln(SL[i]);
  497. FreeAndNil(SL);
  498. end;
  499. procedure RebuildRemoteRepository;
  500. procedure LoadPackageManifest(const AManifestFN:string);
  501. var
  502. X : TFPXMLRepositoryHandler;
  503. i : integer;
  504. DoAdd : Boolean;
  505. P,NewP : TFPPackage;
  506. NewPackages : TFPPackages;
  507. begin
  508. NewPackages:=TFPPackages.Create(TFPPackage);
  509. X:=TFPXMLRepositoryHandler.Create;
  510. try
  511. X.LoadFromXml(NewPackages,AManifestFN);
  512. // Update or Add packages to repository
  513. for i:=0 to NewPackages.Count-1 do
  514. begin
  515. NewP:=NewPackages[i];
  516. DoAdd:=True;
  517. P:=InstalledRepository.FindPackage(NewP.Name);
  518. if assigned(P) then
  519. begin
  520. if NewP.Version.CompareVersion(P.Version)<0 then
  521. begin
  522. Writeln(Format('Ignoring package %s-%s (old %s)',[NewP.Name,NewP.Version.AsString,P.Version.AsString]));
  523. DoAdd:=False;
  524. end
  525. else
  526. Writeln(Format('Updating package %s-%s (old %s)',[NewP.Name,NewP.Version.AsString,P.Version.AsString]));
  527. end
  528. else
  529. P:=InstalledRepository.PackageCollection.AddPackage(NewP.Name);
  530. // Copy contents
  531. if DoAdd then
  532. P.Assign(NewP);
  533. end;
  534. finally
  535. X.Free;
  536. NewPackages.Free;
  537. end;
  538. end;
  539. var
  540. i : integer;
  541. ArchiveSL : TStringList;
  542. ManifestSL : TStringList;
  543. begin
  544. if assigned(InstalledRepository) then
  545. InstalledRepository.Free;
  546. InstalledRepository:=TFPRepository.Create(Nil);
  547. try
  548. ManifestSL:=TStringList.Create;
  549. ManifestSL.Add(ManifestFileName);
  550. { Find all archives }
  551. ArchiveSL:=TStringList.Create;
  552. SearchFiles(ArchiveSL,'*.zip');
  553. if ArchiveSL.Count=0 then
  554. Error('No archive files found');
  555. { Process all archives }
  556. for i:=0 to ArchiveSL.Count-1 do
  557. begin
  558. Writeln('Processing ',ArchiveSL[i]);
  559. { Unzip manifest.xml }
  560. With TUnZipper.Create do
  561. try
  562. Log(vlCommands,SLogUnzippping,[ArchiveSL[i]]);
  563. OutputPath:='.';
  564. UnZipFiles(ArchiveSL[i],ManifestSL);
  565. Finally
  566. Free;
  567. end;
  568. { Load manifest.xml }
  569. if FileExists(ManifestFileName) then
  570. begin
  571. LoadPackageManifest(ManifestFileName);
  572. DeleteFile(ManifestFileName);
  573. end
  574. else
  575. Writeln('No manifest found in archive ',ArchiveSL[i]);
  576. end;
  577. finally
  578. ArchiveSL.Free;
  579. ManifestSL.Free;
  580. end;
  581. end;
  582. procedure SaveRemoteRepository;
  583. var
  584. X : TFPXMLRepositoryHandler;
  585. begin
  586. // Repository
  587. Writeln('Saving repository in packages.xml');
  588. X:=TFPXMLRepositoryHandler.Create;
  589. With X do
  590. try
  591. SaveToXml(InstalledRepository,'packages.xml');
  592. finally
  593. Free;
  594. end;
  595. end;
  596. end.