aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-06-22 20:06:01 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-06-26 13:46:57 +0300
commitf65377448e74fc7e575e4df258fb0a48a09e39cc (patch)
tree1339f77be0b16a46c5e7d500feec037021bfb3d8 /build2
parente755de789e146beea66bf6a4b55685034502df41 (diff)
Add support for $path_search() and $path_match()
Diffstat (limited to 'build2')
-rw-r--r--build2/buildfile1
-rw-r--r--build2/function.cxx6
-rw-r--r--build2/functions-builtin.cxx11
-rw-r--r--build2/functions-filesystem.cxx274
4 files changed, 280 insertions, 12 deletions
diff --git a/build2/buildfile b/build2/buildfile
index 6d48718..8525eb8 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -16,6 +16,7 @@ exe{b}: \
{hxx txx cxx}{ filesystem } \
{hxx cxx}{ function } \
{ cxx}{ functions-builtin } \
+ { cxx}{ functions-filesystem } \
{ cxx}{ functions-path } \
{ cxx}{ functions-process-path } \
{ cxx}{ functions-string } \
diff --git a/build2/function.cxx b/build2/function.cxx
index 9f2fb9c..399d679 100644
--- a/build2/function.cxx
+++ b/build2/function.cxx
@@ -301,9 +301,10 @@ namespace build2
function_map functions;
void builtin_functions (); // functions-builtin.cxx
- void string_functions (); // functions-string.cxx
+ void filesystem_functions (); // functions-filesystem.cxx
void path_functions (); // functions-path.cxx
void process_path_functions (); // functions-process-path.cxx
+ void string_functions (); // functions-string.cxx
void target_triplet_functions (); // functions-target-triplet.cxx
struct functions_init
@@ -311,9 +312,10 @@ namespace build2
functions_init ()
{
builtin_functions ();
- string_functions ();
+ filesystem_functions ();
path_functions ();
process_path_functions ();
+ string_functions ();
target_triplet_functions ();
}
};
diff --git a/build2/functions-builtin.cxx b/build2/functions-builtin.cxx
index f9bbda5..d6d9501 100644
--- a/build2/functions-builtin.cxx
+++ b/build2/functions-builtin.cxx
@@ -47,16 +47,7 @@ namespace build2
f["getenv"] = [](names name)
{
- // Note that if the name size is greater than one, we will fail with the
- // proper diagnostics.
- //
- assert (!name.empty ());
-
- string n (name.size () == 1
- ? convert<string> (move (name[0]))
- : convert<string> (move (name)));
-
- return getenv (n);
+ return getenv (convert<string> (move (name)));
};
}
}
diff --git a/build2/functions-filesystem.cxx b/build2/functions-filesystem.cxx
new file mode 100644
index 0000000..d9e74b7
--- /dev/null
+++ b/build2/functions-filesystem.cxx
@@ -0,0 +1,274 @@
+// file : build2/functions-filesystem.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbutl/filesystem.hxx>
+
+#include <build2/function.hxx>
+#include <build2/variable.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ // Return paths of filesystem entries that match the pattern. See
+ // path_search() overloads (below) for details.
+ //
+ static names
+ path_search (const path& pattern, const optional<dir_path>& start)
+ {
+ names r;
+ auto add = [&r] (path&& p, const std::string&, bool interm) -> bool
+ {
+ // Canonicalizing paths seems to be the right thing to do. Otherwise, we
+ // can end up with different separators in the same path on Windows.
+ //
+ if (!interm)
+ r.emplace_back (
+ value_traits<path>::reverse (move (p.canonicalize ())));
+
+ return true;
+ };
+
+ // Print paths "as is" in the diagnostics.
+ //
+ try
+ {
+ if (pattern.absolute ())
+ path_search (pattern, add);
+ else
+ {
+ // An absolute start directory must be specified for the relative
+ // pattern.
+ //
+ if (!start || start->relative ())
+ {
+ diag_record dr (fail);
+
+ if (!start)
+ dr << "start directory is not specified";
+ else
+ dr << "start directory '" << start->representation ()
+ << "' is relative";
+
+ dr << info << "pattern '" << pattern.representation ()
+ << "' is relative";
+ }
+
+ path_search (pattern, add, *start);
+ }
+ }
+ catch (const system_error& e)
+ {
+ diag_record d (fail);
+ d << "unable to scan";
+
+ // If the pattern is absolute, then the start directory is not used, and
+ // so printing it would be misleading.
+ //
+ if (start && pattern.relative ())
+ d << " '" << start->representation () << "'";
+
+ d << ": " << e
+ << info << "pattern: '" << pattern.representation () << "'";
+ }
+
+ return r;
+ }
+
+ using butl::path_match;
+
+ // Return true if a path for a filesystem entry matches the pattern. See
+ // path_match() overloads (below) for details.
+ //
+ static bool
+ path_match (const path& pattern,
+ const path& entry,
+ const optional<dir_path>& start)
+ {
+ // If pattern and entry are both either absolute or relative and
+ // non-empty, and the first pattern component is not a self-matching
+ // wildcard, then ignore the start directory.
+ //
+ bool rel (pattern.relative () == entry.relative () &&
+ !pattern.empty () && !entry.empty ());
+
+ bool self (!pattern.empty () &&
+ (*pattern.begin ()).find ("***") != string::npos);
+
+ if (rel && !self)
+ return path_match (pattern, entry);
+
+ // The start directory must be specified and be absolute.
+ //
+ if (!start || start->relative ())
+ {
+ diag_record dr (fail);
+
+ // Print paths "as is".
+ //
+ if (!start)
+ dr << "start directory is not specified";
+ else
+ dr << "start directory path '" << start->representation ()
+ << "' is relative";
+
+ dr << info << "pattern: '" << pattern.representation () << "'"
+ << info << "entry: '" << entry.representation () << "'";
+ }
+
+ return path_match (pattern, entry, *start);
+ }
+
+ void
+ filesystem_functions ()
+ {
+ function_family f ("filesystem");
+
+ // path_search
+ //
+ // Return filesystem paths that match the pattern. If the pattern is an
+ // absolute path, then the start directory is ignored (if present).
+ // Otherwise, the start directory must be specified and be absolute.
+ //
+ f["path_search"] = [](path pattern, optional<dir_path> start)
+ {
+ return path_search (pattern, start);
+ };
+
+ f["path_search"] = [](path pattern, names start)
+ {
+ return path_search (pattern, convert<dir_path> (move (start)));
+ };
+
+ f["path_search"] = [](names pattern, optional<dir_path> start)
+ {
+ return path_search (convert<path> (move (pattern)), start);
+ };
+
+ f["path_search"] = [](names pattern, names start)
+ {
+ return path_search (convert<path> (move (pattern)),
+ convert<dir_path> (move (start)));
+ };
+
+ // path_match
+ //
+ // Match a filesystem entry name against a name pattern (both are strings),
+ // or a filesystem entry path against a path pattern. For the latter case
+ // the start directory may also be required (see below). The semantics of
+ // the pattern and name/entry arguments is determined according to the
+ // following rules:
+ //
+ // - The arguments must be of the string or path types, or be untyped.
+ //
+ // - If one of the arguments is typed, then the other one must be of the
+ // same type or be untyped. In the later case, an untyped argument is
+ // converted to the type of the other argument.
+ //
+ // - If both arguments are untyped and the start directory is specified,
+ // then the arguments are converted to the path type.
+ //
+ // - If both arguments are untyped and the start directory is not
+ // specified, then, if one of the arguments is syntactically a path (the
+ // value contains a directory separator), convert them to the path type,
+ // otherwise to the string type (match as names).
+ //
+ // If pattern and entry paths are both either absolute or relative and
+ // non-empty, and the first pattern component is not a self-matching
+ // wildcard (doesn't contain ***), then the start directory is not
+ // required, and is ignored if specified. Otherwise, the start directory
+ // must be specified and be an absolute path.
+ //
+ // Name matching.
+ //
+ f["path_match"] = [](string pattern, string name)
+ {
+ return path_match (pattern, name);
+ };
+
+ f["path_match"] = [](string pattern, names name)
+ {
+ return path_match (pattern, convert<string> (move (name)));
+ };
+
+ f["path_match"] = [](names pattern, string name)
+ {
+ return path_match (convert<string> (move (pattern)), name);
+ };
+
+ // Path matching.
+ //
+ // path path *
+ //
+ f["path_match"] = [](path pat, path ent, optional<dir_path> start)
+ {
+ return path_match (pat, ent, start);
+ };
+
+ f["path_match"] = [](path pat, path ent, names start)
+ {
+ return path_match (pat, ent, convert<dir_path> (move (start)));
+ };
+
+ // path untyped *
+ //
+ f["path_match"] = [](path pat, names ent, optional<dir_path> start)
+ {
+ return path_match (pat, convert<path> (move (ent)), start);
+ };
+
+ f["path_match"] = [](path pat, names ent, names start)
+ {
+ return path_match (pat,
+ convert<path> (move (ent)),
+ convert<dir_path> (move (start)));
+ };
+
+ // untyped path *
+ //
+ f["path_match"] = [](names pat, path ent, optional<dir_path> start)
+ {
+ return path_match (convert<path> (move (pat)), ent, start);
+ };
+
+ f["path_match"] = [](names pat, path ent, names start)
+ {
+ return path_match (convert<path> (move (pat)),
+ ent,
+ convert<dir_path> (move (start)));
+ };
+
+ // The semantics depends on the presence of the start directory or the
+ // first two argument syntactic representation.
+ //
+ // untyped untyped *
+ //
+ f["path_match"] = [](names pat, names ent, optional<dir_path> start)
+ {
+ auto path_arg = [] (const names& a) -> bool
+ {
+ return a.size () == 1 &&
+ (a[0].directory () ||
+ a[0].value.find_first_of (path::traits::directory_separators) !=
+ string::npos);
+ };
+
+ return start || path_arg (pat) || path_arg (ent)
+ ? path_match (convert<path> (move (pat)), // Match as paths.
+ convert<path> (move (ent)),
+ start)
+ : path_match (convert<string> (move (pat)), // Match as names.
+ convert<string> (move (ent)));
+ };
+
+ f["path_match"] = [](names pat, names ent, names start)
+ {
+ // Match as paths.
+ //
+ return path_match (convert<path> (move (pat)),
+ convert<path> (move (ent)),
+ convert<dir_path> (move (start)));
+ };
+ }
+}