diff options
Diffstat (limited to 'libbuild2/functions-path.cxx')
-rw-r--r-- | libbuild2/functions-path.cxx | 438 |
1 files changed, 331 insertions, 107 deletions
diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx index 4b114f5..2f7f159 100644 --- a/libbuild2/functions-path.cxx +++ b/libbuild2/functions-path.cxx @@ -193,6 +193,35 @@ namespace build2 #endif } + template <typename P> + static bool + try_normalize (P& p) + { + try + { + p.normalize (); + return true; + } + catch (const invalid_path&) {} + + return false; + } + + template <typename P> + static bool + try_actualize (P& p) + { + try + { + p.normalize (true); + return true; + } + catch (const invalid_path&) {} + catch (const system_error&) {} + + return false; + } + void path_functions (function_map& m) { @@ -344,138 +373,76 @@ namespace build2 return ns; }; - // $canonicalize(<paths>) - // $path.canonicalize(<untyped>) + // $absolute(<path>) + // $path.absolute(<untyped>) // - // Canonicalize the path (or list of paths) by converting all the - // directory separators to the canonical form for the host platform. Note - // that multiple directory separators are not collapsed. - // - - // @@ TODO: add ability to specify alternative separator. + // Return true if the path is absolute and false otherwise. // - f["canonicalize"] += [](path p) {p.canonicalize (); return p;}; - f["canonicalize"] += [](dir_path p) {p.canonicalize (); return p;}; - - f["canonicalize"] += [](paths v) - { - for (auto& p: v) - p.canonicalize (); - return v; - }; - - f["canonicalize"] += [](dir_paths v) + f["absolute"] += [](path p) { - for (auto& p: v) - p.canonicalize (); - return v; + return p.absolute (); }; - f[".canonicalize"] += [](names ns) + f[".absolute"] += [](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.canonicalize (); - else - n.value = convert<path> (move (n)).canonicalize ().string (); - } - return ns; + return convert<path> (move (ns)).absolute (); }; - // $normalize(<paths>) - // $path.normalize(<untyped>) + // $simple(<path>) + // $path.simple(<untyped>) // - // Normalize the path (or list of paths) by collapsing the `.` and `..` - // components if possible, collapsing multiple directory separators, and - // converting all the directory separators to the canonical form for the - // host platform. + // Return true if the path is simple, that is, has no direcrory component, + // and false otherwise. // - f["normalize"] += [](path p) {p.normalize (); return p;}; - f["normalize"] += [](dir_path p) {p.normalize (); return p;}; + // Note that on POSIX `/foo` is not a simple path (it is `foo` in the root + // directory) while `/` is (it is the root directory). + // + f["simple"] += [](path p) + { + return p.simple (); + }; - f["normalize"] += [](paths v) + f[".simple"] += [](names ns) { - for (auto& p: v) - p.normalize (); - return v; + return convert<path> (move (ns)).simple (); }; - f["normalize"] += [](dir_paths v) + // $sub_path(<path>, <path>) + // $path.sub_path(<untyped>, <untyped>) + // + // Return true if the path specified as the first argument is a sub-path + // of the one specified as the second argument (in other words, the second + // argument is a prefix of the first) and false otherwise. Both paths are + // expected to be normalized. Note that this function returns true if the + // paths are equal. Empty path is considered a prefix of any path. + // + f["sub_path"] += [](path p, value v) { - for (auto& p: v) - p.normalize (); - return v; + return p.sub (convert_to_base<path> (move (v))); }; - f[".normalize"] += [](names ns) + f[".sub_path"] += [](names ns, value v) { - // 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.normalize (); - else - n.value = convert<path> (move (n)).normalize ().string (); - } - return ns; + return convert<path> (move (ns)).sub (convert_to_base<path> (move (v))); }; - // $actualize(<paths>) - // $path.actualize(<untyped>) - // - // Actualize the path (or list of paths) by first normalizing it and then - // for host platforms with case-insensitive filesystems obtaining the - // actual spelling of the path. + // $super_path(<path>, <path>) + // $path.super_path(<untyped>, <untyped>) // - // Note that only an absolute path can be actualized. If a path component - // does not exist, then its (and all subsequent) spelling is - // unchanged. This is a potentially expensive operation. - // - // Note that this function is not pure. + // Return true if the path specified as the first argument is a super-path + // of the one specified as the second argument (in other words, the second + // argument is a suffix of the first) and false otherwise. Both paths are + // expected to be normalized. Note that this function returns true if the + // paths are equal. Empty path is considered a suffix of any path. // + f["super_path"] += [](path p, value v) { - auto e (f.insert ("actualize", false)); - - e += [](path p) {p.normalize (true); return p;}; - e += [](dir_path p) {p.normalize (true); return p;}; - - e += [](paths v) - { - for (auto& p: v) - p.normalize (true); - return v; - }; - - e += [](dir_paths v) - { - for (auto& p: v) - p.normalize (true); - return v; - }; - } + return p.sup (convert_to_base<path> (move (v))); + }; - f.insert (".actualize", false) += [](names ns) + f[".super_path"] += [](names ns, value v) { - // 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.normalize (true); - else - n.value = convert<path> (move (n)).normalize (true).string (); - } - return ns; + return convert<path> (move (ns)).sup (convert_to_base<path> (move (v))); }; // $directory(<paths>) @@ -615,6 +582,8 @@ namespace build2 // specified paths). Issue diagnostics and fail if a relative path cannot // be derived (for example, paths are on different drives on Windows). // + // Note: to check if a path if relative, use `$path.absolute()`. + // f["relative"] += [](path p, dir_path d) { return relative (p, d); @@ -701,6 +670,261 @@ namespace build2 return extension (convert<path> (move (ns))); }; + // $complete(<paths>) + // $path.complete(<untyped>) + // + // Complete the path (or list of paths) by prepending the current working + // directory unless the path is already absolute. + // + f["complete"] += [](path p) {p.complete (); return p;}; + f["complete"] += [](dir_path p) {p.complete (); return p;}; + + f["complete"] += [](paths v) + { + for (auto& p: v) + p.complete (); + return v; + }; + + f["complete"] += [](dir_paths v) + { + for (auto& p: v) + p.complete (); + return v; + }; + + f[".complete"] += [](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.complete (); + else + n.value = convert<path> (move (n)).complete ().string (); + } + return ns; + }; + + // $canonicalize(<paths>) + // $path.canonicalize(<untyped>) + // + // Canonicalize the path (or list of paths) by converting all the + // directory separators to the canonical form for the host platform. Note + // that multiple directory separators are not collapsed. + // + f["canonicalize"] += [](path p) {p.canonicalize (); return p;}; + f["canonicalize"] += [](dir_path p) {p.canonicalize (); return p;}; + + f["canonicalize"] += [](paths v) + { + for (auto& p: v) + p.canonicalize (); + return v; + }; + + f["canonicalize"] += [](dir_paths v) + { + for (auto& p: v) + p.canonicalize (); + return v; + }; + + f[".canonicalize"] += [](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.canonicalize (); + else + n.value = convert<path> (move (n)).canonicalize ().string (); + } + return ns; + }; + + // $normalize(<paths>) + // $path.normalize(<untyped>) + // $try_normalize(<path>) + // $path.try_normalize(<untyped>) + // + // Normalize the path (or list of paths) by collapsing the `.` and `..` + // components if possible, collapsing multiple directory separators, and + // converting all the directory separators to the canonical form for the + // host platform. + // + // If the resulting path would be invalid, the `$normalize()` version + // issues diagnostics and fails while the `$try_normalize()` version + // returns `null`. Note that `$try_normalize()` only accepts a single + // path. + // + f["normalize"] += [](path p) {p.normalize (); return p;}; + f["normalize"] += [](dir_path p) {p.normalize (); return p;}; + + f["normalize"] += [](paths v) + { + for (auto& p: v) + p.normalize (); + return v; + }; + + f["normalize"] += [](dir_paths v) + { + for (auto& p: v) + p.normalize (); + return v; + }; + + f[".normalize"] += [](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.normalize (); + else + n.value = convert<path> (move (n)).normalize ().string (); + } + return ns; + }; + + f["try_normalize"] += [](path p) + { + return try_normalize (p) ? value (move (p)) : value (nullptr); + }; + + f["try_normalize"] += [](dir_path p) + { + return try_normalize (p) ? value (move (p)) : value (nullptr); + }; + + f[".try_normalize"] += [](names ns) + { + if (ns.size () != 1) + throw invalid_argument ("multiple paths"); + + name& n (ns.front ()); + + bool r; + if (n.directory ()) + r = try_normalize (n.dir); + else + { + path p (convert<path> (move (n))); + if ((r = try_normalize (p))) + n.value = move (p).string (); + } + + return r ? value (move (ns)) : value (nullptr); + }; + + // $actualize(<paths>) + // $path.actualize(<untyped>) + // $try_actualize(<path>) + // $path.try_actualize(<untyped>) + // + // Actualize the path (or list of paths) by first normalizing it and then + // for host platforms with case-insensitive filesystems obtaining the + // actual spelling of the path. + // + // Only an absolute path can be actualized. If a path component does not + // exist, then its (and all subsequent) spelling is unchanged. Note that + // this is a potentially expensive operation. + // + // If the resulting path would be invalid or in case of filesystem errors + // (other than non-existent component), the `$actualize()` version issues + // diagnostics and fails while the `$try_actualize()` version returns + // `null`. Note that `$try_actualize()` only accepts a single path. + // + // Note that this function is not pure. + // + { + auto e (f.insert ("actualize", false)); + + e += [](path p) {p.normalize (true); return p;}; + e += [](dir_path p) {p.normalize (true); return p;}; + + e += [](paths v) + { + for (auto& p: v) + p.normalize (true); + return v; + }; + + e += [](dir_paths v) + { + for (auto& p: v) + p.normalize (true); + return v; + }; + } + + f.insert (".actualize", false) += [](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.normalize (true); + else + n.value = convert<path> (move (n)).normalize (true).string (); + } + return ns; + }; + + { + auto e (f.insert ("try_actualize", false)); + + e += [](path p) + { + return try_actualize (p) ? value (move (p)) : value (nullptr); + }; + + e += [](dir_path p) + { + return try_actualize (p) ? value (move (p)) : value (nullptr); + }; + } + + f.insert (".try_actualize", false) += [](names ns) + { + if (ns.size () != 1) + throw invalid_argument ("multiple paths"); + + name& n (ns.front ()); + + bool r; + if (n.directory ()) + r = try_actualize (n.dir); + else + { + path p (convert<path> (move (n))); + if ((r = try_actualize (p))) + n.value = move (p).string (); + } + + return r ? value (move (ns)) : value (nullptr); + }; + + + // Note that we currently do not expose realize(). For one, it might be + // tricky to handle CWD overrides (on POSIX we just call realize(3)). + // Also, our implementation for Windows currently does not handle + // symlinks. + + // $size(<paths>) // $size(<path>) // |