aboutsummaryrefslogtreecommitdiff
path: root/bpkg/rep-fetch.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/rep-fetch.cxx')
-rw-r--r--bpkg/rep-fetch.cxx476
1 files changed, 355 insertions, 121 deletions
diff --git a/bpkg/rep-fetch.cxx b/bpkg/rep-fetch.cxx
index 038d54e..fe25b86 100644
--- a/bpkg/rep-fetch.cxx
+++ b/bpkg/rep-fetch.cxx
@@ -17,6 +17,8 @@
#include <bpkg/rep-remove.hxx>
#include <bpkg/pkg-verify.hxx>
#include <bpkg/diagnostics.hxx>
+#include <bpkg/satisfaction.hxx>
+#include <bpkg/package-query.hxx>
#include <bpkg/manifest-utility.hxx>
using namespace std;
@@ -53,7 +55,8 @@ namespace bpkg
database* db,
const repository_location& rl,
const optional<string>& dependent_trust,
- bool ignore_unknown)
+ bool ignore_unknown,
+ bool ignore_toolchain)
{
// First fetch the repositories list and authenticate the base's
// certificate.
@@ -115,6 +118,18 @@ namespace bpkg
authenticate_repository (co, conf, cert_pem, *cert, sm, rl);
}
+ // If requested, verify that the packages are compatible with the current
+ // toolchain.
+ //
+ if (!ignore_toolchain)
+ {
+ for (const package_manifest& m: fr.packages)
+ {
+ for (const dependency_alternatives& das: m.dependencies)
+ toolchain_buildtime_dependency (co, das, &m.name);
+ }
+ }
+
return rep_fetch_data {{move (fr)}, move (cert_pem), move (cert)};
}
@@ -192,6 +207,23 @@ namespace bpkg
return r;
}
+ static void
+ print_package_info (diag_record& dr,
+ const dir_path& pl,
+ const repository_location& rl,
+ const optional<string>& fragment)
+ {
+ dr << "package ";
+
+ if (!pl.current ())
+ dr << "'" << pl.string () << "' "; // Strip trailing '/'.
+
+ dr << "in repository " << rl;
+
+ if (fragment)
+ dr << ' ' << *fragment;
+ }
+
// Parse package manifests referenced by the package directory manifests.
//
static pair<vector<package_manifest>, vector<package_info>>
@@ -199,27 +231,24 @@ namespace bpkg
const dir_path& repo_dir,
vector<package_manifest>&& pms,
bool iu,
- bool lb,
+ bool it,
const repository_location& rl,
const optional<string>& fragment) // For diagnostics.
{
- auto add_package_info = [&rl, &fragment] (const package_manifest& pm,
- diag_record& dr)
+ auto prn_package_info = [&rl, &fragment] (diag_record& dr,
+ const package_manifest& pm)
{
- dr << "package ";
-
- if (!pm.location->current ())
- dr << "'" << pm.location->string () << "' "; // Strip trailing '/'.
-
- dr << "in repository " << rl;
-
- if (fragment)
- dr << ' ' << *fragment;
+ print_package_info (dr,
+ path_cast<dir_path> (*pm.location),
+ rl,
+ fragment);
};
// Verify that all the package directories contain the package manifest
- // files and retrieve the package versions via the single `b info` call.
- // While at it cache the manifest paths for the future use.
+ // files and retrieve the package versions via the single `b info` call,
+ // but only if the current build2 version is satisfactory for all the
+ // repository packages. While at it cache the manifest paths for the
+ // future use.
//
// Note that if the package is not compatible with the toolchain, not to
// end up with an unfriendly build2 error message (referring a line in the
@@ -229,16 +258,21 @@ namespace bpkg
// since we need the package versions for that. Thus, we cache the
// respective name value lists instead.
//
- package_version_infos pvs;
+ optional<package_version_infos> pvs;
paths mfs;
- dir_paths pds;
vector<vector<manifest_name_value>> nvs;
{
mfs.reserve (pms.size ());
nvs.reserve (pms.size ());
+ dir_paths pds;
pds.reserve (pms.size ());
+ // If true, then build2 version is satisfactory for all the repository
+ // packages.
+ //
+ bool bs (true);
+
for (const package_manifest& pm: pms)
{
assert (pm.location);
@@ -251,19 +285,19 @@ namespace bpkg
{
diag_record dr (fail);
dr << "no manifest file for ";
- add_package_info (pm, dr);
+ prn_package_info (dr, pm);
}
// Provide the context if the package compatibility verification fails.
//
auto g (
make_exception_guard (
- [&pm, &add_package_info] ()
+ [&pm, &prn_package_info] ()
{
diag_record dr (info);
dr << "while retrieving information for ";
- add_package_info (pm, dr);
+ prn_package_info (dr, pm);
}));
try
@@ -275,7 +309,17 @@ namespace bpkg
// (e.g., .bpkg/tmp/6f746365314d/) and it's probably better to omit
// it entirely (the above exception guard will print all we've got).
//
- nvs.push_back (pkg_verify (co, mp, dir_path ()));
+ pkg_verify_result r (pkg_verify (co, mp, it, dir_path ()));
+
+ if (bs &&
+ r.build2_dependency &&
+ !satisfy_build2 (co, *r.build2_dependency))
+ {
+ bs = false;
+ pds.clear (); // Won't now be used.
+ }
+
+ nvs.push_back (move (r));
}
catch (const manifest_parsing& e)
{
@@ -287,17 +331,29 @@ namespace bpkg
}
mfs.push_back (move (f));
- pds.push_back (move (d));
+
+ if (bs)
+ pds.push_back (move (d));
}
- pvs = package_versions (co, pds);
+ // Note that for the directory-based repositories we also query
+ // subprojects since the package information will be used for the
+ // subsequent package_iteration() call (see below).
+ //
+ if (bs)
+ pvs = package_versions (co, pds,
+ (rl.directory_based ()
+ ? b_info_flags::subprojects
+ : b_info_flags::none));
}
// Parse package manifests, fixing up their versions.
//
pair<vector<package_manifest>, vector<package_info>> r;
- r.first.reserve (pms.size ());
- r.second.reserve (pms.size ());
+ r.first.reserve (pms.size ());
+
+ if (pvs)
+ r.second.reserve (pms.size ());
for (size_t i (0); i != pms.size (); ++i)
{
@@ -307,15 +363,18 @@ namespace bpkg
try
{
- optional<version>& pv (pvs[i].version);
-
package_manifest m (
mfs[i].string (),
move (nvs[i]),
- [&pv] (version& v)
+ [&pvs, i] (version& v)
{
- if (pv)
- v = move (*pv);
+ if (pvs)
+ {
+ optional<version>& pv ((*pvs)[i].version);
+
+ if (pv)
+ v = move (*pv);
+ }
},
iu);
@@ -323,34 +382,19 @@ namespace bpkg
//
m.location = move (*pm.location);
- // Load the bootstrap, root, and config/*.build buildfiles into the
- // respective *-build values, if requested and if they are not already
- // specified in the manifest.
- //
- if (lb)
- try
- {
- load_package_buildfiles (m, pds[i], true /* err_path_relative */);
- }
- catch (const runtime_error& e)
- {
- diag_record dr (fail);
- dr << e << info;
- add_package_info (m, dr);
- dr << endf;
- }
-
pm = move (m);
}
catch (const manifest_parsing& e)
{
diag_record dr (fail (e.name, e.line, e.column));
dr << e.description << info;
- add_package_info (pm, dr);
+ prn_package_info (dr, pm);
}
r.first.push_back (move (pm));
- r.second.push_back (move (pvs[i].info));
+
+ if (pvs)
+ r.second.push_back (move ((*pvs)[i].info));
}
return r;
@@ -374,11 +418,11 @@ namespace bpkg
ifdstream is (fp);
string s (is.read_text ());
- if (s.empty ())
+ if (s.empty () && name != "build-file")
fail << name << " manifest value in " << pkg / manifest_file
<< " references empty file " << rp <<
info << "repository " << rl
- << (!fragment.empty () ? " " + fragment : "");
+ << (!fragment.empty () ? ' ' + fragment : "");
return s;
}
@@ -388,7 +432,7 @@ namespace bpkg
<< name << " manifest value in " << pkg / manifest_file << ": "
<< e <<
info << "repository " << rl
- << (!fragment.empty () ? " " + fragment : "") << endf;
+ << (!fragment.empty () ? ' ' + fragment : "") << endf;
}
}
@@ -396,6 +440,7 @@ namespace bpkg
rep_fetch_dir (const common_options& co,
const repository_location& rl,
bool iu,
+ bool it,
bool ev,
bool lb)
{
@@ -423,29 +468,71 @@ namespace bpkg
rd,
move (pms),
iu,
- lb,
+ it,
rl,
empty_string /* fragment */));
fr.packages = move (pmi.first);
fr.package_infos = move (pmi.second);
- // Expand file-referencing package manifest values.
+ // If requested, expand file-referencing package manifest values and load
+ // the buildfiles into the respective *-build values.
//
- if (ev)
+ if (ev || lb)
{
for (package_manifest& m: fr.packages)
- m.load_files (
- [&m, &rd, &rl] (const string& n, const path& p)
- {
- return read_package_file (p,
- n,
- path_cast<dir_path> (*m.location),
- rd,
- rl,
- empty_string /* fragment */);
- },
- iu);
+ {
+ dir_path pl (path_cast<dir_path> (*m.location));
+
+ // Load *-file values.
+ //
+ try
+ {
+ m.load_files (
+ [ev, &rd, &rl, &pl]
+ (const string& n, const path& p) -> optional<string>
+ {
+ // Always expand the build-file values.
+ //
+ if (ev || n == "build-file")
+ {
+ return read_package_file (p,
+ n,
+ pl,
+ rd,
+ rl,
+ empty_string /* fragment */);
+ }
+ else
+ return nullopt;
+ },
+ iu);
+ }
+ catch (const manifest_parsing& e)
+ {
+ diag_record dr (fail);
+ dr << e << info;
+ print_package_info (dr, pl, rl, nullopt /* fragment */);
+ dr << endf;
+ }
+
+ // Load the bootstrap, root, and config/*.build buildfiles into the
+ // respective *-build values, if requested and if they are not already
+ // specified in the manifest.
+ //
+ if (lb)
+ try
+ {
+ load_package_buildfiles (m, rd / pl, true /* err_path_relative */);
+ }
+ catch (const runtime_error& e)
+ {
+ diag_record dr (fail);
+ dr << e << info;
+ print_package_info (dr, pl, rl, nullopt /* fragment */);
+ dr << endf;
+ }
+ }
}
return rep_fetch_data {{move (fr)},
@@ -458,6 +545,7 @@ namespace bpkg
const dir_path* conf,
const repository_location& rl,
bool iu,
+ bool it,
bool ev,
bool lb)
{
@@ -587,42 +675,86 @@ namespace bpkg
td,
move (pms),
iu,
- lb,
+ it,
rl,
fr.friendly_name));
fr.packages = move (pmi.first);
fr.package_infos = move (pmi.second);
- // Expand file-referencing package manifest values checking out
- // submodules, if required.
+ // If requested, expand file-referencing package manifest values
+ // checking out submodules, if required, and load the buildfiles into
+ // the respective *-build values.
//
- if (ev)
+ if (ev || lb)
{
for (package_manifest& m: fr.packages)
- m.load_files (
- [&m, &td, &rl, &fr, &checkout_submodules] (const string& n,
- const path& p)
- {
- // Note that this doesn't work for symlinks on Windows where git
- // normally creates filesystem-agnostic symlinks that are
- // indistinguishable from regular files (see fixup_worktree()
- // for details). It seems like the only way to deal with that is
- // to unconditionally checkout submodules on Windows. Let's not
- // pessimize things for now (if someone really wants this to
- // work, they can always enable real symlinks in git).
- //
- if (!exists (td / *m.location / p))
- checkout_submodules ();
-
- return read_package_file (p,
- n,
- path_cast<dir_path> (*m.location),
- td,
- rl,
- fr.friendly_name);
- },
- iu);
+ {
+ dir_path pl (path_cast<dir_path> (*m.location));
+
+ // Load *-file values.
+ //
+ try
+ {
+ m.load_files (
+ [ev, &td, &rl, &pl, &fr, &checkout_submodules]
+ (const string& n, const path& p) -> optional<string>
+ {
+ // Always expand the build-file values.
+ //
+ if (ev || n == "build-file")
+ {
+ // Check out submodules if the referenced file doesn't exist.
+ //
+ // Note that this doesn't work for symlinks on Windows where
+ // git normally creates filesystem-agnostic symlinks that
+ // are indistinguishable from regular files (see
+ // fixup_worktree() for details). It seems like the only way
+ // to deal with that is to unconditionally checkout
+ // submodules on Windows. Let's not pessimize things for now
+ // (if someone really wants this to work, they can always
+ // enable real symlinks in git).
+ //
+ if (!exists (td / pl / p))
+ checkout_submodules ();
+
+ return read_package_file (p,
+ n,
+ pl,
+ td,
+ rl,
+ fr.friendly_name);
+ }
+ else
+ return nullopt;
+ },
+ iu);
+ }
+ catch (const manifest_parsing& e)
+ {
+ diag_record dr (fail);
+ dr << e << info;
+ print_package_info (dr, pl, rl, fr.friendly_name);
+ dr << endf;
+ }
+
+ // Load the bootstrap, root, and config/*.build buildfiles into the
+ // respective *-build values, if requested and if they are not
+ // already specified in the manifest.
+ //
+ if (lb)
+ try
+ {
+ load_package_buildfiles (m, td / pl, true /* err_path_relative */);
+ }
+ catch (const runtime_error& e)
+ {
+ diag_record dr (fail);
+ dr << e << info;
+ print_package_info (dr, pl, rl, fr.friendly_name);
+ dr << endf;
+ }
+ }
}
np += fr.packages.size ();
@@ -657,14 +789,24 @@ namespace bpkg
const repository_location& rl,
const optional<string>& dt,
bool iu,
+ bool it,
bool ev,
bool lb)
{
switch (rl.type ())
{
- case repository_type::pkg: return rep_fetch_pkg (co, conf, db, rl, dt, iu);
- case repository_type::dir: return rep_fetch_dir (co, rl, iu, ev, lb);
- case repository_type::git: return rep_fetch_git (co, conf, rl, iu, ev, lb);
+ case repository_type::pkg:
+ {
+ return rep_fetch_pkg (co, conf, db, rl, dt, iu, it);
+ }
+ case repository_type::dir:
+ {
+ return rep_fetch_dir (co, rl, iu, it, ev, lb);
+ }
+ case repository_type::git:
+ {
+ return rep_fetch_git (co, conf, rl, iu, it, ev, lb);
+ }
}
assert (false); // Can't be here.
@@ -676,6 +818,7 @@ namespace bpkg
const dir_path* conf,
const repository_location& rl,
bool iu,
+ bool it,
bool ev,
bool lb)
{
@@ -685,6 +828,7 @@ namespace bpkg
rl,
nullopt /* dependent_trust */,
iu,
+ it,
ev,
lb);
}
@@ -971,8 +1115,6 @@ namespace bpkg
//
if (rl.directory_based ())
{
- assert (!pis.empty ());
-
// Note that we can't check if the external package of this upstream
// version and revision is already available in the configuration
// until we fetch all the repositories, as some of the available
@@ -986,7 +1128,7 @@ namespace bpkg
path_cast<dir_path> (rl.path () / *pm.location),
pm.name,
pm.version,
- &pis[i],
+ !pis.empty () ? &pis[i] : nullptr,
false /* check_external */));
if (v)
@@ -1171,6 +1313,12 @@ namespace bpkg
// repository fragments list, as well as its prerequisite and complement
// repository sets.
//
+ // Note that we do this in the forward compatible manner ignoring
+ // unrecognized manifest values and unsatisfied build2 toolchain
+ // constraints in the package manifests. This approach allows older
+ // toolchains to work with newer repositories, successfully building the
+ // toolchain-satisfied packages and only failing for unsatisfied ones.
+ //
rep_fetch_data rfd (
rep_fetch (co,
&db.config_orig,
@@ -1178,6 +1326,7 @@ namespace bpkg
rl,
dependent_trust,
true /* ignore_unknow */,
+ true /* ignore_toolchain */,
false /* expand_values */,
true /* load_buildfiles */));
@@ -1403,6 +1552,10 @@ namespace bpkg
}
}
+#ifndef NDEBUG
+ rep_remove_verify (db, t);
+#endif
+
// Make sure that the external packages are available from a single
// directory-based repository.
//
@@ -1468,12 +1621,17 @@ namespace bpkg
{
dependencies& ds (at.package->dependencies);
- // Note that the special test dependencies entry is always the last
- // one, if present.
+ // Note that there is only one special test dependencies entry in
+ // the test package.
//
- assert (!ds.empty () && ds.back ().type);
-
- ds.pop_back ();
+ for (auto i (ds.begin ()), e (ds.end ()); i != e; ++i)
+ {
+ if (i->type)
+ {
+ ds.erase (i);
+ break;
+ }
+ }
db.update (at.package);
}
@@ -1482,16 +1640,25 @@ namespace bpkg
// Go through the available packages that have external tests and add
// them as the special test dependencies to these test packages.
//
+ // Note that not being able to resolve the test package for a main
+ // package is not an error, since the test package absence doesn't
+ // affect the main package building and internal testing. Dropping of an
+ // external test package from a repository may, however, be intentional.
+ // Think of a private repository crafted as a subset of some public
+ // repository with the external examples packages omitted.
+ //
for (const auto& am: db.query<available_main> ())
{
const shared_ptr<available_package>& p (am.package);
+ const package_name& n (p->id.name);
+ const version& v (p->version);
vector<shared_ptr<repository_fragment>> rfs;
for (const package_location& pl: p->locations)
rfs.push_back (pl.repository_fragment.load ());
- bool module (build2_module (p->id.name));
+ bool module (build2_module (n));
for (const test_dependency& td: p->tests)
{
@@ -1501,7 +1668,7 @@ namespace bpkg
if (module && !td.buildtime)
fail << "run-time " << td.type << ' ' << td.name << " for build "
<< "system module "
- << package_string (p->id.name, p->version) <<
+ << package_string (n, v) <<
info << "build system modules cannot have run-time " << td.type;
vector<pair<shared_ptr<available_package>,
@@ -1519,11 +1686,12 @@ namespace bpkg
dependencies& ds (tp->dependencies);
- if (ds.empty () || !ds.back ().type)
- ds.push_back (dependency_alternatives_ex (td.type,
- td.buildtime));
-
- dependency_alternatives_ex& das (ds.back ());
+ // Find the special test dependencies entry, if already present.
+ //
+ auto b (ds.begin ());
+ auto e (ds.end ());
+ auto oi (b); // Old entry location.
+ for (; oi != e && !oi->type; ++oi) ;
// Note that since we store all the primary packages as
// alternative dependencies (which must be all of the same
@@ -1534,12 +1702,10 @@ namespace bpkg
// `== <version>` constraints (see below), so we can use min
// version of such a constraint as the primary package version.
//
- if (das.buildtime != td.buildtime)
+ if (oi != e && oi->buildtime != td.buildtime)
{
- // Could only be empty if we just added it, which cannot be the
- // case since the build-time flag differs.
- //
- assert (!das.empty ());
+ dependency_alternatives_ex& das (*oi);
+ assert (!das.empty ()); // Cannot be empty if present.
const dependency_alternative& da (das[0]);
@@ -1555,19 +1721,87 @@ namespace bpkg
<< package_string (da[0].name,
*da[0].constraint->min_version) <<
info << (td.buildtime ? "build-time for " : "run-time for ")
- << package_string (p->id.name, p->version);
+ << package_string (n, v);
}
- dependency_alternative da (nullopt /* enable */,
+ // Find the (new) location for the special test dependencies entry.
+ //
+ // Note that if the entry is already present, it can only be moved
+ // towards the end of the list.
+ //
+ auto ni (e);
+
+ // First, find the last depends clause that explicitly specifies
+ // this main package but goes after the special entry current
+ // location, if present. Note that we only consider clauses with
+ // the matching buildtime flag.
+ //
+ for (auto i (oi != e ? oi + 1 : b); i != e; ++i)
+ {
+ const dependency_alternatives_ex& das (*i);
+ if (das.buildtime == td.buildtime)
+ {
+ bool specifies (false);
+
+ for (const dependency_alternative& da: das)
+ {
+ for (const dependency& d: da)
+ {
+ if (d.name == n)
+ {
+ specifies = true;
+ break;
+ }
+ }
+
+ if (specifies)
+ break;
+ }
+
+ if (specifies)
+ ni = i;
+ }
+ }
+
+ // Now, set ni to refer to the special test dependencies entry,
+ // moving or creating one, if required.
+ //
+ if (oi != e) // The entry already exists?
+ {
+ if (ni != e) // Move the entry to the new location?
+ {
+ // Move the [oi + 1, ni] range 1 position to the left and
+ // move the *oi element to the now vacant ni slot.
+ //
+ rotate (oi, oi + 1, ni + 1);
+ }
+ else
+ ni = oi; // Leave the entry at the old location.
+ }
+ else // The entry doesn't exist.
+ {
+ if (ni != e) // Create the entry right after ni?
+ ++ni;
+ else
+ ni = b; // Create the entry at the beginning of the list.
+
+ ni = ds.emplace (ni, td.type, td.buildtime); // Create the entry.
+ }
+
+ // Finally, add the new dependency alternative to the special
+ // entry.
+ //
+ dependency_alternative da (td.enable,
td.reflect,
nullopt /* prefer */,
nullopt /* accept */,
nullopt /* require */);
- da.push_back (
- dependency {p->id.name, version_constraint (p->version)});
+ da.push_back (dependency {n, version_constraint (v)});
+
+ assert (ni != ds.end ()); // Must be deduced by now.
- das.push_back (move (da));
+ ni->push_back (move (da));
db.update (tp);
}