diff options
-rw-r--r-- | NEWS | 391 | ||||
-rwxr-xr-x | bootstrap.sh | 2 | ||||
m--------- | config | 0 | ||||
-rwxr-xr-x | doc/cli.sh | 2 | ||||
-rw-r--r-- | libbuild2/buildfile | 4 | ||||
-rw-r--r-- | libbuild2/cc/compile-rule.cxx | 14 | ||||
-rw-r--r-- | libbuild2/cc/guess.cxx | 44 | ||||
-rw-r--r-- | libbuild2/cc/link-rule.cxx | 70 | ||||
-rw-r--r-- | libbuild2/functions-filesystem.cxx | 51 | ||||
-rw-r--r-- | libbuild2/install/rule.cxx | 14 | ||||
-rw-r--r-- | libbuild2/utility.hxx | 28 | ||||
-rw-r--r-- | libbuild2/utility.txx | 21 | ||||
-rw-r--r-- | libbuild2/variable.cxx | 6 | ||||
-rw-r--r-- | manifest | 8 | ||||
-rw-r--r-- | repositories.manifest | 2 | ||||
-rw-r--r-- | tests/function/filesystem/testscript | 48 | ||||
-rw-r--r-- | tests/function/path/testscript | 2 |
17 files changed, 648 insertions, 59 deletions
@@ -1,3 +1,394 @@ +Version 0.17.0 + + * C++20 modules support improvements: + + - Named modules support in Clang, including automatic building of the + `std` and `std.compat` standard library modules from libc++ with Clang + 18 or later. + + - Named modules support in MSVC, including automatic building of the `std` + and `std.compat` standard library modules. + + Note: if combining the standard library modules importation with the + standard library headers inclusion, MSVC 17.10 or later is recommended. + + - Modules support can now be enabled for any std.cxx value greater or + equal 20 (including `latest`). + + - The "C++ Modules Support" section in the manual has been updated to + match the current state of the implementation. + + * The `latest` and `experimental` cxx.std values are now mapped to C++26 + from GCC 14 and Clang 18. + + * The 23 and 2x c.std values are now mapped to /std:clatest starting from + MSVC 17.9. In particular, this option enables C23 typeof support. + + * /Zc:preprocessor is now added for the `experimental` cxx.std value from + MSVC 17.9. + + * New string_set buildfile value type. + + This exposes the std::set<std::string> type to buildfiles. + + New functions: + + $size(<string-set>) + + Subscript returns true if the value is present and false otherwise (so + it is mapped to std::set::contains()). For example: + + set = [string_set] a b c + + if ($set[b]) + ... + + Note that append (+=) and prepend (=+) have the same semantics + (std::set::insert()). For example: + + set = [string_set] a b + set += c b # a b c + set =+ d b # a b c d + + Example of iteration: + + set = [string_set] a b c + for k: $set + ... + + * New string_map buildfile value type. + + This exposes the std::map<std::string,std::string> type to buildfiles. + + New functions: + + $size(<string-map>) + $keys(<string-map>) + + Subscript can be used to look up a value by key. The result is [null] if + there is no value associated with the specified key. For example: + + map = [string_map] a@1 b@2 c@3 + + b = ($map[b]) # 2 + + if ($map[z] == [null]) + ... + + Note that append (+=) is overriding (like std::map::insert_or_assign()) + while prepend (=+) is not (like std::map::insert()). In a sense, whatever + appears last (from left to right) is kept, which is consistent with what + we expect to happen when specifying the same key repeatedly in a literal + representation. For example: + + map = [string_map] a@0 b@2 a@1 # a@1 b@2 + map += b@0 c@3 # a@1 b@0 c@3 + map =+ b@1 d@4 # a@1 b@0 c@3 d@4 + + Example of iteration: + + map = [string_map] a@1 b@2 c@3 + for p: $map + { + k = $first($p) + v = $second($p) + } + + While the subscript is mapped to key lookup only, index-based access can + be implemented (with a bit of overhead) using the $keys() function: + + map = [string_map] a@1 b@2 c@3 + keys = $keys($m) + for i: $integer_sequence(0, $size($keys)) + { + k = ($keys[$i]) + v = ($map[$k]) + } + + * New JSON buildfile value types. + + New types: + + json + json_array + json_object + + New functions: + + $json.value_type(<json>) + $json.value_size(<json>) + $json.member_{name,value}(<json-member>) + $json.object_names(<json-object>) + $json.array_size(<json-array>) + $json.array_find(<json-array>, <json>) + $json.array_find_index(<json-array>, <json>) + $json.load(<path>) + $json.parse(<text>) + $json.serialize(<json>[, <indentation>]) + + See "JSON Functions" in the manual for details. + + For example, to load a JSON value from a file: + + j = $json.load($src_base/board.json) + + Or to construct it in a buildfile: + + j = [json] one@1 two@([json] 2 3 4) three@([json] x@1 y@-1) + + This can also be done incrementally with append/prepend: + + j = [json_object] + j += one@1 + j += two@([json] 2 3 4) + j += three@([json] x@1 y@-1) + + Instead of using this JSON-like syntax, one can also specify valid JSON + input text: + + j = [json] '{"one":1, "two":[2, 3, 4], "three":{"x":1, "y":-1}' + + Besides the above set of functions, other handy ways to access components + in a JSON value are iteration and subscript. For example: + + for m: $j + print $member_name($m) $member_value($m) + + print ($j[three]) + + A subscript can be nested: + + print ($j[two][1]) + print ($j[three][x]) + + While a JSON value can be printed directly like any other value, the + representation will not be pretty-printed. As a result, for complex + JSON values, printing a serialized representation might be a more + readable option: + + info $serialize($j) + + * New json_map and json_set buildfile value types. + + These expose the std::map<json_value,json_value> and std::set<json_value> + types to buildfiles. + + New functions: + + $size(<json-set>) + $size(<json-map>) + $keys(<json-map>) + + Note that the $keys() function returns the list of map key as a json + array. + + For example: + + m = [json_map] 2@([json] a@1 b@2) 1@([json] 1 2) + s = [json_set] ([json] x@1 y@2) ([json] a@1 b@2) + + print ($m[2][b]) # 2 + print ($s[([json] y@2 x@1)]) # true + + * New $first() and $second() functions which return the first and second + halves of a pair, respectively. + + * New string functions: + + $string.contains() + $string.starts_with() + $string.ends_with() + $string.replace() + + See "String Functions" in the manual for details. + + * New path functions: + + $path.absolute() + $path.simple() + $path.sub_path() + $path.super_path() + $path.complete() + $path.try_normalize() + $path.try_actualize() + + See "Path Functions" in the manual for details. + + * New filesystem functions: + + $filesystem.file_exists() + $filesystem.directory_exists() + + See "Filesystem Functions" in the manual for details. + + * Runtime/buildtime distinction when installing libraries. + + Specifically, now, if a library is installed solely as a prerequisite of + an executable (potentially recursively), then only its runtime files are + installed omitting everything buildtime-related (static/import libraries, + non-versioned symlinks for shared libraries, pkg-config files, headers, + etc). If you are familiar with the runtime and -dev/-devel package splits + for libraries in Debian/Fedora, this is the analogous semantics. + + * Support for extracting C and C++ predefined macros (predefs). + + Specifically, the c and cxx modules now provide the c.predefs and + cxx.predefs submodules which can be loaded in order to register a rule + that generates a C or C++ header with the predefined compiler macros, + respectively. For details, refer to "C Compiler Predefined Macro + Extraction" and "C++ Compiler Predefined Macro Extraction" in the manual. + + * Ability to serialize compilation/linking in C/C++ rules. + + Specifically, both the C/C++ compile and link rules now recognize the + cc.serialize boolean variable which instructs them to compile/link + serially with regards to any other recipe. + + This is primarily useful when compiling large translation units or linking + large binaries that require so much memory that doing that in parallel + with other compilation/linking jobs is likely to summon the OOM killer. + For example: + + obj{memory-hog}: cc.serialize = true + + * Ability to specify compiler mode options in a buildfile. + + Now the configured mode options are appended to buildfile-specified (which + must be specified before loading the guess module). + + In particular, this ability to specify the compiler mode in a buildfile is + useful in embedded development where the project may need to hardcode + options like -target, -nostdinc, etc. For example: + + cxx.std = 20 + cxx.mode = -target riscv32-unknown-unknown -nostdinc + using cxx + + * New ~host-no-warnings and ~build2-no-warnings special configurations. + + These are parallel to ~host and ~build2 but with suppressed C/C++ compiler + warnings. + + Note also that the C++ ad hoc recipes are now by default built in + ~build2-no-warnings instead of ~build2 unless the project is configured + for development with config.<project>.develop=true. + + * New {bin,c,cxx}.types submodules that only register target types. + + This is primarily useful when providing custom C/C++ compilation/linking + rules. + + * New -s|--timeout-success option in the `env` script builtin. + + The semantics is equivalent to the --success option in the `timeout` + builtin. + + * Ability to specify alternative sysroot for pkg-config files. + + Specifically, the new config.cc.pkgconfig.sysroot variable provides + roughly equivalent functionality to PKG_CONFIG_SYSROOT_DIR in + pkg-config. For details and limitations, see "Rewriting Installed + Libraries System Root (sysroot)" in the manual for details. + + * Ability to alias a target type from another project. + + The syntax is: + + define <type> = <proj-scope>/<type> + + For example: + + sdk_proj = ... # Root directory of the SDK. + define ldscript = $sdk_proj/ldscript + + + Additionally, unknown target types of imported targets are now aliased + automatically. + + * New no_default_target attribute for source, buildfile import directives. + + This attribute can be used to disable the default target semantics for the + sources/imported buildfile. + + * Allow imported buildfiles to use config.* variables from own projects + (that is, the project from which the buildfile is imported). + + * The fsdir{} targets are now usable in ad hoc recipes. + + In particular, they can now be used to represent directory symlinks. For + example: + + exe{hello}: ... fsdir{assets} + + fsdir{assets}: + % update + {{ + ln -s $src_base/assets $out_base/assets + }} + % clean + {{ + rm $out_base/assets + }} + + Likewise, file{} targets can now be used to represent file symlinks + created in ad hoc recipes. + + * Ability to specify ad hoc recipes in separate files. + + This can now be achieved with the new `recipe` directive: + + recipe <language> <file> + + Note that similar to the use of if-else and switch directives with recipes, + this directive requires explicit % recipe header. For example, instead of: + + file{foo.output}: + {{ + echo 'hello' >$path($>) + }} + + We can now write: + + file{foo.output}: + % + recipe buildscript hello.buildscript + + With hello.buildscript containing: + + echo 'hello' >$path($>) + + Similarly, for C++ recipes (this time for a pattern rule), instead of: + + [rule_name=hello] file{~'/(.+)\.output/'}: + % update clean + {{ c++ 1 -- + + -- + + ... + + }} + + We can now write: + + [rule_name=hello] file{~'/(.+)\.output/'}: + % update clean + recipe c++ hello.cxx + + With hello.cxx containing: + + // c++ 1 -- + + -- + + ... + + Relative <file> paths are resolved using the buildfile directory that + contains the `recipe` directive as a base. + + Note also that this mechanism can be used in exported buildfiles with + recipe files placed into build/export/ together with buildfiles. + Version 0.16.0 * Support for Objective-C/C++ compilation. diff --git a/bootstrap.sh b/bootstrap.sh index 9bd13b4..26796f9 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -147,4 +147,4 @@ done # mode since 4.9 doesn't recognize c++1z. # set -x -"$cxx" "-I$libbutl" -I. -DBUILD2_BOOTSTRAP '-DBUILD2_HOST_TRIPLET="'"$host"'"' -finput-charset=UTF-8 -std=c++1y "$@" -o build2/b-boot $r -lpthread +"$cxx" "-I$libbutl" -I. -DBUILD2_BOOTSTRAP '-DBUILD2_HOST_TRIPLET="'"$host"'"' -finput-charset=UTF-8 -std=c++1y "$@" -o build2/b-boot $r -pthread diff --git a/config b/config -Subproject 4ad4bb7c30aca1e705448ba8d51a210bbd47bb5 +Subproject 948ae97ca5703224bd3eada06b7a69f40dd15a0 @@ -1,6 +1,6 @@ #! /usr/bin/env bash -version=0.17.0-a.0.z +version=0.18.0-a.0.z trap 'exit 1' ERR set -o errtrace # Trap in functions. diff --git a/libbuild2/buildfile b/libbuild2/buildfile index 3518d93..d9711e6 100644 --- a/libbuild2/buildfile +++ b/libbuild2/buildfile @@ -293,7 +293,7 @@ if ($install.root != [null]) if ($cxx.target.class != 'windows') { - libul{build2}: cxx.libs += -lpthread + libul{build2}: cxx.libs += -pthread # Note: only linking libdl in shared build. # @@ -323,7 +323,7 @@ lib{build2}: # needed for some std::thread implementations (like libstdc++). # if ($cxx.target.class != 'windows') - lib{build2}: cxx.export.libs += -lpthread + lib{build2}: cxx.export.libs += -pthread liba{build2}: cxx.export.poptions += -DLIBBUILD2_STATIC libs{build2}: cxx.export.poptions += -DLIBBUILD2_SHARED diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 7629ed5..95ba89f 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -3213,7 +3213,21 @@ namespace build2 if (f != nullptr) { //cache_cls.fetch_add (1, memory_order_relaxed); + +#if 0 assert (r.first == f); +#else + if (r.first != f) + { + info << "inconsistent header cache content" << + info << "encountered: " << *f << + info << "expected: " << *r.first << + info << "please report at " + << "https://github.com/build2/build2/issues/390"; + + assert (r.first == f); + } +#endif } } diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index d7e9c63..5ae6fb2 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -2580,27 +2580,29 @@ namespace build2 // // Note that this is Apple Clang version and not XCode version. // - // 4.2 -> 3.2svn - // 5.0 -> 3.3svn - // 5.1 -> 3.4svn - // 6.0 -> 3.5svn - // 6.1.0 -> 3.6svn - // 7.0.0 -> 3.7 - // 7.3.0 -> 3.8 - // 8.0.0 -> 3.9 - // 8.1.0 -> ? - // 9.0.0 -> 4.0 - // 9.1.0 -> 5.0 - // 10.0.0 -> 6.0 - // 11.0.0 -> 7.0 - // 11.0.3 -> 8.0 (yes, seriously!) - // 12.0.0 -> 9.0 - // 12.0.5 -> 10.0 (yes, seriously!) - // 13.0.0 -> 11.0 - // 13.1.6 -> 12.0 - // 14.0.0 -> 12.0 (_LIBCPP_VERSION=130000) - // 14.0.3 -> 15.0 (_LIBCPP_VERSION=150006) - // 15.0.0 -> 16.0 (_LIBCPP_VERSION=160002) + // 4.2 -> 3.2svn + // 5.0 -> 3.3svn + // 5.1 -> 3.4svn + // 6.0 -> 3.5svn + // 6.1.0 -> 3.6svn + // 7.0.0 -> 3.7 + // 7.3.0 -> 3.8 + // 8.0.0 -> 3.9 + // 8.1.0 -> ? + // 9.0.0 -> 4.0 + // 9.1.0 -> 5.0 + // 10.0.0 -> 6.0 + // 11.0.0 -> 7.0 + // 11.0.3 -> 8.0 (yes, seriously!) + // 12.0.0 -> 9.0 + // 12.0.5 -> 10.0 (yes, seriously!) + // 13.0.0 -> 11.0 + // 13.1.6 -> 12.0 + // 14.0.0 -> 12.0 (_LIBCPP_VERSION=130000) + // 14.0.3 -> 15.0 (_LIBCPP_VERSION=150006) + // 15.0.0.0 -> 16.0 (_LIBCPP_VERSION=160002) + // 15.0.0.1 -> 16.0 (_LIBCPP_VERSION=160006) + // 15.0.0.3 -> 16.0 (_LIBCPP_VERSION=170006) // uint64_t mj (var_ver->major); uint64_t mi (var_ver->minor); diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 08a60b9..417cba5 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -2585,6 +2585,24 @@ namespace build2 // We don't rpath system libraries. Why, you may ask? There are many // good reasons and I have them written on a napkin somewhere... // + // Well, the main reason is that we naturally assume the dynamic + // linker searches there by default and so there is no need for rpath. + // Plus, rpath would prevent "overriding" distribution-system + // (/usr/lib) libraries with user-system (/usr/local/lib). + // + // Note, however, that some operating systems don't search in + // /usr/local/lib by default (for example, Fedora, RHEL, Mac OS since + // version 13). In a sense, on these platforms /usr/local is + // "half-system" in that the system compiler by default searches in + // /usr/local/include and/or /usr/local/lib (see config_module::init() + // for background) but the dynamic linker does not. While we could + // hack this test for such platforms and add rpath for /usr/local/lib, + // this is still feels wrong (the user can always "fix" such an + // operating system by instructing the dynamic linker to search in + // /usr/local/lib, as many, including ourselves, do). So for now we + // are not going to do anything. In the end, the user can always add + // an rpath for /usr/local/lib manually. + // // We also assume system libraries can only depend on other system // libraries and so can prune the traversal. // @@ -2596,18 +2614,26 @@ namespace build2 size_t p (path::traits_type::rfind_separator (f)); assert (p != string::npos); + // For good measure, also suppress duplicates at the options level. + // This will take care of different libraries built in the same + // directory, system-installed, etc. + if (d.rpath) { string o ("-Wl,-rpath,"); o.append (f, 0, (p != 0 ? p : 1)); // Don't include trailing slash. - d.args.push_back (move (o)); + + if (find (d.args.begin (), d.args.end (), o) == d.args.end ()) + d.args.push_back (move (o)); } if (d.rpath_link) { string o ("-Wl,-rpath-link,"); o.append (f, 0, (p != 0 ? p : 1)); - d.args.push_back (move (o)); + + if (find (d.args.begin (), d.args.end (), o) == d.args.end ()) + d.args.push_back (move (o)); } }; @@ -2660,7 +2686,9 @@ namespace build2 if ((c ? f.compare (p, string::npos, e) : icasecmp (f.c_str () + p, e)) == 0) + { append (f); + } } } @@ -2671,13 +2699,22 @@ namespace build2 { // Top-level shared library dependency. // + // As above, suppress duplicates. + // + if (find (d.ls.begin (), d.ls.end (), &l) != d.ls.end ()) + return; + if (!l.path ().empty ()) // Not binless. { // It is either matched or imported so should be a cc library. // if (!cast_false<bool> (l.vars[c_system])) { - args.push_back ("-Wl,-rpath," + l.path ().directory ().string ()); + string o ("-Wl,-rpath," + l.path ().directory ().string ()); + + if (find (args.begin (), args.end (), o) == args.end ()) + args.push_back (move (o)); + ls.push_back (&l); } } @@ -3332,6 +3369,9 @@ namespace build2 origin = p.directory (); } + // Note: suppress duplicates at the options level, similar to + // rpath_libraries(). + bool origin_used (false); for (const dir_path& p: cast<dir_paths> (l)) { @@ -3368,7 +3408,8 @@ namespace build2 else o += p.string (); - sargs.push_back (move (o)); + if (find (sargs.begin (), sargs.end (), o) == sargs.end ()) + sargs.push_back (move (o)); } // According to the Internet, `-Wl,-z,origin` is not needed except @@ -3386,7 +3427,12 @@ namespace build2 fail << ctgt << " does not support rpath-link"; for (const dir_path& p: cast<dir_paths> (l)) - sargs.push_back ("-Wl,-rpath-link," + p.string ()); + { + string o ("-Wl,-rpath-link," + p.string ()); + + if (find (sargs.begin (), sargs.end (), o) == sargs.end ()) + sargs.push_back (move (o)); + } } } @@ -3433,13 +3479,19 @@ namespace build2 append_args (sargs1); } - else + else if (b != x) { - append_option_values ( - args, + // Use the more canonical combined form (-L/usr/local/lib) even + // though it's less efficient (the split one is just too much of an + // eye-sore in the logs). + // + append_combined_option_values ( + sargs1, "-L", b, x, - [] (const dir_path& d) {return d.string ().c_str ();}); + [] (const dir_path& d) -> const string& {return d.string ();}); + + append_args (sargs1); } } diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx index 665a0f3..340c2bc 100644 --- a/libbuild2/functions-filesystem.cxx +++ b/libbuild2/functions-filesystem.cxx @@ -5,6 +5,7 @@ #include <libbuild2/function.hxx> #include <libbuild2/variable.hxx> +#include <libbuild2/filesystem.hxx> using namespace std; using namespace butl; @@ -95,14 +96,60 @@ namespace build2 return r; } + static bool + file_exists (path&& f) + { + if (f.relative () && path_traits::thread_current_directory () != nullptr) + f.complete (); + + return exists (f); + } + + static bool + directory_exists (dir_path&& d) + { + if (d.relative () && path_traits::thread_current_directory () != nullptr) + d.complete (); + + return exists (d); + } + void filesystem_functions (function_map& m) { - // @@ Maybe we should have the ability to mark the whole family as not - // pure? + // NOTE: anything that depends on relative path must handle the + // thread-specific curren directory override explicitly. function_family f (m, "filesystem"); + // $file_exists(<path>) + // + // Return true if a filesystem entry at the specified path exists and is a + // regular file (or is a symlink to a regular file) and false otherwise. + // + // Note that this function is not pure. + // + { + auto e (f.insert ("file_exists", false)); + + e += [](path f) {return file_exists (move (f));}; + e += [](names ns) {return file_exists (convert<path> (move (ns)));}; + } + + // $directory_exists(<path>) + // + // Return true if a filesystem entry at the specified path exists and is a + // directory (or is a symlink to a directory) and false otherwise. + // + // Note that this function is not pure. + // + { + auto e (f.insert ("directory_exists", false)); + + e += [](path f) {return directory_exists (path_cast<dir_path> (move (f)));}; + e += [](names ns) {return directory_exists (convert<dir_path> (move (ns)));}; + } + // $path_search(<pattern>[, <start-dir>]) // // Return filesystem paths that match the shell-like wildcard pattern. If diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx index 873b2e9..1aa21d0 100644 --- a/libbuild2/install/rule.cxx +++ b/libbuild2/install/rule.cxx @@ -871,16 +871,16 @@ namespace build2 r->sudo = cast_null<string> (s["config.install.sudo"]); if (r->cmd == nullptr) - r->cmd = &cast<path> (s["config.install.cmd"]); + r->cmd = cast_null<path> (s["config.install.cmd"]); if (r->options == nullptr) r->options = cast_null<strings> (s["config.install.options"]); if (r->mode == nullptr) - r->mode = &cast<string> (s["config.install.mode"]); + r->mode = cast_null<string> (s["config.install.mode"]); if (r->dir_mode == nullptr) - r->dir_mode = &cast<string> (s["config.install.dir_mode"]); + r->dir_mode = cast_null<string> (s["config.install.dir_mode"]); return rs; } @@ -1064,6 +1064,10 @@ namespace build2 if (base.sudo != nullptr) args.push_back (base.sudo->c_str ()); + // Wouldn't be here otherwise. + // + assert (base.cmd != nullptr && base.dir_mode != nullptr); + args.push_back (base.cmd->string ().c_str ()); args.push_back ("-d"); @@ -1129,6 +1133,10 @@ namespace build2 if (base.sudo != nullptr) args.push_back (base.sudo->c_str ()); + // Wouldn't be here otherwise. + // + assert (base.cmd != nullptr && base.mode != nullptr); + args.push_back (base.cmd->string ().c_str ()); if (base.options != nullptr) diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index b534f41..151b409 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -877,17 +877,29 @@ namespace build2 // template <typename I, typename F> void - append_option_values (cstrings&, - const char* opt, - I begin, I end, - F&& get = [] (const string& s) {return s.c_str ();}); + append_option_values ( + cstrings&, + const char* opt, + I begin, I end, + F&& get = [] (const string& s) {return s.c_str ();}); template <typename I, typename F> void - append_option_values (sha256&, - const char* opt, - I begin, I end, - F&& get = [] (const string& s) {return s;}); + append_option_values ( + sha256&, + const char* opt, + I begin, I end, + F&& get = [] (const string& s) -> const string& {return s;}); + + // As above but in a combined form (e.g., -L/usr/local/lib). + // + template <typename I, typename F> + void + append_combined_option_values ( + strings&, + const char* opt, + I begin, I end, + F&& get = [] (const string& s) -> const string& {return s;}); // As above but append a single option (used for append/hash uniformity). // diff --git a/libbuild2/utility.txx b/libbuild2/utility.txx index d2fc29c..cdf510f 100644 --- a/libbuild2/utility.txx +++ b/libbuild2/utility.txx @@ -5,16 +5,16 @@ namespace build2 { template <typename I, typename F> void - append_option_values (cstrings& args, const char* o, I b, I e, F&& get) + append_option_values (cstrings& ss, const char* o, I b, I e, F&& get) { if (b != e) { - args.reserve (args.size () + (e - b)); + ss.reserve (ss.size () + (e - b)); for (; b != e; ++b) { - args.push_back (o); - args.push_back (get (*b)); + ss.push_back (o); + ss.push_back (get (*b)); } } } @@ -30,6 +30,19 @@ namespace build2 } } + template <typename I, typename F> + void + append_combined_option_values (strings& ss, const char* o, I b, I e, F&& get) + { + if (b != e) + { + ss.reserve (ss.size () + (e - b)); + + for (; b != e; ++b) + ss.push_back (string (o) += get (*b)); + } + } + template <typename K> basic_path<char, K> relative (const basic_path<char, K>& p) diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index 078c13a..fb9e840 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -510,7 +510,7 @@ namespace build2 &simple_append<bool>, // Prepend same as append. &simple_reverse<bool>, nullptr, // No cast (cast data_ directly). - nullptr, // No compare (compare as POD). + &simple_compare<bool>, nullptr, // Never empty. nullptr, // Subscript. nullptr // Iterate. @@ -570,7 +570,7 @@ namespace build2 &simple_append<int64_t>, // Prepend same as append. &simple_reverse<int64_t>, nullptr, // No cast (cast data_ directly). - nullptr, // No compare (compare as POD). + &simple_compare<int64_t>, nullptr, // Never empty. nullptr, // Subscript. nullptr // Iterate. @@ -632,7 +632,7 @@ namespace build2 &simple_append<uint64_t>, // Prepend same as append. &simple_reverse<uint64_t>, nullptr, // No cast (cast data_ directly). - nullptr, // No compare (compare as POD). + &simple_compare<uint64_t>, nullptr, // Never empty. nullptr, // Subscript. nullptr // Iterate. @@ -1,6 +1,6 @@ : 1 name: build2 -version: 0.17.0-a.0.z +version: 0.18.0-a.0.z summary: build2 build system license: MIT topics: build system, build toolchain @@ -14,8 +14,8 @@ email: users@build2.org build-warning-email: builds@build2.org builds: all : &host requires: c++14 -depends: * build2 >= 0.16.0- -depends: * bpkg >= 0.16.0- +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 # @@ DEP Should probably become conditional dependency. #requires: ? cli ; Only required if changing .cli files. -depends: libbutl [0.17.0-a.0.1 0.17.0-a.1) +depends: libbutl [0.18.0-a.0.1 0.18.0-a.1) diff --git a/repositories.manifest b/repositories.manifest index 3d857bc..25be350 100644 --- a/repositories.manifest +++ b/repositories.manifest @@ -3,4 +3,4 @@ summary: build2 build system repository : role: prerequisite -location: ../libbutl.git##HEAD +location: ../libbutl.git#HEAD diff --git a/tests/function/filesystem/testscript b/tests/function/filesystem/testscript index cf93b8b..c7c08f1 100644 --- a/tests/function/filesystem/testscript +++ b/tests/function/filesystem/testscript @@ -73,3 +73,51 @@ EOE } } + +: file_exists +: +{ + : file + : + touch f; + $* <'print $file_exists(f)' >'true' + + : symlink + : + touch f && ln -s f s; + $* <'print $file_exists([path] s)' >'true' + + : directory + : + mkdir d; + $* <'print $file_exists([dir_path] d)' >'false' + + : testscript + : + touch f; + echo $file_exists(f) >'true' +} + +: directory_exists +: +{ + : directory + : + mkdir d; + $* <'print $directory_exists(d)' >'true' + + : symlink + : + mkdir d && ln -s d s; + $* <'print $directory_exists([dir_path] d)' >'true' + + : file + : + touch f; + $* <'print $directory_exists([path] f)' >'false' + + : testscript + : + mkdir d; + echo $directory_exists(d) >'true' +} diff --git a/tests/function/path/testscript b/tests/function/path/testscript index d49e9e5..6321b3d 100644 --- a/tests/function/path/testscript +++ b/tests/function/path/testscript @@ -210,6 +210,8 @@ s = ($posix ? '/' : '\') $* <'print $complete([path] a)' >"$~$(s)a" : path $* <'print $complete([dir_path] a)' >"$~$(s)a$(s)" : dir-path $* <'print $path.complete(a)' >"$~$(s)a" : untyped + + echo $path.complete(a) > "$~$(s)a" : testscript } : canonicalize |