Skip to content

Commit 8dc8e62

Browse files
committed
rpmmd: Download packages on-demand
1 parent 489dea9 commit 8dc8e62

File tree

4 files changed

+131
-11
lines changed

4 files changed

+131
-11
lines changed

src/asgen/backends/rpmmd/rpmpkg.d

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ module asgen.backends.rpmmd.rpmpkg;
2222
import std.stdio;
2323
import std.string;
2424
import std.array : empty;
25+
import std.path : buildNormalizedPath, baseName;
26+
static import std.file;
2527

28+
import asgen.config : Config;
2629
import asgen.logging;
2730
import asgen.zarchive;
31+
import asgen.downloader : Downloader;
32+
import asgen.utils : isRemote;
2833
import asgen.backends.interfaces;
2934

3035
final class RPMPackage : Package {
@@ -36,6 +41,7 @@ private:
3641
string[string] desc;
3742
string[string] summ;
3843
string pkgFname;
44+
string localPkgFname;
3945

4046
string[] contentsL;
4147

@@ -81,10 +87,26 @@ public:
8187
return desc;
8288
}
8389

84-
override
85-
@property string getFilename () const
86-
{
87-
return pkgFname;
90+
override final
91+
@property
92+
string getFilename ()
93+
{
94+
if (!localPkgFname.empty)
95+
return localPkgFname;
96+
97+
if (pkgFname.isRemote) {
98+
synchronized (this) {
99+
auto conf = Config.get();
100+
auto dl = Downloader.get;
101+
immutable path = buildNormalizedPath(conf.getTmpDir(), format("%s-%s_%s_%s", name, ver, arch, pkgFname.baseName));
102+
dl.downloadFile(pkgFname, path);
103+
localPkgFname = path;
104+
return localPkgFname;
105+
}
106+
} else {
107+
localPkgFname = pkgFname;
108+
return pkgFname;
109+
}
88110
}
89111

90112
@property void filename (string fname)
@@ -137,5 +159,20 @@ public:
137159
override
138160
void finish ()
139161
{
162+
synchronized (this) {
163+
if (archive.isOpen)
164+
archive.close();
165+
166+
try {
167+
if (pkgFname.isRemote && std.file.exists(localPkgFname)) {
168+
logDebug("Deleting temporary package file %s", localPkgFname);
169+
localPkgFname = null;
170+
std.file.remove(localPkgFname);
171+
}
172+
} catch (Exception e) {
173+
// we ignore any error
174+
logDebug("Unable to remove temporary package: %s (%s)", localPkgFname, e.msg);
175+
}
176+
}
140177
}
141178
}

src/asgen/backends/rpmmd/rpmpkgindex.d

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
module asgen.backends.rpmmd.rpmpkgindex;
2121

2222
import std.stdio : writeln;
23-
import std.path : buildPath;
23+
import std.path : buildPath, baseName;
2424
import std.array : appender, empty;
2525
import std.string : format;
2626
import std.algorithm : canFind, endsWith;
@@ -29,22 +29,30 @@ import dxml.dom : parseDOM, EntityType;
2929
static import std.file;
3030

3131
import asgen.logging;
32+
import asgen.config;
33+
import asgen.utils : escapeXml, getTextFileContents, isRemote;
34+
3235
import asgen.backends.interfaces;
3336
import asgen.backends.rpmmd.rpmpkg;
37+
import asgen.backends.rpmmd.rpmutils : downloadIfNecessary;
3438

3539
final class RPMPackageIndex : PackageIndex {
3640

3741
private:
3842
string rootDir;
3943
Package[][string] pkgCache;
44+
string tmpRootDir;
4045

4146
public:
4247

4348
this (string dir)
4449
{
4550
this.rootDir = dir;
46-
if (!std.file.exists(dir))
47-
throw new Exception("Directory '%s' does not exist.", dir);
51+
if (!dir.isRemote && !std.file.exists(dir))
52+
throw new Exception("Directory '%s' does not exist.".format(dir));
53+
54+
auto conf = Config.get();
55+
tmpRootDir = buildPath(conf.getTmpDir, dir.baseName);
4856
}
4957

5058
void release ()
@@ -82,10 +90,14 @@ public:
8290
private RPMPackage[] loadPackages (string suite, string section, string arch)
8391
{
8492
auto repoRoot = buildPath(rootDir, suite, section, arch, "os");
85-
8693
auto primaryIndexFiles = appender!(string[]);
8794
auto filelistFiles = appender!(string[]);
88-
immutable repoMdIndexContent = cast(string) std.file.read(buildPath(repoRoot, "repodata", "repomd.xml"));
95+
96+
string repoMdFname;
97+
synchronized (this)
98+
repoMdFname = downloadIfNecessary(buildPath(repoRoot, "repodata", "repomd.xml"), tmpRootDir);
99+
100+
immutable repoMdIndexContent = cast(string) std.file.read(repoMdFname);
89101

90102
// parse index data
91103
auto indexDoc = parseDOM(repoMdIndexContent);
@@ -115,7 +127,10 @@ public:
115127

116128
// parse the primary metadata
117129
foreach (ref primaryFile; primaryIndexFiles.data) {
118-
immutable metaFname = buildPath(repoRoot, primaryFile);
130+
string metaFname;
131+
synchronized(this)
132+
metaFname = downloadIfNecessary(buildPath(repoRoot, primaryFile), tmpRootDir);
133+
119134
string data;
120135
if (primaryFile.endsWith(".xml")) {
121136
data = cast(string) std.file.read(metaFname);
@@ -188,7 +203,10 @@ public:
188203

189204
// read the filelists
190205
foreach (ref filelistFile; filelistFiles.data) {
191-
immutable flistFname = buildPath(repoRoot, filelistFile);
206+
string flistFname;
207+
synchronized (this)
208+
flistFname = downloadIfNecessary(buildPath(repoRoot, filelistFile), tmpRootDir);
209+
192210
string data;
193211
if (filelistFile.endsWith(".xml")) {
194212
data = cast(string) std.file.read(flistFname);

src/asgen/backends/rpmmd/rpmutils.d

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (C) 2016-2023 Matthias Klumpp <[email protected]>
3+
*
4+
* Licensed under the GNU Lesser General Public License Version 3
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the license, or
9+
* (at your option) any later version.
10+
*
11+
* This software is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this software. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
module asgen.backends.rpmmd.rpmutils;
21+
22+
import std.string : format;
23+
import std.path : buildPath, baseName;
24+
static import std.file;
25+
26+
import asgen.logging;
27+
import asgen.downloader : Downloader, DownloadException;
28+
import asgen.utils : isRemote;
29+
30+
/**
31+
* If URL is remote, download it, otherwise use it verbatim.
32+
*
33+
* Returns: Path to the file, which is guaranteed to exist.
34+
*
35+
* Params:
36+
* url = First part of the address, i.e.
37+
* "http://ftp.debian.org/debian/" or "/srv/mirrors/debian/"
38+
* destPrefix = If the file is remote, the directory to save it under,
39+
* which is created if necessary.
40+
*/
41+
immutable(string) downloadIfNecessary (const string url,
42+
const string destLocation,
43+
Downloader downloader = null)
44+
{
45+
import std.path : buildPath;
46+
47+
if (downloader is null)
48+
downloader = Downloader.get;
49+
50+
if (url.isRemote) {
51+
immutable destFileName = buildPath(destLocation, url.baseName);
52+
try {
53+
downloader.downloadFile(url, destFileName);
54+
return destFileName;
55+
} catch (DownloadException e) {
56+
logDebug("Unable to download: %s", e.msg);
57+
}
58+
} else {
59+
if (std.file.exists(url))
60+
return url;
61+
}
62+
63+
throw new Exception("Could not obtain any file %s".format(url));
64+
}

src/asgen/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ if get_option('rpmmd')
7575
'backends/rpmmd/package.d',
7676
'backends/rpmmd/rpmpkg.d',
7777
'backends/rpmmd/rpmpkgindex.d',
78+
'backends/rpmmd/rpmutils.d',
7879
]
7980
endif
8081

0 commit comments

Comments
 (0)