From 5837974ede1f489fe1e65cb10188f776fb6d1974 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 22 Feb 2023 10:20:14 +0200 Subject: WIP --- bpkg/package.hxx | 6 +- bpkg/pkg-bindist.cli | 12 +-- bpkg/pkg-bindist.cxx | 28 +++-- bpkg/system-package-manager-debian.cxx | 184 ++++++++++++++++++++++++++++++--- bpkg/system-package-manager.hxx | 13 ++- bpkg/utility.cxx | 20 ++++ bpkg/utility.hxx | 3 + bpkg/utility.txx | 11 +- 8 files changed, 234 insertions(+), 43 deletions(-) diff --git a/bpkg/package.hxx b/bpkg/package.hxx index 70fcee2..1c70676 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -1331,8 +1331,7 @@ namespace bpkg return src_root->absolute () ? *src_root : configuration / *src_root; } - // Return the output directory using the configuration directory. Note - // that the output directory is always relative. + // Return the output directory using the configuration directory. // dir_path effective_out_root (const dir_path& configuration) const @@ -1340,6 +1339,9 @@ namespace bpkg // Cast for compiling with ODB (see above). // assert (static_cast (out_root)); + + // Note that out_root is always relative. + // return configuration / *out_root; } diff --git a/bpkg/pkg-bindist.cli b/bpkg/pkg-bindist.cli index 23c70d1..eef0c1e 100644 --- a/bpkg/pkg-bindist.cli +++ b/bpkg/pkg-bindist.cli @@ -145,12 +145,12 @@ namespace bpkg 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}." + 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}." } bool --wipe-out diff --git a/bpkg/pkg-bindist.cxx b/bpkg/pkg-bindist.cxx index e191996..6db7cdb 100644 --- a/bpkg/pkg-bindist.cxx +++ b/bpkg/pkg-bindist.cxx @@ -16,6 +16,7 @@ using namespace butl; namespace bpkg { + using package = system_package_manager::package; using packages = system_package_manager::packages; using recursive_mode = system_package_manager::recursive_mode; @@ -131,13 +132,11 @@ namespace bpkg // Skip duplicates. // - if (ps == nullptr || - find_if (ps->begin (), ps->end (), - [&d] (const pair, - available_packages>& p) - { - return p.first == d; - }) == ps->end ()) + if (ps == nullptr || find_if (ps->begin (), ps->end (), + [&d] (const package& p) + { + return p.selected == d; + }) == ps->end ()) { const selected_package& p (*d); @@ -155,7 +154,13 @@ namespace bpkg } if (ps != nullptr) - ps->push_back (make_pair (move (d), move (aps))); + { + dir_path out; + if (ps != &deps) + out = p.effective_out_root (db.config); + + ps->push_back (package {move (d), move (aps), move (out)}); + } } if (recursive && !sys) @@ -308,7 +313,8 @@ namespace bpkg merge_languages (type, langs, *ap); const selected_package& r (*p); - pkgs.push_back (make_pair (move (p), move (aps))); + 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 @@ -336,7 +342,7 @@ namespace bpkg // 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& sp (pkgs.front ().first); + const shared_ptr& sp (pkgs.front ().selected); package_manifest pm ( pkg_verify (o, @@ -374,7 +380,7 @@ namespace bpkg // if (verb && !o.no_result ()) { - const selected_package& p (*pkgs.front ().first); + const selected_package& p (*pkgs.front ().selected); text << "generated " << spm->os_release.name_id << " package for " << p.name << '/' << p.version; diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx index e78d970..3d9b5d5 100644 --- a/bpkg/system-package-manager-debian.cxx +++ b/bpkg/system-package-manager-debian.cxx @@ -1853,15 +1853,15 @@ namespace bpkg const string& pt, const small_vector& langs, const dir_path& out, - optional) + optional recur) { assert (!langs.empty ()); // Should be effective. - const shared_ptr& sp (pkgs.front ().first); + const shared_ptr& sp (pkgs.front ().selected); const package_name& pn (sp->name); const version& pv (sp->version); - const available_packages& aps (pkgs.front ().second); + const available_packages& aps (pkgs.front ().available); bool lib (pt == "lib"); @@ -1897,10 +1897,10 @@ namespace bpkg vector sdeps; sdeps.reserve (deps.size ()); - for (const pair, available_packages>& p: deps) + for (const package& p: deps) { - const shared_ptr& sp (p.first); - const available_packages& aps (p.second); + const shared_ptr& sp (p.selected); + const available_packages& aps (p.available); package_status s; if (sp->substate == package_substate::system) @@ -2030,7 +2030,7 @@ namespace bpkg if (!e->comment.empty ()) maintainer = e->comment; else - maintainer = pn.string () + " maintainer"; + maintainer = pn.string () + " package maintainer"; maintainer += " <" + *e + '>'; } @@ -2465,18 +2465,131 @@ namespace bpkg // See debhelper(7) for details on these. // if (verb == 0) - os << "export DH_QUIET=1\n" + os << "export DH_QUIET := 1\n" << '\n'; else if (verb == 1) os << "# Uncomment this to turn on verbose mode.\n" - << "#export DH_VERBOSE=1\n" + << "#export DH_VERBOSE := 1\n" << '\n'; else - os << "export DH_VERBOSE=1\n" + os << "export DH_VERBOSE := 1\n" << '\n'; + // We could have instead included architecture.mk but let's avoid an + // extra dependency (most packages that we sampled do it directly). + // + os << "DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)\n" + << '\n'; + + // The debian/tmp/ subdirectory appears to be the canonical destination + // directory (see dh_auto_install(1) for details). + // + os << "DESTDIR := $(CURDIR)/debian/tmp" << '\n' + << '\n'; + + // Let's use absolute path to the build system driver in case we are + // invoked with altered environment or some such. + // + // See --jobs documentation in dpkg-buildpackage(1) for details on + // parallel=N. + // + os << "b := " << search_b (*ops_).effect_string () << '\n' + << '\n' + << "parallel := $(filter parallel=%,$(DEB_BUILD_OPTIONS))" << '\n' + << "ifneq ($(parallel),)" << '\n' + << " parallel := $(patsubst parallel=%,%,$(parallel))" << '\n' + << " ifeq ($(parallel),1)" << '\n' + << " b += --serial-stop" << '\n' + << " else" << '\n' + << " b += --jobs=$(parallel)" << '\n' + << " endif" << '\n' + << "endif" << '\n' + << '\n'; + + // Note that we override every config.install.* variable in order not to + // pick anything configured. + // + // We make use of the substitution since in the recursive mode + // we may be installing multiple projects. Note that the + // directory component is automatically removed if this functionality is + // not enabled. One side-effect of using is that we will be + // using the bpkg package name instead of the main Debian package name. + // But perhaps that's correct: on Debian it's usually the source package + // name, which is the same. To keep things consistent we use the bpkg + // package name for as well. + // + // @@ Some libraries install what looks like architecture-specific + // configuration files to /usr/include/$(DEB_HOST_MULTIARCH). Maybe + // we should invent something like config.install.include_arch to + // support this distinction? + // + bool priv (ops_->private_ ()); + + os << "config := config.install.chroot=$(DESTDIR)/" << '\n' + << "config += 'config.install.sudo=[null]'" << '\n' + + << "config += config.install.root=/usr/" << '\n' + << "config += config.install.data_root=root/" << '\n' + << "config += config.install.exec_root=root/" << '\n' + + << "config += config.install.bin=exec_root/bin/" << '\n' + << "config += config.install.sbin=exec_root/sbin/" << '\n' + + // On Debian shared libraries should not be executable. Also, + // libexec/ is the same as lib/ (note that executables that get + // installed there will still have the executable bit set). + // + << "config += 'config.install.lib=exec_root/lib/$(DEB_HOST_MULTIARCH)//'" << '\n' + << "config += config.install.lib.mode=644" << '\n' + << "config += 'config.install.libexec=lib//'" << '\n' + << "config += config.install.pkgconfig=lib/pkgconfig/" << '\n' + + << "config += config.install.etc=data_root/etc/" << '\n' + << "config += 'config.install.include=data_root/include//'" << '\n' + << "config += config.install.share=data_root/share/" << '\n' + << "config += 'config.install.data=share///'" << '\n' + + << "config += 'config.install.doc=share/doc///'" << '\n' + << "config += config.install.legal=doc/" << '\n' + << "config += config.install.man=share/man/" << '\n' + << "config += config.install.man1=share/man1/" << '\n' + << "config += config.install.man2=share/man2/" << '\n' + << "config += config.install.man3=share/man3/" << '\n' + << "config += config.install.man4=share/man4/" << '\n' + << "config += config.install.man5=share/man5/" << '\n' + << "config += config.install.man6=share/man6/" << '\n' + << "config += config.install.man7=share/man7/" << '\n' + << "config += config.install.man8=share/man8/" << '\n' + + << "config += 'config.install.private=" + << (priv ? pn.string () : "[null]") << "'" << '\n'; + + // If this is a C-based language, add rpath for private installation. + // + if (priv && (lang ("c") || lang ("c++") || lang ("cc"))) + os << "config += config.bin.rpath=/usr/lib/$(DEB_HOST_MULTIARCH)/" + << pn << "/" << '\n'; + + os << '\n'; + + // List of packages we need to install. + // + for (auto b (pkgs.begin ()), i (b); i != pkgs.end (); ++i) + { + os << "packages" << (i == b ? " := " : " += ") + << i->out_root.representation () << '\n'; + } + os << '\n'; + + // Disable synchronization hooks for good measure. + // + os << "export BDEP_SYNC := 0\n" + << '\n'; + + // Default to the dh command sequencer. + // os << "%:\n" - << "\tdh $@\n" + << '\t' << "dh $@" << '\n' << '\n'; // Override dh_auto_configure. @@ -2486,7 +2599,43 @@ namespace bpkg << "override_dh_auto_configure:\n" << '\n'; - // @@ TODO: remember to override config.install.sudo. + // Override dh_auto_build. + // + os << "override_dh_auto_build:\n" + << '\t' << "$b $(config) update-for-install: $(packages)" << '\n' + << '\n'; + + // Override dh_auto_test. + // + // Note that running tests after update-for-install may cause rebuild + // (e.g., relinking without rpath, etc) before tests and again before + // install. So doesn't seem worth the trouble. + // + os << "# Assume any testing has already been done.\n" + << "#\n" + << "override_dh_auto_test:\n" + << '\n'; + + // Override dh_auto_install. + // + // Note that we have to use global install scope for the auto recursive + // mode since things can be spread over multiple linked configurations. + // + string scope (!recur || *recur == recursive_mode::full + ? "project" + : "global"); + + os << "override_dh_auto_install:\n" + << '\t' << "$b $(config) '!config.install.scope=" << scope << "' " + << "install: $(packages)" << '\n' + << '\n'; + + // Override dh_auto_clean. + // + os << "# This is not a real source directory so nothing to clean.\n" + << "#\n" + << "override_dh_auto_clean:\n" + << '\n'; os.close (); } @@ -2495,6 +2644,17 @@ namespace bpkg fail << "unable to write to " << rules << ": " << e; } + // Call dpkg-buildpackage. + // + // @@ Pass our --jobs as --jobs=N. + // @@ Buildinfo stuff fuzzy. + // + // --no-sign + // --target-arch + // + // cd src/ + // dpkg-buildpackage --no-sign --build=binary + // Cleanup intermediate files unless requested not to. // if (!ops_->keep_out ()) diff --git a/bpkg/system-package-manager.hxx b/bpkg/system-package-manager.hxx index 29449f6..28b0c17 100644 --- a/bpkg/system-package-manager.hxx +++ b/bpkg/system-package-manager.hxx @@ -156,7 +156,8 @@ namespace bpkg // // The available packages are loaded for all the packages in pkgs and // deps. For non-system packages (so for all in pkgs) there is always a - // single available package that corresponds to the selected package. + // single available package that corresponds to the selected package. The + // out_root is only set for packages in pkgs. // // The passed package manifest corresponds to the first package in pkgs // (normally used as a source of additional package metadata such as @@ -171,8 +172,14 @@ namespace bpkg // See the pkg-bindist(1) man page and the pkg_bindist() function // implementation for background and details. // - using packages = - vector, available_packages>>; + struct package // @@ TODO: any better name? + { + shared_ptr selected; + available_packages available; + dir_path out_root; // Absolute and normalized. + }; + + using packages = vector; enum class recursive_mode {auto_, full}; diff --git a/bpkg/utility.cxx b/bpkg/utility.cxx index b79c85b..96fda0f 100644 --- a/bpkg/utility.cxx +++ b/bpkg/utility.cxx @@ -369,6 +369,26 @@ namespace bpkg : BPKG_EXE_PREFIX "b" BPKG_EXE_SUFFIX; } + process_path + search_b (const common_options& co) + { + const char* b (name_b (co)); + + try + { + // Use our executable directory as a fallback search since normally the + // entire toolchain is installed into one directory. This way, for + // example, if we installed into /opt/build2 and run bpkg with absolute + // path (and without PATH), then bpkg will be able to find "its" b. + // + return process::path_search (b, exec_dir); + } + catch (const process_error& e) + { + fail << "unable to execute " << b << ": " << e << endf; + } + } + void dump_stderr (auto_fd&& fd) { diff --git a/bpkg/utility.hxx b/bpkg/utility.hxx index 69a02d3..79534f0 100644 --- a/bpkg/utility.hxx +++ b/bpkg/utility.hxx @@ -247,6 +247,9 @@ namespace bpkg const char* name_b (const common_options&); + process_path + search_b (const common_options&); + template process start_b (const common_options&, O&& out, E&& err, verb_b, A&&... args); diff --git a/bpkg/utility.txx b/bpkg/utility.txx index 6113e4e..0f88d53 100644 --- a/bpkg/utility.txx +++ b/bpkg/utility.txx @@ -15,17 +15,10 @@ namespace bpkg verb_b v, A&&... args) { - const char* b (name_b (co)); + process_path pp (search_b (co)); try { - // Use our executable directory as a fallback search since normally the - // entire toolchain is installed into one directory. This way, for - // example, if we installed into /opt/build2 and run bpkg with absolute - // path (and without PATH), then bpkg will be able to find "its" b. - // - process_path pp (process::path_search (b, exec_dir)); - small_vector ops; // Map verbosity level. If we are running quiet or at level 1, @@ -98,7 +91,7 @@ namespace bpkg } catch (const process_error& e) { - fail << "unable to execute " << b << ": " << e << endf; + fail << "unable to execute " << pp.recall_string () << ": " << e << endf; } } -- cgit v1.1