From d38f564abaf6fcbc6c7cf2922f3e8f4c8327362f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 27 Mar 2023 08:46:58 +0200 Subject: Add append/prepend support to pkg-bindist --{debian,archive}-build-meta --- bpkg/pkg-bindist.cli | 19 ++- bpkg/system-package-manager-archive.cxx | 200 ++++++++++++++++++-------------- bpkg/system-package-manager-debian.cxx | 29 ++++- 3 files changed, 154 insertions(+), 94 deletions(-) diff --git a/bpkg/pkg-bindist.cli b/bpkg/pkg-bindist.cli index c9a89da..fd2a197 100644 --- a/bpkg/pkg-bindist.cli +++ b/bpkg/pkg-bindist.cli @@ -259,7 +259,10 @@ namespace bpkg string --debian-build-meta { "", - "Alternative build metadata to include in the binary package version. + "Alternative or additional build metadata to include in the binary + package version. If the specified value starts/ends with \cb{+} then + the value (with \cb{+} removed) is added after/before the default + metadata. Otherwise it is used as is instead of the default metadata. If empty value is specified, then no build metadata is included. By default, the build metadata is the \cb{ID} and \cb{VERSION_ID} components from \cb{os-release(5)}, for example, \cb{debian10} in @@ -568,8 +571,9 @@ namespace bpkg details on the mapping semantics. Instead of mapping languages individually you can specify entire build - metadata as a single value with the \cb{--archive-build-meta}, for - example: + metadata as a single value with the \cb{--archive-build-meta} (it is also + possible to add additional metadata; see the option documentation for + details). For example: \ bpkg bindist \ @@ -695,9 +699,12 @@ namespace bpkg string --archive-build-meta { "", - "Alternative build metadata to include after the version in the binary - package directory and file names. If empty value is specified, then no - build metadata is included." + "Alternative or additional build metadata to include after the version + in the binary package directory and file names. If the specified value + starts/ends with \cb{+} then the value (with \cb{+} removed) is added + after/before the default metadata. Otherwise it is used as is instead + of the default metadata. If empty value is specified, then no build + metadata is included." } dir_path --archive-install-root diff --git a/bpkg/system-package-manager-archive.cxx b/bpkg/system-package-manager-archive.cxx index b8df860..16e635d 100644 --- a/bpkg/system-package-manager-archive.cxx +++ b/bpkg/system-package-manager-archive.cxx @@ -3,6 +3,8 @@ #include +#include + #include #include @@ -503,93 +505,134 @@ namespace bpkg // libhello-1.2.3-x86_64-debian11-gcc12-rust1.62 // string base (pn.string () + '-' + pvs); - - if (ops->archive_build_meta_specified ()) - { - if (!ops->archive_build_meta ().empty ()) - base += '-' + ops->archive_build_meta (); - } - else { - if (!ops->archive_no_cpu ()) - base += '-' + target.cpu; - - if (!ops->archive_no_os ()) - base += '-' + os_release.name_id + os_release.version_id; - - // First collect the interface languages and then add implementation. - // This way if different languages map to the same runtimes (e.g., C and - // C++ mapped to gcc12), then we will always prefer the interface - // version over the implementation (which could be different, for - // example, libstdc++6 vs libstdc++-12-dev; but it's not clear how this - // will be specified, won't they end up with different names as opposed - // to gcc6 and gcc12 -- still fuzzy/unclear). - // - // @@ We will need to split id and version to be able to pick the - // highest version. - // - // @@ Maybe we should just do "soft" version like in ? - // - // Note that we allow multiple values for the same language to support - // cases like --archive-lang cc=gcc12 --archive-lang cc=g++12. - // - vector>> langrt; + bool md_s (ops->archive_build_meta_specified ()); + const string& md (ops->archive_build_meta ()); - auto find = [] (const std::multimap& m, const string& n) + bool md_f (false); + bool md_b (false); + if (md_s && !md.empty ()) { - auto p (m.equal_range (n)); + md_f = md.front () == '+'; + md_b = md.back () == '+'; + + if (md_f && md_b) // Note: covers just `+`. + fail << "invalid build metadata '" << md << "'"; + } - if (p.first == p.second) + if (md_s && !(md_f || md_b)) + { + if (!md.empty ()) + base += '-' + md; + } + else + { + if (md_b) { - // If no mapping for c/c++, fallback to cc. - // - if (n == "c" || n == "c++") - p = m.equal_range ("cc"); + base += '-'; + base.append (md, 0, md.size () - 1); } - return p; - }; + if (!ops->archive_no_cpu ()) + base += '-' + target.cpu; - auto add = [&langrt] (const pair& p) - { - // Suppress duplicates. + if (!ops->archive_no_os ()) + base += '-' + os_release.name_id + os_release.version_id; + + // First collect the interface languages and then add implementation. + // This way if different languages map to the same runtimes (e.g., C + // and C++ mapped to gcc12), then we will always prefer the interface + // version over the implementation (which could be different, for + // example, libstdc++6 vs libstdc++-12-dev; but it's not clear how + // this will be specified, won't they end up with different names as + // opposed to gcc6 and gcc12 -- still fuzzy/unclear). + // + // @@ We will need to split id and version to be able to pick the + // highest version. + // + // @@ Maybe we should just do "soft" version like in ? + // + // Note that we allow multiple values for the same language to support + // cases like --archive-lang cc=gcc12 --archive-lang cc=g++12. // - if (find_if (langrt.begin (), langrt.end (), - [&p] (const pair& x) - { - // @@ TODO: keep highest version. + vector>> langrt; - return p.second == x.second; - }) == langrt.end ()) + auto find = [] (const std::multimap& m, + const string& n) { - langrt.push_back (p); - } - }; + auto p (m.equal_range (n)); - auto& implm (ops->archive_lang_impl ()); + if (p.first == p.second) + { + // If no mapping for c/c++, fallback to cc. + // + if (n == "c" || n == "c++") + p = m.equal_range ("cc"); + } - // The interface/implementation distinction is only relevant to - // libraries. For everything else we treat all the languages as - // implementation. - // - if (lib) - { - auto& intfm (ops->archive_lang ()); + return p; + }; + + auto add = [&langrt] (const pair& p) + { + // Suppress duplicates. + // + if (find_if (langrt.begin (), langrt.end (), + [&p] (const pair& x) + { + // @@ TODO: keep highest version. + + return p.second == x.second; + }) == langrt.end ()) + { + langrt.push_back (p); + } + }; + + auto& implm (ops->archive_lang_impl ()); + + // The interface/implementation distinction is only relevant to + // libraries. For everything else we treat all the languages as + // implementation. + // + if (lib) + { + auto& intfm (ops->archive_lang ()); + + for (const language& l: langs) + { + if (l.impl) + continue; + + auto p (find (intfm, l.name)); + + if (p.first == p.second) + p = find (implm, l.name); + + if (p.first == p.second) + fail << "no runtime mapping for language " << l.name << + info << "consider specifying with --archive-lang[-impl]" << + info << "or alternatively specify --archive-build-meta"; + + for (auto i (p.first); i != p.second; ++i) + { + if (i->second.empty ()) + continue; // Unimportant. + + add (*i); + } + } + } for (const language& l: langs) { - if (l.impl) + if (lib && !l.impl) continue; - auto p (find (intfm, l.name)); + auto p (find (implm, l.name)); if (p.first == p.second) - p = find (implm, l.name); - - if (p.first == p.second) - fail << "no runtime mapping for language " << l.name << - info << "consider specifying with --archive-lang[-impl]" << - info << "or alternatively specify --archive-build-meta"; + continue; // Unimportant. for (auto i (p.first); i != p.second; ++i) { @@ -599,29 +642,16 @@ namespace bpkg add (*i); } } - } - - for (const language& l: langs) - { - if (lib && !l.impl) - continue; - - auto p (find (implm, l.name)); - if (p.first == p.second) - continue; // Unimportant. + for (const pair& p: langrt) + base += '-' + p.second; - for (auto i (p.first); i != p.second; ++i) + if (md_f) { - if (i->second.empty ()) - continue; // Unimportant. - - add (*i); + base += '-'; + base.append (md, 1, md.size () - 1); } } - - for (const pair& p: langrt) - base += '-' + p.second; } dir_path dst (out / dir_path (base)); diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx index b06487f..53d3a07 100644 --- a/bpkg/system-package-manager-debian.cxx +++ b/bpkg/system-package-manager-debian.cxx @@ -1849,13 +1849,36 @@ namespace bpkg if (!no_build_metadata) { sv += '~'; - if (build_metadata) - sv += *build_metadata; - else + + if (!build_metadata) { sv += os_release.name_id; sv += os_release.version_id; // Could be empty. } + else + { + const string& md (*build_metadata); + + bool f (md.front () == '+'); + bool b (md.back () == '+'); + + if (f && b) // Note: covers just `+`. + fail << "invalid build metadata '" << md << "'"; + + if (f || b) + { + if (b) + sv.append (md, 0, md.size () - 1); + + sv += os_release.name_id; + sv += os_release.version_id; + + if (f) + sv.append (md, 1, md.size () - 1); + } + else + sv += md; + } } return r; -- cgit v1.1