diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-03-03 11:43:03 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-03-03 11:43:03 +0200 |
commit | 17287b34a8090d381278c02b7bc6676669968099 (patch) | |
tree | 276fb40c1a06b0b3683b0dcad24e92f59cecce9f /build | |
parent | d4a6fb02ab5741aa41251653f0be3feb4594e553 (diff) |
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.
Diffstat (limited to 'build')
-rw-r--r-- | build/b.cxx | 13 | ||||
-rw-r--r-- | build/parser | 8 | ||||
-rw-r--r-- | build/parser.cxx | 274 | ||||
-rw-r--r-- | build/search.cxx | 1 | ||||
-rw-r--r-- | build/target | 6 | ||||
-rw-r--r-- | 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 |