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.cxx365
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);
+ };
}
}