From 2109dedc473944dbb38756cd48d0c44f996304c4 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 9 Aug 2023 08:54:37 +0200 Subject: Complete and cleanup function documentation in preparation for auto-extraction Also: - Move the $target.*() function family from functions-name.cxx to separate functions-target.cxx. - Get rid of the separate $process_path_ex.*() family, merging it with $process_path.*(). --- libbuild2/function.cxx | 2 + libbuild2/functions-bool.cxx | 3 + libbuild2/functions-builtin.cxx | 58 ++++++++-- libbuild2/functions-filesystem.cxx | 10 +- libbuild2/functions-integer.cxx | 17 ++- libbuild2/functions-name.cxx | 160 +++++++++----------------- libbuild2/functions-path.cxx | 198 +++++++++++++++++++++------------ libbuild2/functions-process-path.cxx | 53 ++++++--- libbuild2/functions-process.cxx | 15 +-- libbuild2/functions-project-name.cxx | 26 +++++ libbuild2/functions-regex.cxx | 140 +++++++++++------------ libbuild2/functions-string.cxx | 40 ++++--- libbuild2/functions-target-triplet.cxx | 11 ++ libbuild2/functions-target.cxx | 108 ++++++++++++++++++ libbuild2/variable.hxx | 5 +- 15 files changed, 537 insertions(+), 309 deletions(-) create mode 100644 libbuild2/functions-target.cxx diff --git a/libbuild2/function.cxx b/libbuild2/function.cxx index 528b396..ac71e1f 100644 --- a/libbuild2/function.cxx +++ b/libbuild2/function.cxx @@ -359,6 +359,7 @@ namespace build2 void process_path_functions (function_map&); // functions-process-path.cxx void regex_functions (function_map&); // functions-regex.cxx void string_functions (function_map&); // functions-string.cxx + void target_functions (function_map&); // functions-target.cxx void target_triplet_functions (function_map&); // functions-target-triplet.cxx void project_name_functions (function_map&); // functions-target-triplet.cxx @@ -375,6 +376,7 @@ namespace build2 process_path_functions (m); regex_functions (m); string_functions (m); + target_functions (m); target_triplet_functions (m); project_name_functions (m); } diff --git a/libbuild2/functions-bool.cxx b/libbuild2/functions-bool.cxx index 1d9c72f..bb2fd3f 100644 --- a/libbuild2/functions-bool.cxx +++ b/libbuild2/functions-bool.cxx @@ -15,6 +15,9 @@ namespace build2 // $string() // + // Convert a boolean value to a string literal `true` or `false`. + // + // Note that we don't handle NULL values for this type since it has no // empty representation. // diff --git a/libbuild2/functions-builtin.cxx b/libbuild2/functions-builtin.cxx index 378ffbc..13b315c 100644 --- a/libbuild2/functions-builtin.cxx +++ b/libbuild2/functions-builtin.cxx @@ -37,11 +37,17 @@ namespace build2 { function_family f (m, "builtin"); - // Note that we may want to extend the scope argument to a more general - // notion of "lookup context" (scope, target, prerequisite). + // $defined() + // + // Return true if the specified variable is defined in the calling scope or + // any outer scopes. // // Note that this function is not pure. // + + // Note that we may want to extend the scope argument to a more general + // notion of "lookup context" (scope, target, prerequisite). + // f.insert ("defined", false) += [](const scope* s, names name) { if (s == nullptr) @@ -50,7 +56,17 @@ namespace build2 return (*s)[convert (move (name))].defined (); }; - // Return variable visibility if it has been entered and NULL otherwise. + // $visibility() + // + // Return variable visibility if it is known and `null` otherwise. + // + // Possible visibility value are: + // + // global -- all outer scopes + // project -- this project (no outer projects) + // scope -- this scope (no outer scopes) + // target -- target and target type/pattern-specific + // prereq -- prerequisite-specific // // Note that this function is not pure. // @@ -67,16 +83,36 @@ namespace build2 : nullopt); }; + // $type() + // + // Return the type name of the value or empty string if untyped. + // f["type"] += [](value* v) {return v->type != nullptr ? v->type->name : "";}; + + // $null() + // + // Return true if the value is `null`. + // f["null"] += [](value* v) {return v->null;}; + + // $empty() + // + // Return true if the value is empty. + // f["empty"] += [](value* v) {return v->null || v->empty ();}; + // Leave this one undocumented for now since it's unclear why would anyone + // want to use it currently (we don't yet have any function composition + // facilities). + // f["identity"] += [](value* v) {return move (*v);}; - // Quote a value returning its string representation. If escape is true, - // then also escape (with a backslash) the quote characters being added - // (this is useful if the result will be re-parsed, for example as a - // Testscript command line). + // $quote([, ]) + // + // Quote the value returning its string representation. If is + // true, then also escape (with a backslash) the quote characters being + // added (this is useful if the result will be re-parsed, for example as a + // script command line). // f["quote"] += [](value* v, optional escape) { @@ -94,13 +130,13 @@ namespace build2 return os.str (); }; - // getenv + // $getenv() // - // Return NULL if the environment variable is not set, untyped value - // otherwise. + // Get the value of the environment variable. Return `null` if the + // environment variable is not set. // // Note that if the build result can be affected by the variable being - // queried, then it should be reported with the config.environment + // queried, then it should be reported with the `config.environment` // directive. // // Note that this function is not pure. diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx index 1acb3d1..665a0f3 100644 --- a/libbuild2/functions-filesystem.cxx +++ b/libbuild2/functions-filesystem.cxx @@ -103,14 +103,16 @@ namespace build2 function_family f (m, "filesystem"); - // $path_search( [, ]) + // $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. + // Return filesystem paths that match the shell-like wildcard 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. // // Note that this function is not pure. // + // @@ In the future we may want to add a flag that controls the // dangling/inaccessible treatment. // diff --git a/libbuild2/functions-integer.cxx b/libbuild2/functions-integer.cxx index a634ae9..934f753 100644 --- a/libbuild2/functions-integer.cxx +++ b/libbuild2/functions-integer.cxx @@ -69,6 +69,16 @@ namespace build2 // $string() // $string([, [, ]]) // + // Convert an integer to a string. For unsigned integers we can specify + // the desired base and width. For example: + // + // x = [uint64] 0x0000ffff + // + // c.poptions += "-DOFFSET=$x" # -DOFFSET=65535 + // c.poptions += "-DOFFSET=$string($x, 16)" # -DOFFSET=0xffff + // c.poptions += "-DOFFSET=$string($x, 16, 8)" # -DOFFSET=0x0000ffff + // + // Note that we don't handle NULL values for these type since they have no // empty representation. // @@ -82,9 +92,10 @@ namespace build2 // $integer_sequence(, [, ]) // // Return the list of uint64 integers starting from (including) to - // (excluding) with the specified or 1 if unspecified. If + // (excluding) with the specified or `1` if unspecified. If // is greater than , empty list is returned. // + // Note that currently negative numbers are not supported but this could // be handled if required (e.g., by returning int64s in this case). // @@ -123,7 +134,7 @@ namespace build2 // // The following flags are supported: // - // dedup - in addition to sorting also remove duplicates + // dedup - in addition to sorting also remove duplicates // f["sort"] += [](int64s v, optional fs) { @@ -164,7 +175,7 @@ namespace build2 // $find_index(, ) // // Return the index of the first element in the integer sequence that is - // equal to the specified integer or $size() if none is found. + // equal to the specified integer or `$size(ints)` if none is found. // f["find_index"] += [](int64s vs, value v) { diff --git a/libbuild2/functions-name.cxx b/libbuild2/functions-name.cxx index a853db1..7dddc3a 100644 --- a/libbuild2/functions-name.cxx +++ b/libbuild2/functions-name.cxx @@ -151,23 +151,30 @@ namespace build2 // on prerequisite names. They also won't always return the same result as // if we were interrogating an actual target (e.g., the directory may be // relative). Plus we now have functions that can only be called on - // targets (see below). + // targets (see functions-target.cxx). // - function_family fn (m, "name"); + function_family f (m, "name"); + // Note: let's leave this undocumented for now since it's not often needed + // and is a can of worms. + // // Note that we must handle NULL values (relied upon by the parser // to provide conversion semantics consistent with untyped values). // - fn["string"] += [](name* n) + f["string"] += [](name* n) { return n != nullptr ? to_string (move (*n)) : string (); }; - fn["name"] += [](const scope* s, name n) + // $name() + // + // Return the name of a target (or a list of names for a list of targets). + // + f["name"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.value; }; - fn["name"] += [](const scope* s, names ns) + f["name"] += [](const scope* s, names ns) { small_vector r; @@ -185,14 +192,18 @@ namespace build2 make_move_iterator (r.end ()))); }; - // Note: returns NULL if extension is unspecified (default) and empty if - // specified as no extension. + // $extension() + // + // Return the extension of a target. + // + // Note that this function returns `null` if the extension is unspecified + // (default) and empty string if it's specified as no extension. // - fn["extension"] += [](const scope* s, name n) + f["extension"] += [](const scope* s, name n) { return to_target_name (s, move (n)).second; }; - fn["extension"] += [](const scope* s, names ns) + f["extension"] += [](const scope* s, names ns) { // Note: can't do multiple due to NULL semantics. // @@ -207,11 +218,16 @@ namespace build2 return to_target_name (s, move (n), o).second; }; - fn["directory"] += [](const scope* s, name n) + // $directory() + // + // Return the directory of a target (or a list of directories for a list + // of targets). + // + f["directory"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.dir; }; - fn["directory"] += [](const scope* s, names ns) + f["directory"] += [](const scope* s, names ns) { small_vector r; @@ -229,11 +245,16 @@ namespace build2 make_move_iterator (r.end ()))); }; - fn["target_type"] += [](const scope* s, name n) + // $target_type() + // + // Return the target type name of a target (or a list of target type names + // for a list of targets). + // + f["target_type"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.type; }; - fn["target_type"] += [](const scope* s, names ns) + f["target_type"] += [](const scope* s, names ns) { small_vector r; @@ -251,13 +272,15 @@ namespace build2 make_move_iterator (r.end ()))); }; - // Note: returns NULL if no project specified. + // $project() + // + // Return the project of a target or `null` if not project-qualified. // - fn["project"] += [](const scope* s, name n) + f["project"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.proj; }; - fn["project"] += [](const scope* s, names ns) + f["project"] += [](const scope* s, names ns) { // Note: can't do multiple due to NULL semantics. // @@ -278,11 +301,11 @@ namespace build2 // this is a dynamic type check that takes into account target type // inheritance. // - fn["is_a"] += [](const scope* s, name n, names t) + f["is_a"] += [](const scope* s, name n, names t) { return is_a (s, move (n), name (), move (t)); }; - fn["is_a"] += [](const scope* s, names ns, names t) + f["is_a"] += [](const scope* s, names ns, names t) { auto i (ns.begin ()); @@ -298,15 +321,15 @@ namespace build2 // $filter(, ) // $filter_out(, ) // - // Return names with target types which are-a (filter) or not are-a - // (filter_out) one of . See $is_a() for background. + // Return names with target types which are-a (`filter`) or not are-a + // (`filter_out`) one of . See `$is_a()` for background. // - fn["filter"] += [](const scope* s, names ns, names ts) + f["filter"] += [](const scope* s, names ns, names ts) { return filter (s, move (ns), move (ts), false /* out */); }; - fn["filter_out"] += [](const scope* s, names ns, names ts) + f["filter_out"] += [](const scope* s, names ns, names ts) { return filter (s, move (ns), move (ts), true /* out */); }; @@ -315,7 +338,7 @@ namespace build2 // // Return the number of elements in the sequence. // - fn["size"] += [] (names ns) + f["size"] += [] (names ns) { size_t n (0); @@ -329,15 +352,15 @@ namespace build2 return n; }; - // $sort( [, ]) + // $sort([, ]) // // Sort names in ascending order. // // The following flags are supported: // - // dedup - in addition to sorting also remove duplicates + // dedup - in addition to sorting also remove duplicates // - fn["sort"] += [] (names ns, optional fs) + f["sort"] += [] (names ns, optional fs) { //@@ TODO: shouldn't we do this in a pair-aware manner? @@ -353,7 +376,7 @@ namespace build2 // // Return true if the name sequence contains the specified name. // - fn["find"] += [](names vs, names v) + f["find"] += [](names vs, names v) { //@@ TODO: shouldn't we do this in a pair-aware manner? @@ -364,9 +387,9 @@ namespace build2 // $find_index(, ) // // Return the index of the first element in the name sequence that is - // equal to the specified name or $size() if none is found. + // equal to the specified name or `$size(names)` if none is found. // - fn["find_index"] += [](names vs, names v) + f["find_index"] += [](names vs, names v) { //@@ TODO: shouldn't we do this in a pair-aware manner? @@ -374,85 +397,6 @@ namespace build2 return i != vs.end () ? i - vs.begin () : vs.size (); }; - // Functions that can be called only on real targets. - // - function_family ft (m, "target"); - - // Note that while this function is not technically pure, we don't mark it - // as such since it can only be called (normally form a recipe) after the - // target has been matched, meaning that this target is a prerequisite and - // therefore this impurity has been accounted for. - // - ft["path"] += [](const scope* s, names ns) - { - if (s == nullptr) - fail << "target.path() called out of scope"; - - // Most of the time we will have a single target so optimize for that. - // - small_vector r; - - for (auto i (ns.begin ()); i != ns.end (); ++i) - { - name& n (*i), o; - const target& t (to_target (*s, move (n), move (n.pair ? *++i : o))); - - if (const auto* pt = t.is_a ()) - { - const path& p (pt->path ()); - - if (&p != &empty_path) - r.push_back (p); - else - fail << "target " << t << " path is not assigned"; - } - else - fail << "target " << t << " is not path-based"; - } - - // We want the result to be path if we were given a single target and - // paths if multiple (or zero). The problem is, we cannot distinguish it - // based on the argument type (e.g., name vs names) since passing an - // out-qualified single target requires two names. - // - if (r.size () == 1) - return value (move (r[0])); - - return value (paths (make_move_iterator (r.begin ()), - make_move_iterator (r.end ()))); - }; - - // This one can only be called on a single target since we don't support - // containers of process_path's (though we probably could). - // - // Note that while this function is not technically pure, we don't mark it - // as such for the same reasons as $path() above. - // - ft["process_path"] += [](const scope* s, names ns) - { - if (s == nullptr) - fail << "target.process_path() called out of scope"; - - if (ns.empty () || ns.size () != (ns[0].pair ? 2 : 1)) - fail << "target.process_path() expects single target"; - - name o; - const target& t ( - to_target (*s, move (ns[0]), move (ns[0].pair ? ns[1] : o))); - - if (const auto* et = t.is_a ()) - { - process_path r (et->process_path ()); - - if (r.empty ()) - fail << "target " << t << " path is not assigned"; - - return r; - } - else - fail << "target " << t << " is not process_path-based" << endf; - }; - // Name-specific overloads from builtins. // function_family fb (m, "builtin"); diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx index 020c8f4..4b114f5 100644 --- a/libbuild2/functions-path.cxx +++ b/libbuild2/functions-path.cxx @@ -198,8 +198,15 @@ namespace build2 { function_family f (m, "path", &path_thunk); - // string + // $string() // + // Return the traditional string representation of a path (or a list of + // string representations for a list of paths). In particular, for + // directory paths, the traditional representation does not include the + // trailing directory separator (except for the POSIX root directory). See + // `$representation()` below for the precise string representation. + // + // Note that we must handle NULL values (relied upon by the parser // to provide conversion semantics consistent with untyped values). // @@ -224,7 +231,12 @@ namespace build2 return r; }; - // posix_string + // $posix_string() + // $path.posix_string() + // + // Return the traditional string representation of a path (or a list of + // string representations for a list of paths) using the POSIX directory + // separators (forward slashes). // f["posix_string"] += [](path p) {return posix_string (move (p));}; f["posix_string"] += [](dir_path p) {return posix_string (move (p));}; @@ -259,7 +271,13 @@ namespace build2 return ns; }; - // representation + // $representation() + // + // Return the precise string representation of a path (or a list of string + // representations for a list of paths). In particular, for directory + // paths, the precise representation includes the trailing directory + // separator. See `$string()` above for the traditional string + // representation. // f["representation"] += [](path p) {return move (p).representation ();}; @@ -279,7 +297,12 @@ namespace build2 return r; }; - // posix_representation + // $posix_representation() + // $path.posix_representation() + // + // Return the precise string representation of a path (or a list of string + // representations for a list of paths) using the POSIX directory + // separators (forward slashes). // f["posix_representation"] += [](path p) { @@ -321,8 +344,14 @@ namespace build2 return ns; }; - // canonicalize + // $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;}; @@ -358,7 +387,13 @@ namespace build2 return ns; }; - // normalize + // $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;}; @@ -393,7 +428,16 @@ namespace build2 return ns; }; - // actualize + // $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. // @@ -434,11 +478,12 @@ namespace build2 return ns; }; - // $directory() // $directory() + // $path.directory() // - // Return the directory part of the path or empty path if there is no - // directory. Directory of a root directory is an empty path. + // Return the directory part of a path (or a list of directory parts for a + // list of paths) or an empty path if there is no directory. A directory of + // a root directory is an empty path. // f["directory"] += &path::directory; @@ -472,11 +517,12 @@ namespace build2 return ns; }; - // $root_directory() // $root_directory() + // $path.root_directory() // - // Return the root directory of the path or empty path if the directory is - // not absolute. + // Return the root directory of a path (or a list of root directories for + // a list of paths) or an empty path if the specified path is not + // absolute. // f["root_directory"] += &path::root_directory; @@ -510,17 +556,22 @@ namespace build2 return ns; }; - // $leaf() - // - f["leaf"] += &path::leaf; - - // $leaf(, ) + // $leaf() + // $path.leaf() // $leaf(, ) + // $path.leaf(, ) // - // 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. + // First form (one argument): return the last component of a path (or a + // list of last components for a list of paths). // + // Second form (two arguments): return a path without the specified + // directory part (or a list of paths without the directory part for a + // list of paths). Return an 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::leaf; + f["leaf"] += [](path p, dir_path d) { return leaf (p, move (d)); @@ -556,13 +607,13 @@ namespace build2 return ns; }; - // $relative(, ) // $relative(, ) + // $path.relative(, ) // - // 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). + // Return the path relative to the specified directory that is equivalent + // to the specified path (or a list of relative paths for a list of + // specified paths). 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) { @@ -599,7 +650,11 @@ namespace build2 return ns; }; - // base + // $base() + // $path.base() + // + // Return the base part (without the extension) of a path (or a list of + // base parts for a list of paths). // f["base"] += &path::base; @@ -633,7 +688,11 @@ namespace build2 return ns; }; - // extension + // $extension() + // $path.extension() + // + // Return the extension part (without the dot) of a path or empty string + // if there is no extension. // f["extension"] += &extension; @@ -643,32 +702,29 @@ namespace build2 }; // $size() - // $size() + // $size() + // + // First form: return the number of elements in the paths sequence. + // + // Second form: 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). // - // Return the number of elements in the sequence. // f["size"] += [] (paths v) {return v.size ();}; f["size"] += [] (dir_paths v) {return v.size ();}; - // $size() - // $size() - // - // 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( [, ]) - // $sort( [, ]) + // $sort([, ]) // - // Sort paths in ascending order. Note that on hosts with a case- - // insensitive filesystem the order is case-insensitive. + // Sort paths in ascending order. Note that on host platforms with a + // case-insensitive filesystem the order is case-insensitive. // // The following flags are supported: // - // dedup - in addition to sorting also remove duplicates + // dedup - in addition to sorting also remove duplicates // f["sort"] += [](paths v, optional fs) { @@ -691,11 +747,10 @@ namespace build2 }; // $find(, ) - // $find(, ) // - // Return true if the path sequence contains the specified path. Note that - // on hosts with a case-insensitive filesystem the comparison is - // case-insensitive. + // Return true if the paths sequence contains the specified path. Note + // that on host platforms with a case-insensitive filesystem the + // comparison is case-insensitive. // f["find"] += [](paths vs, value v) { @@ -710,12 +765,11 @@ namespace build2 }; // $find_index(, ) - // $find_index(, ) // - // Return the index of the first element in the path sequence that is - // equal to the specified path or $size() if none is found. Note - // that on hosts with a case-insensitive filesystem the comparison is - // case-insensitive. + // Return the index of the first element in the paths sequence that is + // equal to the specified path or `$size(paths)` if none is found. Note + // that on host platforms with a case-insensitive filesystem the + // comparison is case-insensitive. // f["find_index"] += [](paths vs, value v) { @@ -729,34 +783,36 @@ namespace build2 return i != vs.end () ? i - vs.begin () : vs.size (); }; - // $path.match(, [, ]) + // $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 + // 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 + // pattern is a shell-like wildcard pattern. The semantics of the + // and arguments is determined according to the // following rules: // - // - The arguments must be of the string or path types, or be untyped. + // 1. 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. + // 2. 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. + // 3. 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). + // 4. 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), then they are converted 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. + // If pattern and entry paths are both either absolute or relative and not + // 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[".match"] += [](string name, string pattern) diff --git a/libbuild2/functions-process-path.cxx b/libbuild2/functions-process-path.cxx index 486a806..6746623 100644 --- a/libbuild2/functions-process-path.cxx +++ b/libbuild2/functions-process-path.cxx @@ -11,24 +11,47 @@ namespace build2 void process_path_functions (function_map& m) { - { - function_family f (m, "process_path"); + function_family f (m, "process_path"); + + // $recall() + // + // Return the recall path of an executable, that is, a path that is not + // necessarily absolute but which nevertheless can be used to re-run the + // executable in the current environment. This path, for example, could be + // used in diagnostics when printing the failing command line. + // + + // As discussed in value_traits, we always have recall. + // + f["recall"] += &process_path::recall; - // As discussed in value_traits, we always have recall. - // - f["recall"] += &process_path::recall; - f["effect"] += [](process_path p) - { - return move (p.effect.empty () ? p.recall : p.effect); - }; - } + // $effect() + // + // Return the effective path of an executable, that is, the absolute path + // to the executable that will also include any omitted extensions, etc. + // + f["effect"] += [] (process_path p) { - function_family f (m, "process_path_ex"); + return move (p.effect.empty () ? p.recall : p.effect); + }; + + // $name() + // + // Return the stable process name for diagnostics. + // + f["name"] += &process_path_ex::name; + + // $checksum() + // + // Return the executable checksum for change tracking. + // + f["checksum"] += &process_path_ex::checksum; - f["name"] += &process_path_ex::name; - f["checksum"] += &process_path_ex::checksum; - f["env_checksum"] += &process_path_ex::env_checksum; - } + // $env_checksum() + // + // Return the environment checksum for change tracking. + // + f["env_checksum"] += &process_path_ex::env_checksum; } } diff --git a/libbuild2/functions-process.cxx b/libbuild2/functions-process.cxx index bbcbbab..6faa798 100644 --- a/libbuild2/functions-process.cxx +++ b/libbuild2/functions-process.cxx @@ -450,12 +450,12 @@ namespace build2 // $process.run([ ...]) // - // Run builtin or external program and return trimmed stdout. + // Run builtin or external program and return trimmed `stdout` output. // // Note that if the result of executing the program can be affected by // environment variables and this result can in turn affect the build // result, then such variables should be reported with the - // config.environment directive. + // `config.environment` directive. // // Note that this function is not pure and can only be called during the // load phase. @@ -470,19 +470,20 @@ namespace build2 return run_process (s, pp, strings ()); }; - // $process.run_regex([ ...], [, ]) + // $process.run_regex([ ...], [, ]) // - // Run builtin or external program and return stdout lines matched and - // optionally processed with regex. + // Run builtin or external program and return `stdout` output lines + // matched and optionally processed with a regular expression. // // Each line of stdout (including the customary trailing blank) is matched // (as a whole) against and, if successful, returned, optionally - // processed with , as an element of a list. + // processed with , as an element of a list. See the `$regex.*()` + // function family for details on regular expressions and format strings. // // Note that if the result of executing the program can be affected by // environment variables and this result can in turn affect the build // result, then such variables should be reported with the - // config.environment directive. + // `config.environment` directive. // // Note that this function is not pure and can only be called during the // load phase. diff --git a/libbuild2/functions-project-name.cxx b/libbuild2/functions-project-name.cxx index 4a8394d..23523f0 100644 --- a/libbuild2/functions-project-name.cxx +++ b/libbuild2/functions-project-name.cxx @@ -13,6 +13,12 @@ namespace build2 { function_family f (m, "project_name"); + // $string() + // + // Return the string representation of a project name. See also the + // `$variable()` function below. + // + // Note that we must handle NULL values (relied upon by the parser // to provide conversion semantics consistent with untyped values). // @@ -21,6 +27,14 @@ namespace build2 return p != nullptr ? move (*p).string () : string (); }; + // $base([, ]) + // + // Return the base part (without the extension) of a project name. + // + // If is specified, then only remove that extension. Note that + // should not include the dot and the comparison is always + // case-insensitive. + // f["base"] += [](project_name p, optional ext) { return ext ? p.base (ext->c_str ()) : p.base (); @@ -31,7 +45,19 @@ namespace build2 return p.base (convert (move (ext)).c_str ()); }; + // $extension() + // + // Return the extension part (without the dot) of a project name or empty + // string if there is no extension. + // f["extension"] += &project_name::extension; + + // $variable() + // + // Return the string representation of a project name that is sanitized to + // be usable as a variable name. Specifically, `.`, `-`, and `+` are + // replaced with `_`. + // f["variable"] += &project_name::variable; // Project name-specific overloads from builtins. diff --git a/libbuild2/functions-regex.cxx b/libbuild2/functions-regex.cxx index 1465108..a7fcf55 100644 --- a/libbuild2/functions-regex.cxx +++ b/libbuild2/functions-regex.cxx @@ -588,16 +588,16 @@ namespace build2 // // Match a value of an arbitrary type against the regular expression. // Convert the value to string prior to matching. Return the boolean value - // unless return_subs flag is specified (see below), in which case return - // names (NULL if no match). + // unless `return_subs` flag is specified (see below), in which case + // return names (or `null` if no match). // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // - // return_subs - return names (rather than boolean), that contain - // sub-strings that match the marked sub-expressions and - // NULL if no match + // return_subs - return names (rather than boolean), that contain + // sub-strings that match the marked sub-expressions + // and null if no match // f[".match"] += [](value v, string re, optional flags) { @@ -616,7 +616,7 @@ namespace build2 // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // f[".find_match"] += [](names ns, string re, optional flags) { @@ -631,13 +631,13 @@ namespace build2 // $regex.filter_match(, [, ]) // $regex.filter_out_match(, [, ]) // - // Return elements of a list that match (filter) or do not match - // (filter_out) the regular expression. Convert the elements to strings + // Return elements of a list that match (`filter`) or do not match + // (`filter_out`) the regular expression. Convert the elements to strings // prior to matching. // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // f[".filter_match"] += [](names ns, string re, optional flags) { @@ -669,23 +669,23 @@ namespace build2 // // Determine if there is a match between the regular expression and some // part of a value of an arbitrary type. Convert the value to string prior - // to searching. Return the boolean value unless return_match or - // return_subs flag is specified (see below) in which case return names - // (NULL if no match). + // to searching. Return the boolean value unless `return_match` or + // `return_subs` flag is specified (see below) in which case return names + // (`null` if no match). // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // - // return_match - return names (rather than boolean), that contain a - // sub-string that matches the whole regular expression and - // NULL if no match + // return_match - return names (rather than boolean), that contain a + // sub-string that matches the whole regular expression + // and null if no match // - // return_subs - return names (rather than boolean), that contain - // sub-strings that match the marked sub-expressions and - // NULL if no match + // return_subs - return names (rather than boolean), that contain + // sub-strings that match the marked sub-expressions + // and null if no match // - // If both return_match and return_subs flags are specified then the + // If both `return_match` and `return_subs` flags are specified then the // sub-string that matches the whole regular expression comes first. // f[".search"] += [](value v, string re, optional flags) @@ -706,7 +706,7 @@ namespace build2 // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // f[".find_search"] += [](names ns, string re, optional flags) { @@ -723,13 +723,13 @@ namespace build2 // $regex.filter_search(, [, ]) // $regex.filter_out_search(, [, ]) // - // Return elements of a list for which there is a match (filter) or no - // match (filter_out) between the regular expression and some part of the - // element. Convert the elements to strings prior to matching. + // Return elements of a list for which there is a match (`filter`) or no + // match (`filter_out`) between the regular expression and some part of + // the element. Convert the elements to strings prior to matching. // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // f[".filter_search"] += [](names ns, string re, optional flags) { @@ -763,19 +763,17 @@ namespace build2 // string. Convert the value to string prior to matching. The result value // is always untyped, regardless of the argument type. // - // Substitution escape sequences are extended with a subset of Perl - // sequences (see libbutl/regex.hxx for details). - // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // - // format_first_only - only replace the first match + // format_first_only - only replace the first match // - // format_no_copy - do not copy unmatched value parts into the result + // format_no_copy - do not copy unmatched value parts into the + // result // - // If both format_first_only and format_no_copy flags are specified then - // the result will only contain the replacement of the first match. + // If both `format_first_only` and `format_no_copy` flags are specified + // then the result will only contain the replacement of the first match. // f[".replace"] += [](value v, string re, string fmt, optional flags) { @@ -793,21 +791,21 @@ namespace build2 // $regex.replace_lines(, , [, ]) // // Convert the value to string, parse it into lines and for each line - // apply the $regex.replace() function with the specified pattern, format, - // and flags. If the format argument is NULL, omit the "all-NULL" - // replacements for the matched lines from the result. Return unmatched - // lines and line replacements as a name list unless return_lines flag is - // specified (see below), in which case return a single multi-line simple - // name value. + // apply the `$regex.replace()` function with the specified pattern, + // format, and flags. If the format argument is `null`, omit the + // "all-`null`" replacements for the matched lines from the result. Return + // unmatched lines and line replacements as a `name` list unless + // `return_lines` flag is specified (see below), in which case return a + // single multi-line simple `name` value. // - // The following flags are supported in addition to the $regex.replace() - // function flags: + // The following flags are supported in addition to the `$regex.replace()` + // function's flags: // - // return_lines - return the simple name (rather than a name list) - // containing the unmatched lines and line replacements - // separated with newlines. + // return_lines - return the simple name (rather than a name list) + // containing the unmatched lines and line replacements + // separated with newlines. // - // Note that if format_no_copy is specified, unmatched lines are not + // Note that if `format_no_copy` is specified, unmatched lines are not // copied either. // f[".replace_lines"] += [](value v, @@ -836,19 +834,17 @@ namespace build2 // // Split a value of an arbitrary type into a list of unmatched value parts // and replacements of the matched parts, omitting empty ones (unless the - // format_copy_empty flag is specified). Convert the value to string prior - // to matching. - // - // Substitution escape sequences are extended with a subset of Perl - // sequences (see libbutl/regex.hxx for details). + // `format_copy_empty` flag is specified). Convert the value to string + // prior to matching. // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // - // format_no_copy - do not copy unmatched value parts into the result + // format_no_copy - do not copy unmatched value parts into the + // result // - // format_copy_empty - copy empty elements into the result + // format_copy_empty - copy empty elements into the result // f[".split"] += [](value v, string re, string fmt, optional flags) { @@ -868,24 +864,22 @@ namespace build2 // Replace matched parts in a list of elements using the regex format // string. Convert the elements to strings prior to matching. The result // value is untyped and contains concatenation of transformed non-empty - // elements (unless the format_copy_empty flag is specified) optionally + // elements (unless the `format_copy_empty` flag is specified) optionally // separated with a delimiter. // - // Substitution escape sequences are extended with a subset of Perl - // sequences (see libbutl/regex.hxx for details). - // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // - // format_first_only - only replace the first match + // format_first_only - only replace the first match // - // format_no_copy - do not copy unmatched value parts into the result + // format_no_copy - do not copy unmatched value parts into the + // result // - // format_copy_empty - copy empty elements into the result + // format_copy_empty - copy empty elements into the result // - // If both format_first_only and format_no_copy flags are specified then - // the result will be a concatenation of only the first match + // If both `format_first_only` and `format_no_copy` flags are specified + // then the result will be a concatenation of only the first match // replacements. // f[".merge"] += [](names ns, @@ -923,23 +917,21 @@ namespace build2 // Replace matched parts of each element in a list using the regex format // string. Convert the elements to strings prior to matching. Return a // list of transformed elements, omitting the empty ones (unless the - // format_copy_empty flag is specified). - // - // Substitution escape sequences are extended with a subset of Perl - // sequences (see libbutl/regex.hxx for details). + // `format_copy_empty` flag is specified). // // The following flags are supported: // - // icase - match ignoring case + // icase - match ignoring case // - // format_first_only - only replace the first match + // format_first_only - only replace the first match // - // format_no_copy - do not copy unmatched value parts into the result + // format_no_copy - do not copy unmatched value parts into the + // result // - // format_copy_empty - copy empty elements into the result + // format_copy_empty - copy empty elements into the result // - // If both format_first_only and format_no_copy flags are specified then - // the result elements will only contain the replacement of the first + // If both `format_first_only` and `format_no_copy` flags are specified + // then the result elements will only contain the replacement of the first // match. // f[".apply"] += [](names ns, string re, string fmt, optional flags) diff --git a/libbuild2/functions-string.cxx b/libbuild2/functions-string.cxx index 06fe89d..8e5a315 100644 --- a/libbuild2/functions-string.cxx +++ b/libbuild2/functions-string.cxx @@ -39,6 +39,9 @@ namespace build2 { function_family f (m, "string"); + // Note: leave undocumented since there is no good reason for the user to + // call this function (which would be converting string to string). + // // Note that we must handle NULL values (relied upon by the parser // to provide conversion semantics consistent with untyped values). // @@ -47,6 +50,9 @@ namespace build2 return s != nullptr ? move (*s) : string (); }; + // $string.icasecmp(, ) + // $icasecmp(, ) + // // Compare ASCII strings ignoring case and returning the boolean value. // f["icasecmp"] += [](string x, string y) @@ -70,7 +76,10 @@ namespace build2 convert (move (y))) == 0; }; - // Trim. + // $string.trim() + // $trim() + // + // Trim leading and trailing whitespaces in a string. // f["trim"] += [](string s) { @@ -82,7 +91,12 @@ namespace build2 return names {name (trim (convert (move (s))))}; }; - // Convert ASCII strings into lower/upper case. + // $string.lcase() + // $string.ucase() + // $lcase() + // $ucase() + // + // Convert ASCII string into lower/upper case. // f["lcase"] += [](string s) { @@ -105,15 +119,13 @@ namespace build2 }; // $size() - // - // Return the number of elements in the sequence. - // - f["size"] += [] (strings v) {return v.size ();}; - // $size() // - // Return the number of characters (bytes) in the string. + // First form: return the number of elements in the sequence. // + // Second form: return the number of characters (bytes) in the string. + // + f["size"] += [] (strings v) {return v.size ();}; f["size"] += [] (string v) {return v.size ();}; // $sort( [, ]) @@ -122,9 +134,9 @@ namespace build2 // // The following flags are supported: // - // icase - sort ignoring case + // icase - sort ignoring case // - // dedup - in addition to sorting also remove duplicates + // dedup - in addition to sorting also remove duplicates // f["sort"] += [](strings v, optional fs) { @@ -168,9 +180,9 @@ namespace build2 // // The following flags are supported: // - // icase - compare ignoring case + // icase - compare ignoring case // - // See also $regex.find_{match,search}(). + // See also `$regex.find_match()` and `$regex.find_search()`. // f["find"] += [](strings vs, value v, optional fs) { @@ -180,12 +192,12 @@ namespace build2 // $find_index(, [, ]) // // Return the index of the first element in the string sequence that - // is equal to the specified string or $size() if none is + // is equal to the specified string or `$size(strings)` if none is // found. // // The following flags are supported: // - // icase - compare ignoring case + // icase - compare ignoring case // f["find_index"] += [](strings vs, value v, optional fs) { diff --git a/libbuild2/functions-target-triplet.cxx b/libbuild2/functions-target-triplet.cxx index b89cadf..6e12c97 100644 --- a/libbuild2/functions-target-triplet.cxx +++ b/libbuild2/functions-target-triplet.cxx @@ -13,6 +13,12 @@ namespace build2 { function_family f (m, "target_triplet"); + // $string() + // + // Return the canonical (that is, without the `unknown` vendor component) + // target triplet string. + // + // Note that we must handle NULL values (relied upon by the parser // to provide conversion semantics consistent with untyped values). // @@ -21,6 +27,11 @@ namespace build2 return t != nullptr ? t->string () : string (); }; + // $representation() + // + // Return the complete target triplet string that always contains the + // vendor component. + // f["representation"] += [](target_triplet t) { return t.representation (); diff --git a/libbuild2/functions-target.cxx b/libbuild2/functions-target.cxx new file mode 100644 index 0000000..d564aa2 --- /dev/null +++ b/libbuild2/functions-target.cxx @@ -0,0 +1,108 @@ +// file : libbuild2/functions-target.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include // to_target() + +#include +#include +#include +#include + +using namespace std; + +namespace build2 +{ + void + target_functions (function_map& m) + { + // Functions that can be called only on real targets. + // + function_family f (m, "target"); + + // $path() + // + // Return the path of a target (or a list of paths for a list of + // targets). The path must be assigned, which normally happens during + // match. As a result, this function is normally called form a recipe. + // + // Note that while this function is technically not pure, we don't mark it + // as such since it can only be called (normally form a recipe) after the + // target has been matched, meaning that this target is a prerequisite and + // therefore this impurity has been accounted for. + // + f["path"] += [](const scope* s, names ns) + { + if (s == nullptr) + fail << "target.path() called out of scope"; + + // Most of the time we will have a single target so optimize for that. + // + small_vector r; + + for (auto i (ns.begin ()); i != ns.end (); ++i) + { + name& n (*i), o; + const target& t (to_target (*s, move (n), move (n.pair ? *++i : o))); + + if (const auto* pt = t.is_a ()) + { + const path& p (pt->path ()); + + if (&p != &empty_path) + r.push_back (p); + else + fail << "target " << t << " path is not assigned"; + } + else + fail << "target " << t << " is not path-based"; + } + + // We want the result to be path if we were given a single target and + // paths if multiple (or zero). The problem is, we cannot distinguish it + // based on the argument type (e.g., name vs names) since passing an + // out-qualified single target requires two names. + // + if (r.size () == 1) + return value (move (r[0])); + + return value (paths (make_move_iterator (r.begin ()), + make_move_iterator (r.end ()))); + }; + + // $process_path() + // + // Return the process path of an executable target. + // + // Note that while this function is not technically pure, we don't mark it + // as such for the same reasons as for `$path()` above. + // + + // This one can only be called on a single target since we don't support + // containers of process_path's (though we probably could). + // + f["process_path"] += [](const scope* s, names ns) + { + if (s == nullptr) + fail << "target.process_path() called out of scope"; + + if (ns.empty () || ns.size () != (ns[0].pair ? 2 : 1)) + fail << "target.process_path() expects single target"; + + name o; + const target& t ( + to_target (*s, move (ns[0]), move (ns[0].pair ? ns[1] : o))); + + if (const auto* et = t.is_a ()) + { + process_path r (et->process_path ()); + + if (r.empty ()) + fail << "target " << t << " path is not assigned"; + + return r; + } + else + fail << "target " << t << " is not executable-based" << endf; + }; + } +} diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index 2d7f8ba..a91a7e0 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -115,8 +115,9 @@ namespace build2 target, // Target and target type/pattern-specific. prereq // Prerequisite-specific. - // Note: remember to update the visibility attribute parsing if adding - // any new values here. + // Note: remember to update the visibility attribute parsing if adding any + // new values here. As well as the $builtin.visibility() function + // documentation. }; // VC14 reports ambiguity but seems to work if we don't provide any. -- cgit v1.1