aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-02-09 15:46:32 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-02-21 04:46:55 +0200
commit5e004f94984476ddda74f90e8adaacd3c0871062 (patch)
tree5c5e4df4a4be02917d118d4cbd547ade87ff569b
parenteaebfcff492cf7f707b44a3d28620e786116faf1 (diff)
Initial work on pkg-bindist command and Debian implementation
-rw-r--r--bpkg/bpkg.cli5
-rw-r--r--bpkg/bpkg.cxx2
-rw-r--r--bpkg/buildfile2
-rw-r--r--bpkg/pkg-bindist.cli123
-rw-r--r--bpkg/pkg-bindist.cxx338
-rw-r--r--bpkg/pkg-bindist.hxx27
-rw-r--r--bpkg/system-package-manager-debian.cxx172
-rw-r--r--bpkg/system-package-manager-debian.hxx6
-rw-r--r--bpkg/system-package-manager-fedora.hxx2
-rw-r--r--bpkg/system-package-manager.cxx8
-rw-r--r--bpkg/system-package-manager.hxx10
-rwxr-xr-xdoc/cli.sh8
12 files changed, 653 insertions, 50 deletions
diff --git a/bpkg/bpkg.cli b/bpkg/bpkg.cli
index 17ac927..6edea97 100644
--- a/bpkg/bpkg.cli
+++ b/bpkg/bpkg.cli
@@ -257,6 +257,11 @@ namespace bpkg
"\l{bpkg-pkg-clean(1)} \- clean package"
}
+ bool pkg-bindist|bindist
+ {
+ "\l{bpkg-pkg-bindist(1)} \- generate binary distribution package"
+ }
+
bool pkg-verify
{
"\l{bpkg-pkg-verify(1)} \- verify package archive"
diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx
index 28ba75f..21cbefc 100644
--- a/bpkg/bpkg.cxx
+++ b/bpkg/bpkg.cxx
@@ -49,6 +49,7 @@
#include <bpkg/cfg-link.hxx>
#include <bpkg/cfg-unlink.hxx>
+#include <bpkg/pkg-bindist.hxx>
#include <bpkg/pkg-build.hxx>
#include <bpkg/pkg-checkout.hxx>
#include <bpkg/pkg-clean.hxx>
@@ -767,6 +768,7 @@ try
// These commands need the '--' separator to be kept in args.
//
+ PKG_COMMAND (bindist, true, true);
PKG_COMMAND (build, true, false);
PKG_COMMAND (clean, true, true);
PKG_COMMAND (configure, true, true);
diff --git a/bpkg/buildfile b/bpkg/buildfile
index ca78218..1531b9a 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -34,6 +34,7 @@ cfg-unlink-options \
common-options \
configuration-options \
help-options \
+pkg-bindist-options \
pkg-build-options \
pkg-checkout-options \
pkg-clean-options \
@@ -148,6 +149,7 @@ if $cli.configured
# pkg-* command.
#
+ cli.cxx{pkg-bindist-options}: cli{pkg-bindist}
cli.cxx{pkg-build-options}: cli{pkg-build}
cli.cxx{pkg-checkout-options}: cli{pkg-checkout}
cli.cxx{pkg-clean-options}: cli{pkg-clean}
diff --git a/bpkg/pkg-bindist.cli b/bpkg/pkg-bindist.cli
new file mode 100644
index 0000000..5686ed6
--- /dev/null
+++ b/bpkg/pkg-bindist.cli
@@ -0,0 +1,123 @@
+// file : bpkg/pkg-bindist.cli
+// license : MIT; see accompanying LICENSE file
+
+include <bpkg/configuration.cli>;
+
+"\section=1"
+"\name=bpkg-pkg-bindist"
+"\summary=generate binary distribution package"
+
+namespace bpkg
+{
+ {
+ "<options> <vars> <out-dir> <pkg>",
+
+ "\h|SYNOPSIS|
+
+ \c{\b{bpkg pkg-bindist}|\b{bindist} [<options>] [<vars>] <out-dir> <pkg> [<pkg>...]}
+
+ \h|DESCRIPTION|
+
+ The \cb{pkg-bindist} command generates a binary distribution package for
+ the specified package. If additional packages are specified, then they
+ are bundled in the same distribution package. All the specified packages
+ must have been previously configured with \l{bpkg-pkg-build(1)} or
+ \l{bpkg-pkg-configure(1)}. The intermediate files and the resulting
+ binary package are created in the <out-dir> directory.
+
+ Underneath, this command roughly performs the following steps: First it
+ installs the specified packages similar to the \l{bpkg-pkg-install(1)}
+ command except that it may override the installation locations (via the
+ \cb{config.install.*} variables) to match the distribution's layout. Then
+ it generates any necessary distribution package metadata files based on
+ the information from the package \cb{manifest} files. Finally, it invokes
+ the distribution-specified command to produce the binary package. Unless
+ overrident with the \cb{--architecture} and \cb{--distribution} options,
+ the binary package is generated for the host architecture using the
+ host's standard system package manager. Additional command line variables
+ (<vars>, normally \cb{config.*}) can be passed to the build system during
+ the installation step.
+
+ The specified packages may have dependencies and the default behavior is
+ to not bundle them but rather to specify them as dependencies in the
+ corresponding distribution package metadata, if applicable. This default
+ behavior can be overridden with the \cb{--recursive} option (see the
+ option description for the available modes). Note, however, that
+ dependencies that are satisfied by system packages are always specified
+ as dependencies in the distribution package metadata.
+ "
+ }
+
+ // @@ Have section for each package manager with options?
+
+ class pkg_bindist_options: configuration_options
+ {
+ "\h|PKG-BINDIST OPTIONS|"
+
+ string --distribution
+ {
+ "<name>",
+ "Alternative system/distribution package manager to generate the binary
+ package for. The valid <name> values are \cb{debian} (Debian and
+ alike, such as Ubuntu, etc) and \cb{fedora} (Fedora and alike,
+ such as RHEL, CentOS, etc). Note that some package managers may
+ only be supported when running on certain host operating systems."
+ }
+
+ string --architecture
+ {
+ "<name>",
+ "Alternative architecture to generate the binary package for. The
+ valid <name> values are system/distribution package manager-specific.
+ If unspecified, the host architecture is used."
+ }
+
+ string --recursive
+ {
+ "<mode>",
+ "Bundle dependencies of the specified packages. The <mode> value can be
+ either \cb{auto}, in which case only the required files from each
+ dependency package are bundled, or \cb{full}, in which case all the
+ files are bundled. Specifically, in the \cb{auto} mode any required
+ files, for example, shared libraries, are pulled implicitly by the
+ \cb{install} build system operation, for example, as part of
+ installing an executable from one of the specified packages. In
+ contrast, in the \cb{full} mode, each dependency package is
+ installed explicitly and completely, as if they were specified
+ as additional package on the command line. See also the \cb{--private}
+ option."
+ }
+
+ bool --private
+ {
+ "Enable the private installation subdirectory functionality using the
+ binary package name as the private subdirectory. This is primarily
+ useful when bundling dependencies, such as shared libraries, of an
+ executable that is being installed into a shared location, such as
+ \cb{/usr/}. See the \cb{config.install.private} configuration variable
+ documentation in the build system manual for details. This option
+ only makes sense together with \cb{--recursive}."
+ }
+ };
+
+ "
+ \h|DEFAULT OPTIONS FILES|
+
+ See \l{bpkg-default-options-files(1)} for an overview of the default
+ options files. For the \cb{pkg-bindist} command the search start
+ directory is the configuration directory. The following options files are
+ searched for in each directory and, if found, loaded in the order listed:
+
+ \
+ bpkg.options
+ bpkg-pkg-bindist.options
+ \
+
+ The following \cb{pkg-bindist} command options cannot be specified in the
+ default options files:
+
+ \
+ --directory|-d
+ \
+ "
+}
diff --git a/bpkg/pkg-bindist.cxx b/bpkg/pkg-bindist.cxx
new file mode 100644
index 0000000..289f34b
--- /dev/null
+++ b/bpkg/pkg-bindist.cxx
@@ -0,0 +1,338 @@
+// file : bpkg/pkg-bindist.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/pkg-bindist.hxx>
+
+#include <bpkg/package.hxx>
+#include <bpkg/package-odb.hxx>
+#include <bpkg/package-query.hxx>
+#include <bpkg/database.hxx>
+#include <bpkg/diagnostics.hxx>
+#include <bpkg/system-package-manager.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ using packages = system_package_manager::packages;
+ using recursive_mode = system_package_manager::recursive_mode;
+
+ // Find the available package(s) for the specified selected package.
+ //
+ // Specifically, for non-system packages we look for a single available
+ // package failing if it's an orphan. For system packages we look for all
+ // the available packages analogous to pkg-build. If none are found then
+ // we assume the --sys-no-stub option was used to configure this package
+ // and return an empty list. @@ What if it was configured with a specific
+ // bpkg version or `*`?
+ //
+ static available_packages
+ find_available_packages (const common_options& co,
+ database& db,
+ const shared_ptr<selected_package>& p)
+ {
+ assert (p->state == package_state::configured);
+
+ available_packages r;
+ if (p->substate == package_substate::system)
+ {
+ r = find_available_all (repo_configs, p->name);
+ }
+ else
+ {
+ pair<shared_ptr<available_package>,
+ lazy_shared_ptr<repository_fragment>> ap (
+ find_available_fragment (co, db, p));
+
+ if (ap.second.loaded () && ap.second == nullptr)
+ fail << "package " << p->name << " is orphaned";
+
+ r.push_back (move (ap));
+ }
+
+ return r;
+ }
+
+ // Collect dependencies of the specified package, potentially recursively.
+ // System dependencies go to deps, non-system -- to pkgs, which could be the
+ // same as deps or NULL, depending on the desired semantics (see the call
+ // site for details). Find available packages for deps.
+ //
+ static void
+ collect_dependencies (const common_options& co,
+ packages* pkgs,
+ packages& deps,
+ const selected_package& p,
+ bool recursive)
+ {
+ for (const auto& pr: p.prerequisites)
+ {
+ const lazy_shared_ptr<selected_package>& ld (pr.first);
+
+ // We only consider dependencies from target configurations, similar
+ // to pkg-install.
+ //
+ database& db (ld.database ());
+ if (db.type == host_config_type || db.type == build2_config_type)
+ continue;
+
+ shared_ptr<selected_package> d (ld.load ());
+
+ // The selected package can only be configured if all its dependencies
+ // are configured.
+ //
+ assert (d->state == package_state::configured);
+
+ bool sys (d->substate == package_substate::system);
+ packages* ps (sys ? &deps : pkgs);
+
+ // Skip duplicates.
+ //
+ if (ps == nullptr ||
+ find_if (ps->begin (), ps->end (),
+ [&d] (const pair<shared_ptr<selected_package>,
+ available_packages>& p)
+ {
+ return p.first == d;
+ }) == ps->end ())
+ {
+ available_packages aps;
+ if (ps == &deps) // Note: covers the (pkgs == &deps) case.
+ aps = find_available_packages (co, db, d);
+
+ const selected_package& p (*d);
+
+ if (ps != nullptr)
+ ps->push_back (make_pair (move (d), move (aps)));
+
+ if (recursive && !sys)
+ collect_dependencies (co, pkgs, deps, p, recursive);
+ }
+ }
+ }
+
+ int
+ pkg_bindist (const pkg_bindist_options& o, cli::scanner& args)
+ {
+ tracer trace ("pkg_bindist");
+
+ dir_path c (o.directory ());
+ l4 ([&]{trace << "configuration: " << c;});
+
+ // Verify options.
+ //
+ optional<recursive_mode> rec;
+ {
+ diag_record dr;
+
+ if (o.recursive_specified ())
+ {
+ const string& m (o.recursive ());
+
+ if (m == "auto") rec = recursive_mode::auto_;
+ else if (m == "full") rec = recursive_mode::full;
+ else
+ dr << fail << "unknown mode '" << m << "' specified with --recursive";
+ }
+ else if (o.private_ ())
+ dr << fail << "--private specified without --recursive";
+
+ if (!dr.empty ())
+ dr << info << "run 'bpkg help pkg-bindist' for more information";
+ }
+
+ // Sort arguments into the output directory, package names, and
+ // configuration variables.
+ //
+ dir_path out;
+ vector<package_name> pns;
+ strings vars;
+ {
+ bool sep (false); // Seen `--`.
+
+ while (args.more ())
+ {
+ string a (args.next ());
+
+ // If we see the `--` separator, then we are done parsing variables
+ // (while they won't clash with package names, we may be given a
+ // directory path that contains `=`).
+ //
+ if (!sep && a == "--")
+ {
+ sep = true;
+ continue;
+ }
+
+ if (a.find ('=') != string::npos)
+ vars.push_back (move (trim (a)));
+ else if (out.empty ())
+ {
+ try
+ {
+ out = dir_path (move (a));
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid output directory '" << e.path << "'";
+ }
+ }
+ else
+ {
+ try
+ {
+ pns.push_back (package_name (move (a))); // Not moved on failure.
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "invalid package name '" << a << "': " << e;
+ }
+ }
+ }
+
+ if (out.empty () || pns.empty ())
+ fail << "output directory or package name argument expected" <<
+ info << "run 'bpkg help pkg-bindist' for more information";
+ }
+
+ database db (c, trace, true /* pre_attach */);
+
+ // Similar to pkg-install we disallow generating packages from the
+ // host/build2 configurations.
+ //
+ if (db.type == host_config_type || db.type == build2_config_type)
+ {
+ fail << "unable to generate distribution package from " << db.type
+ << " configuration" <<
+ info << "use target configuration instead";
+ }
+
+ // Prepare for the find_available_*() calls.
+ //
+ repo_configs.push_back (db);
+
+ transaction t (db);
+
+ // We need to suppress duplicate dependencies for the recursive mode.
+ //
+ session ses;
+
+ // Resolve package names to selected packages and verify they are all
+ // configured. While at it collect their available packages and
+ // dependencies.
+ //
+ packages pkgs, deps;
+
+ for (const package_name& n: pns)
+ {
+ shared_ptr<selected_package> p (db.find<selected_package> (n));
+
+ if (p == nullptr)
+ fail << "package " << n << " does not exist in configuration " << c;
+
+ if (p->state != package_state::configured)
+ fail << "package " << n << " is " << p->state <<
+ info << "expected it to be configured";
+
+ if (p->substate == package_substate::system)
+ fail << "package " << n << " is configured as system";
+
+ // If this is the first package, load its available package for the
+ // mapping information. We don't need it for any additional packages.
+ //
+ available_packages aps;
+ if (pkgs.empty ())
+ aps = find_available_packages (o, db, p);
+
+ const selected_package& r (*p);
+ pkgs.push_back (make_pair (move (p), move (aps)));
+
+ // 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.
+ //
+ // 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,
+ (rec
+ ? *rec == recursive_mode::full ? &pkgs : nullptr
+ : &deps),
+ deps,
+ r,
+ rec.has_value ());
+ }
+
+ t.commit ();
+
+ // 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";
+ }
+
+ // @@ TODO: pass/handle --private.
+
+ // Note that we certain move the arguments to allow the implementation to
+ // rearrange things if/as convenient.
+ //
+ spm->generate (move (pkgs),
+ move (deps),
+ move (vars),
+ out,
+ rec);
+
+
+ // @@ TODO: need to save name/version (or change the output, maybe
+ // to something returned by spm?
+ //
+#if 0
+ if (verb && !o.no_result ())
+ {
+ const selected_package& p (*pkgs.front ().first);
+
+ text << "generated " << spm->os_release.name_id << " package for "
+ << p.name << '/' << p.version;
+ }
+#endif
+
+ return 0;
+ }
+
+ pkg_bindist_options
+ merge_options (const default_options<pkg_bindist_options>& defs,
+ const pkg_bindist_options& cmd)
+ {
+ // NOTE: remember to update the documentation if changing anything here.
+
+ return merge_default_options (
+ defs,
+ cmd,
+ [] (const default_options_entry<pkg_bindist_options>& e,
+ const pkg_bindist_options&)
+ {
+ const pkg_bindist_options& o (e.options);
+
+ auto forbid = [&e] (const char* opt, bool specified)
+ {
+ if (specified)
+ fail (e.file) << opt << " in default options file";
+ };
+
+ forbid ("--directory|-d", o.directory_specified ());
+ });
+ }
+}
diff --git a/bpkg/pkg-bindist.hxx b/bpkg/pkg-bindist.hxx
new file mode 100644
index 0000000..3a756f8
--- /dev/null
+++ b/bpkg/pkg-bindist.hxx
@@ -0,0 +1,27 @@
+// file : bpkg/pkg-bindist.hxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_PKG_BINDIST_HXX
+#define BPKG_PKG_BINDIST_HXX
+
+#include <bpkg/types.hxx>
+#include <bpkg/utility.hxx>
+
+#include <bpkg/pkg-command.hxx>
+#include <bpkg/pkg-bindist-options.hxx>
+
+namespace bpkg
+{
+ // Note that for now it doesn't seem we need to bother with package-
+ // specific configuration variables so it's scanner instead of
+ // group_scanner.
+ //
+ int
+ pkg_bindist (const pkg_bindist_options&, cli::scanner&);
+
+ pkg_bindist_options
+ merge_options (const default_options<pkg_bindist_options>&,
+ const pkg_bindist_options&);
+}
+
+#endif // BPKG_PKG_BINDIST_HXX
diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx
index b541541..d3e63b6 100644
--- a/bpkg/system-package-manager-debian.cxx
+++ b/bpkg/system-package-manager-debian.cxx
@@ -894,14 +894,6 @@ namespace bpkg
optional<const system_package_status*> system_package_manager_debian::
pkg_status (const package_name& pn, const available_packages* aps)
{
- // For now we ignore -doc and -dbg package components (but we may want to
- // have options controlling this later). Note also that we assume -common
- // is pulled automatically by the main package so we ignore it as well
- // (see equivalent logic in parse_name_value()).
- //
- bool need_doc (false);
- bool need_dbg (false);
-
// First check the cache.
//
{
@@ -914,6 +906,25 @@ namespace bpkg
return nullopt;
}
+ optional<package_status> r (status (pn, *aps));
+
+ // Cache.
+ //
+ auto i (status_cache_.emplace (pn, move (r)).first);
+ return i->second ? &*i->second : nullptr;
+ }
+
+ optional<package_status> system_package_manager_debian::
+ status (const package_name& pn, const available_packages& aps)
+ {
+ // For now we ignore -doc and -dbg package components (but we may want to
+ // have options controlling this later). Note also that we assume -common
+ // is pulled automatically by the main package so we ignore it as well
+ // (see equivalent logic in parse_name_value()).
+ //
+ bool need_doc (false);
+ bool need_dbg (false);
+
vector<package_status> candidates;
// Translate our package name to the Debian package names.
@@ -927,8 +938,8 @@ namespace bpkg
});
strings ns;
- if (!aps->empty ())
- ns = system_package_names (*aps,
+ if (!aps.empty ())
+ ns = system_package_names (aps,
os_release.name_id,
os_release.version_id,
os_release.like_ids);
@@ -1269,9 +1280,9 @@ namespace bpkg
string sv (r->system_version, 0, r->system_version.rfind ('-'));
optional<version> v;
- if (!aps->empty ())
+ if (!aps.empty ())
v = downstream_package_version (sv,
- *aps,
+ aps,
os_release.name_id,
os_release.version_id,
os_release.like_ids);
@@ -1304,10 +1315,7 @@ namespace bpkg
r->version = move (*v);
}
- // Cache.
- //
- auto i (status_cache_.emplace (pn, move (r)).first);
- return i->second ? &*i->second : nullptr;
+ return r;
}
void system_package_manager_debian::
@@ -1455,8 +1463,8 @@ namespace bpkg
// create the package completely manually without using any of the Debian
// tools and while some implementations (for example, cargo-deb) do it this
// way, we are not going to go this route because it does not scale well to
- // more complex packages which may require additional functionality, such as
- // managing systemd files, and which is covered by the Debian tools (for an
+ // more complex packages which may require additional functionality (such as
+ // managing systemd files) and which is covered by the Debian tools (for an
// example of where this leads, see the partial debhelper re-implementation
// in cargo-deb). Another issues with this approach is that it's not
// amenable to customizations, at least not in a way familiar to Debian
@@ -1464,24 +1472,24 @@ namespace bpkg
//
// At the lowest level of the Debian tools for creating packages sits the
// dpkg-deb --build|-b command (also accessible as dpkg --build|-b). Given a
- // directory with all the binary contents (including the package metadata,
- // such as the control file, in the debian/ subdirectory) this command will
- // pack everything up into a .deb file. While an improvement over the fully
- // manual packaging, this approach has essentially the same drawbacks. In
- // particular, this command generates a single package which means we will
- // have to manually sort out things into -dev, -doc, etc.
+ // directory with all the binary package contents (including the package
+ // metadata, such as the control file, in the debian/ subdirectory) this
+ // command will pack everything up into a .deb file. While an improvement
+ // over the fully manual packaging, this approach has essentially the same
+ // drawbacks. In particular, this command generates a single package which
+ // means we will have to manually sort out things into -dev, -doc, etc.
//
// Next up the stack is dpkg-buildpackage. This tool expects the package to
- // follow the Debian way, that is, to provide the debian/rules makefile with
- // a number of required targets which it then invokes to build, install, and
- // pack a package from source (and somewhere in this process it calls
- // dpkg-deb --build). The dpkg-buildpackage(1) man page has an overview of
- // all the steps that this command performs and it is the recommended,
- // lower-level, way to build packages on Debian.
+ // follow the Debian way of packaging, that is, to provide the debian/rules
+ // makefile with a number of required targets which it then invokes to
+ // build, install, and pack a package from source (and sometime during this
+ // process it calls dpkg-deb --build). The dpkg-buildpackage(1) man page has
+ // an overview of all the steps that this command performs and it is the
+ // recommended, lower-level, way to build packages on Debian.
//
// At the top of the stack sits debuild which calls dpkg-buildpackage, then
- // lintian and finally design (though signing can also be performed by
- // dpkg-buildpackage).
+ // lintian, and finally design (though signing can also be performed by
+ // dpkg-buildpackage itself).
//
// Based on this our plan is to use dpkg-buildpackage which brings us to the
// Debian way of packaging with debian/rules at its core. As it turns out,
@@ -1498,7 +1506,7 @@ namespace bpkg
// While debhelper tools definitely simplify debian/rules, there is often
// still a lot of boilerplate code. So second-level helpers are often used,
// with the dominant option being the dh(1) command sequencer (there is also
- // CDBS but it appears to be mostly obsolete).
+ // CDBS but it appears to be fading into obsolescence).
//
// Based on that our options appear to be classic debhelper and dh. Looking
// at the statistics, it's clear that the majority of packages (including
@@ -1508,16 +1516,104 @@ namespace bpkg
// So, to sum up, the plan is to produce debian/rules that uses the dh
// command sequencer and then invoke dpkg-buildpackage to produce the binary
// package from that. While this approach is normally used to build things
- // from source, it feels like we should be able to pretend that we are by,
- // for example, overriding the install target to invoke the build system to
- // install all the packages directly from their bpkg locations.
+ // from source, it feels like we should be able to pretend that we are.
+ // Specifially, we can override the install target to invoke the build
+ // system and install all the packages directly from their bpkg locations.
//
void system_package_manager_debian::
- generate (packages&&,
- packages&&,
+ generate (packages&& pkgs,
+ packages&& deps,
strings&&,
const dir_path&,
optional<recursive_mode>)
{
+ // @@ What are we doing with extras, in both deps and the package being
+ // generated?
+
+ // Map non-system bpkg package to system package name(s) and version.
+ //
+ auto map_package = [this] (const selected_package& sp,
+ const available_packages& aps) -> package_status
+ {
+ // We should only have one available package corresponding to the
+ // selected package.
+ //
+ assert (sp.substate != package_substate::system && aps.size () == 1);
+
+ strings ns (system_package_names (aps,
+ os_release.name_id,
+ os_release.version_id,
+ os_release.like_ids));
+ package_status r;
+ if (ns.empty ())
+ {
+ // Automatically translate our package name similar to the consumption
+ // case above. Except here we don't attempt to deduce main from -dev,
+ // naturally.
+ //
+ const string& pn (sp.name.string ());
+
+ // The best we can do in trying to detect whether this is a library is
+ // to check for the lib prefix. Libraries without the lib prefix and
+ // non-libraries with the lib prefix (both of which we do not
+ // recomment) will have to provide a manual mapping.
+ //
+ if (pn.compare (0, 3, "lib") == 0 && pn.size () > 3)
+ {
+ r = package_status (pn, pn + "-dev");
+ }
+ else
+ r = package_status (pn);
+ }
+ else
+ {
+ // Even though we only pass one available package, we may still end up
+ // with multiple mappings. In this case we take the first per the
+ // documentation.
+ //
+ r = parse_name_value (sp.name,
+ ns.front (),
+ false /* need_doc */,
+ false /* need_dbg */);
+ }
+
+ return r;
+ };
+
+ // As a first step, figure out the system names and version of the package
+ // we are generating and all the dependencies, diagnosing anything fishy.
+ //
+ // Note that there should be no duplicate dependencies and we can sidestep
+ // the status cache.
+ //
+ const selected_package& sp (*pkgs.front ().first);
+ const available_packages& aps (pkgs.front ().second);
+ package_status s (map_package (sp, aps));
+
+ vector<package_status> sdeps;
+ sdeps.reserve (deps.size ());
+ for (const pair<shared_ptr<selected_package>, available_packages>& p: deps)
+ {
+ const selected_package& sp (*p.first);
+ const available_packages& aps (p.second);
+
+ package_status s;
+ if (sp.substate == package_substate::system)
+ {
+ optional<package_status> os (status (sp.name, aps));
+
+ if (!os)
+ fail << "bad boy";
+
+ // @@ We should confirm configured version matches mapped back.
+ // Can be `*`!
+
+ s = move (*os);
+ }
+ else
+ s = map_package (sp, aps);
+
+ sdeps.push_back (move (s));
+ }
}
}
diff --git a/bpkg/system-package-manager-debian.hxx b/bpkg/system-package-manager-debian.hxx
index 0186b76..1e53e38 100644
--- a/bpkg/system-package-manager-debian.hxx
+++ b/bpkg/system-package-manager-debian.hxx
@@ -233,7 +233,11 @@ namespace bpkg
const simulation* simulate_ = nullptr;
- protected:
+ private:
+ optional<system_package_status_debian>
+ status (const package_name&, const available_packages&);
+
+ private:
bool fetched_ = false; // True if already fetched metadata.
bool installed_ = false; // True if already installed.
diff --git a/bpkg/system-package-manager-fedora.hxx b/bpkg/system-package-manager-fedora.hxx
index 6c72b81..7692822 100644
--- a/bpkg/system-package-manager-fedora.hxx
+++ b/bpkg/system-package-manager-fedora.hxx
@@ -327,7 +327,7 @@ namespace bpkg
const simulation* simulate_ = nullptr;
- protected:
+ private:
bool fetched_ = false; // True if already fetched metadata.
bool installed_ = false; // True if already installed.
diff --git a/bpkg/system-package-manager.cxx b/bpkg/system-package-manager.cxx
index 2ec7a60..f25c0e0 100644
--- a/bpkg/system-package-manager.cxx
+++ b/bpkg/system-package-manager.cxx
@@ -13,6 +13,8 @@
#include <bpkg/database.hxx>
#include <bpkg/diagnostics.hxx>
+#include <bpkg/pkg-bindist-options.hxx>
+
#include <bpkg/system-package-manager-debian.hxx>
#include <bpkg/system-package-manager-fedora.hxx>
@@ -122,15 +124,15 @@ namespace bpkg
}
unique_ptr<system_package_manager>
- make_production_system_package_manager (const common_options& co,
+ make_production_system_package_manager (const pkg_bindist_options& o,
const target_triplet& host,
const string& name,
const string& arch)
{
// Note: similar to make_production_system_package_manager() above.
- optional<bool> progress (co.progress () ? true :
- co.no_progress () ? false :
+ optional<bool> progress (o.progress () ? true :
+ o.no_progress () ? false :
optional<bool> ());
unique_ptr<system_package_manager> r;
diff --git a/bpkg/system-package-manager.hxx b/bpkg/system-package-manager.hxx
index 941c981..81adeee 100644
--- a/bpkg/system-package-manager.hxx
+++ b/bpkg/system-package-manager.hxx
@@ -155,9 +155,11 @@ namespace bpkg
virtual void
pkg_install (const vector<package_name>&) = 0;
- // Generate a binary distribution package.
+ // Generate a binary distribution package. @@ TODO: doc more
//
- // @@ TODO: doc
+ // The available packages are loaded for the first package in pkgs and for
+ // all the packages in deps. For non-system packages there is always a
+ // single available package that corresponds to the selected package.
//
// See the pkg-bindist(1) man page and the pkg_bindist() function
// implementation for background and details.
@@ -321,8 +323,10 @@ namespace bpkg
bool yes,
const string& sudo);
+ class pkg_bindist_options;
+
unique_ptr<system_package_manager>
- make_production_system_package_manager (const common_options&,
+ make_production_system_package_manager (const pkg_bindist_options&,
const target_triplet&,
const string& name,
const string& arch);
diff --git a/doc/cli.sh b/doc/cli.sh
index 30ed7b2..dd3cb37 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -81,10 +81,10 @@ compile "pkg-build" $o --class-doc bpkg::pkg_build_pkg_options=exclude-base
# NOTE: remember to update a similar list in buildfile and bpkg.cli as well as
# the help topics sections in bpkg/buildfile and help.cxx.
#
-pages="cfg-create cfg-info cfg-link cfg-unlink help pkg-clean pkg-configure \
-pkg-disfigure pkg-drop pkg-fetch pkg-checkout pkg-install pkg-purge \
-pkg-status pkg-test pkg-uninstall pkg-unpack pkg-update pkg-verify rep-add \
-rep-remove rep-list rep-create rep-fetch rep-info repository-signing \
+pages="cfg-create cfg-info cfg-link cfg-unlink help pkg-bindist pkg-clean \
+pkg-configure pkg-disfigure pkg-drop pkg-fetch pkg-checkout pkg-install \
+pkg-purge pkg-status pkg-test pkg-uninstall pkg-unpack pkg-update pkg-verify \
+rep-add rep-remove rep-list rep-create rep-fetch rep-info repository-signing \
repository-types argument-grouping default-options-files"
for p in $pages; do