From c106259517d7693ea8e24564bc890fe575d5edcd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 16 Jan 2015 14:11:14 +0200 Subject: Implement rule chaining for cxx::link --- build/cxx/rule | 10 ++- build/cxx/rule.cxx | 211 ++++++++++++++++++++++++++++++++++++++------------- build/cxx/target | 22 ++++++ build/cxx/target.cxx | 6 ++ 4 files changed, 195 insertions(+), 54 deletions(-) (limited to 'build/cxx') diff --git a/build/cxx/rule b/build/cxx/rule index 8924c7c..cf9ee51 100644 --- a/build/cxx/rule +++ b/build/cxx/rule @@ -22,8 +22,11 @@ namespace build class compile: public rule { public: + virtual void* + match (target&, const std::string& hint) const; + virtual recipe - match (target&, bool single, std::string& hint) const; + select (target&, void*) const; static target_state update (target&); @@ -36,8 +39,11 @@ namespace build class link: public rule { public: + virtual void* + match (target&, const std::string& hint) const; + virtual recipe - match (target&, bool single, std::string& hint) const; + select (target&, void*) const; static target_state update (target&); diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 3a97576..c9dd7d6 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -4,10 +4,11 @@ #include -#include // size_t -#include // exit #include #include +#include // size_t +#include // exit +#include // move() #include #include @@ -27,8 +28,8 @@ namespace build { // compile // - recipe compile:: - match (target& t, bool single, std::string& hint) const + void* compile:: + match (target& t, const string&) const { tracer tr ("cxx::compile::match"); @@ -48,24 +49,21 @@ namespace build // of dependency info. // - // See if we have a source file. + // See if we have a C++ source file. // - prerequisite* sp (nullptr); for (prerequisite& p: t.prerequisites) { if (p.type.id == typeid (cxx)) - { - sp = &p; - break; - } + return &p; } - if (sp == nullptr) - { - trace (3, [&]{tr << "no c++ source file for target " << t;}); - return recipe (); - } + trace (3, [&]{tr << "no c++ source file for target " << t;}); + return nullptr; + } + recipe compile:: + select (target& t, void* v) const + { // Derive object file name from target name. // obj& o (dynamic_cast (t)); @@ -77,9 +75,10 @@ namespace build // this in order to get the source file path for prerequisite // injections. // + prerequisite* sp (static_cast (v)); cxx* st ( dynamic_cast ( - sp->target != nullptr ? sp->target : search (*sp))); + sp->target != nullptr ? sp->target : &search (*sp))); if (st != nullptr) { @@ -92,7 +91,7 @@ namespace build } } - return recipe (&update); + return &update; } // Return the next make prerequisite starting from the specified @@ -222,37 +221,18 @@ namespace build // Find or insert. // - auto r (ds.prerequisites.emplace ( - hxx::static_type, move (d), move (n), e, ds)); - - auto& p (const_cast (*r.first)); - - // Update extension if the existing prerequisite has it - // unspecified. - // - if (p.ext != e) - { - trace (4, [&]{ - tracer::record r (tr); - r << "assuming prerequisite " << p << " is the same as the " - << "one with "; - if (e->empty ()) - r << "no extension"; - else - r << "extension " << *e; - }); - - p.ext = e; - } + prerequisite& p ( + ds.prerequisites.insert ( + hxx::static_type, move (d), move (n), e, ds, tr).first); // Resolve to target so that we can assign its path. // path_target& t ( dynamic_cast ( - p.target != nullptr ? *p.target : *search (p))); + p.target != nullptr ? *p.target : search (p))); if (t.path ().empty ()) - t.path (file); + t.path (move (file)); o.prerequisites.push_back (p); } @@ -374,9 +354,11 @@ namespace build // link // - recipe link:: - match (target& t, bool single, std::string& hint) const + void* link:: + match (target& t, const string& hint) const { + tracer tr ("cxx::link::match"); + // @@ TODO: // // - check prerequisites: object files, libraries @@ -387,23 +369,53 @@ namespace build // - if there is no .o, are we going to check if the one derived // from target exist or can be built? If we do that, then it // probably makes sense to try other rules first (two passes). - // What if there is a library. Probably ok if .a, not is .so. + // What if there is a library. Probably ok if .a, not if .so. // - // See if we have at least one object file. + // Scan prerequisites and see if we can work with what we've got. // - prerequisite* op (nullptr); + bool seen_cxx (false), seen_c (false), seen_obj (false); + for (prerequisite& p: t.prerequisites) { - if (p.type.id == typeid (obj)) + if (p.type.id == typeid (cxx)) { - op = &p; - break; + if (!seen_cxx) + seen_cxx = true; + } + else if (p.type.id == typeid (c)) + { + if (!seen_c) + seen_c = true; + } + else if (p.type.id == typeid (obj)) + { + if (!seen_obj) + seen_obj = true; + } + else + { + trace (3, [&]{tr << "unexpected prerequisite type " << p.type;}); + return nullptr; } } - if (op == nullptr) - return recipe (); + // We will only chain C source if there is also C++ source or we + // we explicitly asked to. + // + if (seen_c && !seen_cxx && hint < "cxx") + { + trace (3, [&]{tr << "c prerequisite(s) without c++ or hint";}); + return nullptr; + } + + return seen_cxx || seen_c || seen_obj ? &t : nullptr; + } + + recipe link:: + select (target& t, void*) const + { + tracer tr ("cxx::link::select"); // Derive executable file name from target name. // @@ -412,7 +424,102 @@ namespace build if (e.path ().empty ()) e.path (e.directory / path (e.name)); - return recipe (&update); + // Do rule chaining for C and C++ source files. + // + // @@ OPT: match() could indicate whether this is necesssary. + // + for (auto& pr: t.prerequisites) + { + prerequisite& cp (pr); + + if (cp.type.id != typeid (c) && cp.type.id != typeid (cxx)) + continue; + + // Come up with the obj{} prerequisite. The c(xx){} prerequisite + // directory can be relative (to the scope) or absolute. If it is + // relative, then we use it as is. If it is absolute, then translate + // it to the corresponding directory under out_root. While the + // c(xx){} directory is most likely under src_root, it is also + // possible it is under out_root (e.g., generated source). + // + path d; + if (cp.directory.relative () || cp.directory.sub (out_root)) + d = cp.directory; + else + { + if (!cp.directory.sub (src_root)) + { + cerr << "error: out of project prerequisite " << cp << endl; + cerr << "info: specify corresponding obj{} target explicitly" + << endl; + throw error (); + } + + d = out_root / cp.directory.leaf (src_root); + } + + prerequisite& op ( + cp.scope.prerequisites.insert ( + obj::static_type, move (d), cp.name, nullptr, cp.scope, tr).first); + + // Resolve this prerequisite to target. + // + target& ot (search (op)); + + // If this target already exists, then it needs to be "compatible" + // with what we doing. + // + bool add (true); + for (prerequisite& p: ot.prerequisites) + { + // Ignore some known target types (headers). + // + if (p.type.id == typeid (h) || + cp.type.id == typeid (cxx) && (p.type.id == typeid (hxx) || + p.type.id == typeid (ixx) || + p.type.id == typeid (txx))) + continue; + + if (p.type.id == typeid (cxx)) + { + // We need to make sure they are the same which we can only + // do by comparing the targets to which they resolve. + // + target* t (p.target != nullptr ? p.target : &search (p)); + target* ct (cp.target != nullptr ? cp.target : &search (cp)); + + if (t == ct) + { + add = false; + continue; // Check the rest of the prerequisites. + } + } + + cerr << "error: synthesized target for prerequisite " << cp + << " would be incompatible with existing target " << ot + << endl; + + if (p.type.id == typeid (cxx)) + cerr << "info: existing prerequsite " << p << " does not " + << "match " << cp << endl; + else + cerr << "info: unknown existing prerequsite " << p << endl; + + cerr << "info: specify corresponding obj{} target explicitly" + << endl; + + throw error (); + } + + if (add) + ot.prerequisites.push_back (cp); + + // Change the exe{} target's prerequsite from cxx{} to obj{}. + // + pr = op; + } + + return &update; } target_state link:: diff --git a/build/cxx/target b/build/cxx/target index 37b093b..39ebdd3 100644 --- a/build/cxx/target +++ b/build/cxx/target @@ -50,6 +50,28 @@ namespace build virtual const target_type& type () const {return static_type;} static const target_type static_type; }; + + //@@ TMP + // + class h: public file + { + public: + using file::file; + + public: + virtual const target_type& type () const {return static_type;} + static const target_type static_type; + }; + + class c: public file + { + public: + using file::file; + + public: + virtual const target_type& type () const {return static_type;} + static const target_type static_type; + }; } } diff --git a/build/cxx/target.cxx b/build/cxx/target.cxx index c4502b4..f57c963 100644 --- a/build/cxx/target.cxx +++ b/build/cxx/target.cxx @@ -21,5 +21,11 @@ namespace build const target_type cxx::static_type { typeid (cxx), "cxx", &file::static_type, &target_factory}; + + const target_type h::static_type { + typeid (h), "h", &file::static_type, &target_factory}; + + const target_type c::static_type { + typeid (c), "c", &file::static_type, &target_factory}; } } -- cgit v1.1