From a18661636cd169b0912cc58c623fdd69e3250229 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 20 Mar 2020 12:56:12 +0200 Subject: Generate common .pc file in addition to static/staged when installing lib{} The common .pc file is produced by ignoring any static/shared-specific poptions and splitting loptions/libs into Libs/Libs.private. It is "best effort", in a sense that it's not guaranteed to be sufficient in all cases, but it will probably cover the majority of cases, even on Windows, thanks to automatic dllimport'ing of functions. --- libbuild2/cc/link-rule.cxx | 58 +++++++++++++++++++++++++++++++++++++++------- libbuild2/cc/link-rule.hxx | 2 +- libbuild2/cc/module.cxx | 1 + libbuild2/cc/pkgconfig.cxx | 36 +++++++++++++++++++++------- libbuild2/cc/target.cxx | 14 ++++++----- libbuild2/cc/target.hxx | 3 ++- 6 files changed, 88 insertions(+), 26 deletions(-) (limited to 'libbuild2/cc') diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 6047206..b11ee42 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -17,6 +17,7 @@ #include #include +#include // lib_rule::build_members() #include #include @@ -125,7 +126,7 @@ namespace build2 // doing here since if there is no existing target, then there can // be no prerequisites. // - // Note, however, that we cannot linkup a prerequisite target + // Note, however, that we cannot link-up a prerequisite target // member to its group since we are not matching this target. As // result we have to do all the steps except for setting t.group // and pass both member and group (we also cannot query t.group @@ -1029,21 +1030,41 @@ namespace build2 // cleanup automagically. The actual generation happens in // perform_update() below. // + // Things are even trickier for the common .pc file: we only want to + // have it in the shared library if we are not installing static + // (see pkgconfig_save() for details). But we can't know it at this + // stage. So what we are going to do is conceptually tie the common + // file to the lib{} group (which does somehow feel correct) by only + // installing it if the lib{} group is installed. Specifically, here + // we will use its bin.lib to decide what will be installed and in + // perform_update() we will confirm that it is actually installed. + // if (ot != otype::e) { - file& pc (add_adhoc_member (t, - (ot == otype::a - ? pca::static_type - : pcs::static_type))); - // Note that here we always use the lib name prefix, even on // Windows with VC. The reason is the user needs a consistent name // across platforms by which they can refer to the library. This // is also the reason why we use the .static and .shared second- // level extensions rather that a./.lib and .so/.dylib/.dll. + + // Note also that the order in which we are adding these members + // is important (see add_addhoc_member() for details). // - if (pc.path ().empty ()) - pc.derive_path (nullptr, (p == nullptr ? "lib" : p), s); + if (ot == otype::a || !lib_rule::build_members (rs).a) + { + auto& pc (add_adhoc_member (t)); + + if (pc.path ().empty ()) + pc.derive_path (nullptr, (p == nullptr ? "lib" : p), s); + } + + auto& pcx (add_adhoc_member (t, + (ot == otype::a + ? pca::static_type + : pcs::static_type))); + + if (pcx.path ().empty ()) + pcx.derive_path (nullptr, (p == nullptr ? "lib" : p), s); } // Add the Windows rpath emulating assembly directory as fsdir{}. @@ -1984,7 +2005,26 @@ namespace build2 // for install, we have no idea where-to things will be installed. // if (for_install && lt.library () && !lt.utility) - pkgconfig_save (a, t, lt.static_library (), binless); + { + bool la (lt.static_library ()); + + pkgconfig_save (a, t, la, false /* common */, binless); + + // Generate the common .pc file if the lib{} rule is matched (see + // apply() for details on this two-stage logic). + // + auto* m (find_adhoc_member (t)); // Will be pca/pcs if not found. + + if (!m->is_a (la ? pca::static_type : pcs::static_type)) + { + if (t.group->matched (a)) + pkgconfig_save (a, t, la, true /* common */, binless); + else + // Mark as non-existent not to confuse the install rule. + // + m->mtime (timestamp_nonexistent); + } + } // If we have no binary to build then we are done. // diff --git a/libbuild2/cc/link-rule.hxx b/libbuild2/cc/link-rule.hxx index 6fa8343..ce514b5 100644 --- a/libbuild2/cc/link-rule.hxx +++ b/libbuild2/cc/link-rule.hxx @@ -178,7 +178,7 @@ namespace build2 // pkg-config's .pc file generation (pkgconfig.cxx). // void - pkgconfig_save (action, const file&, bool, bool) const; + pkgconfig_save (action, const file&, bool, bool, bool) const; private: const string rule_id; diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 70cbc47..d6c337d 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -776,6 +776,7 @@ namespace build2 if (*x_hdr != &h::static_type) insert_hdr (h::static_type); + rs.insert_target_type (); rs.insert_target_type (); rs.insert_target_type (); diff --git a/libbuild2/cc/pkgconfig.cxx b/libbuild2/cc/pkgconfig.cxx index 64dd9ca..f95599f 100644 --- a/libbuild2/cc/pkgconfig.cxx +++ b/libbuild2/cc/pkgconfig.cxx @@ -1301,8 +1301,19 @@ namespace build2 #endif + // If common is true, generate a "best effort" (i.e., not guaranteed to be + // sufficient in all cases) common .pc file by ignoring any static/shared- + // specific poptions and splitting loptions/libs into Libs/Libs.private. + // Note that if both static and shared are being installed, the common + // file must be generated based on the static library to get accurate + // Libs.private. + // void link_rule:: - pkgconfig_save (action a, const file& l, bool la, bool binless) const + pkgconfig_save (action a, + const file& l, + bool la, + bool common, + bool binless) const { tracer trace (x, "pkgconfig_save"); @@ -1311,15 +1322,22 @@ namespace build2 const scope& bs (l.base_scope ()); const scope& rs (*bs.root_scope ()); - auto* t (find_adhoc_member (l)); + auto* t (find_adhoc_member (l, (common ? pc::static_type : + la ? pca::static_type : + /* */ pcs::static_type))); assert (t != nullptr); + // This is the lib{} group if we are generating the common file and the + // target itself otherwise. + // + const file& g (common ? l.group->as () : l); + // By default we assume things go into install.{include, lib}. // using install::resolve_dir; - dir_path idir (resolve_dir (l, cast (l["install.include"]))); - dir_path ldir (resolve_dir (l, cast (l["install.lib"]))); + dir_path idir (resolve_dir (g, cast (g["install.include"]))); + dir_path ldir (resolve_dir (g, cast (g["install.lib"]))); const path& p (t->path ()); @@ -1363,9 +1381,9 @@ namespace build2 os << "URL: " << *u << endl; } - auto save_poptions = [&l, &os] (const variable& var) + auto save_poptions = [&g, &os] (const variable& var) { - if (const strings* v = cast_null (l[var])) + if (const strings* v = cast_null (g[var])) { for (auto i (v->begin ()); i != v->end (); ++i) { @@ -1469,11 +1487,11 @@ namespace build2 // we still want to sort things out into Libs/Libs.private. This is // necessary to distinguish between interface and implementation // dependencies if we don't have the shared variant (see the load - // logic for details). + // logic for details). And also for the common .pc file, naturally. // //@@ TODO: would be nice to weed out duplicates. But is it always // safe? Think linking archives: will have to keep duplicates in - // the second position, not first. Gets even trickier with + // the second position, not first. Gets even trickier with the // Libs.private split. // { @@ -1556,7 +1574,7 @@ namespace build2 }; vector modules; - for (const target* pt: l.prerequisite_targets[a]) + for (const target* pt: g.prerequisite_targets[a]) { // @@ UTL: we need to (recursively) see through libu*{} (and // also in search_modules()). diff --git a/libbuild2/cc/target.cxx b/libbuild2/cc/target.cxx index a962575..b17e1ef 100644 --- a/libbuild2/cc/target.cxx +++ b/libbuild2/cc/target.cxx @@ -54,16 +54,18 @@ namespace build2 false }; + extern const char pc_ext[] = "pc"; // VC14 rejects constexpr. + const target_type pc::static_type { "pc", &file::static_type, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, + &target_factory, + &target_extension_fix, + nullptr, /* default_extension */ + &target_pattern_fix, + &target_print_0_ext_verb, // Fixed extension, no use printing. + &file_search, false }; diff --git a/libbuild2/cc/target.hxx b/libbuild2/cc/target.hxx index 42d15c8..7067421 100644 --- a/libbuild2/cc/target.hxx +++ b/libbuild2/cc/target.hxx @@ -61,13 +61,14 @@ namespace build2 // pkg-config file targets. // - class LIBBUILD2_CC_SYMEXPORT pc: public file + class LIBBUILD2_CC_SYMEXPORT pc: public file // .pc (common) { public: using file::file; public: static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_CC_SYMEXPORT pca: public pc // .static.pc -- cgit v1.1