pkgrepos.pp 18 KB

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