aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-02-15 11:39:38 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-02-21 04:46:55 +0200
commit8166e873c0cad830001ff84a15450cd9a7958c19 (patch)
tree298acca1cb08d147522cd4cdabc546f76f7b223a
parent112428df3d899af280aca6f03c721bc7065bb787 (diff)
WIP
-rw-r--r--bpkg/buildfile3
-rw-r--r--bpkg/pkg-bindist.cli45
-rw-r--r--bpkg/pkg-bindist.cxx30
-rw-r--r--bpkg/system-package-manager-debian.cxx189
-rw-r--r--bpkg/system-package-manager-debian.hxx15
-rw-r--r--bpkg/system-package-manager-fedora.cxx7
-rw-r--r--bpkg/system-package-manager-fedora.hxx7
-rw-r--r--bpkg/system-package-manager.hxx14
-rwxr-xr-xdoc/cli.sh12
9 files changed, 275 insertions, 47 deletions
diff --git a/bpkg/buildfile b/bpkg/buildfile
index 1531b9a..3ba9ea6 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -215,6 +215,9 @@ if $cli.configured
cli.cxx{pkg-build-options}: cli.options += --class-doc \
bpkg::pkg_build_pkg_options=exclude-base --generate-modifier
+ cli.cxx{pkg-bindist-options}: cli.options += --class-doc \
+bpkg::pkg_bindist_debian_options=exclude-base
+
# Avoid generating CLI runtime and empty inline file for help topics.
#
cli.cxx{repository-signing repository-types argument-grouping \
diff --git a/bpkg/pkg-bindist.cli b/bpkg/pkg-bindist.cli
index ad743d9..7dd5832 100644
--- a/bpkg/pkg-bindist.cli
+++ b/bpkg/pkg-bindist.cli
@@ -48,11 +48,50 @@ namespace bpkg
"
}
- // @@ Have section for each package manager with options?
+ // Place distribution-specific options into separate classes in case one day
+ // we want to only pass their own options to each implementation.
+ //
+ class pkg_bindist_debian_options
+ {
+ "\h|PKG-BINDIST DEBIAN OPTIONS|"
+
+ string --debian-section
+ {
+ "<val>",
+ "Alternative \cb{Section} field value in the \cb{control} file for
+ the main binary package. The default is either \cb{libs} or \cb{devel},
+ depending on the package type."
+ }
+
+ string --debian-priority
+ {
+ "<val>",
+ "Alternative \cb{Priority} field value in the \cb{control} file. The
+ default is \cb{optional}."
+ }
+
+ string --debian-maintainer
+ {
+ "<val>",
+ "Alternative \cb{Maintainer} field value in the \cb{control} file. The
+ default is the \cb{package-email} value from package \cb{manifest}."
+ }
+
+ string --debian-architecture
+ {
+ "<vl>",
+ "Alternative \cb{Architecture} field value in the \cb{control} file for
+ the main binary package. The default is \cb{any}."
+ }
+ };
- class pkg_bindist_options: configuration_options
+ // NOTE: remember to add the corresponding `--class-doc ...=exclude-base`
+ // (both in bpkg/ and doc/) if adding a new base class.
+ //
+ class pkg_bindist_options: configuration_options,
+ pkg_bindist_debian_options
{
- "\h|PKG-BINDIST OPTIONS|"
+ "\h|PKG-BINDIST COMMON OPTIONS|"
string --distribution
{
diff --git a/bpkg/pkg-bindist.cxx b/bpkg/pkg-bindist.cxx
index 289f34b..ac774b1 100644
--- a/bpkg/pkg-bindist.cxx
+++ b/bpkg/pkg-bindist.cxx
@@ -7,6 +7,7 @@
#include <bpkg/package-odb.hxx>
#include <bpkg/package-query.hxx>
#include <bpkg/database.hxx>
+#include <bpkg/pkg-verify.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/system-package-manager.hxx>
@@ -269,6 +270,20 @@ namespace bpkg
t.commit ();
+ // 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 ().first);
+
+ 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;}));
+
// Note that we shouldn't need to install anything or use sudo.
//
unique_ptr<system_package_manager> spm (
@@ -286,20 +301,10 @@ namespace bpkg
// @@ 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);
-
+ spm->generate (pkgs, deps, vars, pm, out, rec);
- // @@ TODO: need to save name/version (or change the output, maybe
- // to something returned by spm?
+ // @@ TODO: change the output, maybe to something returned by spm?
//
-#if 0
if (verb && !o.no_result ())
{
const selected_package& p (*pkgs.front ().first);
@@ -307,7 +312,6 @@ namespace bpkg
text << "generated " << spm->os_release.name_id << " package for "
<< p.name << '/' << p.version;
}
-#endif
return 0;
}
diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx
index 88bc6df..0670e24 100644
--- a/bpkg/system-package-manager-debian.cxx
+++ b/bpkg/system-package-manager-debian.cxx
@@ -1830,9 +1830,10 @@ namespace bpkg
// system and install all the packages directly from their bpkg locations.
//
void system_package_manager_debian::
- generate (packages&& pkgs,
- packages&& deps,
- strings&&,
+ generate (const packages& pkgs,
+ const packages& deps,
+ const strings&,
+ const package_manifest& pm,
const dir_path& out,
optional<recursive_mode>)
{
@@ -1845,7 +1846,13 @@ namespace bpkg
const shared_ptr<selected_package>& sp (pkgs.front ().first);
const package_name& pn (sp->name);
const version& pv (sp->version);
- package_status s (map_package (pn, pv, pkgs.front ().second));
+
+ // @@ TODO: need to factor this to system_package_manager.
+ //
+ bool lib (pn.string ().compare (0, 3, "lib") == 0 && pn.string ().size () > 3);
+
+ const available_packages& aps (pkgs.front ().second);
+ package_status st (map_package (pn, pv, aps));
vector<package_status> sdeps;
sdeps.reserve (deps.size ());
@@ -1898,12 +1905,12 @@ namespace bpkg
};
diag_record dr (text);
- print_status (dr, s);
+ print_status (dr, st);
- for (const package_status& ds: sdeps)
+ for (const package_status& st: sdeps)
{
dr << "\n ";
- print_status (dr, ds);
+ print_status (dr, st);
}
}
@@ -1934,6 +1941,172 @@ namespace bpkg
dir_path deb (src / dir_path ("debian"));
mk_p (deb);
+ // The control file.
+ //
+ // See the "Control files and their fields" chapter in the Debian Policy
+ // Manual for details (for example, which fields are mandatory).
+ //
+ // Note that we try to do a reasonably thorough job (e.g., filling in
+ // sections, etc) with the view that this can be used as a starting point
+ // for manual packaging (and perhaps we could add a mode for this in the
+ // future).
+ //
+ path ctrl (deb / "control");
+ try
+ {
+ ofdstream os (ctrl);
+
+ // First comes the general (source package) stanza.
+ //
+ // Note that Priority semantics is not the same as our priority. Rather
+ // it should reflect the overall functionality of the package. Our
+ // priority is more appropriately mapped to urgency in the changelog.
+ //
+ // If this is not a library, then by default we assume its some kind of
+ // a development tool and use the devel section.
+ //
+ string section (
+ ops_->debian_section_specified () ? ops_->debian_section () :
+ lib ? "libs" :
+ "devel");
+
+ string priority (
+ ops_->debian_priority_specified () ? ops_->debian_priority () :
+ "optional");
+
+ string maintainer (
+ ops_->debian_maintainer_specified () ? ops_->debian_maintainer () :
+ pm.package_email ? static_cast<const string&> (*pm.package_email) :
+ pm.email ? static_cast<const string&> (*pm.email) :
+ string ());
+
+ if (maintainer.empty ())
+ fail << "unable to determine package maintainer from manifest" <<
+ info << "specify explicitly with --debian-maintainer";
+
+ optional<string> homepage (pm.package_url ? pm.package_url->string () :
+ pm.url ? pm.url->string () :
+ optional<string> ());
+
+ os << "Source: " << pn.string () << '\n'
+ << "Section: " << section << '\n'
+ << "Priority: " << priority << '\n'
+ << "Maintainer: " << maintainer << '\n'
+ << "Standards-Version: " << "4.6.2" << '\n'
+ << "Build-Depends: " << "debhelper-compat (= 13)" << '\n'
+ << "Rules-Requires-Root: " << "no" << '\n';
+ if (homepage)
+ os << "Homepage: " << *homepage << '\n';
+ if (pm.src_url)
+ os << "Vcs-Browser: " << pm.src_url->string () << '\n';
+
+ // Then we have one or more binary package stanzas.
+ //
+ // Note that values from the source package (such as Section, Priority)
+ // are used as defaults for the binary packages.
+ //
+ // We cannot easily detect architecture-independent packages (think
+ // libbutl.bash) and providing an option feels like the best we can do.
+ // Note that the value `any` means architecture-dependent while `all`
+ // means architecture-independent.
+ //
+ // The Multi-Arch hint can be `same` or `foreign`. The former means that
+ // a separate copy of the package may be installed for each architecture
+ // (e.g., library) while the latter -- that a single copy may be used by
+ // all architectures (e.g., executable, -doc, -common). Not that for
+ // some murky reasons Multi-Arch:foreign needs to be explicitly
+ // specified for Architecture:all.
+ //
+ // The Description field is quite messy: it requires both the short
+ // description (our summary) as a first line and a long description (our
+ // description) as the following lines in the multiline format.
+ // Converting our description to the Debian format is not going to be
+ // easy: it can be arbitrarily long and may not even be plain text (it's
+ // commonly the contents of the README.md file). So for now we fake it
+ // with a description of the package component.
+ //
+ string arch (ops_->debian_architecture_specified ()
+ ? ops_->debian_architecture ()
+ : "any");
+
+ string march (arch == "all" || !lib ? "foreign" : "same");
+
+ os << '\n'
+ << "Package: " << st.main << '\n'
+ << "Architecture: " << arch << '\n'
+ << "Multi-Arch: " << march << '\n'
+ << "Description: " << pm.summary << '\n'
+ << " This package contains the runtime files." << '\n';
+
+ // @@ Depends: common (if any)
+
+ if (!st.dev.empty ())
+ {
+ // Feels like the architecture should be the same as for the main
+ // package.
+ //
+ os << '\n'
+ << "Package: " << st.dev << '\n'
+ << "Section: " << (lib ? "libdevel" : "devel") << '\n'
+ << "Architecture: " << arch << '\n'
+ << "Multi-Arch: " << march << '\n';
+ if (!st.doc.empty ())
+ os << "Suggests: " << st.doc << '\n';
+ os << "Description: " << pm.summary << '\n'
+ << " This package contains the development files." << '\n';
+
+ // @@ Depends: main
+ }
+
+ if (!st.doc.empty ())
+ {
+ os << '\n'
+ << "Package: " << st.doc << '\n'
+ << "Section: " << "doc" << '\n'
+ << "Architecture: " << "all" << '\n'
+ << "Multi-Arch: " << "foreign" << '\n'
+ << "Description: " << pm.summary << '\n'
+ << " This package contains the documentation." << '\n';
+ }
+
+ if (!st.dbg.empty ())
+ {
+ os << '\n'
+ << "Package: " << st.dbg << '\n'
+ << "Section: " << "debug" << '\n'
+ << "Priority: " << "extra" << '\n'
+ << "Architecture: " << arch << '\n'
+ << "Multi-Arch: " << march << '\n'
+ << "Description: " << pm.summary << '\n'
+ << " This package contains the debugging information." << '\n';
+
+ // @@ Depends: main
+ }
+
+ if (!st.common.empty ())
+ {
+ // Generally, this package is not necessarily architecture-independent
+ // (for example, it could contain something shared between multiple
+ // binary packages produced from the same source package rather than
+ // something shared between all the architectures of a binary
+ // package). But seeing that we always generate one binary package,
+ // for us it only makes sense as architecture-independent.
+ //
+ os << '\n'
+ << "Package: " << st.common << '\n'
+ << "Architecture: " << "all" << '\n'
+ << "Multi-Arch: " << "foreign" << '\n'
+ << "Description: " << pm.summary << '\n'
+ << " This package contains the architecture-independent files." << '\n';
+ }
+
+ os.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write to " << ctrl << ": " << e;
+ }
+
// The rules makefile. Note that it must be executable.
//
path rules (deb / "rules");
@@ -1959,6 +2132,8 @@ namespace bpkg
else if (verb >= 2)
os << "export DH_VERBOSE=1\n";
+ // @@ TODO: remember to override config.install.sudo.
+
os.close ();
}
catch (const io_error& e)
diff --git a/bpkg/system-package-manager-debian.hxx b/bpkg/system-package-manager-debian.hxx
index 8fccb55..3708faa 100644
--- a/bpkg/system-package-manager-debian.hxx
+++ b/bpkg/system-package-manager-debian.hxx
@@ -23,10 +23,10 @@ namespace bpkg
// For background, a library in Debian is normally split up into several
// packages: the shared library package (e.g., libfoo1 where 1 is the ABI
// version), the development files package (e.g., libfoo-dev), the
- // documentation files package (e.g., libfoo-doc), the debug symbols
- // package (e.g., libfoo1-dbg), and the architecture-independent files
- // (e.g., libfoo1-common). All the packages except -dev are optional
- // and there is quite a bit of variability here. Here are a few examples:
+ // documentation files package (e.g., libfoo-doc), the debug symbols package
+ // (e.g., libfoo1-dbg), and the (usually) architecture-independent files
+ // (e.g., libfoo1-common). All the packages except -dev are optional and
+ // there is quite a bit of variability here. Here are a few examples:
//
// libsqlite3-0 libsqlite3-dev
//
@@ -130,9 +130,10 @@ namespace bpkg
pkg_install (const vector<package_name>&) override;
virtual void
- generate (packages&&,
- packages&&,
- strings&&,
+ generate (const packages&,
+ const packages&,
+ const strings&,
+ const package_manifest&,
const dir_path&,
optional<recursive_mode>) override;
diff --git a/bpkg/system-package-manager-fedora.cxx b/bpkg/system-package-manager-fedora.cxx
index 3178e4e..381ebfe 100644
--- a/bpkg/system-package-manager-fedora.cxx
+++ b/bpkg/system-package-manager-fedora.cxx
@@ -1783,9 +1783,10 @@ namespace bpkg
}
void system_package_manager_fedora::
- generate (packages&&,
- packages&&,
- strings&&,
+ generate (const packages&,
+ const packages&,
+ const strings&,
+ const package_manifest&,
const dir_path&,
optional<recursive_mode>)
{
diff --git a/bpkg/system-package-manager-fedora.hxx b/bpkg/system-package-manager-fedora.hxx
index 7692822..df0e4f6 100644
--- a/bpkg/system-package-manager-fedora.hxx
+++ b/bpkg/system-package-manager-fedora.hxx
@@ -197,9 +197,10 @@ namespace bpkg
pkg_install (const vector<package_name>&) override;
virtual void
- generate (packages&&,
- packages&&,
- strings&&,
+ generate (const packages&,
+ const packages&,
+ const strings&,
+ const package_manifest&,
const dir_path&,
optional<recursive_mode>) override;
diff --git a/bpkg/system-package-manager.hxx b/bpkg/system-package-manager.hxx
index 91f9a49..ad41174 100644
--- a/bpkg/system-package-manager.hxx
+++ b/bpkg/system-package-manager.hxx
@@ -4,9 +4,6 @@
#ifndef BPKG_SYSTEM_PACKAGE_MANAGER_HXX
#define BPKG_SYSTEM_PACKAGE_MANAGER_HXX
-#include <libbpkg/manifest.hxx> // version
-#include <libbpkg/package-name.hxx>
-
#include <bpkg/types.hxx>
#include <bpkg/utility.hxx>
@@ -161,6 +158,10 @@ namespace bpkg
// all the packages in deps. For non-system packages there is always a
// single available package that corresponds to the selected package.
//
+ // The passed package manifest corresponds to the first package in pkgs
+ // (normally used as a source of additional package metadata such as
+ // summary, emails, urls, etc).
+ //
// See the pkg-bindist(1) man page and the pkg_bindist() function
// implementation for background and details.
//
@@ -170,9 +171,10 @@ namespace bpkg
enum class recursive_mode {auto_, full};
virtual void
- generate (packages&& pkgs,
- packages&& deps,
- strings&& vars,
+ generate (const packages& pkgs,
+ const packages& deps,
+ const strings& vars,
+ const package_manifest&,
const dir_path& out,
optional<recursive_mode>) = 0;
diff --git a/doc/cli.sh b/doc/cli.sh
index dd3cb37..4f7ea18 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -78,14 +78,16 @@ compile "bpkg" $o --output-prefix "" --class-doc bpkg::commands=short --class-do
compile "pkg-build" $o --class-doc bpkg::pkg_build_pkg_options=exclude-base
+compile "pkg-bindist" $o --class-doc bpkg::pkg_bindist_debian_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-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"
+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 repository-types \
+argument-grouping default-options-files"
for p in $pages; do
compile $p $o