From 17287b34a8090d381278c02b7bc6676669968099 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 3 Mar 2015 11:43:03 +0200 Subject: Implement new default target logic, canonical directory name (empty value) The logic is as follows: if we have an explicit current directory target, then that's the default target. Otherwise, we take the first target and use it as a prerequisite to create an implicit current directory target, effectively making it the default target via an alias. If there are no targets in this buildfile, then we don't do anything. --- build/b.cxx | 13 +-- build/parser | 8 ++ build/parser.cxx | 274 ++++++++++++++++++++++++++++--------------------------- build/search.cxx | 1 + build/target | 6 +- build/target.cxx | 1 - 6 files changed, 158 insertions(+), 145 deletions(-) diff --git a/build/b.cxx b/build/b.cxx index 659eb40..edefbe9 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -205,20 +205,21 @@ main (int argc, char* argv[]) // Build. // - if (default_target == nullptr) - fail << "no default target"; + auto i (targets.find (dir::static_type.id, out_base, "", nullptr, trace)); + if (i == targets.end ()) + fail << "no targets in " << bf; - target& d (*default_target); + target& t (**i); - match (d); + match (t); dump (); - switch (update (d)) + switch (update (t)) { case target_state::uptodate: { - info << "target " << d << " is up to date"; + info << "target " << t << " is up to date"; break; } case target_state::updated: diff --git a/build/parser b/build/parser index 2e399c9..e5aeb60 100644 --- a/build/parser +++ b/build/parser @@ -17,6 +17,7 @@ namespace build { class scope; + class target; class lexer; class parser @@ -61,6 +62,12 @@ namespace build // Utilities. // private: + void + process_default_target (token&); + + // Lexer. + // + private: token_type next (token&, token_type&); @@ -83,6 +90,7 @@ namespace build const std::string* path_; // Path processed by diagnostic_string(). lexer* lexer_; scope* scope_; + target* default_target_; token peek_ {token_type::eos, false, 0, 0}; bool peeked_ {false}; diff --git a/build/parser.cxx b/build/parser.cxx index 20918bf..d5b07b1 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -36,20 +36,82 @@ namespace build // Given a target or prerequisite name, figure out its type, taking // into account extensions, trailing '/', or anything else that might - // be relevant. + // be relevant. Also process the name (in place) by extracting the + // extension, adjusting dir/value, etc. // - static const char* - find_target_type (const string& n, const string* e) + const target_type& + find_target_type (name& n, const location& l, const string*& ext) { - // Empty name or a name ending with a directory separator - // signifies a directory. + string& v (n.value); + + // First determine the target type. // - if (n.empty () || path::traits::is_separator (n.back ())) - return "dir"; + const char* tt; + if (n.type.empty ()) + { + // Empty name, '.' and '..', or a name ending with a directory + // separator signifies a directory. + // + if (v.empty () || v == "." || v == ".." || + path::traits::is_separator (v.back ())) + tt = "dir"; + else + //@@ TODO: derive type from extension. + // + tt = "file"; + } + else + tt = n.type.c_str (); + + auto i (target_types.find (tt)); + if (i == target_types.end ()) + fail (l) << "unknown target type " << tt; + + const target_type& ti (i->second); - //@@ TODO: derive type from extension. + ext = nullptr; + + // Directories require special name processing. If we find that more + // targets deviate, then we should make this target-type-specific. // - return "file"; + if (ti.id == dir::static_type.id || ti.id == fsdir::static_type.id) + { + // The canonical representation of a directory name is with empty + // value. + // + if (!v.empty ()) + { + n.dir /= path (v); // Move name value to dir. + v.clear (); + } + } + else + { + // 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::size_type i (path::traits::rfind_separator (v)); + + if (i != string::npos) + { + n.dir /= path (v, i != 0 ? i : 1); // Special case: "/". + v = string (v, i + 1, string::npos); + } + + // Extract the extension. + // + string::size_type j (path::traits::find_extension (v)); + + if (j != string::npos) + { + ext = &extension_pool.find (v.c_str () + j); + v.resize (j - 1); + } + } + + return ti; } void parser:: @@ -61,6 +123,7 @@ namespace build lexer l (is, p.string ()); lexer_ = &l; scope_ = &s; + default_target_ = nullptr; token t (type::eos, false, 0, 0); type tt; @@ -70,6 +133,8 @@ namespace build if (tt != type::eos) fail (t) << "unexpected " << t; + + process_default_target (t); } void parser:: @@ -118,6 +183,7 @@ namespace build // ': foo' is equvalent to '{}: foo' and to 'dir{}: foo'. // + location nloc (get_location (t, &path_)); names_type ns (tt != type::colon ? names (t, tt) : names_type ({name ("")})); @@ -215,6 +281,7 @@ namespace build tt == type::newline || tt == type::eos) { + location ploc (get_location (t, &path_)); names_type pns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -226,156 +293,46 @@ namespace build for (auto& pn: pns) { - // 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 (pn.dir); - string n; - const string* e (nullptr); - - { - string& v (pn.value); - - path::size_type i (path::traits::rfind_separator (v)); - - if (i == string::npos) - n = move (v); // NOTE: steal! - else - { - d /= path (v, i != 0 ? i : 1); // Special case: "/". - n.assign (v, i + 1, string::npos); - } - - // Handle '.' and '..'. - // - if (n == ".") - n.clear (); - else if (n == "..") - { - d /= path (n); - n.clear (); - } - - d.normalize (); - - // Extract extension. - // - string::size_type j (path::traits::find_extension (n)); - - if (j != string::npos) - { - e = &extension_pool.find (n.c_str () + j); - n.resize (j - 1); - } - } - - // Resolve prerequisite type. - // - const char* tt (pn.type.empty () - ? find_target_type (n, e) - : pn.type.c_str ()); - - auto i (target_types.find (tt)); + const string* e; + const target_type& ti (find_target_type (pn, ploc, e)); - if (i == target_types.end ()) - { - //@@ TODO name (or better yet, type) location - - fail (t) << "unknown prerequisite type " << tt; - } - - const target_type& ti (i->second); + pn.dir.normalize (); // Find or insert. // prerequisite& p ( scope_->prerequisites.insert ( - ti, move (d), move (n), e, *scope_, trace).first); + ti, move (pn.dir), move (pn.value), e, *scope_, trace).first); ps.push_back (p); } for (auto& tn: ns) { - path d (tn.dir); - string n; - const string* e (nullptr); - - // The same deal as in handling prerequisites above. - // - { - string& v (tn.value); - - path::size_type i (path::traits::rfind_separator (v)); - - if (i == string::npos) - n = move (v); // NOTE: steal! - else - { - d /= path (v, i != 0 ? i : 1); // Special case: "/". - n.assign (v, i + 1, string::npos); - } - - // Handle '.' and '..'. - // - if (n == ".") - n.clear (); - else if (n == "..") - { - d /= path (n); - n.clear (); - } - - if (d.empty ()) - d = scope_->path (); // Already normalized. - else - { - if (d.relative ()) - d = scope_->path () / d; - - d.normalize (); - } - - // Extract extension. - // - string::size_type j (path::traits::find_extension (n)); - - if (j != string::npos) - { - e = &extension_pool.find (n.c_str () + j); - n.resize (j - 1); - } - } - - // Resolve target type. - // - const char* tt (tn.type.empty () - ? find_target_type (n, e) - : tn.type.c_str ()); + const string* e; + const target_type& ti (find_target_type (tn, nloc, e)); + path& d (tn.dir); - auto i (target_types.find (tt)); - - if (i == target_types.end ()) + if (d.empty ()) + d = scope_->path (); // Already normalized. + else { - //@@ TODO name (or better yet, type) location + if (d.relative ()) + d = scope_->path () / d; - fail (t) << "unknown target type " << tt; + d.normalize (); } - const target_type& ti (i->second); - // Find or insert. // target& t ( targets.insert ( - ti, move (d), move (n), e, trace).first); + ti, move (tn.dir), move (tn.value), e, trace).first); t.prerequisites = ps; //@@ OPT: move if last target. - if (default_target == nullptr) - default_target = &t; + if (default_target_ == nullptr) + default_target_ = &t; } if (tt == type::newline) @@ -585,14 +542,20 @@ namespace build scope* os (scope_); scope_ = &scopes[(in_out ? p : out_src (p)).directory ()]; + target* odt (default_target_); + default_target_ = nullptr; + next (t, tt); clause (t, tt); if (tt != type::eos) fail (t) << "unexpected " << t; + process_default_target (t); + level4 ([&]{trace (t) << "leaving " << p;}); + default_target_ = odt; scope_ = os; lexer_ = ol; path_ = op; @@ -864,6 +827,47 @@ namespace build } } + void parser:: + process_default_target (token& t) + { + tracer trace ("parser::process_default_target", &path_); + + // The logic is as follows: if we have an explicit current directory + // target, then that's the default target. Otherwise, we take the + // first target and use it as a prerequisite to create an implicit + // current directory target, effectively making it the default + // target via an alias. If there are no targets in this buildfile, + // then we don't do anything. + // + if (default_target_ == nullptr || // No targets in this buildfile. + targets.find (dir::static_type.id, // Explicit current dir target. + scope_->path (), + "", + nullptr, + trace) != targets.end ()) + return; + + target& dt (*default_target_); + + level4 ([&]{trace (t) << "creating current directory alias for " << dt;}); + + target& ct ( + targets.insert ( + dir::static_type, scope_->path (), "", nullptr, trace).first); + + prerequisite& p ( + scope_->prerequisites.insert ( + dt.type (), + dt.dir, + dt.name, + dt.ext, + *scope_, // Doesn't matter which scope since dir is absolute. + trace).first); + + p.target = &dt; + ct.prerequisites.push_back (p); + } + token_type parser:: next (token& t, token_type& tt) { diff --git a/build/search.cxx b/build/search.cxx index 966f5f1..e80f7e7 100644 --- a/build/search.cxx +++ b/build/search.cxx @@ -67,6 +67,7 @@ namespace build for (const path& d: sp) { path f (d / p.dir / path (p.name)); + f.normalize (); // @@ TMP: use target name as an extension. // diff --git a/build/target b/build/target index 0f5de4f..31a590d 100644 --- a/build/target +++ b/build/target @@ -135,10 +135,11 @@ namespace build find (const std::type_index& type, const path& dir, const std::string& name, - const std::string*& ext, + const std::string* ext, tracer& trace) const { - return find (key {&type, &dir, &name, &ext}, trace); + const std::string* e (ext); + return find (key {&type, &dir, &name, &e}, trace); } iterator begin () const {return map_.begin ();} @@ -156,7 +157,6 @@ namespace build }; extern target_set targets; - extern target* default_target; class target_type_map: public std::map< const char*, diff --git a/build/target.cxx b/build/target.cxx index 4168991..136b912 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -113,7 +113,6 @@ namespace build } target_set targets; - target* default_target = nullptr; target_type_map target_types; // path_target -- cgit v1.1