From ce8a94e6a76097ef7eeb34df4257991a20599712 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 14 Jan 2015 11:39:21 +0200 Subject: Track file extension in target, prerequisite --- build/algorithm.cxx | 27 ++++++++++++++-- build/buildfile | 4 ++- build/context.cxx | 4 +-- build/cxx/rule.cxx | 27 ++++++++++++++-- build/parser.cxx | 84 +++++++++++++++++++++++++++++++++++++++++++++----- build/path | 5 +++ build/path.txx | 28 +++++++++++++++++ build/prerequisite | 11 ++++--- build/prerequisite.cxx | 18 +++++++++-- build/target | 25 ++++++++++----- build/target.cxx | 7 ++++- build/trace | 13 ++++++-- build/utility | 13 ++++++++ build/utility.cxx | 12 ++++++++ 14 files changed, 244 insertions(+), 34 deletions(-) create mode 100644 build/utility.cxx (limited to 'build') diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 419bfaf..1d7f215 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -45,13 +45,34 @@ namespace build // auto r ( targets.emplace ( - unique_ptr (p.type.factory (p.name, move (d))))); + unique_ptr (p.type.factory (move (d), p.name, p.ext)))); + + target& t (**r.first); trace (4, [&]{ - tr << (r.second ? "new" : "existing") << " target " << **r.first + tr << (r.second ? "new" : "existing") << " target " << t << " for prerequsite " << p;}); - return (p.target = r.first->get ()); + // Update extension if the existing target has it unspecified. + // + if (t.ext != p.ext) + { + trace (4, [&]{ + tracer::record r (tr); + r << "assuming target " << t << " is the same as the one with "; + if (p.ext == nullptr) + r << "unspecified extension"; + else if (p.ext->empty ()) + r << "no extension"; + else + r << "extension " << *p.ext; + }); + + if (p.ext != nullptr) + t.ext = p.ext; + } + + return (p.target = &t); } bool diff --git a/build/buildfile b/build/buildfile index 3afe19b..73f2e91 100644 --- a/build/buildfile +++ b/build/buildfile @@ -1,5 +1,6 @@ exe{b1}: obj{b algorithm scope parser lexer trace target prerequisite rule \ - native context diagnostics cxx/target cxx/rule process timestamp path} + native context diagnostics cxx/target cxx/rule process timestamp path \ + utility} obj{b}: cxx{b} obj{algorithm}: cxx{algorithm} @@ -18,3 +19,4 @@ obj{cxx/rule}: cxx{cxx/rule} obj{process}: cxx{process} obj{timestamp}: cxx{timestamp} obj{path}: cxx{path} +obj{utility}: cxx{utility} diff --git a/build/context.cxx b/build/context.cxx index fc4ec1c..2eb76c0 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -2,10 +2,10 @@ // copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file -#include - #include +#include + using namespace std; namespace build diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 1d2a2c4..c6ceb1a 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -211,19 +211,40 @@ namespace build // @@ TODO: // - // Split the name into its directory part and the name part. - // Here we assume the name part is a valid filesystem name. + // Split the name into its directory part, the name part, and + // extension. Here we can assume the name part is a valid + // filesystem name. // path d (file.directory ()); string n (file.leaf ().base ().string ()); + const char* es (file.extension ()); + const string* e (&extension_pool.find (es != nullptr ? es : "")); // Find or insert. // auto r (ds.prerequisites.emplace ( - hxx::static_type, move (n), move (d), ds)); + 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; + } + // Resolve to target so that we can assign its path. // path_target& t ( diff --git a/build/parser.cxx b/build/parser.cxx index 289e571..77571fb 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -13,6 +13,7 @@ #include #include #include +#include using namespace std; @@ -49,6 +50,8 @@ namespace build void parser:: parse_clause (token& t, token_type& tt) { + tracer tr ("parser::parse_clause"); + while (tt != type::eos) { // We always start with one or more names. @@ -93,13 +96,14 @@ namespace build const target_type& ti (i->second); - // We need to split the name into its directory part (if any) - // and the name part. We cannot assume the name part is a - // valid filesystem name so we will have to do the splitting - // manually. + // We need to split the path into its directory part (if any) + // the name part, and the extension (if any). We cannot assume + // the name part is a valid filesystem name so we will have + // to do the splitting manually. // path d; string n; + const string* e (nullptr); { path::size_type i (path::traits::rfind_separator (pn.name)); @@ -112,6 +116,16 @@ namespace build n.assign (pn.name, i + 1, string::npos); d.normalize (); } + + // Extract extension. + // + string::size_type j (n.rfind ('.')); + + if (j != string::npos) + { + e = &extension_pool.find (n.c_str () + j + 1); + n.resize (j); + } } //cout << "prerequisite " << tt << " " << n << " " << d << endl; @@ -119,15 +133,39 @@ namespace build // Find or insert. // auto r (scope_->prerequisites.emplace ( - ti, move (n), move (d), *scope_)); + ti, move (d), move (n), e, *scope_)); + + 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 == nullptr) + r << "unspecified extension"; + else if (e->empty ()) + r << "no extension"; + else + r << "extension " << *e; + }); + + if (e != nullptr) + p.ext = e; + } - ps.push_back (const_cast (*r.first)); + ps.push_back (p); } for (auto& tn: tns) { path d; string n; + const string* e (nullptr); // The same deal as in handling prerequisites above. // @@ -149,6 +187,16 @@ namespace build d.normalize (); } + + // Extract extension. + // + string::size_type j (n.rfind ('.')); + + if (j != string::npos) + { + e = &extension_pool.find (n.c_str () + j + 1); + n.resize (j); + } } // Resolve target type. @@ -178,11 +226,31 @@ namespace build // auto r ( targets.emplace ( - unique_ptr (ti.factory (move (n), move (d))))); + unique_ptr (ti.factory (move (d), move (n), e)))); target& t (**r.first); - t.prerequisites = ps; //@@ TODO: move is last target. + // Update extension if the existing target has it unspecified. + // + if (t.ext != e) + { + trace (4, [&]{ + tracer::record r (tr); + r << "assuming target " << t << " is the same as the " + << "one with "; + if (e == nullptr) + r << "unspecified extension"; + else if (e->empty ()) + r << "no extension"; + else + r << "extension " << *e; + }); + + if (e != nullptr) + t.ext = e; + } + + t.prerequisites = ps; //@@ TODO: move if last target. if (default_target == nullptr) default_target = &t; diff --git a/build/path b/build/path index 92832a1..dbd048d 100644 --- a/build/path +++ b/build/path @@ -259,6 +259,11 @@ namespace build basic_path base () const; + // Return the extension or NULL if not present. + // + const C* + extension () 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). diff --git a/build/path.txx b/build/path.txx index 3e952d8..f576870 100644 --- a/build/path.txx +++ b/build/path.txx @@ -62,6 +62,34 @@ namespace build return *this; } + template + const C* basic_path:: + extension () const + { + size_type i (path_.size ()); + + for (; i > 0; --i) + { + if (path_[i - 1] == '.') + break; + + if (traits::is_separator (path_[i - 1])) + { + i = 0; + break; + } + } + + // Weed out paths like ".txt" and "/.txt" + // + if (i > 1 && !traits::is_separator (path_[i - 2])) + { + return path_.c_str () + i; + } + else + return nullptr; + } + #ifdef _WIN32 template typename basic_path::string_type basic_path:: diff --git a/build/prerequisite b/build/prerequisite index e58532d..6c9c171 100644 --- a/build/prerequisite +++ b/build/prerequisite @@ -12,6 +12,7 @@ #include #include +#include // extension_pool namespace build { @@ -27,18 +28,20 @@ namespace build typedef build::scope scope_type; prerequisite (const target_type_type& t, - std::string n, path d, + std::string n, + const std::string* e, scope_type& s) - : type (t), name (std::move (n)), directory (std::move (d)), + : type (t), directory (std::move (d)), name (std::move (n)), ext (e), scope (s), target (0) {} public: const target_type_type& type; + const path directory; // Normalized absolute or relative (to scope). const std::string name; - const path directory; // Normalized absolute or relative (to scope). + const std::string* ext; // NULL if unspecified. scope_type& scope; - target_type* target; // NULL if not yet resolved. + target_type* target; // NULL if not yet resolved. }; std::ostream& diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index c43827d..370f5d0 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -43,7 +43,12 @@ namespace build os << s << path::traits::directory_separator; } - os << p.name << '}'; + os << p.name; + + if (p.ext != nullptr) + os << '.' << *p.ext; + + os << '}'; } return os; @@ -52,10 +57,19 @@ namespace build bool operator< (const prerequisite& x, const prerequisite& y) { + //@@ TODO: use compare() to compare once. + + // Unspecified and specified extension are assumed equal. The + // extension strings are from the pool, so we can just compare + // pointers. + // return (x.type.id < y.type.id) || (x.type.id == y.type.id && x.name < y.name) || (x.type.id == y.type.id && x.name == y.name && - x.directory < y.directory); + x.directory < y.directory) || + (x.type.id == y.type.id && x.name == y.name && + x.directory == y.directory && + x.ext != nullptr && y.ext != nullptr && x.ext < y.ext); } } diff --git a/build/target b/build/target index a381fd0..508aedb 100644 --- a/build/target +++ b/build/target @@ -19,7 +19,7 @@ #include #include #include -#include // compare_c_string, compare_pointer_target +#include // compare_*, extension_pool namespace build { @@ -33,17 +33,18 @@ namespace build std::type_index id; const char* name; const target_type* base; - target* (*const factory) (std::string, path); + target* (*const factory) (path, std::string, const std::string*); }; class target { public: - target (std::string n, path d) - : name (std::move (n)), directory (std::move (d)) {} + target (path d, std::string n, const std::string* e) + : directory (std::move (d)), name (std::move (n)), ext (e) {} + const path directory; // Absolute and normalized. const std::string name; - const path directory; // Absolute and normalized. + const std::string* ext; // Extension, NULL means unspecified. public: typedef @@ -89,10 +90,18 @@ namespace build { std::type_index tx (typeid (x)), ty (typeid (y)); + //@@ TODO: use compare() to compare once. + + // Unspecified and specified extension are assumed equal. The + // extension strings are from the pool, so we can just compare + // pointers. + // return (tx < ty) || (tx == ty && x.name < y.name) || - (tx == ty && x.name == y.name && x.directory < y.directory); + (tx == ty && x.name == y.name && x.directory < y.directory) || + (tx == ty && x.name == y.name && x.directory == y.directory && + x.ext != nullptr && y.ext != nullptr && x.ext < y.ext); } typedef std::set, compare_pointer_target> target_set; @@ -113,9 +122,9 @@ namespace build template target* - target_factory (std::string n, path d) + target_factory (path d, std::string n, const std::string* e) { - return new T (std::move (n), std::move (d)); + return new T (std::move (d), std::move (n), e); } // Modification time-based target. diff --git a/build/target.cxx b/build/target.cxx index 4dffe7f..61423db 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -27,7 +27,12 @@ namespace build os << s << path::traits::directory_separator; } - os << t.name << '}'; + os << t.name; + + if (t.ext != nullptr) + os << '.' << *t.ext; + + os << '}'; return os; } diff --git a/build/trace b/build/trace index f1c0567..1764a69 100644 --- a/build/trace +++ b/build/trace @@ -34,9 +34,11 @@ namespace build return std::cerr << x; } + explicit record (tracer& t): empty_ (false) {t.begin ();} + explicit record (bool e = true): empty_ (e) {} + // 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;} @@ -51,10 +53,17 @@ namespace build record operator<< (const T& x) const { - std::cerr << "trace: " << name_ << ": " << x; + begin (); + std::cerr << x; return record (false); } + void + begin () const + { + std::cerr << "trace: " << name_ << ": "; + } + private: const char* name_; }; diff --git a/build/utility b/build/utility index bef6335..bcbf834 100644 --- a/build/utility +++ b/build/utility @@ -5,8 +5,11 @@ #ifndef BUILD_UTILITY #define BUILD_UTILITY +#include +#include #include // strcmp + namespace build { struct compare_c_string @@ -22,6 +25,16 @@ namespace build template bool operator() (const P& x, const P& y) const {return *x < *y;} }; + + // Pools (@@ perhaps move into a separate header). + // + struct string_pool: std::unordered_set + { + const std::string& + find (const char* s) {return *emplace (s).first;} + }; + + extern string_pool extension_pool; } #endif // BUILD_UTILITY diff --git a/build/utility.cxx b/build/utility.cxx new file mode 100644 index 0000000..f3fd51b --- /dev/null +++ b/build/utility.cxx @@ -0,0 +1,12 @@ +// file : build/utility.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +using namespace std; + +namespace build +{ + string_pool extension_pool; +} -- cgit v1.1