diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-01-08 13:27:15 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-01-08 13:27:15 +0200 |
commit | ab4421747146aa7995f0cfb1a639c9121c82c915 (patch) | |
tree | deb08893c02ed0238f73becbbe80ede5568b946e /build | |
parent | d3e624ef7c0fb274e62b81c4c7bd59640770520a (diff) |
Implement tracing support
Also use to-relative path translation in diagnostics.
Diffstat (limited to 'build')
-rw-r--r-- | build/algorithm.cxx | 9 | ||||
-rw-r--r-- | build/b.cxx | 27 | ||||
-rw-r--r-- | build/buildfile | 6 | ||||
-rw-r--r-- | build/context | 21 | ||||
-rw-r--r-- | build/context.cxx | 36 | ||||
-rw-r--r-- | build/cxx/rule.cxx | 56 | ||||
-rw-r--r-- | build/diagnostics | 14 | ||||
-rw-r--r-- | build/diagnostics.cxx | 20 | ||||
-rw-r--r-- | build/parser.cxx | 1 | ||||
-rw-r--r-- | build/path | 19 | ||||
-rw-r--r-- | build/path.txx | 30 | ||||
-rw-r--r-- | build/pre.build | 0 | ||||
-rw-r--r-- | build/prerequisite.cxx | 33 | ||||
-rw-r--r-- | build/target.cxx | 16 | ||||
-rw-r--r-- | build/trace | 71 | ||||
-rw-r--r-- | build/trace.cxx | 12 |
16 files changed, 343 insertions, 28 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx index fac0bf6..419bfaf 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -23,6 +23,8 @@ namespace build target* search (prerequisite& p) { + tracer tr ("search"); + assert (p.target == nullptr); //@@ TODO for now we just default to the directory scope. @@ -36,7 +38,7 @@ namespace build d.normalize (); } - //@@ TODO would be nice to first check if this target is + //@@ TODO: would be nice to first check if this target is // already in the set before allocating a new instance. // Find or insert. @@ -45,8 +47,9 @@ namespace build targets.emplace ( unique_ptr<target> (p.type.factory (p.name, move (d))))); - //if (r.second) - // cout << "new target for prerequsite " << p << " " << d << endl; + trace (4, [&]{ + tr << (r.second ? "new" : "existing") << " target " << **r.first + << " for prerequsite " << p;}); return (p.target = r.first->get ()); } diff --git a/build/b.cxx b/build/b.cxx index a5123f1..ad68e21 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -110,6 +110,8 @@ namespace build void dump () { + cout << endl; + for (const auto& pt: targets) { target& t (*pt); @@ -123,6 +125,8 @@ namespace build cout << endl; } + + cout << endl; } } @@ -138,10 +142,16 @@ using namespace build; int main (int argc, char* argv[]) { + tracer tr ("main"); + // Initialize time conversion data that is used by localtime_r(). // tzset (); + // Trace verbosity. + // + verb = 5; + // Register target types. // target_types.insert (file::static_type); @@ -200,12 +210,15 @@ main (int argc, char* argv[]) else out_root = out_base.directory (src_base.leaf (src_root)); - cerr << "work dir: " << work << endl; - cerr << "home dir: " << home << endl; - cerr << "out_base: " << out_base << endl; - cerr << "src_base: " << src_base << endl; - cerr << "out_root: " << out_root << endl; - cerr << "src_root: " << src_root << endl; + if (verb >= 4) + { + tr << "work dir: " << work.string (); + tr << "home dir: " << home.string (); + tr << "out_base: " << out_base.string (); + tr << "src_base: " << src_base.string (); + tr << "out_root: " << out_root.string (); + tr << "src_root: " << src_root.string (); + } // Parse buildfile. // @@ -267,7 +280,7 @@ main (int argc, char* argv[]) if (!match_recursive (d)) return 1; // Diagnostics has already been issued. - //dump (); + dump (); switch (update (d)) { diff --git a/build/buildfile b/build/buildfile index 88ea4d6..3afe19b 100644 --- a/build/buildfile +++ b/build/buildfile @@ -1,16 +1,18 @@ -exe{b1}: obj{b algorithm scope parser lexer target prerequisite rule \ - native context cxx/target cxx/rule process timestamp path} +exe{b1}: obj{b algorithm scope parser lexer trace target prerequisite rule \ + native context diagnostics cxx/target cxx/rule process timestamp path} obj{b}: cxx{b} obj{algorithm}: cxx{algorithm} obj{scope}: cxx{scope} obj{parser}: cxx{parser} obj{lexer}: cxx{lexer} +obj{trace}: cxx{trace} obj{target}: cxx{target} obj{prerequisite}: cxx{prerequisite} obj{rule}: cxx{rule} obj{native}: cxx{native} obj{context}: cxx{context} +obj{diagnostics}: cxx{diagnostics} obj{cxx/target}: cxx{cxx/target} obj{cxx/rule}: cxx{cxx/rule} obj{process}: cxx{process} diff --git a/build/context b/build/context index 4c86b14..a369ade 100644 --- a/build/context +++ b/build/context @@ -5,6 +5,9 @@ #ifndef BUILD_CONTEXT #define BUILD_CONTEXT +#include <string> +#include <ostream> + #include <build/path> namespace build @@ -17,6 +20,24 @@ namespace build extern path src_base; extern path out_base; + + // If possible, translate an absolute, normalized path into relative to + // the work directory. + // + path + translate (const path&); + + // In addition to calling translate() above, this function also uses + // shorter notations such as ~/. + // + std::string + diagnostic_string (const path&); + + inline std::ostream& + operator<< (std::ostream& os, const path& p) + { + return os << diagnostic_string (p); + } } #endif // BUILD_CONTEXT diff --git a/build/context.cxx b/build/context.cxx index ce80324..fc4ec1c 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include <ostream> + #include <build/context> using namespace std; @@ -16,4 +18,38 @@ namespace build path src_base; path out_base; + + path + translate (const path& p) + { + if (p.sub (work)) + return p.leaf (work); + + // If work is a sub-path of {src,out}_root and this path is also a + // sub-bath of it, then use '..' to form a relative path. + // + if (work.sub (src_root) && p.sub (src_root) || + work.sub (out_root) && p.sub (out_root)) // @@ cache + return p.relative (work); + + return p; + } + + std::string + diagnostic_string (const path& p) + { + if (p.absolute ()) + { + path rp (translate (p)); + +#ifndef _WIN32 + if (rp.absolute () && rp.sub (home)) + return "~/" + rp.leaf (home).string (); +#endif + + return rp.string (); + } + + return p.string (); + } } diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 3cab2d4..1d2a2c4 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -17,6 +17,7 @@ #include <build/process> #include <build/timestamp> #include <build/diagnostics> +#include <build/context> using namespace std; @@ -29,6 +30,8 @@ namespace build recipe compile:: match (target& t) const { + tracer tr ("cxx::compile::match"); + // @@ TODO: // // - check prerequisites: single source file @@ -59,7 +62,7 @@ namespace build if (sp == nullptr) { - cout << "no source file" << endl; + trace (3, [&]{tr << "no c++ source file for target " << t;}); return recipe (); } @@ -137,16 +140,27 @@ namespace build void compile:: inject_prerequisites (obj& o, const cxx& s, scope& ds) const { + tracer tr ("cxx::compile::inject_prerequisites"); + + // We are using absolute source file path in order to get + // absolute paths in the result. + // const char* args[] = { "g++-4.9", "-std=c++14", - "-I..", - "-MM", //@@ TMP -M + "-I", src_root.string ().c_str (), + "-MM", //@@ -M "-MG", // Treat missing headers as generated. "-MQ", "*", // Quoted target (older version can't handle empty name). s.path ().string ().c_str (), nullptr}; + if (verb >= 2) + print_process (args); + + if (verb >= 5) + tr << "target: " << o; + try { process pr (args, false, false, true); @@ -186,6 +200,9 @@ namespace build path file (next (l, pos)); file.normalize (); + if (verb >= 5) + tr << "prerequisite path: " << file.string (); + // If there is no extension (e.g., standard C++ headers), // then assume it is a header. Otherwise, let the standard // mechanism derive the type from the extension. @@ -282,17 +299,26 @@ namespace build if (!u) return target_state::uptodate; + // Translate paths to relative (to working directory) ones. This + // results in easier to read diagnostics. + // + path ro (translate (o.path ())); + path rs (translate (s->path ())); + const char* args[] = { "g++-4.9", "-std=c++14", "-g", - "-I..", + "-I", src_root.string ().c_str (), "-c", - "-o", o.path ().string ().c_str (), - s->path ().string ().c_str (), + "-o", ro.string ().c_str (), + rs.string ().c_str (), nullptr}; - cerr << "c++ " << *s << endl; + if (verb >= 1) + print_process (args); + else + cerr << "c++ " << *s << endl; try { @@ -407,19 +433,29 @@ namespace build if (!u) return target_state::uptodate; + // Translate paths to relative (to working directory) ones. This + // results in easier to read diagnostics. + // + path re (translate (e.path ())); + vector<path> ro; + vector<const char*> args {"g++-4.9", "-std=c++14", "-g", "-o"}; - args.push_back (e.path ().string ().c_str ()); + args.push_back (re.string ().c_str ()); for (const prerequisite& p: t.prerequisites) { const obj& o (dynamic_cast<const obj&> (*p.target)); - args.push_back (o.path ().string ().c_str ()); + ro.push_back (translate (o.path ())); + args.push_back (ro.back ().string ().c_str ()); } args.push_back (nullptr); - cerr << "ld " << e << endl; + if (verb >= 1) + print_process (args); + else + cerr << "ld " << e << endl; try { diff --git a/build/diagnostics b/build/diagnostics index c8fb169..98f481f 100644 --- a/build/diagnostics +++ b/build/diagnostics @@ -6,9 +6,12 @@ #define BUILD_DIAGNOSTICS #include <tuple> +#include <vector> #include <utility> #include <exception> +#include <build/trace> + namespace build { // Throw this exception to terminate the build. The handler should @@ -16,6 +19,17 @@ namespace build // class error: public std::exception {}; + // Print process commmand line. + // + void + print_process (const char* const* args); + + inline void + print_process (const std::vector<const char*>& args) + { + print_process (args.data ()); + } + // Call a function if there is an exception. // template <typename F, typename T> diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx new file mode 100644 index 0000000..f213707 --- /dev/null +++ b/build/diagnostics.cxx @@ -0,0 +1,20 @@ +// file : build/diagnostics.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include <build/diagnostics> + +#include <iostream> + +using namespace std; + +namespace build +{ + void + print_process (const char* const* args) + { + for (const char* const* p (args); *p != nullptr; p++) + cerr << (p != args ? " " : "") << *p; + cerr << endl; + } +} diff --git a/build/parser.cxx b/build/parser.cxx index e300201..289e571 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -243,6 +243,7 @@ namespace build if (p.relative ()) p = prev.path () / p; + p.normalize (); scope_ = &scopes[p]; // A directory scope can contain anything that a top level can. @@ -218,6 +218,16 @@ namespace build bool root () const; + // Return true if *this is a sub-path of the specified path (i.e., + // the specified path is a prefix). Expects both paths to be + // normalized. + // + bool + sub (const basic_path& p) const + { + return path_.compare (0, p.path_.size (), p.path_) == 0; + } + public: // Return the path without the directory part. // @@ -249,6 +259,13 @@ namespace build basic_path base () const; + // Return a path relative to the specified path that is equivalent + // to *this. Throws invalid_path if a relative path cannot be derived + // (e.g., paths are on different drives on Windows). + // + basic_path + relative (basic_path) const; + public: // Normalize the path. This includes collapsing the '.' and '..' // directories if possible, collapsing multiple directory @@ -334,12 +351,14 @@ namespace build string_type path_; }; + /* template <typename C> inline std::basic_ostream<C>& operator<< (std::basic_ostream<C>& os, basic_path<C> const& p) { return os << p.string (); } + */ } #include <build/path.ixx> diff --git a/build/path.txx b/build/path.txx index 6a01547..3e952d8 100644 --- a/build/path.txx +++ b/build/path.txx @@ -117,8 +117,12 @@ namespace build if (m < n || path_.compare (0, n, d.path_) != 0) throw invalid_basic_path<C> (path_); - if (n != m) - n++; // Skip the directory separator. + if (n != m +#ifndef _WIN32 + && !d.root () +#endif + ) + n++; // Skip the directory separator (unless it is POSIX root). return basic_path (path_.c_str () + n, m - n); } @@ -144,6 +148,28 @@ namespace build } template <typename C> + basic_path<C> basic_path<C>:: + relative (basic_path<C> d) const + { + basic_path r; + + for (;; d = d.directory ()) + { + if (sub (d)) + break; + + r /= path (".."); + + // Roots of the paths do not match. + // + if (d.root ()) + throw invalid_basic_path<C> (path_); + } + + return r / leaf (d); + } + + template <typename C> basic_path<C>& basic_path<C>:: normalize () { diff --git a/build/pre.build b/build/pre.build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/build/pre.build diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index 8153a6c..c43827d 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -6,7 +6,9 @@ #include <ostream> +#include <build/scope> #include <build/target> // target_type +#include <build/context> using namespace std; @@ -15,9 +17,36 @@ namespace build ostream& operator<< (ostream& os, const prerequisite& p) { - // @@ TODO: need to come up with a relative (to current) path. + if (p.target != nullptr) + os << *p.target; + else + { + os << p.type.name << '{'; - return os << p.type.name << '{' << p.name << '}'; + // Print scope unless the directory is absolute. + // + if (!p.directory.absolute ()) + { + string s (diagnostic_string (p.scope.path ())); + + if (!s.empty ()) + os << s << path::traits::directory_separator << ": "; + } + + // Print directory. + // + if (!p.directory.empty ()) + { + string s (diagnostic_string (p.directory)); + + if (!s.empty ()) + os << s << path::traits::directory_separator; + } + + os << p.name << '}'; + } + + return os; } bool diff --git a/build/target.cxx b/build/target.cxx index 8314ba4..4dffe7f 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -6,6 +6,8 @@ #include <ostream> +#include <build/context> + using namespace std; namespace build @@ -15,9 +17,19 @@ namespace build ostream& operator<< (ostream& os, const target& t) { - // @@ TODO: need to come up with a relative (to current) path. + os << t.type ().name << '{'; + + if (!t.directory.empty ()) + { + string s (diagnostic_string (t.directory)); + + if (!s.empty ()) + os << s << path::traits::directory_separator; + } + + os << t.name << '}'; - return os << t.type ().name << '{' << t.name << '}'; + return os; } target_set targets; diff --git a/build/trace b/build/trace new file mode 100644 index 0000000..f1c0567 --- /dev/null +++ b/build/trace @@ -0,0 +1,71 @@ +// file : build/trace -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD_TRACE +#define BUILD_TRACE + +#include <cstdint> +#include <iostream> + +namespace build +{ + // 1 - command lines to update explicit targets (e.g., .o) + // 2 - command lines to update implicit targets (e.g., .d) + // 3 - things didn't work out (e.g., rule did not match) + // 4 - additional information + // 5 - more additional information + // + extern std::uint8_t verb; + + struct tracer + { + explicit + tracer (const char* name): name_ (name) {} + + struct record + { + ~record () {if (!empty_) std::cerr << std::endl;} + + template <typename T> + std::ostream& + operator<< (const T& x) const + { + return std::cerr << x; + } + + // Movable-only type. + // + explicit record (bool e = true): empty_ (e) {} + record (record&& r) {empty_ = r.empty_; r.empty_ = true;} + record& operator= (record&& r) {empty_ = r.empty_; r.empty_ = true;} + + record (const record&) = delete; + record& operator= (const record&) = delete; + + private: + mutable bool empty_; + }; + + template <typename T> + record + operator<< (const T& x) const + { + std::cerr << "trace: " << name_ << ": " << x; + return record (false); + } + + private: + const char* name_; + }; + + template <typename F> + inline void + trace (std::uint8_t level, const F& f) + { + if (verb >= level) + f (); + } +} + +#endif // BUILD_TRACE diff --git a/build/trace.cxx b/build/trace.cxx new file mode 100644 index 0000000..a7e6566 --- /dev/null +++ b/build/trace.cxx @@ -0,0 +1,12 @@ +// file : build/trace.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include <build/trace> + +using namespace std; + +namespace build +{ + uint8_t verb; +} |