diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2014-12-11 07:35:54 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2014-12-11 07:35:54 +0200 |
commit | f4ed3e569cb5ebae855ea5309bfc17aa6b35874a (patch) | |
tree | 58481ae13a9c5d1c4f6853b59bd66a059592074c /build | |
parent | 5e9eb843f6ccadfb47fa603260783425da9e7805 (diff) |
Initial implementation of dependency injection (g++ -M output)
Diffstat (limited to 'build')
-rw-r--r-- | build/bd.cxx | 10 | ||||
-rw-r--r-- | build/cxx/rule | 7 | ||||
-rw-r--r-- | build/cxx/rule.cxx | 146 |
3 files changed, 155 insertions, 8 deletions
diff --git a/build/bd.cxx b/build/bd.cxx index e48de21..faae4c4 100644 --- a/build/bd.cxx +++ b/build/bd.cxx @@ -22,6 +22,10 @@ namespace build bool match (target& t) { + // Because we match the target first and then prerequisites, + // any additional dependency information injected by the rule + // will be covered as well. + // if (!t.recipe ()) { for (auto ti (&t.type_id ()); @@ -44,7 +48,7 @@ namespace build if (!t.recipe ()) { - cerr << "error: no rule to build target " << t << endl; + cerr << "error: no rule to update target " << t << endl; return false; } } @@ -132,11 +136,7 @@ main (int argc, char* argv[]) cxx::cxx bd_cxx ("bd"); bd_cxx.path (path ("bd.cxx")); - hxx target ("target"); - target.path (path ("target")); - bd_o.prerequisite (bd_cxx); - bd_o.prerequisite (target); // // diff --git a/build/cxx/rule b/build/cxx/rule index d4412e4..85352b7 100644 --- a/build/cxx/rule +++ b/build/cxx/rule @@ -6,6 +6,9 @@ #define BUILD_CXX_RULE #include <build/rule> +#include <build/native> + +#include <build/cxx/target> namespace build { @@ -23,6 +26,10 @@ namespace build static target_state update (target&); + + private: + bool + inject_prerequisites (obj&, const cxx&) const; }; class link: public rule diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 9f8f1ae..31c27dc 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -4,15 +4,17 @@ #include <build/cxx/rule> +#include <cstddef> // size_t +#include <cstdlib> // exit +#include <string> #include <vector> #include <iostream> -#include <build/native> +#include <ext/stdio_filebuf.h> + #include <build/process> #include <build/timestamp> -#include <build/cxx/target> - using namespace std; namespace build @@ -59,9 +61,147 @@ namespace build if (o.path ().empty ()) o.path (path (o.name () + ".o")); + // Inject additional prerequisites. + // + // @@ If this failed, saying that the rule did not match is + // not quite correct. + // + if (!inject_prerequisites (o, *s)) + return recipe (); + return recipe (&update); } + // Return the next make prerequisite starting from the specified + // position and update position to point to the start of the + // following prerequisite or l.size() if there are none left. + // + static string + next (const string& l, size_t& p) + { + size_t n (l.size ()); + + // Skip leading spaces. + // + for (; p != n && l[p] == ' '; p++) ; + + // Lines containing multiple prerequisites are 80 characters max. + // + string r; + r.reserve (n); + + // Scan the next prerequisite while watching out for escape sequences. + // + for (; p != n && l[p] != ' '; p++) + { + char c (l[p]); + + if (c == '\\') + c = l[++p]; + + r += c; + } + + // Skip trailing spaces. + // + for (; p != n && l[p] == ' '; p++) ; + + // Skip final '\'. + // + if (p == n - 1 && l[p] == '\\') + p++; + + return r; + } + + bool compile:: + inject_prerequisites (obj& o, const cxx& s) const + { + const char* args[] = { + "g++-4.9", + "-std=c++11", + "-I..", + "-M", + "-MG", // Treat missing headers as generated. + "-MQ", "*", // Quoted target (older version can't handle empty name). + s.path ().string ().c_str (), + nullptr}; + + try + { + process pr (args, false, false, true); + bool r (true); + + __gnu_cxx::stdio_filebuf<char> fb (pr.in_ofd, ios_base::in); + istream is (&fb); + + for (bool first (true); !is.eof (); ) + { + string l; + getline (is, l); + + if (is.fail () && !is.eof ()) + { + cerr << "warning: io error while parsing output" << endl; + r = false; + break; + } + + size_t p (0); + + if (first) + { + // Empty output usually means the wait() call below will return + // false. + // + if (l.empty ()) + { + r = false; + break; + } + + first = false; + assert (l[0] == '*' && l[1] == ':' && l[2] == ' '); + next (l, (p = 3)); // Skip the source file. + } + + while (p != l.size ()) + { + path d (next (l, p)); + + // If there is no extension (e.g., std C++ headers), then + // assume it is a header. Otherwise, let the normall + // mechanism to figure the type from the extension. + // + + // @@ TODO: + // + // - memory leak + + hxx& h (*new hxx (d.leaf ().base ().string ())); + h.path (d); + + o.prerequisite (h); + } + } + + //@@ Any diagnostics if wait() returns false. Or do we assume + // the child process issued something? + // + return pr.wait () && r; + } + catch (const process_error& e) + { + cerr << "warning: unable to execute '" << args[0] << "': " << + e.what () << endl; + + if (e.child ()) + exit (1); + + return false; + } + } + target_state compile:: update (target& t) { |