diff options
Diffstat (limited to 'libbuild2/functions-filesystem.cxx')
-rw-r--r-- | libbuild2/functions-filesystem.cxx | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx new file mode 100644 index 0000000..d98c75d --- /dev/null +++ b/libbuild2/functions-filesystem.cxx @@ -0,0 +1,220 @@ +// file : libbuild2/functions-filesystem.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbutl/filesystem.mxx> + +#include <libbuild2/function.hxx> +#include <libbuild2/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); + }; + + // Path matching. + // + f["path_match"] = [](path pat, path ent, optional<dir_path> start) + { + return path_match (pat, ent, start); + }; + + // The semantics depends on the presence of the start directory or the + // first two argument syntactic representation. + // + f["path_match"] = [](names pat, names ent, optional<names> start) + { + auto path_arg = [] (const names& a) -> bool + { + return a.size () == 1 && + (a[0].directory () || + a[0].value.find_first_of (path::traits_type::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 + ? convert<dir_path> (move (*start)) + : optional<dir_path> ()) + : path_match (convert<string> (move (pat)), // Match as strings. + convert<string> (move (ent))); + }; + } +} |