aboutsummaryrefslogtreecommitdiff
path: root/bpkg/pkg-fetch.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/pkg-fetch.cxx')
-rw-r--r--bpkg/pkg-fetch.cxx161
1 files changed, 121 insertions, 40 deletions
diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx
index 24883c5..837c968 100644
--- a/bpkg/pkg-fetch.cxx
+++ b/bpkg/pkg-fetch.cxx
@@ -10,6 +10,7 @@
#include <bpkg/package-odb.hxx>
#include <bpkg/checksum.hxx>
#include <bpkg/database.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/manifest-utility.hxx>
@@ -24,7 +25,7 @@ namespace bpkg
// Return the selected package object which may replace the existing one.
//
static shared_ptr<selected_package>
- pkg_fetch (dir_path c,
+ pkg_fetch (database& db,
transaction& t,
package_name n,
version v,
@@ -35,27 +36,32 @@ namespace bpkg
{
tracer trace ("pkg_fetch");
- database& db (t.database ());
tracer_guard tg (db, trace);
- // Make the archive and configuration paths absolute and normalized.
- // If the archive is inside the configuration, use the relative path.
- // This way we can move the configuration around.
+ // Make the archive path absolute and normalized. If the archive is
+ // inside the configuration, use the relative path. This way we can move
+ // the configuration around.
//
- normalize (c, "configuration");
normalize (a, "archive");
- if (a.sub (c))
- a = a.leaf (c);
-
+ // Only purge the existing archive if its path differs from the new path.
+ //
shared_ptr<selected_package> p (db.find<selected_package> (n));
+
+ bool purge_archive (p != nullptr &&
+ p->archive &&
+ p->effective_archive (db.config) != a);
+
+ if (a.sub (db.config))
+ a = a.leaf (db.config);
+
if (p != nullptr)
{
// Clean up the source directory and archive of the package we are
- // replacing. Once this is done, there is no going back. If things
- // go badly, we can't simply abort the transaction.
+ // replacing. Once this is done, there is no going back. If things go
+ // badly, we can't simply abort the transaction.
//
- pkg_purge_fs (c, t, p, simulate);
+ pkg_purge_fs (db, t, p, simulate, purge_archive);
// Note that if the package name spelling changed then we need to update
// it, to make sure that the subsequent commands don't fail and the
@@ -96,6 +102,7 @@ namespace bpkg
nullopt, // No source directory yet.
false,
nullopt, // No manifest checksum.
+ nullopt, // No buildfiles checksum.
nullopt, // No output directory yet.
{}}); // No prerequisites captured yet.
@@ -113,14 +120,13 @@ namespace bpkg
// or fetching one.
//
static void
- pkg_fetch_check (const dir_path& c,
- transaction& t,
+ pkg_fetch_check (database& db,
+ transaction&,
const package_name& n,
bool replace)
{
tracer trace ("pkg_fetch_check");
- database& db (t.database ());
tracer_guard tg (db, trace);
if (shared_ptr<selected_package> p = db.find<selected_package> (n))
@@ -131,6 +137,7 @@ namespace bpkg
if (!replace || !s)
{
diag_record dr (fail);
+ const dir_path& c (db.config_orig);
dr << "package " << n << " already exists in configuration " << c <<
info << "version: " << p->version_string ()
@@ -145,7 +152,7 @@ namespace bpkg
shared_ptr<selected_package>
pkg_fetch (const common_options& co,
- const dir_path& c,
+ database& db,
transaction& t,
path a,
bool replace,
@@ -164,18 +171,20 @@ namespace bpkg
package_manifest m (pkg_verify (co,
a,
true /* ignore_unknown */,
- false /* expand_values */));
+ false /* ignore_toolchain */,
+ false /* expand_values */,
+ false /* load_buildfiles */));
l4 ([&]{trace << m.name << " " << m.version;});
// Check/diagnose an already existing package.
//
- pkg_fetch_check (c, t, m.name, replace);
+ pkg_fetch_check (db, t, m.name, replace);
// Use the special root repository fragment as the repository fragment of
// this package.
//
- return pkg_fetch (c,
+ return pkg_fetch (db,
t,
move (m.name),
move (m.version),
@@ -187,23 +196,25 @@ namespace bpkg
shared_ptr<selected_package>
pkg_fetch (const common_options& co,
- const dir_path& c,
+ database& pdb,
+ database& rdb,
transaction& t,
package_name n,
version v,
bool replace,
bool simulate)
{
+ assert (session::has_current ());
+
tracer trace ("pkg_fetch");
- database& db (t.database ());
- tracer_guard tg (db, trace);
+ tracer_guard tg (pdb, trace); // NOTE: sets tracer for the whole cluster.
// Check/diagnose an already existing package.
//
- pkg_fetch_check (c, t, n, replace);
+ pkg_fetch_check (pdb, t, n, replace);
- check_any_available (c, t);
+ check_any_available (rdb, t);
// Note that here we compare including the revision (unlike, say in
// pkg-status). Which means one cannot just specify 1.0.0 and get 1.0.0+1
@@ -211,7 +222,7 @@ namespace bpkg
// a low-level command where some extra precision doesn't hurt.
//
shared_ptr<available_package> ap (
- db.find<available_package> (available_package_id (n, v)));
+ rdb.find<available_package> (available_package_id (n, v)));
if (ap == nullptr)
fail << "package " << n << " " << v << " is not available";
@@ -223,14 +234,17 @@ namespace bpkg
for (const package_location& l: ap->locations)
{
- const repository_location& rl (l.repository_fragment.load ()->location);
-
- if (rl.archive_based () && (pl == nullptr || rl.local ()))
+ if (!rep_masked_fragment (l.repository_fragment))
{
- pl = &l;
+ const repository_location& rl (l.repository_fragment.load ()->location);
+
+ if (rl.archive_based () && (pl == nullptr || rl.local ()))
+ {
+ pl = &l;
- if (rl.local ())
- break;
+ if (rl.local ())
+ break;
+ }
}
}
@@ -243,10 +257,76 @@ namespace bpkg
<< "from " << pl->repository_fragment->name;
auto_rmfile arm;
- path a (c / pl->location.leaf ());
+ path an (pl->location.leaf ());
+ path a (pdb.config_orig / an);
+
+ // Note that in the replace mode we first fetch the new package version
+ // archive and then update the existing selected package object, dropping
+ // the previous package version archive, if present. This way we, in
+ // particular, keep the existing selected package/archive intact if the
+ // fetch operation fails. However, this approach requires to handle
+ // re-fetching (potentially from a different repository) of the same
+ // package version specially.
+ //
+ // Specifically, if we need to overwrite the package archive file, then we
+ // stash the existing archive in the temporary directory and remove it on
+ // success. On failure, we try to move the stashed archive to the original
+ // place. Failed that either, we mark the package as broken.
+ //
+ // (If you are wondering why don't we instead always fetch into a
+ // temporary file, the answer is Windows, where moving a newly created
+ // file may not succeed because it is being scanned by Windows Defender
+ // or some such.)
+ //
+ auto_rmfile earm;
+ shared_ptr<selected_package> sp;
+
+ auto g (
+ make_exception_guard (
+ [&arm, &a, &earm, &sp, &pdb, &t] ()
+ {
+ // Restore stashed archive.
+ //
+ if (!earm.path.empty () && exists (earm.path))
+ {
+ if (mv (earm.path, a, true /* ignore_error */))
+ {
+ earm.cancel ();
+ arm.cancel (); // Note: may not be armed yet, which is ok.
+ }
+ //
+ // Note: can already be marked as broken by pkg_purge_fs().
+ //
+ else if (sp->state != package_state::broken)
+ {
+ sp->state = package_state::broken;
+ pdb.update (sp);
+ t.commit ();
+
+ // Here we assume that mv() has already issued the diagnostics.
+ //
+ info << "package " << sp->name << pdb << " is now broken; "
+ << "use 'pkg-purge --force' to remove";
+ }
+ }
+ }));
if (!simulate)
{
+ // Stash the existing package archive if it needs to be overwritten (see
+ // above for details).
+ //
+ // Note: compare the archive absolute paths.
+ //
+ if (replace &&
+ (sp = pdb.find<selected_package> (n)) != nullptr &&
+ sp->archive &&
+ sp->effective_archive (pdb.config) == pdb.config / an)
+ {
+ earm = tmp_file (pdb.config_orig, n.string () + '-' + v.string ());
+ mv (a, earm.path);
+ }
+
pkg_fetch_archive (
co, pl->repository_fragment->location, pl->location, a);
@@ -256,20 +336,20 @@ namespace bpkg
//
assert (ap->sha256sum);
- const string& sha256sum (sha256 (co, a));
- if (sha256sum != *ap->sha256sum)
+ const string& cs (sha256sum (co, a));
+ if (cs != *ap->sha256sum)
{
fail << "checksum mismatch for " << n << " " << v <<
info << pl->repository_fragment->name << " has " << *ap->sha256sum <<
- info << "fetched archive has " << sha256sum <<
+ info << "fetched archive has " << cs <<
info << "consider re-fetching package list and trying again" <<
info << "if problem persists, consider reporting this to "
- << "the repository maintainer";
+ << "the repository maintainer";
}
}
shared_ptr<selected_package> p (
- pkg_fetch (c,
+ pkg_fetch (pdb,
t,
move (n),
move (v),
@@ -290,7 +370,7 @@ namespace bpkg
dir_path c (o.directory ());
l4 ([&]{trace << "configuration: " << c;});
- database db (open (c, trace));
+ database db (c, trace, true /* pre_attach */);
transaction t (db);
session s;
@@ -305,7 +385,7 @@ namespace bpkg
info << "run 'bpkg help pkg-fetch' for more information";
p = pkg_fetch (o,
- c,
+ db,
t,
path (args.next ()),
o.replace (),
@@ -327,7 +407,8 @@ namespace bpkg
info << "run 'bpkg help pkg-fetch' for more information";
p = pkg_fetch (o,
- c,
+ db /* pdb */,
+ db /* rdb */,
t,
move (n),
move (v),