From c2b4305349ca855c497904282db354de56c74842 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 7 Aug 2018 14:59:07 +0200 Subject: Add support for default extension specification, trailing dot escaping For example: cxx{*}: extension = cxx cxx{foo} # foo.cxx cxx{foo.test} # foo.test (probably what we want...) cxx{foo.test...} # foo.test.cxx (... is this) cxx{foo..} # foo. cxx{foo....} # foo.. cxx{foo.....} # error (must come in escape pair) --- build2/target.cxx | 142 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 23 deletions(-) (limited to 'build2/target.cxx') diff --git a/build2/target.cxx b/build2/target.cxx index e6188cd..5d11337 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -192,6 +192,79 @@ namespace build2 return r; } + optional target:: + split_name (string& v, const location& loc) + { + // We treat a single trailing dot as "specified no extension", double dots + // as a single trailing dot (that is, an escape sequence which can be + // repeated any number of times; in such cases we naturally assume there + // is no default extension) and triple dots as "unspecified (default) + // extension" (used when the extension in the name is not "ours", for + // example, cxx{foo.test...} for foo.test.cxx). An odd number of dots + // other than one or three is invalid. + // + optional r; + + size_t p; + if (v.back () != '.') + { + if ((p = path::traits::find_extension (v)) != string::npos) + r = string (v.c_str () + p + 1); + } + else + { + if ((p = v.find_last_not_of ('.')) == string::npos) + fail (loc) << "invalid target name '" << v << "'"; + + p++; // Position of the first trailing dot. + size_t n (v.size () - p); // Number of the trailing dots. + + if (n == 1) + r = string (); + else if (n == 3) + ; + else if (n % 2 == 0) + { + p += n / 2; // Keep half of the dots. + r = string (); + } + else + fail (loc) << "invalid trailing dot sequence in target name '" + << v << "'"; + } + + if (p != string::npos) + v.resize (p); + + return r; + } + + void target:: + combine_name (string& v, const optional& e, bool de) + { + if (v.back () == '.') + { + assert (e && e->empty ()); + + size_t p (v.find_last_not_of ('.')); + assert (p != string::npos); + + p++; // Position of the first trailing dot. + size_t n (v.size () - p); // Number of the trailing dots. + v.append (n, '.'); // Double them. + } + else if (e) + { + v += '.'; + v += *e; // Empty or not. + } + else if (de) + { + if (path::traits::find_extension (v) != string::npos) + v += "..."; + } + } + // target_set // target_set targets; @@ -802,7 +875,12 @@ namespace build2 } static bool - dir_pattern (const target_type&, const scope&, string& v, bool r) + dir_pattern (const target_type&, + const scope&, + string& v, + optional&, + const location&, + bool r) { // Add/strip trailing directory separator unless already there. // @@ -872,19 +950,27 @@ namespace build2 #ifdef _WIN32 static bool - exe_target_pattern (const target_type&, const scope&, string& v, bool r) + exe_target_pattern (const target_type&, + const scope&, + string& v, + optional& e, + const location& l, + bool r) { - size_t p (path::traits::find_extension (v)); - if (r) { - assert (p != string::npos); - v.resize (p); + assert (e); + e = nullopt; } - else if (p == string::npos) + else { - v += ".exe"; - return true; + e = target::split_name (v, l); + + if (!e) + { + e = "exe"; + return true; + } } return false; @@ -921,19 +1007,24 @@ namespace build2 buildfile_target_pattern (const target_type&, const scope&, string& v, + optional& e, + const location& l, bool r) { - size_t p (path::traits::find_extension (v)); - if (r) { - assert (p != string::npos); - v.resize (p); + assert (e); + e = nullopt; } - else if (p == string::npos && v != "buildfile") + else { - v += ".build"; - return true; + e = target::split_name (v, l); + + if (!e && v != "buildfile") + { + e = "build"; + return true; + } } return false; @@ -1015,19 +1106,24 @@ namespace build2 manifest_target_pattern (const target_type&, const scope&, string& v, + optional& e, + const location& l, bool r) { - size_t p (path::traits::find_extension (v)); - if (r) { - assert (p != string::npos); - v.resize (p); + assert (e); + e = nullopt; } - else if (p == string::npos && v != "manifest") + else { - v += ".manifest"; - return true; + e = target::split_name (v, l); + + if (!e && v != "manifest") + { + e = "manifest"; + return true; + } } return false; -- cgit v1.1