diff options
Diffstat (limited to 'libbuild2/functions-path.cxx')
-rw-r--r-- | libbuild2/functions-path.cxx | 365 |
1 files changed, 349 insertions, 16 deletions
diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx index b7c9a8d..020c8f4 100644 --- a/libbuild2/functions-path.cxx +++ b/libbuild2/functions-path.cxx @@ -10,6 +10,9 @@ using namespace std; namespace build2 { + extern bool + functions_sort_flags (optional<names>); // functions-builtin.cxx + static value path_thunk (const scope* base, vector_view<value> args, @@ -96,6 +99,20 @@ namespace build2 } } + template <typename P> + static inline P + relative (const P& p, const dir_path& d) + { + try + { + return p.relative (d); // Note: cannot move due to diagnostics. + } + catch (const invalid_path&) + { + fail << "'" << p << "' cannot be made relative to '" << d << "'" << endf; + } + } + using butl::path_match; // Return true if a path matches the pattern. See path_match() overloads @@ -137,6 +154,45 @@ namespace build2 return path_match (entry, pattern, *start); } + // Don't fail for absolute paths on Windows and, for example, just return + // c:/foo for c:\foo. + // + template <typename P> + static inline string + posix_string (P&& p) + { +#ifndef _WIN32 + return move (p).posix_string (); +#else + if (p.relative ()) + return move (p).posix_string (); + + // Note: also handles root directories. + // + dir_path d (p.root_directory ()); + return d.string () + '/' + p.leaf (d).posix_string (); +#endif + } + + // Similar to the above don't fail for absolute paths on Windows. + // + template <typename P> + static inline string + posix_representation (P&& p) + { +#ifndef _WIN32 + return move (p).posix_representation (); +#else + if (p.relative ()) + return move (p).posix_representation (); + + // Note: also handles root directories. + // + dir_path d (p.root_directory ()); + return d.string () + '/' + p.leaf (d).posix_representation (); +#endif + } + void path_functions (function_map& m) { @@ -144,7 +200,13 @@ namespace build2 // string // - f["string"] += [](path p) {return move (p).string ();}; + // Note that we must handle NULL values (relied upon by the parser + // to provide conversion semantics consistent with untyped values). + // + f["string"] += [](path* p) + { + return p != nullptr ? move (*p).string () : string (); + }; f["string"] += [](paths v) { @@ -162,6 +224,41 @@ namespace build2 return r; }; + // posix_string + // + f["posix_string"] += [](path p) {return posix_string (move (p));}; + f["posix_string"] += [](dir_path p) {return posix_string (move (p));}; + + f["posix_string"] += [](paths v) + { + strings r; + for (auto& p: v) + r.push_back (posix_string (move (p))); + return r; + }; + + f["posix_string"] += [](dir_paths v) + { + strings r; + for (auto& p: v) + r.push_back (posix_string (move (p))); + return r; + }; + + f[".posix_string"] += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of strings. + // + for (name& n: ns) + { + n = n.directory () + ? posix_string (move (n.dir)) + : posix_string (convert<path> (move (n))); + } + return ns; + }; + // representation // f["representation"] += [](path p) {return move (p).representation ();}; @@ -182,6 +279,48 @@ namespace build2 return r; }; + // posix_representation + // + f["posix_representation"] += [](path p) + { + return posix_representation (move (p)); + }; + + f["posix_representation"] += [](dir_path p) + { + return posix_representation (move (p)); + }; + + f["posix_representation"] += [](paths v) + { + strings r; + for (auto& p: v) + r.push_back (posix_representation (move (p))); + return r; + }; + + f["posix_representation"] += [](dir_paths v) + { + strings r; + for (auto& p: v) + r.push_back (posix_representation (move (p))); + return r; + }; + + f[".posix_representation"] += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of strings. + // + for (name& n: ns) + { + n = n.directory () + ? posix_representation (move (n.dir)) + : posix_representation (convert<path> (move (n))); + } + return ns; + }; + // canonicalize // // @@ TODO: add ability to specify alternative separator. @@ -295,7 +434,11 @@ namespace build2 return ns; }; - // directory + // $directory(<path>) + // $directory(<paths>) + // + // Return the directory part of the path or empty path if there is no + // directory. Directory of a root directory is an empty path. // f["directory"] += &path::directory; @@ -329,44 +472,55 @@ namespace build2 return ns; }; - // base + // $root_directory(<path>) + // $root_directory(<paths>) // - f["base"] += &path::base; + // Return the root directory of the path or empty path if the directory is + // not absolute. + // + f["root_directory"] += &path::root_directory; - f["base"] += [](paths v) + f["root_directory"] += [](paths v) { - for (path& p: v) - p = p.base (); - return v; + dir_paths r; + for (const path& p: v) + r.push_back (p.root_directory ()); + return r; }; - f["base"] += [](dir_paths v) + f["root_directory"] += [](dir_paths v) { for (dir_path& p: v) - p = p.base (); + p = p.root_directory (); return v; }; - f[".base"] += [](names ns) + f[".root_directory"] += [](names ns) { // For each path decide based on the presence of a trailing slash - // whether it is a directory. Return as untyped list of (potentially - // mixed) paths. + // whether it is a directory. Return as list of directory names. // for (name& n: ns) { if (n.directory ()) - n.dir = n.dir.base (); + n.dir = n.dir.root_directory (); else - n.value = convert<path> (move (n)).base ().string (); + n = convert<path> (move (n)).root_directory (); } return ns; }; - // leaf + // $leaf(<path>) // f["leaf"] += &path::leaf; + // $leaf(<path>, <dir-path>) + // $leaf(<paths>, <dir-path>) + // + // Return the path without the specified directory part. Return empty path + // if the paths are the same. Issue diagnostics and fail if the directory + // is not a prefix of the path. Note: expects both paths to be normalized. + // f["leaf"] += [](path p, dir_path d) { return leaf (p, move (d)); @@ -402,6 +556,83 @@ namespace build2 return ns; }; + // $relative(<path>, <dir-path>) + // $relative(<paths>, <dir-path>) + // + // Return a path relative to the specified directory that is equivalent to + // the specified path. Issue diagnostics and fail if a relative path + // cannot be derived (for example, paths are on different drives on + // Windows). + // + f["relative"] += [](path p, dir_path d) + { + return relative (p, d); + }; + + f["relative"] += [](paths v, dir_path d) + { + for (path& p: v) + p = relative (p, d); + return v; + }; + + f["relative"] += [](dir_paths v, dir_path d) + { + for (dir_path& p: v) + p = relative (p, d); + return v; + }; + + f[".relative"] += [](names ns, dir_path d) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of (potentially + // mixed) paths. + // + for (name& n: ns) + { + if (n.directory ()) + n.dir = relative (n.dir, d); + else + n.value = relative (convert<path> (move (n)), d).string (); + } + return ns; + }; + + // base + // + f["base"] += &path::base; + + f["base"] += [](paths v) + { + for (path& p: v) + p = p.base (); + return v; + }; + + f["base"] += [](dir_paths v) + { + for (dir_path& p: v) + p = p.base (); + return v; + }; + + f[".base"] += [](names ns) + { + // For each path decide based on the presence of a trailing slash + // whether it is a directory. Return as untyped list of (potentially + // mixed) paths. + // + for (name& n: ns) + { + if (n.directory ()) + n.dir = n.dir.base (); + else + n.value = convert<path> (move (n)).base ().string (); + } + return ns; + }; + // extension // f["extension"] += &extension; @@ -411,6 +642,93 @@ namespace build2 return extension (convert<path> (move (ns))); }; + // $size(<paths>) + // $size(<dir_paths>) + // + // Return the number of elements in the sequence. + // + f["size"] += [] (paths v) {return v.size ();}; + f["size"] += [] (dir_paths v) {return v.size ();}; + + // $size(<path>) + // $size(<dir_path>) + // + // Return the number of characters (bytes) in the path. Note that for + // dir_path the result does not include the trailing directory separator + // (except for the POSIX root directory). + // + f["size"] += [] (path v) {return v.size ();}; + f["size"] += [] (dir_path v) {return v.size ();}; + + // $sort(<paths> [, <flags>]) + // $sort(<dir_paths> [, <flags>]) + // + // Sort paths in ascending order. Note that on hosts with a case- + // insensitive filesystem the order is case-insensitive. + // + // The following flags are supported: + // + // dedup - in addition to sorting also remove duplicates + // + f["sort"] += [](paths v, optional<names> fs) + { + sort (v.begin (), v.end ()); + + if (functions_sort_flags (move (fs))) + v.erase (unique (v.begin(), v.end()), v.end ()); + + return v; + }; + + f["sort"] += [](dir_paths v, optional<names> fs) + { + sort (v.begin (), v.end ()); + + if (functions_sort_flags (move (fs))) + v.erase (unique (v.begin(), v.end()), v.end ()); + + return v; + }; + + // $find(<paths>, <path>) + // $find(<dir_paths>, <dir_path>) + // + // Return true if the path sequence contains the specified path. Note that + // on hosts with a case-insensitive filesystem the comparison is + // case-insensitive. + // + f["find"] += [](paths vs, value v) + { + return find (vs.begin (), vs.end (), + convert<path> (move (v))) != vs.end (); + }; + + f["find"] += [](dir_paths vs, value v) + { + return find (vs.begin (), vs.end (), + convert<dir_path> (move (v))) != vs.end (); + }; + + // $find_index(<paths>, <path>) + // $find_index(<dir_paths>, <dir_path>) + // + // Return the index of the first element in the path sequence that is + // equal to the specified path or $size(<paths>) if none is found. Note + // that on hosts with a case-insensitive filesystem the comparison is + // case-insensitive. + // + f["find_index"] += [](paths vs, value v) + { + auto i (find (vs.begin (), vs.end (), convert<path> (move (v)))); + return i != vs.end () ? i - vs.begin () : vs.size (); + }; + + f["find_index"] += [](dir_paths vs, value v) + { + auto i (find (vs.begin (), vs.end (), convert<dir_path> (move (v)))); + return i != vs.end () ? i - vs.begin () : vs.size (); + }; + // $path.match(<val>, <pat> [, <start>]) // // Match a filesystem entry name against a name pattern (both are strings), @@ -498,6 +816,11 @@ namespace build2 // function_family b (m, "builtin", &path_thunk); + // Note that while we should normally handle NULL values (relied upon by + // the parser to provide concatenation semantics consistent with untyped + // values), the result will unlikely be what the user expected, especially + // if the NULL value is on the LHS. So for now we keep it a bit tighter. + // b[".concat"] += &concat_path_string; b[".concat"] += &concat_dir_path_string; @@ -510,5 +833,15 @@ namespace build2 { return concat_dir_path_string (move (l), convert<string> (move (ur))); }; + + b[".concat"] += [](dir_path l, dir_path r) + { + return value (move (l /= r)); + }; + + b[".concat"] += [](dir_path l, path r) + { + return value (path_cast<path> (move (l)) /= r); + }; } } |