aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/functions-path.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/functions-path.cxx')
-rw-r--r--libbuild2/functions-path.cxx438
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>)
//