From 7e46e1fd6fa31de4a3f7ff013d2d1a4be225f45f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 21 Feb 2023 07:23:13 +0200 Subject: Collect languages for recursive dependencies --- bpkg/pkg-bindist.cxx | 97 ++++++++++++++++++++++++++++------ bpkg/system-package-manager-debian.cxx | 18 +++---- bpkg/system-package-manager-debian.hxx | 2 + bpkg/system-package-manager-fedora.cxx | 2 + bpkg/system-package-manager-fedora.hxx | 2 + bpkg/system-package-manager.hxx | 12 ++++- 6 files changed, 105 insertions(+), 28 deletions(-) diff --git a/bpkg/pkg-bindist.cxx b/bpkg/pkg-bindist.cxx index ac774b1..e191996 100644 --- a/bpkg/pkg-bindist.cxx +++ b/bpkg/pkg-bindist.cxx @@ -55,15 +55,56 @@ namespace bpkg return r; } + // Merge dependency languages for the (ultimate) dependent of the specified + // type. + // + static void + merge_languages (const string& type, + small_vector& langs, + const available_package& ap) + { + for (const language& l: ap.effective_languages ()) + { + // Unless both the dependent and dependency types are libraries, the + // interface/implementation distinction does not apply. + // + bool lib (type == "lib" && ap.effective_type () == "lib"); + + auto i (find_if (langs.begin (), langs.end (), + [&l] (const language& x) + { + return x.name == l.name; + })); + + if (i == langs.end ()) + { + // If this is an implementation language for a dependency, then it is + // also an implementation language for a dependent. The converse, + // howevere, depends on whether this dependency is an interface or + // imlementation of this dependent, which we do not know. So we have + // to assume it's interface. + // + langs.push_back (language {l.name, lib && l.impl}); + } + else + { + i->impl = i->impl && (lib && l.impl); // Merge. + } + } + } + // 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. + // site for details). Find available packages for pkgs and deps and merge + // languages. // static void collect_dependencies (const common_options& co, packages* pkgs, packages& deps, + const string& type, + small_vector& langs, const selected_package& p, bool recursive) { @@ -98,17 +139,27 @@ namespace bpkg 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 (ps != nullptr || (recursive && !sys)) + { + available_packages aps (find_available_packages (co, db, d)); + + // Load and merge languages. + // + if (recursive && !sys) + { + const shared_ptr& ap (aps.front ().first); + db.load (*ap, ap->languages_section); + merge_languages (type, langs, *ap); + } + + if (ps != nullptr) + ps->push_back (make_pair (move (d), move (aps))); + } if (recursive && !sys) - collect_dependencies (co, pkgs, deps, p, recursive); + collect_dependencies (co, pkgs, deps, type, langs, p, recursive); } } } @@ -221,9 +272,11 @@ namespace bpkg // Resolve package names to selected packages and verify they are all // configured. While at it collect their available packages and - // dependencies. + // dependencies as well as figure out type and languages. // packages pkgs, deps; + string type; + small_vector langs; for (const package_name& n: pns) { @@ -239,12 +292,20 @@ namespace bpkg 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. + // Load the available package for type/languages as well as the mapping + // information. // - available_packages aps; - if (pkgs.empty ()) - aps = find_available_packages (o, db, p); + available_packages aps (find_available_packages (o, db, p)); + const shared_ptr& 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 (make_pair (move (p), move (aps))); @@ -264,6 +325,8 @@ namespace bpkg ? *rec == recursive_mode::full ? &pkgs : nullptr : &deps), deps, + type, + langs, r, rec.has_value ()); } @@ -301,7 +364,11 @@ namespace bpkg // @@ TODO: pass/handle --private. - spm->generate (pkgs, deps, vars, pm, out, rec); + // 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. + // + spm->generate (pkgs, deps, vars, pm, type, langs, out, rec); // @@ TODO: change the output, maybe to something returned by spm? // diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx index 4d9c77d..2856fdf 100644 --- a/bpkg/system-package-manager-debian.cxx +++ b/bpkg/system-package-manager-debian.cxx @@ -1850,31 +1850,27 @@ namespace bpkg const packages& deps, const strings&, const package_manifest& pm, + const string& pt, + const small_vector& langs, const dir_path& out, optional) { + assert (!langs.empty ()); // Should be effective. + const shared_ptr& sp (pkgs.front ().first); const package_name& pn (sp->name); const version& pv (sp->version); const available_packages& aps (pkgs.front ().second); - const shared_ptr& ap (aps.front ().first); - - bool lib (ap->effective_type () == "lib"); - struct package_lang - { - string name; - bool impl; // True if implementation-only. - }; - small_vector langs {{"cc", false}}; + bool lib (pt == "lib"); // For now we only know how to handle libraries with C-common interface // languages. But we allow other implementation languages. // if (lib) { - for (const package_lang& l: langs) + for (const language& l: langs) if (!l.impl && l.name != "c" && l.name != "c++" && l.name != "cc") fail << l.name << " libraries are not yet supported"; } @@ -1885,7 +1881,7 @@ namespace bpkg auto lang = [&langs] (const char* n, bool intf_only = false) -> bool { return find_if (langs.begin (), langs.end (), - [n, intf_only] (const package_lang& l) + [n, intf_only] (const language& l) { return (!intf_only || !l.impl) && l.name == n; }) != langs.end (); diff --git a/bpkg/system-package-manager-debian.hxx b/bpkg/system-package-manager-debian.hxx index 9400a53..8398b70 100644 --- a/bpkg/system-package-manager-debian.hxx +++ b/bpkg/system-package-manager-debian.hxx @@ -134,6 +134,8 @@ namespace bpkg const packages&, const strings&, const package_manifest&, + const string&, + const small_vector&, const dir_path&, optional) override; diff --git a/bpkg/system-package-manager-fedora.cxx b/bpkg/system-package-manager-fedora.cxx index 381ebfe..5a81dbf 100644 --- a/bpkg/system-package-manager-fedora.cxx +++ b/bpkg/system-package-manager-fedora.cxx @@ -1787,6 +1787,8 @@ namespace bpkg const packages&, const strings&, const package_manifest&, + const string&, + const small_vector&, const dir_path&, optional) { diff --git a/bpkg/system-package-manager-fedora.hxx b/bpkg/system-package-manager-fedora.hxx index df0e4f6..b4a2c2f 100644 --- a/bpkg/system-package-manager-fedora.hxx +++ b/bpkg/system-package-manager-fedora.hxx @@ -201,6 +201,8 @@ namespace bpkg const packages&, const strings&, const package_manifest&, + const string&, + const small_vector&, const dir_path&, optional) override; diff --git a/bpkg/system-package-manager.hxx b/bpkg/system-package-manager.hxx index ad41174..29449f6 100644 --- a/bpkg/system-package-manager.hxx +++ b/bpkg/system-package-manager.hxx @@ -154,14 +154,20 @@ namespace bpkg // Generate a binary distribution package. @@ TODO: doc more // - // 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 + // 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. // // 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). // + // The passed package type corresponds to the first package in pkgs while + // the languages -- to all the packages in pkgs plus, in the recursive + // mode, to all the non-system dependencies. In other words, the languages + // list contains every language that is used by anything that ends up in + // the package. + // // See the pkg-bindist(1) man page and the pkg_bindist() function // implementation for background and details. // @@ -175,6 +181,8 @@ namespace bpkg const packages& deps, const strings& vars, const package_manifest&, + const string& type, + const small_vector&, const dir_path& out, optional) = 0; -- cgit v1.1