From af8046d5bcf2aec918b1802a7719a09ce0111662 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 21 May 2024 08:00:23 +0200 Subject: Add $path.complete(), $path.try_normalize(), $path.try_actualize() --- libbuild2/functions-path.cxx | 418 +++++++++++++++++++++++++++++-------------- 1 file changed, 284 insertions(+), 134 deletions(-) (limited to 'libbuild2/functions-path.cxx') diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx index 7dfbd67..2f7f159 100644 --- a/libbuild2/functions-path.cxx +++ b/libbuild2/functions-path.cxx @@ -193,6 +193,35 @@ namespace build2 #endif } + template + static bool + try_normalize (P& p) + { + try + { + p.normalize (); + return true; + } + catch (const invalid_path&) {} + + return false; + } + + template + 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) { @@ -416,140 +445,6 @@ namespace build2 return convert (move (ns)).sup (convert_to_base (move (v))); }; - // $canonicalize() - // $path.canonicalize() - // - // 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. - // - 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 (move (n)).canonicalize ().string (); - } - return ns; - }; - - // $normalize() - // $path.normalize() - // - // 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. - // - 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 (move (n)).normalize ().string (); - } - return ns; - }; - - // $actualize() - // $path.actualize() - // - // 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. - // - // 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. - // - { - 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 (move (n)).normalize (true).string (); - } - return ns; - }; - // $directory() // $path.directory() // @@ -775,6 +670,261 @@ namespace build2 return extension (convert (move (ns))); }; + // $complete() + // $path.complete() + // + // 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 (move (n)).complete ().string (); + } + return ns; + }; + + // $canonicalize() + // $path.canonicalize() + // + // 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 (move (n)).canonicalize ().string (); + } + return ns; + }; + + // $normalize() + // $path.normalize() + // $try_normalize() + // $path.try_normalize() + // + // 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 (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 (move (n))); + if ((r = try_normalize (p))) + n.value = move (p).string (); + } + + return r ? value (move (ns)) : value (nullptr); + }; + + // $actualize() + // $path.actualize() + // $try_actualize() + // $path.try_actualize() + // + // 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 (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 (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() // $size() // -- cgit v1.1