diff options
Diffstat (limited to 'libbuild2/bin')
-rw-r--r-- | libbuild2/bin/def-rule.cxx | 239 | ||||
-rw-r--r-- | libbuild2/bin/def-rule.hxx | 2 | ||||
-rw-r--r-- | libbuild2/bin/guess.cxx | 73 | ||||
-rw-r--r-- | libbuild2/bin/guess.hxx | 8 | ||||
-rw-r--r-- | libbuild2/bin/init.cxx | 201 | ||||
-rw-r--r-- | libbuild2/bin/init.hxx | 6 | ||||
-rw-r--r-- | libbuild2/bin/rule.cxx | 149 | ||||
-rw-r--r-- | libbuild2/bin/rule.hxx | 31 | ||||
-rw-r--r-- | libbuild2/bin/target.cxx | 61 | ||||
-rw-r--r-- | libbuild2/bin/target.hxx | 208 | ||||
-rw-r--r-- | libbuild2/bin/utility.cxx | 9 |
11 files changed, 740 insertions, 247 deletions
diff --git a/libbuild2/bin/def-rule.cxx b/libbuild2/bin/def-rule.cxx index ab31fde..143cc35 100644 --- a/libbuild2/bin/def-rule.cxx +++ b/libbuild2/bin/def-rule.cxx @@ -7,6 +7,7 @@ #include <libbuild2/scope.hxx> #include <libbuild2/target.hxx> #include <libbuild2/algorithm.hxx> +#include <libbuild2/filesystem.hxx> #include <libbuild2/diagnostics.hxx> #include <libbuild2/bin/target.hxx> @@ -16,17 +17,26 @@ namespace build2 { namespace bin { + // In C global uninitialized data becomes a "common symbol" (an equivalent + // definition compiled as C++ results in a BSS symbol) which allows some + // archaic merging of multiple such definitions during linking (see GNU ld + // --warn-common for background). Note that this merging may happen with + // other data symbol types, not just common. + // struct symbols { set<string> d; // data set<string> r; // read-only data set<string> b; // uninitialized data (BSS) + set<string> c; // common uninitialized data set<string> t; // text (code) }; static void - read_dumpbin (istream& is, symbols& syms) + read_dumpbin (diag_buffer& dbuf, ifdstream& is, symbols& syms) { + // Note: io_error is handled by the caller. + // Lines that describe symbols look like: // // 0 1 2 3 4 5 6 @@ -62,29 +72,29 @@ namespace build2 // B44 00000000 SECT4 notype Static | .rdata$r // AA2 00000000 SECT5 notype Static | .bss // - - // Map of read-only (.rdata, .xdata) and uninitialized (.bss) sections - // to their types (R and B, respectively). If a section is not found in - // this map, then it's assumed to be normal data (.data). + // Note that an UNDEF data symbol with non-zero OFFSET is a "common + // symbol", equivalent to the nm `C` type. // - map<string, char> sections; - - string l; - while (!eof (getline (is, l))) + // We keep a map of read-only (.rdata, .xdata) and uninitialized (.bss) + // sections to their types (R and B, respectively). If a section is not + // found in this map, then it's assumed to be normal data (.data). + // + auto parse_line = [&syms, + secs = map<string, char> ()] (const string& l) mutable { size_t b (0), e (0), n; // IDX (note that it can be more than 3 characters). // if (next_word (l, b, e) == 0) - continue; + return; // OFFSET (always 8 characters). // n = next_word (l, b, e); if (n != 8) - continue; + return; string off (l, b, n); @@ -92,8 +102,8 @@ namespace build2 // n = next_word (l, b, e); - if (n == 0 || l.compare (b, n, "UNDEF") == 0) - continue; + if (n == 0) + return; string sec (l, b, n); @@ -102,23 +112,23 @@ namespace build2 n = next_word (l, b, e); if (l.compare (b, n, "notype") != 0) - continue; + return; - bool d; + bool dat; if (l[e] == ' ' && l[e + 1] == '(' && l[e + 2] == ')') { e += 3; - d = false; + dat = false; } else - d = true; + dat = true; // VISIBILITY // n = next_word (l, b, e); if (n == 0) - continue; + return; string vis (l, b, n); @@ -127,20 +137,24 @@ namespace build2 n = next_word (l, b, e); if (n != 1 || l[b] != '|') - continue; + return; // SYMNAME // n = next_word (l, b, e); if (n == 0) - continue; + return; string s (l, b, n); // See if this is the section type symbol. // - if (d && off == "00000000" && vis == "Static" && s[0] == '.') + if (dat && + off == "00000000" && + sec != "UNDEF" && + vis == "Static" && + s[0] == '.') { auto cmp = [&s] (const char* n, size_t l) { @@ -148,43 +162,88 @@ namespace build2 }; if (cmp (".rdata", 6) || - cmp (".xdata", 6)) sections.emplace (move (sec), 'R'); - else if (cmp (".bss", 4)) sections.emplace (move (sec), 'B'); + cmp (".xdata", 6)) secs.emplace (move (sec), 'R'); + else if (cmp (".bss", 4)) secs.emplace (move (sec), 'B'); - continue; + return; } // We can only export extern symbols. // if (vis != "External") - continue; + return; - if (d) + if (dat) { - auto i (sections.find (sec)); - switch (i == sections.end () ? 'D' : i->second) + if (sec != "UNDEF") { - case 'D': syms.d.insert (move (s)); break; - case 'R': syms.r.insert (move (s)); break; - case 'B': syms.b.insert (move (s)); break; + auto i (secs.find (sec)); + switch (i == secs.end () ? 'D' : i->second) + { + case 'D': syms.d.insert (move (s)); break; + case 'R': syms.r.insert (move (s)); break; + case 'B': syms.b.insert (move (s)); break; + } + } + else + { + if (off != "00000000") + syms.c.insert (move (s)); } } else - syms.t.insert (move (s)); + { + if (sec != "UNDEF") + syms.t.insert (move (s)); + } + }; + + // Read until we reach EOF on all streams. + // + // Note that if dbuf is not opened, then we automatically get an + // inactive nullfd entry. + // + fdselect_set fds {is.fd (), dbuf.is.fd ()}; + fdselect_state& ist (fds[0]); + fdselect_state& dst (fds[1]); + + for (string l; ist.fd != nullfd || dst.fd != nullfd; ) + { + if (ist.fd != nullfd && getline_non_blocking (is, l)) + { + if (eof (is)) + ist.fd = nullfd; + else + { + parse_line (l); + l.clear (); + } + + continue; + } + + ifdselect (fds); + + if (dst.ready) + { + if (!dbuf.read ()) + dst.fd = nullfd; + } } } static void - read_posix_nm (istream& is, symbols& syms) + read_posix_nm (diag_buffer& dbuf, ifdstream& is, symbols& syms) { + // Note: io_error is handled by the caller. + // Lines that describe symbols look like: // // <NAME> <TYPE> <VALUE> <SIZE> // // The types that we are interested in are T, D, R, and B. // - string l; - while (!eof (getline (is, l))) + auto parse_line = [&syms] (const string& l) { size_t b (0), e (0), n; @@ -193,7 +252,7 @@ namespace build2 n = next_word (l, b, e); if (n == 0) - continue; + return; string s (l, b, n); @@ -202,15 +261,50 @@ namespace build2 n = next_word (l, b, e); if (n != 1) - continue; + return; switch (l[b]) { case 'D': syms.d.insert (move (s)); break; case 'R': syms.r.insert (move (s)); break; case 'B': syms.b.insert (move (s)); break; + case 'c': + case 'C': syms.c.insert (move (s)); break; case 'T': syms.t.insert (move (s)); break; } + }; + + // Read until we reach EOF on all streams. + // + // Note that if dbuf is not opened, then we automatically get an + // inactive nullfd entry. + // + fdselect_set fds {is.fd (), dbuf.is.fd ()}; + fdselect_state& ist (fds[0]); + fdselect_state& dst (fds[1]); + + for (string l; ist.fd != nullfd || dst.fd != nullfd; ) + { + if (ist.fd != nullfd && getline_non_blocking (is, l)) + { + if (eof (is)) + ist.fd = nullfd; + else + { + parse_line (l); + l.clear (); + } + + continue; + } + + ifdselect (fds); + + if (dst.ready) + { + if (!dbuf.read ()) + dst.fd = nullfd; + } } } @@ -311,11 +405,20 @@ namespace build2 if (const char* v = filter (s)) os << " " << v << " DATA\n"; + // For common symbols, only write extern C. + // + for (const string& s: syms.c) + if (extern_c (s)) + if (const char* v = filter (s)) + os << " " << v << " DATA\n"; + // Read-only data contains an especially large number of various // special symbols. Instead of trying to filter them out case by case, // we will try to recognize C/C++ identifiers plus the special symbols // that we need to export (e.g., vtable). // + // Note that it looks like rdata should not be declared DATA. It is + // known to break ??_7 (vtable) exporting (see GH issue 315). // for (const string& s: syms.r) { @@ -323,7 +426,7 @@ namespace build2 (s[0] == '?' && s[1] != '?') || // C++ s.compare (0, 4, "??_7") == 0) // vtable { - os << " " << strip (s) << " DATA\n"; + os << " " << strip (s) << '\n'; } } } @@ -386,11 +489,21 @@ namespace build2 if (const char* v = filter (s)) os << " " << v << " DATA\n"; + for (const string& s: syms.c) + if (const char* v = filter (s)) + os << " " << v << " DATA\n"; + // Read-only data contains an especially large number of various // special symbols. Instead of trying to filter them out case by case, // we will try to recognize C/C++ identifiers plus the special symbols // that we need to export (e.g., vtable and typeinfo). // + // For the description of GNU binutils .def format, see: + // + // https://sourceware.org/binutils/docs/binutils/def-file-format.html + // + // @@ Maybe CONSTANT is more appropriate than DATA? + // for (const string& s: syms.r) { if (s.find_first_of (".") != string::npos) // Special (.refptr.*) @@ -411,7 +524,7 @@ namespace build2 } bool def_rule:: - match (action a, target& t, const string&) const + match (action a, target& t) const { tracer trace ("bin::def_rule::match"); @@ -615,8 +728,12 @@ namespace build2 const char*& arg (*(args.end () - 2)); + // We could print the prerequisite if it's a single obj{}/libu{} (with + // the latter being the common case). But it doesn't feel like that's + // worth the variability and the associated possibility of confusion. + // if (verb == 1) - text << "def " << t; + print_diag ("def", t); // Extract symbols from each object file. // @@ -636,22 +753,37 @@ namespace build2 // Both dumpbin.exe and nm send their output to stdout. While nm sends // diagnostics to stderr, dumpbin sends it to stdout together with the - // output. + // output. To keep things uniform we will buffer stderr in both cases. // - process pr (run_start (nm, - args, - 0 /* stdin */, - -1 /* stdout */)); + process pr ( + run_start (nm, + args, + 0 /* stdin */, + -1 /* stdout */, + diag_buffer::pipe (ctx) /* stderr */)); + + // Note that while we read both streams until eof in the normal + // circumstances, we cannot use fdstream_mode::skip for the exception + // case on both of them: we may end up being blocked trying to read + // one stream while the process may be blocked writing to the other. + // So in case of an exception we only skip the diagnostics and close + // stdout hard. The latter should happen first so the order of the + // dbuf/is variables is important. + // + diag_buffer dbuf (ctx, args[0], pr, (fdstream_mode::non_blocking | + fdstream_mode::skip)); + bool io (false); try { - ifdstream is ( - move (pr.in_ofd), fdstream_mode::skip, ifdstream::badbit); + ifdstream is (move (pr.in_ofd), + fdstream_mode::non_blocking, + ifdstream::badbit); if (lid == "msvc" || nid == "msvc") - read_dumpbin (is, syms); + read_dumpbin (dbuf, is, syms); else - read_posix_nm (is, syms); + read_posix_nm (dbuf, is, syms); is.close (); } @@ -663,16 +795,17 @@ namespace build2 io = true; } - if (!run_finish_code (args.data (), pr) || io) + if (!run_finish_code (dbuf, args, pr, 1 /* verbosity */) || io) fail << "unable to extract symbols from " << arg; } - /* +#if 0 for (const string& s: syms.d) text << "D " << s; for (const string& s: syms.r) text << "R " << s; for (const string& s: syms.b) text << "B " << s; + for (const string& s: syms.c) text << "C " << s; for (const string& s: syms.t) text << "T " << s; - */ +#endif if (verb >= 3) text << "cat >" << tp; @@ -712,6 +845,6 @@ namespace build2 return target_state::changed; } - const string def_rule::rule_id_ {"bin.def 1"}; + const string def_rule::rule_id_ {"bin.def 2"}; } } diff --git a/libbuild2/bin/def-rule.hxx b/libbuild2/bin/def-rule.hxx index 32423a0..acdf841 100644 --- a/libbuild2/bin/def-rule.hxx +++ b/libbuild2/bin/def-rule.hxx @@ -24,7 +24,7 @@ namespace build2 def_rule () {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; diff --git a/libbuild2/bin/guess.cxx b/libbuild2/bin/guess.cxx index 905bd0a..e9759b8 100644 --- a/libbuild2/bin/guess.cxx +++ b/libbuild2/bin/guess.cxx @@ -34,9 +34,12 @@ namespace build2 // Return 0-version if the version is invalid. // static inline semantic_version - parse_version (const string& s, size_t p = 0, const char* bs = ".-+~ ") + parse_version (const string& s, size_t p = 0, + semantic_version::flags f = semantic_version::allow_omit_patch | + semantic_version::allow_build, + const char* bs = ".-+~ ") { - optional<semantic_version> v (parse_semantic_version (s, p, bs)); + optional<semantic_version> v (parse_semantic_version (s, p, f, bs)); return v ? *v : semantic_version (); } @@ -89,7 +92,7 @@ namespace build2 static global_cache<ar_info> ar_cache; const ar_info& - guess_ar (const path& ar, const path* rl, const char* paths) + guess_ar (context& ctx, const path& ar, const path* rl, const char* paths) { tracer trace ("bin::guess_ar"); @@ -177,7 +180,11 @@ namespace build2 // "LLVM version 3.5.2" // "LLVM version 5.0.0" // - if (l.compare (0, 13, "LLVM version ") == 0) + // But it can also be prefixed with some stuff, for example: + // + // "Debian LLVM version 14.0.6" + // + if (l.find ("LLVM version ") != string::npos) { semantic_version v (parse_version (l, l.rfind (' ') + 1)); return guess_result ("llvm", move (l), move (v)); @@ -227,7 +234,11 @@ namespace build2 // (yes, it goes to stdout) but that seems harmless. // sha256 cs; - arr = run<guess_result> (3, are, "--version", f, false, false, &cs); + arr = run<guess_result> (ctx, + 3, + are, "--version", + f, + false , false, &cs); if (!arr.empty ()) arr.checksum = cs.string (); @@ -247,10 +258,10 @@ namespace build2 : guess_result (); }; - // Redirect STDERR to STDOUT and ignore exit status. + // Redirect stderr to stdout and ignore exit status. // sha256 cs; - arr = run<guess_result> (3, are, f, false, true, &cs); + arr = run<guess_result> (ctx, 3, are, f, false, true, &cs); if (!arr.empty ()) { @@ -280,7 +291,7 @@ namespace build2 // "LLVM version ". // - if (l.compare (0, 13, "LLVM version ") == 0) + if (l.find ("LLVM version ") != string::npos) return guess_result ("llvm", move (l), semantic_version ()); // On FreeBSD we get "ranlib" rather than "BSD ranlib" for some @@ -293,7 +304,11 @@ namespace build2 }; sha256 cs; - rlr = run<guess_result> (3, rle, "--version", f, false, false, &cs); + rlr = run<guess_result> (ctx, + 3, + rle, "--version", + f, + false, false, &cs); if (!rlr.empty ()) rlr.checksum = cs.string (); @@ -310,10 +325,10 @@ namespace build2 : guess_result (); }; - // Redirect STDERR to STDOUT and ignore exit status. + // Redirect stderr to stdout and ignore exit status. // sha256 cs; - rlr = run<guess_result> (3, rle, f, false, true, &cs); + rlr = run<guess_result> (ctx, 3, rle, f, false, true, &cs); if (!rlr.empty ()) { @@ -378,7 +393,7 @@ namespace build2 static global_cache<ld_info> ld_cache; const ld_info& - guess_ld (const path& ld, const char* paths) + guess_ld (context& ctx, const path& ld, const char* paths) { tracer trace ("bin::guess_ld"); @@ -437,17 +452,22 @@ namespace build2 string id; optional<semantic_version> ver; + size_t p; + // Microsoft link.exe output starts with "Microsoft (R) ". // if (l.compare (0, 14, "Microsoft (R) ") == 0) { id = "msvc"; } - // LLD prints a line in the form "LLD X.Y.Z ...". + // LLD prints a line in the form "LLD X.Y.Z ...". But it can also + // be prefixed with some stuff, for example: // - else if (l.compare (0, 4, "LLD ") == 0) + // Debian LLD 14.0.6 (compatible with GNU linkers) + // + else if ((p = l.find ("LLD ")) != string::npos) { - ver = parse_version (l, 4); + ver = parse_version (l, p + 4); // The only way to distinguish between various LLD drivers is via // their name. Handle potential prefixes (say a target) and @@ -485,12 +505,12 @@ namespace build2 : guess_result (move (id), move (l), move (ver))); }; - // Redirect STDERR to STDOUT and ignore exit status. Note that in case + // Redirect stderr to stdout and ignore exit status. Note that in case // of link.exe we will hash the diagnostics (yes, it goes to stdout) // but that seems harmless. // sha256 cs; - r = run<guess_result> (3, env, "--version", f, false, true, &cs); + r = run<guess_result> (ctx, 3, env, "--version", f, false, true, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -521,7 +541,7 @@ namespace build2 }; sha256 cs; - r = run<guess_result> (3, env, "-v", f, false, false, &cs); + r = run<guess_result> (ctx, 3, env, "-v", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -548,7 +568,7 @@ namespace build2 // option. // sha256 cs; - r = run<guess_result> (3, env, "-version", f, false, false, &cs); + r = run<guess_result> (ctx, 3, env, "-version", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -586,7 +606,7 @@ namespace build2 static global_cache<rc_info> rc_cache; const rc_info& - guess_rc (const path& rc, const char* paths) + guess_rc (context& ctx, const path& rc, const char* paths) { tracer trace ("bin::guess_rc"); @@ -642,7 +662,7 @@ namespace build2 // option. // sha256 cs; - r = run<guess_result> (3, env, "--version", f, false, false, &cs); + r = run<guess_result> (ctx, 3, env, "--version", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -675,7 +695,7 @@ namespace build2 }; sha256 cs; - r = run<guess_result> (3, env, "/?", f, false, false, &cs); + r = run<guess_result> (ctx, 3, env, "/?", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -703,7 +723,7 @@ namespace build2 static global_cache<nm_info> nm_cache; const nm_info& - guess_nm (const path& nm, const char* paths) + guess_nm (context& ctx, const path& nm, const char* paths) { tracer trace ("bin::guess_nm"); @@ -764,7 +784,10 @@ namespace build2 // LLVM nm --version output has a line that starts with // "LLVM version" followed by a version. // - if (l.compare (0, 13, "LLVM version ") == 0) + // But let's assume it can be prefixed with some stuff like the rest + // of the LLVM tools (see above). + // + if (l.find ("LLVM version ") != string::npos) return guess_result ("llvm", move (l), semantic_version ()); if (l.compare (0, 14, "Microsoft (R) ") == 0) @@ -784,7 +807,7 @@ namespace build2 // option. // sha256 cs; - r = run<guess_result> (3, env, "--version", f, false, false, &cs); + r = run<guess_result> (ctx, 3, env, "--version", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); diff --git a/libbuild2/bin/guess.hxx b/libbuild2/bin/guess.hxx index 52c0e1b..7dc7b33 100644 --- a/libbuild2/bin/guess.hxx +++ b/libbuild2/bin/guess.hxx @@ -54,7 +54,7 @@ namespace build2 // attemplated and the returned ranlib_* members will be left empty. // const ar_info& - guess_ar (const path& ar, const path* ranlib, const char* paths); + guess_ar (context&, const path& ar, const path* ranlib, const char* paths); // ld information. // @@ -100,7 +100,7 @@ namespace build2 }; const ld_info& - guess_ld (const path& ld, const char* paths); + guess_ld (context&, const path& ld, const char* paths); // rc information. // @@ -132,7 +132,7 @@ namespace build2 }; const rc_info& - guess_rc (const path& rc, const char* paths); + guess_rc (context&, const path& rc, const char* paths); // nm information. // @@ -166,7 +166,7 @@ namespace build2 }; const nm_info& - guess_nm (const path& nm, const char* paths); + guess_nm (context&, const path& nm, const char* paths); } } diff --git a/libbuild2/bin/init.cxx b/libbuild2/bin/init.cxx index ab3980a..610082e 100644 --- a/libbuild2/bin/init.cxx +++ b/libbuild2/bin/init.cxx @@ -41,24 +41,30 @@ namespace build2 bool vars_init (scope& rs, - scope&, - const location&, - bool first, + scope& bs, + const location& loc, + bool, bool, module_init_extra&) { tracer trace ("bin::vars_init"); l5 ([&]{trace << "for " << rs;}); - assert (first); + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "bin.vars module must be loaded in project root"; // Enter variables. // + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); + // Target is a string and not target_triplet because it can be // specified by the user. // - auto& vp (rs.var_pool ()); - vp.insert<string> ("config.bin.target"); vp.insert<string> ("config.bin.pattern"); @@ -76,6 +82,9 @@ namespace build2 // example, addition of rpaths for prerequisite libraries (see the cc // module for an example). Default is true. // + // Note also that a rule may need to make rpath relative if + // install.relocatable is true. + // vp.insert<dir_paths> ("config.bin.rpath"); vp.insert<bool> ("config.bin.rpath.auto"); @@ -104,12 +113,12 @@ namespace build2 // Link whole archive. Note: with target visibility. // // The lookup semantics is as follows: we first look for a prerequisite- - // specific value, then for a target-specific value in the library being - // linked, and then for target type/pattern-specific value starting from - // the scope of the target being linked-to. In that final lookup we do - // not look in the target being linked-to itself since that is used to - // indicate how this target should be linked to other targets. For - // example: + // specific value, then for a target-specific value in the prerequisite + // library, and then for target type/pattern-specific value starting + // from the scope of the target being linked. In that final lookup we do + // not look in the target being linked itself since that is used to + // indicate how this target should be used as a prerequisite of other + // targets. For example: // // exe{test}: liba{foo} // liba{foo}: libua{foo1 foo2} @@ -150,6 +159,68 @@ namespace build2 return true; } + bool + types_init (scope& rs, + scope& bs, + const location& loc, + bool, + bool, + module_init_extra&) + { + tracer trace ("bin::types_init"); + l5 ([&]{trace << "for " << rs;}); + + // We only support root loading (which means there can only be one). + // + if (rs != bs) + fail (loc) << "bin.types module must be loaded in project root"; + + // Register target types. + // + // Note that certain platform-specific and toolchain-specific types are + // registered in bin and bin.ld. + // + // Note also that it would make sense to configure their default + // "installability" here but that requires the knowledge of the platform + // in some cases. So we do it all in bin for now. One way to support + // both use-cases would be to detect if we are loaded after bin.guess + // and then decide whether to do it here or delay to bin. + // + // NOTE: remember to update the documentation if changing anything here! + // + rs.insert_target_type<obj> (); + rs.insert_target_type<obje> (); + rs.insert_target_type<obja> (); + rs.insert_target_type<objs> (); + + rs.insert_target_type<bmi> (); + rs.insert_target_type<bmie> (); + rs.insert_target_type<bmia> (); + rs.insert_target_type<bmis> (); + + rs.insert_target_type<hbmi> (); + rs.insert_target_type<hbmie> (); + rs.insert_target_type<hbmia> (); + rs.insert_target_type<hbmis> (); + + rs.insert_target_type<libul> (); + rs.insert_target_type<libue> (); + rs.insert_target_type<libua> (); + rs.insert_target_type<libus> (); + + rs.insert_target_type<lib> (); + rs.insert_target_type<liba> (); + rs.insert_target_type<libs> (); + + // Register the def{} target type. Note that we do it here since it is + // input and can be specified unconditionally (i.e., not only when + // building for Windows). + // + rs.insert_target_type<def> (); + + return true; + } + void functions (function_map&); // functions.cxx @@ -195,6 +266,8 @@ namespace build2 // const target_triplet* tgt (nullptr); { + // Note: go straight for the public variable pool. + // const variable& var (ctx.var_pool["config.bin.target"]); // We first see if the value was specified via the configuration @@ -231,9 +304,9 @@ namespace build2 // if (!hint && config_sub) { - s = run<string> (3, - *config_sub, - s.c_str (), + s = run<string> (ctx, + 3, + *config_sub, s.c_str (), [] (string& l, bool) {return move (l);}); l5 ([&]{trace << "config.sub target: '" << s << "'";}); } @@ -272,6 +345,8 @@ namespace build2 // const string* pat (nullptr); { + // Note: go straight for the public variable pool. + // const variable& var (ctx.var_pool["config.bin.pattern"]); // We first see if the value was specified via the configuration @@ -440,53 +515,22 @@ namespace build2 tracer trace ("bin::init"); l5 ([&]{trace << "for " << bs;}); - // Load bin.config. + // Load bin.{config,types}. // load_module (rs, rs, "bin.config", loc, extra.hints); + load_module (rs, rs, "bin.types", loc); // Cache some config values we will be needing below. // const target_triplet& tgt (cast<target_triplet> (rs["bin.target"])); - // Register target types and configure their default "installability". + // Configure target type default "installability". Also register + // additional platform-specific types. // bool install_loaded (cast_false<bool> (rs["install.loaded"])); { using namespace install; - if (first) - { - rs.insert_target_type<obj> (); - rs.insert_target_type<obje> (); - rs.insert_target_type<obja> (); - rs.insert_target_type<objs> (); - - rs.insert_target_type<bmi> (); - rs.insert_target_type<bmie> (); - rs.insert_target_type<bmia> (); - rs.insert_target_type<bmis> (); - - rs.insert_target_type<hbmi> (); - rs.insert_target_type<hbmie> (); - rs.insert_target_type<hbmia> (); - rs.insert_target_type<hbmis> (); - - rs.insert_target_type<libul> (); - rs.insert_target_type<libue> (); - rs.insert_target_type<libua> (); - rs.insert_target_type<libus> (); - - rs.insert_target_type<lib> (); - rs.insert_target_type<liba> (); - rs.insert_target_type<libs> (); - - // Register the def{} target type. Note that we do it here since it - // is input and can be specified unconditionally (i.e., not only - // when building for Windows). - // - rs.insert_target_type<def> (); - } - // Note: libu*{} members are not installable. // if (install_loaded) @@ -536,6 +580,8 @@ namespace build2 if (tgt.cpu == "wasm32" || tgt.cpu == "wasm64") { + // @@ TODO: shouldn't this be wrapped in if(first) somehow? + const target_type& wasm ( rs.derive_target_type( target_type { @@ -546,8 +592,8 @@ namespace build2 nullptr, /* default_extension */ &target_pattern_fix<wasm_ext>, &target_print_0_ext_verb, // Fixed extension, no use printing. - &file_search, - false /* see_through */})); + &target_search, // Note: don't look for an existing file. + target_type::flag::none})); if (install_loaded) { @@ -578,8 +624,6 @@ namespace build2 // Similar to alias. // - - //@@ outer r.insert<lib> (perform_id, 0, "bin.lib", lib_); r.insert<lib> (configure_id, 0, "bin.lib", lib_); @@ -600,6 +644,18 @@ namespace build2 if (rs.find_module ("dist")) { + // Note that without custom dist rules in setups along the follwing + // lines the source file will be unreachable by dist: + // + // lib{foo}: obj{foo} + // obja{foo}: cxx{foo} + // objs{foo}: cxx{foo} + // + r.insert<obj> (dist_id, 0, "bin.obj", obj_); + r.insert<bmi> (dist_id, 0, "bin.bmi", obj_); + r.insert<hbmi> (dist_id, 0, "bin.hbmi", obj_); + r.insert<libul> (dist_id, 0, "bin.libul", libul_); + r.insert<lib> (dist_id, 0, "bin.lib", lib_); } } @@ -626,7 +682,10 @@ namespace build2 // if (first) { - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); vp.insert<path> ("config.bin.ar"); vp.insert<path> ("config.bin.ranlib"); @@ -684,7 +743,7 @@ namespace build2 nullptr, config::save_default_commented))); - const ar_info& ari (guess_ar (ar, ranlib, pat.paths)); + const ar_info& ari (guess_ar (rs.ctx, ar, ranlib, pat.paths)); // If this is a configuration with new values, then print the report // at verbosity level 2 and up (-v). @@ -800,7 +859,10 @@ namespace build2 // if (first) { - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); vp.insert<path> ("config.bin.ld"); } @@ -832,7 +894,7 @@ namespace build2 path (apply_pattern (ld_d, pat.pattern)), config::save_default_commented))); - const ld_info& ldi (guess_ld (ld, pat.paths)); + const ld_info& ldi (guess_ld (rs.ctx, ld, pat.paths)); // If this is a configuration with new values, then print the report // at verbosity level 2 and up (-v). @@ -916,6 +978,8 @@ namespace build2 if (lid == "msvc") { + // @@ TODO: shouldn't this be wrapped in if(first) somehow? + const target_type& pdb ( rs.derive_target_type( target_type { @@ -926,8 +990,8 @@ namespace build2 nullptr, /* default_extension */ &target_pattern_fix<pdb_ext>, &target_print_0_ext_verb, // Fixed extension, no use printing. - &file_search, - false /* see_through */})); + &target_search, // Note: don't look for an existing file. + target_type::flag::none})); if (cast_false<bool> (rs["install.loaded"])) { @@ -958,7 +1022,10 @@ namespace build2 // if (first) { - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); vp.insert<path> ("config.bin.rc"); } @@ -990,7 +1057,7 @@ namespace build2 path (apply_pattern (rc_d, pat.pattern)), config::save_default_commented))); - const rc_info& rci (guess_rc (rc, pat.paths)); + const rc_info& rci (guess_rc (rs.ctx, rc, pat.paths)); // If this is a configuration with new values, then print the report // at verbosity level 2 and up (-v). @@ -1057,7 +1124,10 @@ namespace build2 // if (first) { - auto& vp (rs.var_pool ()); + // All the variables we enter are qualified so go straight for the + // public variable pool. + // + auto& vp (rs.var_pool (true /* public */)); vp.insert<path> ("config.bin.nm"); } @@ -1099,7 +1169,7 @@ namespace build2 path (apply_pattern (nm_d, pat.pattern)), config::save_default_commented))); - const nm_info& nmi (guess_nm (nm, pat.paths)); + const nm_info& nmi (guess_nm (rs.ctx, nm, pat.paths)); // If this is a configuration with new values, then print the report // at verbosity level 2 and up (-v). @@ -1184,8 +1254,8 @@ namespace build2 // changing anything here. {"bin.vars", nullptr, vars_init}, + {"bin.types", nullptr, types_init}, {"bin.config", nullptr, config_init}, - {"bin", nullptr, init}, {"bin.ar.config", nullptr, ar_config_init}, {"bin.ar", nullptr, ar_init}, {"bin.ld.config", nullptr, ld_config_init}, @@ -1195,6 +1265,7 @@ namespace build2 {"bin.nm.config", nullptr, nm_config_init}, {"bin.nm", nullptr, nm_init}, {"bin.def", nullptr, def_init}, + {"bin", nullptr, init}, {nullptr, nullptr, nullptr} }; diff --git a/libbuild2/bin/init.hxx b/libbuild2/bin/init.hxx index 4eb0f10..b163bf5 100644 --- a/libbuild2/bin/init.hxx +++ b/libbuild2/bin/init.hxx @@ -20,9 +20,11 @@ namespace build2 // Submodules: // // `bin.vars` -- registers some variables. + // `bin.types` -- registers target types. // `bin.config` -- loads bin.vars and sets some variables. - // `bin` -- loads bin.config and registers target types and - // rules. + // `bin` -- loads bin.{types,config} and registers rules and + // functions. + // // `bin.ar.config` -- loads bin.config and registers/sets more variables. // `bin.ar` -- loads bin and bin.ar.config. // diff --git a/libbuild2/bin/rule.cxx b/libbuild2/bin/rule.cxx index 021a768..c7147bf 100644 --- a/libbuild2/bin/rule.cxx +++ b/libbuild2/bin/rule.cxx @@ -17,12 +17,30 @@ namespace build2 { namespace bin { + // Search for an existing (declared real) member and match it if found. + // + static void + dist_match (action a, target& t, const target_type& tt) + { + if (const target* m = search_existing (t.ctx, tt, t.dir, t.out, t.name)) + { + // Only a real target declaration can have prerequisites (which is + // the reason we are doing this). + // + if (m->decl == target_decl::real) + match_sync (a, *m); + } + } + // obj_rule // bool obj_rule:: - match (action a, target& t, const string&) const + match (action a, target& t) const { - const char* n (t.dynamic_type ().name); // Ignore derived type. + if (a.meta_operation () == dist_id) + return true; + + const char* n (t.dynamic_type->name); // Ignore derived type. fail << diag_doing (a, t) << " target group" << info << "explicitly select " << n << "e{}, " << n << "a{}, or " @@ -30,27 +48,142 @@ namespace build2 } recipe obj_rule:: - apply (action, target&) const {return empty_recipe;} + apply (action a, target& t) const + { + // We only get here for dist. + // + const target_type* ett (nullptr); + const target_type* att (nullptr); + const target_type* stt (nullptr); + + if (t.is_a<obj> ()) + { + ett = &obje::static_type; + att = &obja::static_type; + stt = &objs::static_type; + } + else if (t.is_a<bmi> ()) + { + ett = &bmie::static_type; + att = &bmia::static_type; + stt = &bmis::static_type; + } + else if (t.is_a<hbmi> ()) + { + ett = &hbmie::static_type; + att = &hbmia::static_type; + stt = &hbmis::static_type; + } + else + assert (false); + + dist_match (a, t, *ett); + dist_match (a, t, *att); + dist_match (a, t, *stt); + + // Delegate to the default dist rule to match prerequisites. + // + return dist::rule::apply (a, t); + } // libul_rule // bool libul_rule:: - match (action a, target& t, const string&) const + match (action, target&) const { - fail << diag_doing (a, t) << " target group" << - info << "explicitly select libua{} or libus{} member" << endf; + return true; } recipe libul_rule:: - apply (action, target&) const {return empty_recipe;} + apply (action a, target& t) const + { + if (a.meta_operation () == dist_id) + { + dist_match (a, t, libua::static_type); + dist_match (a, t, libus::static_type); + + // Delegate to the default dist rule to match prerequisites. + // + return dist::rule::apply (a, t); + } + + // Pick one of the members. First looking for the one already matched. + // + const target* m (nullptr); + + const libus* ls (nullptr); + { + ls = search_existing<libus> (t.ctx, t.dir, t.out, t.name); + + if (ls != nullptr && ls->matched (a)) + m = ls; + } + + const libua* la (nullptr); + if (m == nullptr) + { + la = search_existing<libua> (t.ctx, t.dir, t.out, t.name); + + if (la != nullptr && la->matched (a)) + m = la; + } + + if (m == nullptr) + { + const scope& bs (t.base_scope ()); + + lmembers lm (link_members (*bs.root_scope ())); + + if (lm.s && lm.a) + { + // Use the bin.exe.lib order as a heuristics to pick the library + // (i.e., the most likely utility library to be built is the one + // most likely to be linked). + // + lorder lo (link_order (bs, otype::e)); + + (lo == lorder::s_a || lo == lorder::s ? lm.a : lm.s) = false; + } + + if (lm.s) + m = ls != nullptr ? ls : &search<libus> (t, t.dir, t.out, t.name); + else + m = la != nullptr ? la : &search<libua> (t, t.dir, t.out, t.name); + } + + // Save the member we picked in case others (e.g., $x.lib_poptions()) + // need this information. + // + t.prerequisite_targets[a].push_back (m); + + if (match_sync (a, *m, unmatch::safe).first) + return noop_recipe; + + return [] (action a, const target& t) + { + const target* m (t.prerequisite_targets[a].back ()); + + // For update always return unchanged so we are consistent whether we + // managed to unmatch or now. Note that for clean we may get postponed + // so let's return the actual target state. + // + target_state r (execute_sync (a, *m)); + return a == perform_update_id ? target_state::unchanged : r; + }; + } // lib_rule // // The whole logic is pretty much as if we had our two group members as // our prerequisites. // + // Note also that unlike the obj and libul rules above, we don't need to + // delegate to the default dist rule since any group prerequisites will be + // matched by one of the members (the key difference here is that unlike + // those rules, we insert and match members unconditionally). + // bool lib_rule:: - match (action a, target& xt, const string&) const + match (action a, target& xt) const { lib& t (xt.as<lib> ()); diff --git a/libbuild2/bin/rule.hxx b/libbuild2/bin/rule.hxx index ffb975d..9dd1d14 100644 --- a/libbuild2/bin/rule.hxx +++ b/libbuild2/bin/rule.hxx @@ -9,6 +9,8 @@ #include <libbuild2/rule.hxx> +#include <libbuild2/dist/rule.hxx> + #include <libbuild2/bin/export.hxx> namespace build2 @@ -18,28 +20,41 @@ namespace build2 // "Fail rule" for obj{} and [h]bmi{} that issues diagnostics if someone // tries to build these groups directly. // - class obj_rule: public simple_rule + // Note that for dist it acts as a pass-through to all existing (declared) + // members. + // + class obj_rule: public dist::rule { public: obj_rule () {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; }; - // "Fail rule" for libul{} that issues diagnostics if someone tries to - // build this group directly. + // This rule picks, matches, and unmatches (if possible) a member for the + // purpose of making its metadata (for example, library's poptions, if + // it's one of the cc libraries) available. + // + // The underlying idea here is that someone else (e.g., cc::link_rule) + // makes a more informed choice and we piggy back on that decision, + // falling back to making our own based on bin.lib and bin.exe.lib. Note + // that for update this rule always returns target_state::unchanged. // - class libul_rule: public simple_rule + // Note also that for dist it acts as a pass-through to all existing + // (declared) members. + // + class libul_rule: public dist::rule { public: + explicit libul_rule () {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; @@ -47,13 +62,15 @@ namespace build2 // Pass-through to group members rule, similar to alias. // + // Note that for dist it always passes to both members. + // class LIBBUILD2_BIN_SYMEXPORT lib_rule: public simple_rule { public: lib_rule () {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; diff --git a/libbuild2/bin/target.cxx b/libbuild2/bin/target.cxx index bf701c9..7e4875a 100644 --- a/libbuild2/bin/target.cxx +++ b/libbuild2/bin/target.cxx @@ -21,7 +21,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none }; const target_type bmix::static_type @@ -34,7 +34,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none }; const target_type hbmix::static_type @@ -47,7 +47,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none }; const target_type libx::static_type @@ -60,7 +60,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::member_hint // Use untyped hint for group members. }; const target_type libux::static_type @@ -73,7 +73,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none }; // Note that we link groups during the load phase since this is often @@ -108,7 +108,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type bmie::static_type @@ -121,7 +121,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type hbmie::static_type @@ -134,7 +134,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type obja::static_type @@ -147,7 +147,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type bmia::static_type @@ -160,7 +160,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type hbmia::static_type @@ -173,7 +173,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type objs::static_type @@ -186,7 +186,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type bmis::static_type @@ -199,7 +199,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type hbmis::static_type @@ -212,7 +212,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type libue::static_type @@ -225,7 +225,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type libua::static_type @@ -238,7 +238,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; const target_type libus::static_type @@ -251,7 +251,7 @@ namespace build2 &target_pattern_var<nullptr>, nullptr, &target_search, // Note: not _file(); don't look for an existing file. - false + target_type::flag::none }; // obj{}, [h]bmi{}, and libu{} group factory. @@ -292,7 +292,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::member_hint // Use untyped hint for group members. }; const target_type bmi::static_type @@ -305,7 +305,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::member_hint // Use untyped hint for group members. }; const target_type hbmi::static_type @@ -318,7 +318,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::member_hint // Use untyped hint for group members. }; // The same as g_factory() but without E. @@ -352,7 +352,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::member_hint // Use untyped hint for group members. }; // What extensions should we use? At the outset, this is platform- @@ -374,8 +374,8 @@ namespace build2 &target_extension_var<nullptr>, &target_pattern_var<nullptr>, nullptr, - &file_search, - false + &target_search, // Note: not _file(); don't look for an existing file. + target_type::flag::none }; const target_type libs::static_type @@ -387,8 +387,8 @@ namespace build2 &target_extension_var<nullptr>, &target_pattern_var<nullptr>, nullptr, - &file_search, - false + &target_search, // Note: not _file(); don't look for an existing file. + target_type::flag::none }; // lib @@ -435,7 +435,10 @@ namespace build2 nullptr, nullptr, &target_search, - false // Note: not see-through ("alternatives" group). + + // Note: not see-through ("alternatives" group). + // + target_type::flag::member_hint // Use untyped hint for group members. }; // libi @@ -449,8 +452,8 @@ namespace build2 &target_extension_var<nullptr>, &target_pattern_var<nullptr>, nullptr, - &file_search, - false + &target_search, // Note: not _file(); don't look for an existing file. + target_type::flag::none }; // def @@ -467,7 +470,7 @@ namespace build2 &target_pattern_fix<def_ext>, nullptr, &file_search, - false + target_type::flag::none }; } } diff --git a/libbuild2/bin/target.hxx b/libbuild2/bin/target.hxx index f8d2dd0..8f2a92e 100644 --- a/libbuild2/bin/target.hxx +++ b/libbuild2/bin/target.hxx @@ -22,7 +22,11 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT objx: public file { public: - using file::file; + objx (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; @@ -31,41 +35,55 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT obje: public objx { public: - using objx::objx; + obje (context& c, dir_path d, dir_path o, string n) + : objx (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT obja: public objx { public: - using objx::objx; + obja (context& c, dir_path d, dir_path o, string n) + : objx (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT objs: public objx { public: - using objx::objx; + objs (context& c, dir_path d, dir_path o, string n) + : objx (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; + // Note: this is a "choice" target group. + // class LIBBUILD2_BIN_SYMEXPORT obj: public target { public: - using target::target; + obj (context& c, dir_path d, dir_path o, string n) + : target (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; // Binary module interface (BMI). @@ -100,7 +118,11 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT bmix: public file { public: - using file::file; + bmix (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; @@ -111,7 +133,11 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT hbmix: public bmix { public: - using bmix::bmix; + hbmix (context& c, dir_path d, dir_path o, string n) + : bmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; @@ -120,84 +146,111 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT bmie: public bmix { public: - using bmix::bmix; + bmie (context& c, dir_path d, dir_path o, string n) + : bmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT hbmie: public hbmix { public: - using hbmix::hbmix; + hbmie (context& c, dir_path d, dir_path o, string n) + : hbmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT bmia: public bmix { public: - using bmix::bmix; + bmia (context& c, dir_path d, dir_path o, string n) + : bmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT hbmia: public hbmix { public: - using hbmix::hbmix; + hbmia (context& c, dir_path d, dir_path o, string n) + : hbmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT bmis: public bmix { public: - using bmix::bmix; + bmis (context& c, dir_path d, dir_path o, string n) + : bmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT hbmis: public hbmix { public: - using hbmix::hbmix; + hbmis (context& c, dir_path d, dir_path o, string n) + : hbmix (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; + // Note: this is a "choice" target group (similar to obj{}). + // class LIBBUILD2_BIN_SYMEXPORT bmi: public target { public: - using target::target; + bmi (context& c, dir_path d, dir_path o, string n) + : target (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; + // Note: this is a "choice" target group (similar to bmi{} and obj{}). + // class LIBBUILD2_BIN_SYMEXPORT hbmi: public target { public: - using target::target; + hbmi (context& c, dir_path d, dir_path o, string n) + : target (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; - // Common base for lib{} and libul{} groups. // // Use mtime_target as a base for the "trust me it exists" functionality @@ -207,7 +260,11 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT libx: public mtime_target { public: - using mtime_target::mtime_target; + libx (context& c, dir_path d, dir_path o, string n) + : mtime_target (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; @@ -240,7 +297,11 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT libux: public file { public: - using file::file; + libux (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; @@ -249,41 +310,58 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT libue: public libux { public: - using libux::libux; + libue (context& c, dir_path d, dir_path o, string n) + : libux (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT libua: public libux { public: - using libux::libux; + libua (context& c, dir_path d, dir_path o, string n) + : libux (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT libus: public libux { public: - using libux::libux; + libus (context& c, dir_path d, dir_path o, string n) + : libux (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; + // Note: this is a "choice" target group. + // + // @@ Ideally this shouldn't derive from mtime_target (via libx). Maybe + // get rid of libx? + // class LIBBUILD2_BIN_SYMEXPORT libul: public libx { public: - using libx::libx; + libul (context& c, dir_path d, dir_path o, string n) + : libx (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; // The lib{} target group. @@ -291,23 +369,27 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT liba: public file { public: - using file::file; + liba (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; class LIBBUILD2_BIN_SYMEXPORT libs: public file { public: - using file::file; + libs (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - - virtual const target_type& - dynamic_type () const override {return static_type;} }; // Standard layout type compatible with group_view's const target*[2]. @@ -321,16 +403,32 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT lib: public libx, public lib_members { public: - using libx::libx; + lib (context& c, dir_path d, dir_path o, string n) + : libx (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } virtual group_view group_members (action) const override; + // Match options for the install operation on the liba{}/libs{} and + // libua{}/libus{} target types (note: not lib{}/libul{} nor libue{}). + // + // If only install_runtime option is specified, then only install the + // runtime files omitting everything buildtime (headers, pkg-config + // files, shared library version-related symlinks, etc). + // + // Note that it's either runtime-only or runtime and buildtime (i.e., + // everything), so match with install_all instead of install_buildtime + // (the latter is only useful in the rule implementations). + // + static constexpr uint64_t option_install_runtime = 0x01; + static constexpr uint64_t option_install_buildtime = 0x02; + static constexpr uint64_t option_install_all = match_extra::all_options; + public: static const target_type static_type; - - virtual const target_type& - dynamic_type () const override {return static_type;} }; // Windows import library. @@ -338,11 +436,14 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT libi: public file { public: - using file::file; + libi (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; // Windows module definition (.def). @@ -350,11 +451,14 @@ namespace build2 class LIBBUILD2_BIN_SYMEXPORT def: public file { public: - using file::file; + def (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; } } diff --git a/libbuild2/bin/utility.cxx b/libbuild2/bin/utility.cxx index cb06287..a03ea50 100644 --- a/libbuild2/bin/utility.cxx +++ b/libbuild2/bin/utility.cxx @@ -57,6 +57,11 @@ namespace build2 // prefer static over shared since it could be faster (but I am sure // someone will probably want this configurable). // + // Maybe we should use the bin.exe.lib order as a heuristics (i.e., + // the most likely utility library to be built is the one most likely + // to be linked)? Will need the variables rs-only, similar to + // bin.lib, which probably is a good thing. See also libul_rule. + // if (li.type == otype::e) { // Utility libraries are project-local which means the primarily @@ -84,7 +89,9 @@ namespace build2 // Make sure group members are resolved. // group_view gv (resolve_members (a, l)); - assert (gv.members != nullptr); + + if (gv.members == nullptr) + fail << "group " << l << " has no members"; pair<otype, bool> p ( link_member (lmembers {l.a != nullptr, l.s != nullptr}, li.order)); |