aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS391
-rwxr-xr-xbootstrap.sh2
m---------config0
-rwxr-xr-xdoc/cli.sh2
-rw-r--r--libbuild2/buildfile4
-rw-r--r--libbuild2/cc/compile-rule.cxx14
-rw-r--r--libbuild2/cc/guess.cxx44
-rw-r--r--libbuild2/cc/link-rule.cxx70
-rw-r--r--libbuild2/functions-filesystem.cxx51
-rw-r--r--libbuild2/install/rule.cxx14
-rw-r--r--libbuild2/utility.hxx28
-rw-r--r--libbuild2/utility.txx21
-rw-r--r--libbuild2/variable.cxx6
-rw-r--r--manifest8
-rw-r--r--repositories.manifest2
-rw-r--r--tests/function/filesystem/testscript48
-rw-r--r--tests/function/path/testscript2
17 files changed, 648 insertions, 59 deletions
diff --git a/NEWS b/NEWS
index 597156e..1c285c7 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/doc/cli.sh b/doc/cli.sh
index 398371c..8acbe2a 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -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.
diff --git a/manifest b/manifest
index fecd6c5..6c57b2b 100644
--- a/manifest
+++ b/manifest
@@ -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