From 2a0f9e035f673f1ee387924501a31990de37f18d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 24 Apr 2015 12:29:20 +0200 Subject: Implement lib/liba/libso{} target group, shared/static library build --- build/cxx/module.cxx | 144 ++++++++++------------------------ build/cxx/rule | 7 +- build/cxx/rule.cxx | 213 +++++++++++++++++++++++++++++++++++---------------- 3 files changed, 195 insertions(+), 169 deletions(-) (limited to 'build/cxx') diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx index 3e0101f..45f8271 100644 --- a/build/cxx/module.cxx +++ b/build/cxx/module.cxx @@ -12,6 +12,8 @@ #include #include +#include + using namespace std; namespace build @@ -41,130 +43,68 @@ namespace build // config.cxx // - for (bool f (true); f; f = false) { - auto val (root["config.cxx"]); - - string v; + auto r (config::required (root, "config.cxx", "g++")); - if (val) + // If we actually set a new value, test it by trying to execute. + // + if (r.second) { - if (!val.belongs (*global_scope)) - break; // A value from (some) config.build. - - v = val.as (); - } - else - v = "g++"; // Default. + const string& cxx (r.first); + const char* args[] = {cxx.c_str (), "-dumpversion", nullptr}; - // Test it by trying to execute. - // - const char* args[] = {v.c_str (), "-dumpversion", nullptr}; + if (verb) + print_process (args); + else + text << "test " << cxx; - if (verb) - print_process (args); - else - text << "test " << v; + string ver; + try + { + process pr (args, false, false, true); - string ver; - try - { - process pr (args, false, false, true); + __gnu_cxx::stdio_filebuf fb (pr.in_ofd, ios_base::in); + istream is (&fb); - __gnu_cxx::stdio_filebuf fb (pr.in_ofd, ios_base::in); - istream is (&fb); + bool r (getline (is, ver)); - bool r (getline (is, ver)); + if (!pr.wait ()) + throw failed (); - if (!pr.wait ()) - throw failed (); + if (!r) + fail << "unexpected output from " << cxx; + } + catch (const process_error& e) + { + error << "unable to execute " << cxx << ": " << e.what (); - if (!r) - fail << "unexpected output from " << v; - } - catch (const process_error& e) - { - error << "unable to execute " << v << ": " << e.what (); + if (e.child ()) + exit (1); - if (e.child ()) - exit (1); + throw failed (); + } - throw failed (); + //text << "toolchain version " << ver; } - - //text << "toolchain version " << ver; - - // Set on the project root. - // - root.assign ("config.cxx") = move (v); } // config.cxx.{p,c,l}options // config.cxx.libs // - // These are optional so all we need to do is "import" them - // into the root scope if they were specified on the command - // line and set them to NULL if unspecified (the last part - // is important to distinguish between the "configured as - // unspecified" and "not configured" cases). - // - // We also merge them into the corresponding cxx.* variables. + // These are optional. We also merge them into the corresponding + // cxx.* variables. // - { - auto v (root["config.cxx.poptions"]); + if (auto* v = config::optional (root, "config.cxx.poptions")) + root.append ("cxx.poptions") += *v; - if (v.defined ()) - { - if (v.belongs (*global_scope)) - root.assign ("config.cxx.poptions") = v; + if (auto* v = config::optional (root, "config.cxx.coptions")) + root.append ("cxx.coptions") += *v; - root.append ("cxx.poptions") += v; - } - else - root.assign ("config.cxx.poptions") = nullptr; - } + if (auto* v = config::optional (root, "config.cxx.loptions")) + root.append ("cxx.loptions") += *v; - { - auto v (root["config.cxx.coptions"]); - - if (v.defined ()) - { - if (v.belongs (*global_scope)) - root.assign ("config.cxx.coptions") = v; - - root.append ("cxx.coptions") += v; - } - else - root.assign ("config.cxx.coptions") = nullptr; - } - - { - auto v (root["config.cxx.loptions"]); - - if (v.defined ()) - { - if (v.belongs (*global_scope)) - root.assign ("config.cxx.loptions") = v; - - root.append ("cxx.loptions") += v; - } - else - root.assign ("config.cxx.loptions") = nullptr; - } - - { - auto v (root["config.cxx.libs"]); - - if (v.defined ()) - { - if (v.belongs (*global_scope)) - root.assign ("config.cxx.libs") = v; - - root.append ("cxx.libs") += v; - } - else - root.assign ("config.cxx.libs") = nullptr; - } + if (auto* v = config::optional (root, "config.cxx.libs")) + root.append ("cxx.libs") += *v; } } } diff --git a/build/cxx/rule b/build/cxx/rule index 24879d4..d139cc7 100644 --- a/build/cxx/rule +++ b/build/cxx/rule @@ -6,9 +6,6 @@ #define BUILD_CXX_RULE #include -#include - -#include namespace build { @@ -16,6 +13,8 @@ namespace build namespace cxx { + class cxx; + // @@ Can't we do match(obj&) and then registration code extracts // that. And no virtuals? // @@ -39,6 +38,8 @@ namespace build class link: public rule { public: + enum class type {exe, liba, libso}; + virtual void* match (action, target&, const std::string& hint) const; diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 65616f8..9d25143 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -20,12 +20,18 @@ #include #include +#include + +#include + using namespace std; namespace build { namespace cxx { + using namespace bin; + // T is either target or scope. // template @@ -73,27 +79,16 @@ namespace build // - if path already assigned, verify extension? // - if (t.is_a ()) - fail << diag_doing (a, t) << " target group" << - info << "explicitly select either obja{} or objso{} member"; - - // See if we have a C++ source file. + // See if we have a C++ source file. Iterate in reverse so that + // a source file specified for an obj member overrides the one + // specified for the group. // - for (prerequisite_target& pe: t.prerequisites) + for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t))) { if (pe.prereq->type.id == typeid (cxx)) return &pe; } - if (t.group != nullptr) - { - for (prerequisite_target& pe: t.group->prerequisites) - { - if (pe.prereq->type.id == typeid (cxx)) - return &pe; - } - } - level3 ([&]{trace << "no c++ source file for target " << t;}); return nullptr; } @@ -146,7 +141,7 @@ namespace build switch (a) { case perform_update_id: return &perform_update; - case perform_clean_id: return &perform_clean_file; + case perform_clean_id: return &perform_clean; default: return default_recipe; // Forward to prerequisites. } } @@ -273,9 +268,13 @@ namespace build path f (next (l, pos)); f.normalize (); - assert (f.absolute ()); // Logic below depends on this. + if (!f.absolute ()) + { + level5 ([&]{trace << "skipping generated/non-existent " << f;}); + continue; + } - level5 ([&]{trace << "prerequisite path: " << f;}); + level5 ([&]{trace << "injecting " << f;}); // Split the name into its directory part, the name part, and // extension. Here we can assume the name part is a valid @@ -343,7 +342,7 @@ namespace build perform_update (action a, target& xt) { path_target& t (static_cast (xt)); - cxx* s (execute_prerequisites (a, t, t.mtime ())); + cxx* s (execute_find_prerequisites (a, t, t.mtime ())); if (s == nullptr) return target_state::unchanged; @@ -431,13 +430,13 @@ namespace build // (i.e., a utility library). // - bool so (t.is_a ()); + bool so (t.is_a ()); // Scan prerequisites and see if we can work with what we've got. // bool seen_cxx (false), seen_c (false), seen_obj (false); - for (prerequisite& p: t.prerequisites) + for (prerequisite& p: group_prerequisites (t)) { if (p.type.id == typeid (cxx)) // @@ Should use is_a (add to p.type). { @@ -478,6 +477,20 @@ namespace build return seen_cxx || seen_c || seen_obj ? &t : nullptr; } + static inline target_state + select_a (action a, target& t) + { + obj* o (t.is_a ()); + return execute (a, o != nullptr ? *o->a : t); + } + + static inline target_state + select_so (action a, target& t) + { + obj* o (t.is_a ()); + return execute (a, o != nullptr ? *o->so : t); + } + recipe link:: apply (action a, target& xt, void*) const { @@ -485,16 +498,22 @@ namespace build path_target& t (static_cast (xt)); - bool so (t.is_a ()); + type tt (t.is_a () + ? type::exe + : (t.is_a () ? type::liba : type::libso)); + + bool so (tt == type::libso); // Derive file name from target name. // if (t.path ().empty ()) { - if (so) - t.path (t.derived_path ("so", "lib")); - else - t.path (t.derived_path ()); // exe + switch (tt) + { + case type::exe: t.path (t.derived_path ( )); break; + case type::liba: t.path (t.derived_path ("a", "lib")); break; + case type::libso: t.path (t.derived_path ("so", "lib")); break; + } } // We may need the project roots for rule chaining (see below). @@ -506,8 +525,9 @@ namespace build // Process prerequisites: do rule chaining for C and C++ source // files as well as search and match. // - for (prerequisite_target& pe: t.prerequisites) + for (prerequisite_target& pe: group_prerequisites (t)) { + bool group (!pe.belongs (t)); // Target group's prerequisite. prerequisite& p (pe); if (!p.is_a () && !p.is_a ()) @@ -525,21 +545,37 @@ namespace build // If this is the obj{} target group, then pick the appropriate // member and make sure it is searched and matched. // + target* pt; + if (obj* o = pe.target->is_a ()) { - pe.target = so ? static_cast (o->so) : o->a; + pt = so ? static_cast (o->so) : o->a; - if (pe.target == nullptr) + if (pt == nullptr) { const target_type& type ( so ? objso::static_type : obja::static_type); - pe.target = &search ( + pt = &search ( prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope}); } + + // This is a bit tricky: if this is a group's prerequisite, + // then we have to keep pe.target pointing to the obj{} group + // since there could be another match that uses a different + // member of this prerequisite. In this case we specify the + // "executor" (see below) which will pick the right member + // for one of common algorithms. However, if this is our own + // prerequisite, then we are free to hard-wire the member + // we need directly in pe.target. + // + if (!group) + pe.target = pt; } + else + pt = pe.target; - build::match (a, *pe.target); + build::match (a, *pt); continue; } @@ -557,7 +593,9 @@ namespace build prerequisite& cp (p); const target_type& o_type ( - so ? objso::static_type : obja::static_type); + group + ? obj::static_type + : (so ? objso::static_type : obja::static_type)); // Come up with the obj*{} prerequisite. The c(xx){} prerequisite // directory can be relative (to the scope) or absolute. If it is @@ -590,7 +628,7 @@ namespace build // Resolve this prerequisite to target. // - target& ot (search (op)); + target* ot (&search (op)); // If we are cleaning, check that this target is in the same or // a subdirectory of ours. @@ -601,7 +639,7 @@ namespace build // update will come and finish the rewrite process (it will even // reuse op that we have created but then ignored). So all is good. // - if (a.operation () == clean_id && !ot.dir.sub (t.dir)) + if (a.operation () == clean_id && !ot->dir.sub (t.dir)) { // If we shouldn't clean obj{}, then it is fair to assume // we shouldn't clean cxx{} either (generated source will @@ -612,6 +650,26 @@ namespace build continue; } + pe.target = ot; + + // If we have created the obj{} target group, pick one of its + // members; the rest would be primarily concerned with it. + // + if (group) + { + obj& o (static_cast (*ot)); + ot = so ? static_cast (o.so) : o.a; + + if (ot == nullptr) + { + const target_type& type ( + so ? objso::static_type : obja::static_type); + + ot = &search ( + prerequisite_key {&type, &o.dir, &o.name, &o.ext, nullptr}); + } + } + // If this target already exists, then it needs to be "compatible" // with what we are doing here. // @@ -625,7 +683,7 @@ namespace build // speculatively doesn't really hurt. // prerequisite* cp1 (nullptr); - for (prerequisite& p: ot.prerequisites) + for (prerequisite& p: reverse_iterate (group_prerequisites (*ot))) { // Ignore some known target types (fsdir, headers). // @@ -643,19 +701,19 @@ namespace build } fail << "synthesized target for prerequisite " << cp - << " would be incompatible with existing target " << ot << + << " would be incompatible with existing target " << *ot << info << "unknown existing prerequsite type " << p << info << "specify corresponding obj{} target explicitly"; } if (cp1 != nullptr) { - build::match (a, ot); // Now cp1 should be resolved. - search (cp); // Our own prerequisite, so this is ok. + build::match (a, *ot); // Now cp1 should be resolved. + search (cp); // Our own prerequisite, so this is ok. if (cp.target != cp1->target) fail << "synthesized target for prerequisite " << cp - << " would be incompatible with existing target " << ot << + << " would be incompatible with existing target " << *ot << info << "existing prerequsite " << *cp1 << " does not " << "match " << cp << info << "specify corresponding " << o_type.name << "{} " @@ -663,40 +721,55 @@ namespace build } else { - ot.prerequisites.emplace_back (cp); - build::match (a, ot); + // Note: add the source to the group, not member. + // + pe.target->prerequisites.emplace_back (cp); + build::match (a, *ot); } // Change the exe{} target's prerequsite ref from cxx{} to obj*{}. // pe.prereq = &op; - pe.target = &ot; } // Inject dependency on the output directory. // inject_parent_fsdir (a, t); - switch (a) + if (so) { - case perform_update_id: return &perform_update; - case perform_clean_id: return &perform_clean_file; - default: return default_recipe; // Forward to prerequisites. + switch (a) + { + case perform_update_id: return &perform_update; + case perform_clean_id: return &perform_clean; + default: return default_action; // Forward to prerequisites. + } + } + else + { + switch (a) + { + case perform_update_id: return &perform_update; + case perform_clean_id: return &perform_clean; + default: return default_action; // Forward to prerequisites. + } } } target_state link:: perform_update (action a, target& xt) { - // @@ Q: - // - // - what are we doing with libraries? - // path_target& t (static_cast (xt)); - bool so (t.is_a ()); + type tt (t.is_a () + ? type::exe + : (t.is_a () ? type::liba : type::libso)); + + bool so (tt == type::libso); - if (!execute_prerequisites (a, t, t.mtime ())) + if (so + ? !execute_prerequisites (a, t, t.mtime ()) + : !execute_prerequisites (a, t, t.mtime ())) return target_state::unchanged; // Translate paths to relative (to working directory) ones. This @@ -706,24 +779,35 @@ namespace build vector relo; scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr. - const string& cxx (rs["config.cxx"].as ()); + vector args; + string storage1; - vector args {cxx.c_str ()}; + if (tt == type::liba) + { + //@@ ranlib + // + args.push_back ("ar"); + args.push_back ("-rc"); + args.push_back (relt.string ().c_str ()); + } + else + { + args.push_back (rs["config.cxx"].as ().c_str ()); - append_options (args, t, "cxx.coptions"); + append_options (args, t, "cxx.coptions"); - string std; // Storage. - append_std (args, t, std); + append_std (args, t, storage1); - if (so) - args.push_back ("-shared"); + if (so) + args.push_back ("-shared"); - args.push_back ("-o"); - args.push_back (relt.string ().c_str ()); + args.push_back ("-o"); + args.push_back (relt.string ().c_str ()); - append_options (args, t, "cxx.loptions"); + append_options (args, t, "cxx.loptions"); + } - for (target* pt: t.prerequisites) + for (target* pt: group_prerequisites (t)) { if (pt == nullptr) continue; // Skipped. @@ -743,7 +827,8 @@ namespace build args.push_back (relo.back ().string ().c_str ()); } - append_options (args, t, "cxx.libs"); + if (tt != type::liba) + append_options (args, t, "cxx.libs"); args.push_back (nullptr); -- cgit v1.1