aboutsummaryrefslogtreecommitdiff
path: root/bpkg/pkg-bindist.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/pkg-bindist.cxx')
-rw-r--r--bpkg/pkg-bindist.cxx314
1 files changed, 215 insertions, 99 deletions
diff --git a/bpkg/pkg-bindist.cxx b/bpkg/pkg-bindist.cxx
index 8c2163b..cb29af1 100644
--- a/bpkg/pkg-bindist.cxx
+++ b/bpkg/pkg-bindist.cxx
@@ -3,6 +3,8 @@
#include <bpkg/pkg-bindist.hxx>
+#include <list>
+
#include <bpkg/package.hxx>
#include <bpkg/package-odb.hxx>
#include <bpkg/package-query.hxx>
@@ -18,7 +20,6 @@ namespace bpkg
{
using package = system_package_manager::package;
using packages = system_package_manager::packages;
- using recursive_mode = system_package_manager::recursive_mode;
// Find the available package(s) for the specified selected package.
//
@@ -220,6 +221,8 @@ namespace bpkg
// Verify options.
//
+ enum class recursive_mode {auto_, full, separate};
+
optional<recursive_mode> rec;
{
diag_record dr;
@@ -228,13 +231,24 @@ namespace bpkg
{
const string& m (o.recursive ());
- if (m == "auto") rec = recursive_mode::auto_;
- else if (m == "full") rec = recursive_mode::full;
+ if (m == "auto") rec = recursive_mode::auto_;
+ else if (m == "full") rec = recursive_mode::full;
+ else if (m == "separate") rec = recursive_mode::separate;
else
dr << fail << "unknown mode '" << m << "' specified with --recursive";
}
- else if (o.private_ ())
- dr << fail << "--private specified without --recursive";
+
+ if (o.private_ ())
+ {
+ if (!rec)
+ {
+ dr << fail << "--private specified without --recursive";
+ }
+ else if (*rec == recursive_mode::separate)
+ {
+ dr << fail << "--private specified without --recursive=separate";
+ }
+ }
if (!dr.empty ())
dr << info << "run 'bpkg help pkg-bindist' for more information";
@@ -281,6 +295,25 @@ namespace bpkg
info << "run 'bpkg help pkg-bindist' for more information";
}
+ // Note that we shouldn't need to install anything or use sudo.
+ //
+ unique_ptr<system_package_manager> spm (
+ make_production_system_package_manager (o,
+ host_triplet,
+ o.distribution (),
+ o.architecture ()));
+ if (spm == nullptr)
+ {
+ fail << "no standard distribution package manager for this host "
+ << "or it is not yet supported" <<
+ info << "consider specifying alternative distribution package "
+ << "manager with --distribution" <<
+ info << "specify --distribution=archive to generate installation "
+ << "archive" <<
+ info << "consider specifying --os-release-* if unable to correctly "
+ << "auto-detect host operating system";
+ }
+
database db (c, trace, true /* pre_attach */);
// Similar to pkg-install we disallow generating packages from the
@@ -303,128 +336,211 @@ namespace bpkg
//
session ses;
- // Resolve package names to selected packages and verify they are all
- // configured. While at it collect their available packages and
- // dependencies as well as figure out type and languages.
+ // Generate one binary package.
//
- packages pkgs, deps;
- string type;
- small_vector<language, 1> langs;
-
- for (const package_name& n: pns)
+ struct result
+ {
+ paths bins;
+ packages deps;
+ shared_ptr<selected_package> pkg;
+ };
+
+ auto generate = [&o, &vars,
+ rec, &spm,
+ &c, &db] (const vector<package_name>& pns,
+ bool first) -> result
{
- shared_ptr<selected_package> p (db.find<selected_package> (n));
+ // Resolve package names to selected packages and verify they are all
+ // configured. While at it collect their available packages and
+ // dependencies as well as figure out type and languages.
+ //
+ packages pkgs, deps;
+ string type;
+ small_vector<language, 1> langs;
- if (p == nullptr)
- fail << "package " << n << " does not exist in configuration " << c;
+ for (const package_name& n: pns)
+ {
+ shared_ptr<selected_package> p (db.find<selected_package> (n));
- if (p->state != package_state::configured)
- fail << "package " << n << " is " << p->state <<
- info << "expected it to be configured";
+ if (p == nullptr)
+ fail << "package " << n << " does not exist in configuration " << c;
- if (p->substate == package_substate::system)
- fail << "package " << n << " is configured as system";
+ if (p->state != package_state::configured)
+ fail << "package " << n << " is " << p->state <<
+ info << "expected it to be configured";
- // Load the available package for type/languages as well as the mapping
- // information.
- //
- available_packages aps (find_available_packages (o, db, p));
- const shared_ptr<available_package>& ap (aps.front ().first);
- db.load (*ap, ap->languages_section);
+ if (p->substate == package_substate::system)
+ fail << "package " << n << " is configured as system";
- if (pkgs.empty ()) // First.
- {
- type = ap->effective_type ();
- langs = ap->effective_languages ();
- }
- else
- merge_languages (type, langs, *ap);
+ // Make sure there are no dependent configuration variables. The
+ // rationale here is that we most likely don't want to generate a
+ // binary package in a configuration that is specific to some
+ // dependents.
+ //
+ if (!o.allow_dependent_config ())
+ {
+ for (const config_variable& v: p->config_variables)
+ {
+ switch (v.source)
+ {
+ case config_source::dependent:
+ {
+ fail << "configuration variable " << v.name << " is imposed "
+ << " by dependent package" <<
+ info << "specify it as user configuration to allow" <<
+ info << "or specify --allow-dependent-config" << endf;
+ }
+ case config_source::user:
+ case config_source::reflect:
+ break;
+ }
+ }
+ }
+
+ // Load the available package for type/languages as well as the
+ // mapping information.
+ //
+ available_packages aps (find_available_packages (o, db, p));
+ const shared_ptr<available_package>& ap (aps.front ().first);
+ db.load (*ap, ap->languages_section);
+
+ if (pkgs.empty ()) // First.
+ {
+ type = ap->effective_type ();
+ langs = ap->effective_languages ();
+ }
+ else
+ merge_languages (type, langs, *ap);
- const selected_package& r (*p);
- pkgs.push_back (
- package {move (p), move (aps), r.effective_out_root (db.config)});
+ const selected_package& r (*p);
+ pkgs.push_back (
+ package {move (p), move (aps), r.effective_out_root (db.config)});
- // If --recursive is not specified then we want all the immediate
- // (system and non-) dependecies in deps. Otherwise, if the recursive
- // mode is full, then we want all the transitive non-system dependecies
- // in pkgs. In both recursive modes we also want all the transitive
- // system dependecies in deps.
+ // If --recursive is not specified or specified with the seperate mode
+ // then we want all the immediate (system and non-) dependecies in
+ // deps. Otherwise, if the recursive mode is full, then we want all
+ // the transitive non-system dependecies in pkgs. In both recursive
+ // modes we also want all the transitive system dependecies in deps.
+ //
+ // Note also that in the auto recursive mode it's possible that some
+ // of the system dependencies are not really needed. But there is no
+ // way for us to detect this and it's better to over- than
+ // under-specify.
+ //
+ collect_dependencies (
+ o,
+ db,
+ (!rec || *rec == recursive_mode::separate
+ ? &deps
+ : *rec == recursive_mode::full ? &pkgs : nullptr),
+ deps,
+ type,
+ langs,
+ r,
+ rec.has_value ());
+ }
+
+ // Load the package manifest (source of extra metadata). This should be
+ // always possible since the package is configured and is not system.
//
- // Note also that in the auto recursive mode it's possible that some of
- // the system dependencies are not really needed. But there is no way
- // for us to detect this and it's better to over- than under-specify.
+ const shared_ptr<selected_package>& sp (pkgs.front ().selected);
+
+ package_manifest pm (
+ pkg_verify (o,
+ sp->effective_src_root (db.config_orig),
+ true /* ignore_unknown */,
+ false /* ignore_toolchain */,
+ false /* load_buildfiles */,
+ // Copy potentially fixed up version from selected package.
+ [&sp] (version& v) {v = sp->version;}));
+
+ optional<bool> recursive_full;
+ if (rec && *rec != recursive_mode::separate)
+ recursive_full = *rec != recursive_mode::full;
+
+ // Note that we pass type from here in case one day we want to provide
+ // an option to specify/override it (along with languages). Note that
+ // there will probably be no way to override type for dependencies.
//
- collect_dependencies (o,
- db,
- (rec
- ? *rec == recursive_mode::full ? &pkgs : nullptr
- : &deps),
- deps,
- type,
- langs,
- r,
- rec.has_value ());
- }
+ paths r (spm->generate (pkgs,
+ deps,
+ vars,
+ db.config,
+ pm,
+ type, langs,
+ recursive_full,
+ first));
+
+ return result {move (r), move (deps), move (pkgs.front ().selected)};
+ };
+
+ list<result> rs; // Note: list for reference stability.
+
+ // Generate packages for dependencies, recursively, suppressing
+ // duplicates. Note: recursive lambda.
+ //
+ auto generate_deps = [&generate, &rs] (const packages& deps,
+ const auto& generate_deps) -> void
+ {
+ for (const package& d: deps)
+ {
+ const shared_ptr<selected_package>& p (d.selected);
- t.commit ();
+ // Skip system dependencies.
+ //
+ if (p->substate == package_substate::system)
+ continue;
- // Load the package manifest (source of extra metadata). This should be
- // always possible since the package is configured and is not system.
- //
- const shared_ptr<selected_package>& sp (pkgs.front ().selected);
+ // Make sure we don't generate the same dependency multiple times.
+ //
+ if (find_if (rs.begin (), rs.end (),
+ [&p] (const result& r)
+ {
+ return r.pkg == p;
+ }) != rs.end ())
+ continue;
- package_manifest pm (
- pkg_verify (o,
- sp->effective_src_root (db.config_orig),
- true /* ignore_unknown */,
- false /* ignore_toolchain */,
- false /* load_buildfiles */,
- // Copy potentially fixed up version from selected package.
- [&sp] (version& v) {v = sp->version;}));
+ if (verb >= 1)
+ text << "generating package for dependency " << p->name;
- // Note that we shouldn't need to install anything or use sudo.
+ rs.push_back (generate ({p->name}, false /* first */));
+ generate_deps (rs.back ().deps, generate_deps);
+ }
+ };
+
+ // Generate top-level package(s).
//
- unique_ptr<system_package_manager> spm (
- make_production_system_package_manager (o,
- host_triplet,
- o.distribution (),
- o.architecture ()));
- if (spm == nullptr)
- {
- fail << "no standard distribution package manager for this host "
- << "or it is not yet supported" <<
- info << "consider specifying alternative distribution package "
- << "manager with --distribution" <<
- info << "specify --distribution=archive to generate installation "
- << "archive" <<
- info << "consider specifying --os-release-* if unable to correctly "
- << "auto-detect host operating system";
- }
+ rs.push_back (generate (pns, true /* first */));
- // Note that we pass type from here in case one day we want to provide an
- // option to specify/override it (along with languages). Note that there
- // will probably be no way to override type for dependencies.
+ // Generate dependencies, if requested.
//
- paths r (spm->generate (pkgs, deps, vars, db.config, pm, type, langs, rec));
+ if (rec && rec == recursive_mode::separate)
+ generate_deps (rs.back ().deps, generate_deps);
- if (r.empty ())
+ t.commit ();
+
+ if (rs.front ().bins.empty ())
return 0; // Assume prepare-only mode or similar.
if (verb && !o.no_result ())
{
- const selected_package& p (*pkgs.front ().selected);
-
- diag_record dr (text);
-
const string& d (o.distribution_specified ()
? o.distribution ()
: spm->os_release.name_id);
- dr << "generated " << d << " package for " << p.name << '/' << p.version
- << ':';
+ for (auto b (rs.begin ()), i (b); i != rs.end (); ++i)
+ {
+ const selected_package& p (*i->pkg);
+
+ diag_record dr (text);
- for (const path& p: r)
- dr << "\n " << p;
+ dr << "generated " << d << " package for "
+ << (i != b ? "dependency " : "")
+ << p.name << '/' << p.version << ':';
+
+ for (const path& p: i->bins)
+ dr << "\n " << p;
+ }
}
return 0;