aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/.gitignore4
-rw-r--r--doc/buildfile278
-rwxr-xr-xdoc/cli.sh28
-rw-r--r--doc/manual.cli3358
-rw-r--r--doc/testscript.cli441
5 files changed, 3159 insertions, 950 deletions
diff --git a/doc/.gitignore b/doc/.gitignore
index 9d45a89..d33dca5 100644
--- a/doc/.gitignore
+++ b/doc/.gitignore
@@ -3,3 +3,7 @@ b.1
build2-*-manual.xhtml
*.ps
*.pdf
+
+# Auto-extracted documentation.
+#
+functions-*.cli
diff --git a/doc/buildfile b/doc/buildfile
index c797761..5508ddb 100644
--- a/doc/buildfile
+++ b/doc/buildfile
@@ -11,8 +11,286 @@ xhtml{*}: extension = xhtml
css{common pre-box man} \
file{man-*}
+# @@ TODO: why no testscript manual?
+
./: doc{build2-build-system-manual*} \
css{code-box common doc pre-box toc} \
file{manual.cli doc-* *.html2ps}
./: file{cli.sh}
+
+# The build2 function documentation format for auto-extraction.
+#
+# Each listed .cxx file is expected to provide functions for one function
+# family. In order to plug a new family/file, perform the following steps:
+#
+# 1. List the corresponding functions-<family>.cxx file stem below.
+# 2. Add a section and source the generated .cli file in manual.cli.
+#
+# The functions-<family>.cxx file is expected to contain one or more comments
+# in the following form:
+#
+# // <synopsis-line>+
+# // <blank-line>
+# // (<paragraph-line>+|<code-block-line>+
+# // <blank-line>)+
+#
+# That is, the comment starts with one or more synopsis lines followed by a
+# blank line followed by a mixture of text paragraphs and/or preformatted code
+# blocks separated by blank lines. The comment must be terminated with a blank
+# line. See functions-regex.cxx for comprehensive examples.
+#
+# The synopsis line should be in the form:
+#
+# // $[<family>.]<name>(...)
+#
+# Each synopsis line may or may not be be qualified with <family>. The rule is
+# as follows: If the function can only be called qualified, then the synopsis
+# should contains a single qualified line. If the function can be called
+# unqualified, then the synopsis should contains a single unqualified line.
+# If some signatures can be called unqualifed while some -- only qualified,
+# then there should be both qualified and unqualified lines. Note that there
+# can also be functions with different <name>s in a single synopsis block.
+#
+# The text paragraphs may contain `...` and <...> fragments which are
+# translated to \c{} and \ci{}, respectively. Note that these fragments cannot
+# span multiple lines.
+#
+# The preformatted code blocks must be indented four spaces (not counting
+# the space after //).
+#
+# There is problem with distinguishing blanks within a code block and a blank
+# that separates the code block from the subsequent paragraph (or another code
+# block). Strictly speaking, such a blank should be indented with four spaces
+# but trailing spaces at the end of the line are generally frowned upon and in
+# our code should be automatically zapped on save.
+#
+# So what we are going to do is treat a single blank line between two code
+# lines as belonging to the code block rather than separating two code
+# blocks. The latter can be achieved with a double blank line. Note that this
+# means we cannot have double blank lines within a code block.
+
+# @@ TODO: using file{.cli}, change to cli{} once switch to ad hoc recipes.
+# @@ TODO: get rid of backlink below once switch to ad hoc recipes.
+
+for ff: functions-builtin \
+ functions-string \
+ functions-integer \
+ functions-json \
+ functions-bool \
+ functions-path \
+ functions-name \
+ functions-target \
+ functions-regex \
+ functions-process \
+ functions-filesystem \
+ functions-project-name \
+ functions-process-path \
+ functions-target-triplet
+{
+ alias{functions}: file{$(ff).cli}: $src_root/libbuild2/cxx{$ff}
+ file{$(ff).cli}: backlink = true # @@ TMP until migrate to recipe (see cli.sh)
+}
+
+file{~'/(functions-.+)\.cli/'}: cxx{~'/\1/'}
+{{
+ diag doc $< -> $>
+
+ i = $path($<) # Input.
+ o = $path($>) # Output.
+
+ # Extract the family name.
+ #
+ family = $regex.replace($name($<), 'functions-(.+)', '\1')
+ family = $regex.replace($family, '-', '_')
+
+ echo "// Auto-extracted from $leaf($i) for \$$(family).*\(\)" >$o
+
+ # The overall plan is as follows: read the file line by line recognizing the
+ # function documentation comments and maintaining the parsing state.
+ #
+ # Parsing state, one of:
+ #
+ # none -- outside of a documentation comment
+ # syno -- inside synopsis
+ # para -- inside text
+ # code -- inside preformatted code block
+ # blnk -- blank line separating synopsis/para/code
+ #
+ s = none # Current state.
+ p = none # Previous state.
+
+ ln = [uint64] 0 # Line number.
+ for -n l <=$i
+ ln += 1
+
+ # Look for a C++ comments and extract its text.
+ #
+ t = $regex.match($l, '\s*// ?(.*)', return_subs)
+
+ # Note that when writing the output we use the "leading blank line" rather
+ # than trailing approach. That is, we write the blank before starting the
+ # next block rather than after.
+
+ if ($t == [null])
+ if ($s != 'none')
+ if ($s != 'blnk')
+ exit "$i:$ln: blank line expected after description"
+ end
+
+ # Close delayed code block (see below for details).
+ #
+ if ($p == 'code')
+ echo "\\" >>$o # end code
+ end
+
+ echo "\"" >>$o # end cli doc string
+ end
+
+ p = $s
+ s = 'none'
+ else
+ # This is a comment. What we do next depends on which state we are in.
+ #
+ if ($s == 'none' || $s == 'syno')
+ p = $s
+
+ # See if this is a synopsys line.
+ #
+ if $regex.match($t, '\$.+\(.+\)')
+ if ($s == 'none')
+ synopsis = [strings] # Accumulate synopsis lines.
+ s = 'syno'
+ end
+
+ synopsis += $t
+ elif ($s == 'syno')
+ if ($t != '')
+ exit "$i:$ln: blank line expected after synopsis"
+ end
+
+ echo "$\n\"" >>$o # start cli doc string
+
+ # Write the heading. Use the first function name as id.
+ #
+ # Note that while the functions in the synopsis could be
+ # unqualified, in the heading we always write them qualified. We
+ # also have to suppress duplicates since the same function can be
+ # mentioned in the synopsis both qualified and unqualified.
+ #
+ id = [null]
+ hs = [strings]
+ for t: $synopsis
+ t = $regex.replace($t, '\$(.+)\(.+\)', '\1') # Extract func name.
+ f = $regex.match($t, '(.+)\..+', return_subs) # Extract family.
+
+ if ($f == [null])
+ t = "$(family).$t" # Qualify.
+ elif ($f != $family)
+ exit "$i:$ln: function family in $t does not match $family"
+ end
+
+ if ($id == [null]) # First.
+ id = $regex.replace($t, '\.', '-')
+ end
+
+ # Suppress duplicates.
+ #
+ if! $find($hs, $t)
+ hs += $t
+ end
+ end
+
+ h = $regex.merge($hs, '(.+)', '\\c{$\1()}', ', ')
+
+ echo "\\h2#functions-$id|$h|$\n" >>$o # heading
+
+ echo "\\" >>$o # start synopsis
+ for t: $synopsis
+ echo $t >>$o # synopsis line
+ end
+ echo "\\" >>$o # end synopsis
+
+ s = 'blnk'
+ end
+ else # para|code|blnk
+ # See if this is a code line.
+ #
+ c = $regex.match($t, ' (.+)', return_subs)
+
+ if ($c != [null])
+ # Code line.
+ #
+ if ($s == 'para')
+ exit "$i:$ln: blank line expected before code block"
+ end
+
+ # Treat a single blank line between two code lines as belonging to
+ # the code block rather than separating two code blocks (see above
+ # for details).
+ #
+ if ($s == 'blnk')
+ if ($p == 'code')
+ echo '' >>$o # continue code, write preceding blank
+ s = 'code'
+ else
+ echo "$\n\\" >>$o # start code
+ end
+ end
+
+ echo $regex.replace($c, '"', '\\"') >>$o # write code line
+
+ p = $s
+ s = 'code'
+ elif ($t != '')
+ # Paragraph line.
+ #
+ if ($s == 'code')
+ exit "$i:$ln: blank line expected after code block"
+ end
+
+ # Close delayed code block (see above for details).
+ #
+ if ($p == 'code')
+ echo "\\" >>$o # end code
+ end
+
+ if ($s == 'blnk')
+ echo '' >>$o # start para
+ end
+
+ t = $regex.replace($t, '\\', '\\\\') # Escape backslashed
+ t = $regex.replace($t, '"', '\\"') # Escape double quotes.
+
+ # Convert `` to \c{} and <> to \ci{}.
+ #
+ t = $regex.replace($t, '`([^`]+)`', '\\c{\1}')
+ t = $regex.replace($t, '<([^\s<>]+)>', '\\ci{\1}')
+
+ echo $t >>$o # write para line
+
+ p = $s
+ s = 'para'
+ else
+ # Blank line.
+ #
+
+ # Note that we delay closing the code block in case this blank line
+ # is followed by another code line (and is therefore treated as
+ # belonging to the code block; see above for details).
+ #
+ if ($s != 'code' && $p == 'code')
+ echo "\\" >>$o # end code
+ end
+
+ #if ($s == 'para')
+ # end para
+ #end
+
+ p = $s
+ s = 'blnk'
+ end
+ end
+ end
+ end
+}}
diff --git a/doc/cli.sh b/doc/cli.sh
index 7aa1aff..398371c 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -1,6 +1,6 @@
#! /usr/bin/env bash
-version=0.16.0-a.0.z
+version=0.17.0-a.0.z
trap 'exit 1' ERR
set -o errtrace # Trap in functions.
@@ -47,6 +47,7 @@ function compile ()
--generate-html --html-suffix .xhtml \
--html-prologue-file man-prologue.xhtml \
--html-epilogue-file man-epilogue.xhtml \
+--link-regex '%b(#.+)?%build2-build-system-manual.xhtml$1%' \
../libbuild2/$n.cli
cli -I .. \
@@ -58,6 +59,7 @@ function compile ()
--generate-man --man-suffix .1 --ascii-tree \
--man-prologue-file man-prologue.1 \
--man-epilogue-file man-epilogue.1 \
+--link-regex '%b(#.+)?%$1%' \
../libbuild2/$n.cli
}
@@ -91,6 +93,13 @@ function xhtml_to_ps () # <from> <to> [<html2ps-options>]
function compile_doc () # <file> <prefix> <suffix>
{
+ local file="$1"
+ shift
+ local prefix="$1"
+ shift
+ local suffix="$1"
+ shift
+
cli -I .. \
-v version="$(echo "$version" | sed -e 's/^\([^.]*\.[^.]*\).*/\1/')" \
-v date="$date" \
@@ -104,11 +113,12 @@ function compile_doc () # <file> <prefix> <suffix>
--link-regex '%bdep([-.].+)%../../bdep/doc/bdep$1%' \
--link-regex '%testscript(#.+)?%build2-testscript-manual.xhtml$1%' \
--link-regex '%build2(#.+)?%build2-build-system-manual.xhtml$1%' \
---output-prefix "$2" \
---output-suffix "$3" \
-"$1"
+--output-prefix "$prefix" \
+--output-suffix "$suffix" \
+"${@}" \
+"$file"
- local n="$2$(basename -s .cli $1)$3"
+ local n="$prefix$(basename -s .cli $file)$suffix"
xhtml_to_ps "$n.xhtml" "$n-a4.ps" -f doc.html2ps:a4.html2ps
ps2pdf14 -sPAPERSIZE=a4 -dOptimize=true -dEmbedAllFonts=true "$n-a4.ps" "$n-a4.pdf"
@@ -117,7 +127,13 @@ function compile_doc () # <file> <prefix> <suffix>
ps2pdf14 -sPAPERSIZE=letter -dOptimize=true -dEmbedAllFonts=true "$n-letter.ps" "$n-letter.pdf"
}
-compile_doc manual.cli 'build2-build-system-'
+# @@ TODO: replace -I. with $out_base and get rid of backlinking once
+# migrated to reciped.
+#
+# Note: we have to manually map \h to h2 since we break the doc string.
+#
+b update: alias{functions}
+compile_doc manual.cli 'build2-build-system-' '' --html-heading-map h=h2 -I .
compile_doc testscript.cli 'build2-' '-manual'
# Generate INSTALL in ../
diff --git a/doc/manual.cli b/doc/manual.cli
index 0f170ea..4c55374 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -156,7 +156,7 @@ Let's now try to build and run our program (\c{b} is the build system driver):
$ cd hello/ # Change to project root.
$ b
-c++ cxx{hello}
+c++ cxx{hello} -> obje{hello}
ld exe{hello}
$ ls -1
@@ -177,7 +177,7 @@ Or, if we are on Windows and using Visual Studio:
> cd hello
> b
-c++ cxx{hello}
+c++ cxx{hello} -> obje{hello}
ld exe{hello}
> dir /b
@@ -235,7 +235,7 @@ noise, the commands being executed are by default shown abbreviated and with
the same target type notation as we used in the \c{buildfile}. For example:
\
-c++ cxx{hello}
+c++ cxx{hello} -> obje{hello}
ld exe{hello}
\
@@ -275,7 +275,8 @@ above listings. In our \c{buildfile} we refer to the executable target as
extension, if any, will be determined based on the compiler's target platform
by the rule doing the linking. In this sense, target types are a
platform-independent replacement of file extensions (though they do have other
-benefits, such as allowing non-file targets as well as being hierarchical).
+benefits, such as allowing non-file targets as well as being hierarchical;
+see \l{#targets-types Target Types} for details).
Let's revisit the dependency declaration line from our \c{buildfile}:
@@ -371,7 +372,7 @@ Nothing really new here: we've specified the default extension for the
prerequisites. If you have experience with other build systems, then
explicitly listing headers might seem strange to you. As will be discussed
later, in \c{build2} we have to explicitly list all the prerequisites of a
-target that should end up in a distribution of our project.
+target that should end up in a source distribution of our project.
\N|You don't have to list \i{all} headers that you include, only the ones
belonging to your project. Like all modern C/C++ build systems, \c{build2}
@@ -421,11 +422,11 @@ exe{hello}: {hxx cxx}{**}
development more pleasant and less error prone: you don't need to update your
\c{buildfile} every time you add, remove, or rename a source file and you
won't forget to explicitly list headers, a mistake that is often only detected
-when trying to build a distribution of a project. On the other hand, there is
-the possibility of including stray source files into your build without
-noticing. And, for more complex projects, name patterns can become fairly
-complex (see \l{#name-patterns Name Patterns} for details). Note also that on
-modern hardware the performance of wildcard searches hardly warrants a
+when trying to build a source distribution of a project. On the other hand,
+there is the possibility of including stray source files into your build
+without noticing. And, for more complex projects, name patterns can become
+fairly complex (see \l{#name-patterns Name Patterns} for details). Note also
+that on modern hardware the performance of wildcard searches hardly warrants a
consideration.
In our experience, when combined with modern version control systems like
@@ -597,7 +598,7 @@ configuration \i{persistent}. We will see an example of this shortly.
Next up are the \c{test}, \c{install}, and \c{dist} modules. As their names
suggest, they provide support for testing, installation and preparation of
-distributions. Specifically, the \c{test} module defines the \c{test}
+source distributions. Specifically, the \c{test} module defines the \c{test}
operation, the \c{install} module defines the \c{install} and \c{uninstall}
operations, and the \c{dist} module defines the \c{dist}
(meta-)operation. Again, we will try them out in a moment.
@@ -681,7 +682,8 @@ notation for \c{dir{\}}. As we will see shortly, it fits naturally with other
uses of directories in \c{buildfiles} (for example, in scopes).
The \c{dir{\}} target type is an \i{alias} (and, in fact, is derived from more
-general \c{alias{\}}). Building it means building all its prerequisites.
+general \c{alias{\}}; see \l{#targets-types Target Types} for
+details). Building it means building all its prerequisites.
\N|If you are familiar with \c{make}, then you can probably see the similarity
with the ubiquitous \c{all} pseudo-target. In \c{build2} we instead use
@@ -756,7 +758,7 @@ Let's take a look at a slightly more realistic root \c{buildfile}:
Here we have the customary \c{README.md} and \c{LICENSE} files as well as the
package \c{manifest}. Listing them as prerequisites achieves two things: they
will be installed if/when our project is installed and, as mentioned earlier,
-they will be included into the project distribution.
+they will be included into the project source distribution.
The \c{README.md} and \c{LICENSE} files use the \c{doc{\}} target type. We
could have used the generic \c{file{\}} but using the more precise \c{doc{\}}
@@ -815,7 +817,7 @@ in this new layout:
\
$ cd hello/ # Change to project root.
$ b
-c++ hello/cxx{hello}
+c++ hello/cxx{hello} -> hello/obje{hello}
ld hello/exe{hello}
$ tree ./
@@ -892,8 +894,8 @@ libhello/
└── README.md
\
-The overall layout (\c{build/}, \c{libhello/} source directory) as well as the
-contents of the root files (\c{bootstrap.build}, \c{root.build}, root
+The overall layout (\c{build/}, \c{libhello/} source subdirectory) as well as
+the contents of the root files (\c{bootstrap.build}, \c{root.build}, root
\c{buildfile}) are exactly the same. There is, however, the new file
\c{export.build} in \c{build/}, the new subdirectory \c{tests/}, and the
contents of the project's source subdirectory \c{libhello/} look quite a bit
@@ -973,7 +975,7 @@ To start, let's build it in the \c{hello-out/} directory next to the project:
$ b hello/@hello-out/
mkdir fsdir{hello-out/}
mkdir hello-out/fsdir{hello/}
-c++ hello/hello/cxx{hello}@hello-out/hello/
+c++ hello/hello/cxx{hello} -> hello-out/hello/obje{hello}
ld hello-out/hello/exe{hello}
$ ls -1
@@ -1053,21 +1055,25 @@ We could have also specified out for an in source build, but that's redundant:
$ b hello/@hello/
\
-There is another example of this elaborate target specification in the build
-diagnostics:
+There is another example of this elaborate target specification that can be
+seen in the build diagnostics, for instance, when installing headers of a
+library (the \c{install} operation is discussed in the next section):
\
-c++ hello/hello/cxx{hello}@hello-out/hello/
+$ b install: libhello/@libhello-out/
+...
+install libhello/libhello/hxx{hello}@libhello-out/libhello/ ->
+ /usr/local/include/
\
-Notice, however, that now the target (\c{cxx{hello\}}) is on the left of
+Notice, however, that now the target (\c{hxx{hello\}}) is on the left of
\c{@}, that is, in the src directory. It does, however, make sense if you
-think about it: our \c{hello.cxx} is a \i{source file}, it is not built and it
-resides in the project's source directory. This is in contrast, for example,
-to the \c{exe{hello\}} target which is the output of the build system and goes
-to the out directory. So in \c{build2} targets can be either in src or in out
-(there can also be \i{out of any project} targets, for example, installed
-files).
+think about it: our \c{hello.hxx} is a \i{source file}, in a sense that it is
+not built and it resides in the project's source directory. This is in
+contrast, for example, to the \c{exe{hello\}} target which is the output of
+the build system and goes to the out directory. So in \c{build2} targets can
+be either in src or in out (there can also be \i{out of any project} targets,
+for example, installed files).
The elaborate target specification can also be used in \c{buildfiles}. We
haven't encountered any so far because targets mentioned without explicit
@@ -1360,7 +1366,7 @@ prerequisite-specific). These and other variable-related topics will be
covered in subsequent sections.|
One typical place to find \c{src/out_root} expansions is in the include search
-path options. For example, the source directory \c{buildfile} generated by
+path options. For example, the source subdirectory \c{buildfile} generated by
\l{bdep-new(1)} for an executable project actually looks like this
(\c{poptions} stands for \i{preprocessor options}):
@@ -1500,7 +1506,7 @@ In the few cases that we do, we use the following syntax:
If the scope directory is relative, then it is assumed to be relative to the
current scope. As an exercise for understanding, let's reimplement our
\c{hello} project as a single \c{buildfile}. That is, we move the contents of
-the source directory \c{buildfile} into the root \c{buildfile}:
+the source subdirectory \c{buildfile} into the root \c{buildfile}:
\
$ tree hello/
@@ -1530,7 +1536,7 @@ which explicitly opens scopes to define the build over the upstream project's
subdirectory structure.|
Seeing this merged \c{buildfile} may make you wonder what exactly caused the
-loading of the source directory \c{buildfile} in our normal setup. In other
+loading of the source subdirectory \c{buildfile} in our normal setup. In other
words, when we build our \c{hello} from the project root, who loads
\c{hello/buildfile} and why?
@@ -1876,7 +1882,7 @@ $ b configure: hello/@hello-gcc/,forward
$ cd hello/ # Change to project root.
$ b
-c++ hello/cxx{hello}@../hello-gcc/hello/
+c++ hello/cxx{hello} -> ../hello-gcc/hello/obje{hello}
ld ../hello-gcc/hello/exe{hello}
ln ../hello-gcc/hello/exe{hello} -> hello/
\
@@ -1986,7 +1992,7 @@ actual output:
\
$ b test
-c++ hello/cxx{hello}
+c++ hello/cxx{hello} -> hello/obje{hello}
ld hello/exe{hello}
test hello/exe{hello}
--- test.out
@@ -2088,9 +2094,9 @@ expect to see when running the tests:
\
b test
-c++ hello/cxx{hello}
+c++ hello/cxx{hello} -> hello/obje{hello}
ld hello/exe{hello}
-test hello/testscript{testscript} hello/exe{hello}
+test hello/exe{hello} + hello/testscript{testscript}
hello/testscript:7:1: error: hello/hello exit code 0 == 0
info: stdout: hello/test-hello/missing-name/stdout
\
@@ -2137,7 +2143,7 @@ libhello/
\
Specifically, there is no \c{testscript} in \c{libhello/}, the project's
-source directory. Instead, we have the \c{tests/} subdirectory which itself
+source subdirectory. Instead, we have the \c{tests/} subdirectory which itself
looks like a project: it contains the \c{build/} subdirectory with all the
familiar files, etc. In fact, \c{tests} is a \i{subproject} of our
\c{libhello} project.
@@ -2312,36 +2318,40 @@ If the value of the \c{install} variable is not \c{false}, then it is normally
a relative path with the first path component being one of these names:
\
-name default override
----- ------- --------
-root config.install.root
+name default override
+---- ------- --------
+root config.install.root
-data_root root/ config.install.data_root
-exec_root root/ config.install.exec_root
+data_root root/ config.install.data_root
+exec_root root/ config.install.exec_root
-bin exec_root/bin/ config.install.bin
-sbin exec_root/sbin/ config.install.sbin
-lib exec_root/lib/ config.install.lib
-libexec exec_root/libexec/<project>/ config.install.libexec
-pkgconfig lib/pkgconfig/ config.install.pkgconfig
+bin exec_root/bin/ config.install.bin
+sbin exec_root/sbin/ config.install.sbin
+lib exec_root/lib/ config.install.lib
+libexec exec_root/libexec/<project>/ config.install.libexec
+pkgconfig lib/pkgconfig/ config.install.pkgconfig
-etc data_root/etc/ config.install.etc
-include data_root/include/ config.install.include
-share data_root/share/ config.install.share
-data share/<project>/ config.install.data
+etc data_root/etc/ config.install.etc
+include data_root/include/ config.install.include
+include_arch include/ config.install.include_arch
+share data_root/share/ config.install.share
+data share/<project>/ config.install.data
+buildfile share/build2/export/<project>/ config.install.buildfile
-doc share/doc/<project>/ config.install.doc
-legal doc/ config.install.legal
-man share/man/ config.install.man
-man<N> man/man<N>/ config.install.man<N>
+doc share/doc/<project>/ config.install.doc
+legal doc/ config.install.legal
+man share/man/ config.install.man
+man<N> man/man<N>/ config.install.man<N>
\
Let's see what's going on here: The default install directory tree is derived
from the \c{config.install.root} value but the location of each node in this
tree can be overridden by the user that installs our project using the
-corresponding \c{config.install.*} variables. In our \c{buildfiles}, in turn,
-we use the node names instead of actual directories. As an example, here is a
-\c{buildfile} fragment from the source directory of our \c{libhello} project:
+corresponding \c{config.install.*} variables (see the \l{#module-install
+\c{install}} module documentation for details on their meaning). In our
+\c{buildfiles}, in turn, we use the node names instead of actual
+directories. As an example, here is a \c{buildfile} fragment from the source
+subdirectory of our \c{libhello} project:
\
hxx{*}:
@@ -2367,7 +2377,7 @@ root/include/libhello/
In the above \c{buildfile} fragment we also see the use of the
\c{install.subdirs} variable. Setting it to \c{true} instructs the \c{install}
module to recreate subdirectories starting from this point in the project's
-directory hierarchy. For example, if our \c{libhello/} source directory had
+directory hierarchy. For example, if our \c{libhello/} source subdirectory had
the \c{details/} subdirectory with the \c{utility.hxx} header, then this
header would have been installed as
\c{.../include/libhello/details/utility.hxx}.
@@ -2397,11 +2407,11 @@ lib{hello}: cxx.pkgconfig.include = include/hello/
\h2#intro-operations-dist|Distributing|
The last module that we load in our \c{bootstrap.build} is \c{dist} which
-provides support for the preparation of distributions and defines the \c{dist}
-meta-operation. Similar to \c{configure}, \c{dist} is a meta-operation rather
-than an operation because, conceptually, we are preparing a distribution for
-performing operations (like \c{update}, \c{test}) on targets rather than
-targets themselves.
+provides support for the preparation of source distributions and defines the
+\c{dist} meta-operation. Similar to \c{configure}, \c{dist} is a
+meta-operation rather than an operation because, conceptually, we are
+preparing a distribution for performing operations (like \c{update}, \c{test})
+on targets rather than targets themselves.
The preparation of a correct distribution requires that all the necessary
project files (sources, documentation, etc) be listed as prerequisites in the
@@ -2481,7 +2491,7 @@ $ b dist
Let's now take a look at an example of customizing what gets distributed.
Most of the time you will be using this mechanism to include certain targets
-from out. Here is a fragment from the \c{libhello} source directory
+from out. Here is a fragment from the \c{libhello} source subdirectory
\c{buildfile}:
\
@@ -2519,9 +2529,9 @@ state identical to distributed.
\h#intro-import|Target Importation|
Recall that if we need to depend on a target defined in another \c{buildfile}
-within our project, then we simply include said \c{buildfile} and reference
-the target. For example, if our \c{hello} included both an executable and a
-library in separate subdirectories next to each other:
+within our project, then we simply include the said \c{buildfile} and
+reference the target. For example, if our \c{hello} included both an
+executable and a library in separate subdirectories next to each other:
\
hello/
@@ -2596,9 +2606,9 @@ source directory to use its in source build (\c{out_root\ ==\ src_root}):
\
$ b hello/ config.import.libhello=libhello/
-c++ libhello/libhello/cxx{hello}
+c++ libhello/libhello/cxx{hello} -> libhello/libhello/objs{hello}
ld libhello/libhello/libs{hello}
-c++ hello/hello/cxx{hello}
+c++ hello/hello/cxx{hello} -> hello/hello/obje{hello}
ld hello/hello/exe{hello}
\
@@ -2613,9 +2623,9 @@ $ b configure: hello/@hello-clang/ config.cxx=clang++ \
config.import.libhello=libhello-clang/
$ b hello-clang/
-c++ libhello/libhello/cxx{hello}@libhello-clang/libhello/
+c++ libhello/libhello/cxx{hello} -> libhello-clang/libhello/objs{hello}
ld libhello-clang/libhello/libs{hello}
-c++ hello/hello/cxx{hello}@hello-clang/hello/
+c++ hello/hello/cxx{hello} -> hello-clang/hello/obje{hello}
ld hello-clang/hello/exe{hello}
\
@@ -2653,7 +2663,8 @@ subproject, or from an installation directory.
\N|Importation of an installed library will work even if it is not a
\c{build2} project. Besides finding the library itself, the link rule will
also try to locate its \c{pkg-config(1)} file and, if present, extract
-additional compile/link flags from it. The link rule also automatically
+additional compile/link flags from it (see \l{#cc-import-installed Importation
+of Installed Libraries} for details). The link rule also automatically
produces \c{pkg-config(1)} files for libraries that it installs.|
\N|A common problem with importing and using third-party C/C++ libraries is
@@ -2891,7 +2902,16 @@ recursively. And when the library is installed, this information is carried
over to its \c{pkg-config(1)} file.
\N|Similar to the \c{c.*} and \c{cc.*} sets discussed earlier, there are also
-\c{c.export.*} and \c{cc.export.*} sets.|
+\c{c.export.*} and \c{cc.export.*} sets.
+
+Note, however, that there is no \c{*.export.coptions} since a library imposing
+compilation options on its consumers is bad practice (too coarse-grained, does
+not compose, etc). Instead, the recommended approach is to specify in the
+library documentation that it expects its consumers to use a certain
+compilation option. And if your library is unusable without exporting a
+compilation option and you are sure benefits outweigh the drawbacks, then you
+can specify it as part of \c{*.export.poptions} (it is still a good idea to
+prominently document this).|
Here are the parts relevant to the library metadata protocol in the above
\c{buildfile}:
@@ -2989,7 +3009,12 @@ memory.
Note also that this only applies to shared libraries. In case of static
libraries, both interface and implementation dependencies are always linked,
-recursively.|
+recursively. Specifically, when linking a shared library, only libraries
+specified in its \c{*.export.libs} are linked. While when linking a static
+library, all its library prerequisites as well as those specified in
+\c{*.libs} are linked. Note that \c{*.export.libs} is not used when linking a
+static library since it is naturally assumed that all such libraries are also
+specified as library prerequisites or in \c{*.libs}.|
The remaining lines in the library metadata fragment are:
@@ -3048,6 +3073,16 @@ a binary-ful (\i{binful}) shared variants. Note also that binless libraries
can depend on binful libraries and are fully supported where the
\c{pkg-config(1)} functionality is concerned.
+One counter-intuitive aspect of having a binless library that depends on a
+system binful library, for example, \c{-lm}, is that you still have to specify
+the system library in both \c{*.export.libs} and \c{*.libs} because the latter
+is used when linking the static variant of the binless library. For example:
+
+\
+cxx.libs = -lm
+lib{hello}: cxx.export.libs = -lm
+\
+
If you are creating a new library with \l{bdep-new(1)} and are certain that it
will always be binless and in all configurations, then you can produce a
simplified \c{buildfile} by specifying the \c{binless} option, for example:
@@ -3162,9 +3197,10 @@ hello/
└── buildfile
$ b hello/
-c++ hello/libhello/libhello/cxx{hello}
+c++ hello/libhello/libhello/cxx{hello} ->
+ hello/libhello/libhello/objs{hello}
ld hello/libhello/libhello/libs{hello}
-c++ hello/hello/cxx{hello}
+c++ hello/hello/cxx{hello} -> hello/hello/obje{hello}
ld hello/hello/exe{hello}
\
@@ -3219,7 +3255,7 @@ configuration inheritance. As an example, let's configure the above bundled
\
$ b configure: hello/ config.cxx=clang++ config.cxx.coptions=-g
-$ b tree
+$ tree
hello/
├── build/
│ ├── config.build
@@ -3602,8 +3638,9 @@ info $path.directory($src_base) # $src_base
info $path.base($path.leaf($src_base)) # foo
\
-Note that functions in \c{build2} are \i{pure} in a sense that they do not
-alter the build state in any way.
+Note that the majority of functions in \c{build2} are \i{pure} in a sense that
+they do not alter the build state in any way (see \l{#functions Functions} for
+details).
\N|Functions in \c{build2} are currently defined either by the build system
core or build system modules and are implemented in C++. In the future it will
@@ -4122,7 +4159,7 @@ source subdirectory \c{buildfile} of an executable created with this option:
# Unit tests.
#
-exe{*.test}
+exe{*.test}:
{
test = true
install = false
@@ -4211,18 +4248,8 @@ files as belonging to unit tests. Because it is a second-level extension, we
have to indicate this fact to the pattern matching machinery with the trailing
triple dot (meaning \"there are more extensions coming\"). If we didn't do
that, \c{.test} would have been treated as a first-level extension explicitly
-specified for our source files.
-
-\N|If you need to specify a name that does not have an extension, then end it
-with a single dot. For example, for a header \c{utility} you would write
-\c{hxx{utility.\}}. If you need to specify a name with an actual trailing dot,
-then escape it with a double dot, for example, \c{hxx{utility..\}}.
-
-More generally, anywhere in a name, a double dot can be used to specify a dot
-that should not be considered the extension separator while a triple dot \-
-which should. For example, in \c{obja{foo.a.o\}} the extension is \c{.o} and
-if instead we wanted \c{.a.o} to be considered the extension, then we could
-rewrite it either as \c{obja{foo.a..o\}} or as \c{obja{foo...a.o\}}.|
+specified for our source files (see \l{#targets-types Target Types} for
+details).
The next couple of lines set target type/pattern-specific variables to treat
all unit test executables as tests that should not be installed:
@@ -4427,7 +4454,7 @@ scopes. For that we use the \c{dump} directive.
Without any arguments, \c{dump} prints (to \c{stderr}) the contents of the
scope it was encountered in and at that point of processing the \c{buildfile}.
Its output includes variables, targets and their prerequisites, as well as
-nested scopes, recursively. As an example, let's print the source directory
+nested scopes, recursively. As an example, let's print the source subdirectory
scope of our \c{hello} executable project. Here is its \c{buildfile} with
the \c{dump} directive at the end:
@@ -4490,7 +4517,8 @@ buildfile:5:1: dump:
The output of \c{dump} might look familiar: in \l{#intro-dirs-scopes Output
Directories and Scopes} we've used the \c{--dump} option to print the entire
build state, which looks pretty similar. In fact, the \c{dump} directive uses
-the same mechanism but allows us to print individual scopes and targets.
+the same mechanism but allows us to print individual scopes and targets from
+within a \c{buildfile}.
There is, however, an important difference to keep in mind: \c{dump} prints
the state of a target or scope at the point in the \c{buildfile} load phase
@@ -4504,6 +4532,9 @@ a result, while the \c{dump} directive should be sufficient in most cases,
sometimes you may need to use the \c{--dump} option to examine the build state
just before rule execution.
+\N|It is possible to limit the output of \c{--dump} to specific scopes and/or
+targets with the \c{--dump-scope} and \c{--dump-target} options.|
+
Let's now move from state to behavior. As we already know, to see the
underlying commands executed by the build system we use the \c{-v} options
(which is equivalent to \c{--verbose\ 2}). Note, however, that these are
@@ -4771,6 +4802,9 @@ executable target in the \c{<project>%exe{<project>\}} form, the
\c{config.<project>} variable is treated as an alias for
\c{config.import.<project>.<project>.exe}.
+For an imported \c{buildfile}, \c{<project>} may refer to either the importing
+project or the project from which the said \c{buildfile} was imported.
+
The build system core reserves \c{build} and \c{import} as the second
component in configuration variables as well as \c{configured} as the third
and subsequent components.|
@@ -4932,6 +4966,32 @@ if! $defined(config.libhello.database)
fail 'config.libhello.database must be specified'
\
+\N|A configuration variable without a default value is omitted from
+\c{config.build} unless the value is specified by the user. This semantics is
+useful for values that are normally derived from other configuration values
+but could also be specified by the user. If the value is derived, then we
+don't want it saved in \c{config.build} since that would prevent it from
+being re-derived if the configuration values it is based on are changed.
+For example:
+
+\
+config [strings] config.hello.database
+
+assert ($size($config.hello.database) > 0) \
+ 'database must be specified with config.hello.database'
+
+config [bool, config.report.variable=multi] config.hello.multi_database
+
+multi = ($defined(config.hello.multi_database) \
+ ? $config.hello.multi_database \
+ : $size(config.hello.database) > 1)
+
+assert ($multi || $size(config.hello.database) == 1) \
+ 'one database can be specified if config.hello.multi_database=false'
+\
+
+|
+
If computing the default value is expensive or requires elaborate logic, then
the handling of a configuration variable can be broken down into two steps
along these lines:
@@ -5236,6 +5296,29 @@ config libhello@/tmp/libhello/
woptions -Wall -Wextra -Wno-extra -Werror
\
+The \c{config.report.module} attribute can be used to override the reporting
+module name, that is, \c{config} in the \c{config\ libhello@/tmp/libhello/}
+line above. It is primarily useful in imported \c{buildfiles} that wish to
+report non-\c{config.*} variables under their own name. For example:
+
+\
+config [string] config.rtos.board
+
+# Load the board description and report key information such as the
+# capability revoker.
+#
+...
+revoker = ...
+
+config [config.report.module=rtos] revoker
+\
+
+\
+$ b config.rtos.board=ibex-safe-simulator -v
+rtos hello@/tmp/hello/
+ board ibex-safe-simulator
+ revoker hardware
+\
\h#proj-config-propag|Configuration Propagation|
@@ -5505,6 +5588,684 @@ configuration header into two, one public and installed while the other
private.|
+\h1#targets|Targets and Target Types|
+
+\N{This chapter is a work in progress and is incomplete.}
+
+
+\h#targets-types|Target Types|
+
+A target type is part of a target's identity. The core idea behind the concept
+of target types is to abstract away from file extensions which can vary from
+project to project (for example, C++ source files extensions) or from platform
+to platform (for example, executable file extensions). It also allows us to
+have non-file-based targets.
+
+Target types form a \i{base-derived} inheritance tree. The root of this tree
+is the abstract \c{target{\}} type. The \c{build2} core defines a number of
+standard target types, such as \c{file{\}}, \c{doc{\}}, and \c{exe{\}}. Build
+system modules can define additional target types that are based on the
+standard ones (or on types defined by other modules). For example, the \c{c}
+module that provides the C compilation support defines the \c{h{\}} and
+\c{c{\}} target types. Finally, \c{buildfiles} can derive project-local target
+types using the \c{define} directive.
+
+\N|If a target type represents a file type with a well-established extension,
+then by convention such an extension is used as the target type name. For
+example, the C language header and source files use the \c{.h} and \c{.c}
+extensions and the target types are called \c{h{\}} and \c{c{\}}.
+
+Speaking of conventions, as you may have noticed, when mentioning a target
+type we customarily add \c{{\}} after its name. We found that this helps with
+comprehension since target type names are often short (you can also search for
+\c{<type>{} to narrow it down to target types). In a way this is a similar
+approach to adding \c{()} after a function name except here we use \c{{\}},
+which mimics target type usage in target names, for example \c{c{hello\}} for
+\c{hello.c}.|
+
+The following listing shows the hierarchy of the standard target types defined
+by the \c{build2} core (the abstract target types are marked with \c{*}) while
+the following sections describe each standard target type in detail. For
+target types defined by a module refer to the respective module documentation.
+
+\
+ .-----target*------------.
+ | | |
+ mtime_target*---. alias fsdir
+ | | |
+ path_target* group dir
+ |
+ .---------file----.
+ | | |
+ .----doc-----. exe buildfile
+ | | |
+legal man manifest
+ |
+ man<N>
+\
+
+While target types replace (potentially variable) extensions, there still
+needs to be a mechanism for specifying them since in most cases targets have
+to be mapped to files. There are several ways this can be achieved.
+
+If a target type represents a file type with a well-established extension,
+then such an extension is normally used by default and we don't need to take
+any extra steps. For example the \c{h{\}} and \c{c{\}} target types for C
+header and source files default to the \c{.h} and \c{.c} extensions,
+respectively, and if our project follows this convention, then we can simply
+write:
+
+\
+exe{utility}: c{utility} h{utility}
+\
+
+And \c{c{utility\}} will be mapped to \c{utility.c} and \c{h{utility\}} \-
+to \c{utility.h}.
+
+There are two variants of this default extension case: fixed extension and
+customizable extension. A target type may choose to fix the default extension
+if it's a bad idea to deviate from the default extension. A good example of
+such a target is \c{man1{\}}, which fixes the default extension to be
+\c{.1}. More commonly, however, a target will have a default extension but
+will allow customizing it with the \c{extension} variable.
+
+A good example where extension customization is often required are the
+\c{hxx{\}} and \c{cxx{\}} target types for C++ header and source files, which
+default to the \c{.hxx} and \c{.cxx} extensions, respectively. If our project
+uses other extensions, for example, \c{.hpp} and \c{.cpp}, then we can adjust
+the defaults (typically done in \c{root.build}, after loading the \c{cxx}
+module):
+
+\
+hxx{*}: extension = hpp
+cxx{*}: extension = cpp
+\
+
+Then we can write:
+
+\
+exe{utility}: cxx{utility} hxx{utility}
+\
+
+And \c{cxx{utility\}} will be mapped to \c{utility.cpp} and \c{hxx{utility\}}
+\- to \c{utility.hpp}.
+
+What about \c{exe{utility\}}, where does its extension come from? This is an
+example of a target type with an extension that varies from platform to
+platform. In such cases the extension is expected to be assigned by the rule
+that matches the target. In the above example, the link rule from the \c{cxx}
+module that matches updating \c{exe{utility\}} will assign a suitable
+extension based on the target platform of the C++ compiler that it was
+instructed to use.
+
+Finally, it is always possible to specify the file extension explicitly as
+part of the target name. For example:
+
+\
+exe{utility}: cxx{utility.cc} hxx{utility.hh}
+\
+
+This is normally only needed if the default extension is not appropriate or if
+the target type does not have a default extension, as is the case, for
+example, for the \l{#targets-types-file \c{file{\}}} and \l{#targets-types-doc
+\c{doc{\}}} target types. This mechanism can also be used to override the
+automatically derived extension. For example:
+
+\
+exe{($cxx.target.class == 'windows' ? utility.com : utility)}: ...
+\
+
+\N|If you need to specify a name that does not have an extension, then end it
+with a single dot. For example, for a header \c{utility} you would write
+\c{hxx{utility.\}}. If you need to specify a name with an actual trailing dot,
+then escape it with a double dot, for example, \c{hxx{utility..\}}.
+
+More generally, anywhere in a name, a double dot can be used to specify a dot
+that should not be considered the extension separator while a triple dot \-
+which should. For example, in \c{obja{foo.a.o\}} the extension is \c{.o} and
+if instead we wanted \c{.a.o} to be considered the extension, then we could
+rewrite it either as \c{obja{foo.a..o\}} or as \c{obja{foo...a.o\}}.|
+
+To derive a new target type in a \c{buildfile} we use the \c{define}
+directive. Such target types are project-local, meaning they cannot be
+exported to other projects. Typically this is used to provide a more
+meaningful name to a set of files and also avoid having to specify their
+extensions explicitly. Compare:
+
+\
+./: doc{README.md PACKAGE-README.md INSTALL.md}
+\
+
+To:
+
+\
+define md: doc
+doc{*}: extension = md
+
+./: md{README PACKAGE-README INSTALL}
+\
+
+
+\h2#targets-types-target|\c{target{\}}|
+
+The \c{target{\}} target type is a root of the target type hierarchy. It is
+abstract and is not commonly used directly, except perhaps in patterns (target
+type/pattern-specific variable, pattern rules).
+
+
+\h2#targets-types-alias|\c{alias{\}} and \c{dir{\}}|
+
+The \c{alias{\}} target type is used for non-file-based targets that serve as
+aliases for their prerequisite.
+
+\N|Alias targets in \c{build2} are roughly equivalent to phony targets in
+\c{make}.|
+
+For example:
+
+\
+alias{tests}: exe{test1 test2 test3}
+\
+
+\
+$ b test: alias{tests}
+\
+
+An \c{alias{\}} target can also serve as an \"action\" if supplied with an ad
+hoc recipe (or matched by an ad hoc pattern rule). For example:
+
+\
+alias{strip}: exe{hello}
+{{
+ diag strip $<
+ strip $path($<)
+}}
+\
+
+The \c{dir{\}} target type is a special kind of alias that represents a
+directory. Building it means building everything inside the directory. See
+\l{#intro-proj-struct Project Structure} for background.
+
+A target without a type that ends with a directory separator (\c{/}) is
+automatically treated as \c{dir{\}}. For example, the following two lines are
+equivalent:
+
+\
+./: exe{test1 test2}
+dir{./}: exe{test1 test2}
+\
+
+Omitting the target type in such situations is customary.
+
+
+\h2#targets-types-fsdir|\c{fsdir{\}}|
+
+The \c{fsdir{\}} target type represents a filesystem directory. Unlike
+\c{dir{\}} above, it is not an alias and listing an \c{fsdir{\}} directory as
+a prerequisite of a target will cause that directory to be created on
+\c{update} and removed on \c{clean}.
+
+While we usually don't need to list explicit \c{fsdir{\}} prerequisites for
+our targets, one situation where this is necessary is when the target resides
+in a subdirectory that does not correspond to an existing source directory. A
+typical example of this situation is placing object files into subdirectories.
+Compare:
+
+\
+obj{foo}: c{foo}
+sub/obj{bar}: c{bar} fsdir{sub/}
+\
+
+
+\h2#targets-types-mtime-path|\c{mtime_target{\}} and \c{path_target{\}}|
+
+The \c{mtime_target{\}} target type represents a target that uses modification
+times to determine if it is out of date. The \c{path_target{\}} target type
+represents a target that has a corresponding filesystem entry. It is derived
+from \c{mtime_target{\}} and uses the modification time of that filesystem
+entry to determine if the target is out of date.
+
+Both of these target types are abstract and are not commonly used directly,
+except perhaps in patterns (target type/pattern-specific variable, pattern
+rules).
+
+
+\h2#targets-types-group|\c{group{\}}|
+
+The \c{group{\}} target type represents a user-defined explicit target group,
+that is, a target that has multiple member targets that are all built together
+with a single recipe.
+
+Normally this target type is not used to declare targets or prerequisites but
+rather as a base of a derived group. If desired, such a derived group can be
+marked with an attribute as \"see-through\", meaning that when the group is
+listed as a prerequisite of a target, the matching rule \"sees\" its members,
+rather than the group itself. For example:
+
+\
+define [see_through] thrift_cxx: group
+\
+
+
+\h2#targets-types-file|\c{file{\}}|
+
+The \c{file{\}} target type represents a generic file. This target type is
+used as a base for most of the file-based targets and can also be used to
+declare targets and prerequisites when there are no more specific target
+types.
+
+A target or prerequisite without a target type is automatically treated as
+\c{file{\}}. However, omitting a target type in such situations is not
+customary.
+
+The \c{file{\}} target type has no default extension and one cannot be
+assigned with the \c{extension} variable. As a result, if a \c{file{\}} target
+has an extension, then it must be specified explicitly as part of the target
+name. For example:
+
+\
+./: file{example.conf}
+\
+
+\h2#targets-types-doc|\c{doc{\}}, \c{legal{\}}, and \c{man{\}}|
+
+The \c{doc{\}} target type represents a generic documentation file. It has
+semantics similar to \c{file{\}} (from which it derives): it can be used as a
+base or declare targets/prerequisites and there is no default extension. One
+notable difference, however, is that \c{doc{\}} targets are by default
+installed into the \c{doc/} installation location (see \l{#module-install
+\c{install} Module}). For example:
+
+\
+./: doc{README.md ChangeLog.txt}
+\
+
+The \c{legal{\}} target type is derived from \c{doc{\}} and represents a legal
+documentation file, such as a license, copyright notice, authorship
+information, etc. The main purpose of having a separate target type like this
+is to help with installing licensing-related files into a different
+location. To this effect, \c{legal{\}} targets are installed into the
+\c{legal/} installation location, which by default is the same as \c{doc/} but
+can be customized. For example:
+
+\
+./: legal{COPYRIGHT LICENSE AUTHORS.md}
+\
+
+The \c{man{\}} target type is derived from \c{doc{\}} and represents a manual
+page. This target type requires an explicit extension specification and is
+installed into the \c{man/} installation location
+
+\N|If you are using the \c{man{\}} target type directly (instead of one of
+\c{man<N>{\}} described below), for example, to install a localized version of
+a man page, then you will likely need to adjust the installation location
+on the per target basis.|
+
+The \c{man<N>{\}} target types (where \c{<N>} is an integer between 1 and 9)
+are derived from \c{man{\}} and represent manual pages in the respective
+sections. These target types have fixed default extensions \c{.<N>} (but an
+explicit extension can still be specified, for example \c{man1{foo.1p\}}) and
+are installed into the \c{man<N>/} installation locations. For example:
+
+\
+./: man1{foo}
+\
+
+
+\h2#targets-types-exe|\c{exe{\}}|
+
+The \c{exe{\}} target type represents an executable file. Executables in
+\c{build2} appear in two distinct but sometimes overlapping contexts: We can
+build an executable target, for example from C source files. Or we can list an
+executable target as a prerequisite in order to execute it as part of a
+recipe. And sometimes this can be the same executable target. For example,
+one project may build an executable target that is a source code generator and
+another project may import this executable target and use it in its recipes in
+order to generate some source code.
+
+To support this semantics the \c{exe{\}} target type has a peculiar default
+extension logic. Specifically, if the \c{exe{\}} target is \"output\", then
+the extension is expected to be assigned by the matching rule according to the
+target platform for which this executable is built. But if it does not,
+then we fall back to no extension (for example, a script). If, however, the
+\c{exe{\}} target is \"input\" (that is, it's listed as a prerequisite and
+there is no corresponding \"output\" target), then the extension of the host
+platform is used as the default.
+
+In all these cases the extension can also be specified explicitly. This, for
+example, would be necessary if the executable were a batch file:
+
+\
+h{generate}: exe{generate.bat}
+{{
+ diag $< -> $>
+ $< -o $path($>)
+}}
+\
+
+Here, without the explicit extension, the \c{.exe} extension would have been
+used by default.
+
+
+\h1#variables|Variables|
+
+\N{This chapter is a work in progress and is incomplete.}
+
+The following variable/value types can currently be used in \c{buildfiles}:
+
+\
+bool
+
+int64
+int64s
+
+uint64
+uint64s
+
+string
+strings
+string_set
+string_map
+
+path
+paths
+dir_path
+dir_paths
+
+json
+json_array
+json_object
+json_set
+json_map
+
+name
+names
+name_pair
+
+cmdline
+project_name
+target_triplet
+\
+
+Note that while expansions in the target and prerequisite-specific assignments
+happen in the corresponding target and prerequisite contexts, respectively,
+for type/pattern-specific assignments they happen in the scope context. Plus,
+a type/pattern-specific prepend/append is applied at the time of expansion for
+the actual target. For example:
+
+\
+x = s
+
+file{foo}: # target
+{
+ x += t # s t
+ y = $x y # s t y
+}
+
+file{foo}: file{bar} # prerequisite
+{
+ x += p # x t p
+ y = $x y # x t p y
+}
+
+file{b*}: # type/pattern
+{
+ x += w # <append w>
+ y = $x w # <assign s w>
+}
+
+x = S
+
+info $(file{bar}: x) # S w
+info $(file{bar}: y) # s w
+\
+
+
+\h1#functions|Functions|
+
+\N{This chapter is a work in progress and is incomplete.}
+
+
+Functions in \c{build2} are organized into families, such as the
+\c{$string.*()} family for manipulating strings or \c{$regex.*()} for working
+with regular expressions. Most functions are pure and those that are not,
+such as \c{$builtin.getenv()}, are explicitly documented as such.
+
+Some functions, such as from the \c{$regex.*()} family, can only be called
+fully qualified with their family name. For example:
+
+\
+if $regex.match($name, '(.+)-(.+)')
+ ...
+\
+
+While other functions can be called without explicit qualification. For
+example:
+
+\
+path = $getenv('PATH')
+\
+
+There are also functions that can be called unqualified only for certain types
+of arguments (this fact will be reflected in their synopsis and/or
+documentation). Note, however, that every function can always be called
+qualified.
+"
+
+// $builtin.*()
+//
+"
+\h#functions-builtin|Builtin Functions|
+
+The \c{$builtin.*()} function family contains fundamental \c{build2}
+functions.
+"
+source <functions-builtin.cli>;
+
+// $string.*()
+//
+"
+\h#functions-string|String Functions|
+"
+source <functions-string.cli>;
+
+
+// $integer.*()
+//
+"
+\h#functions-integer|Integer Functions|
+"
+source <functions-integer.cli>;
+
+
+// $bool.*()
+//
+"
+\h#functions-bool|Bool Functions|
+"
+source <functions-bool.cli>;
+
+
+// $path.*()
+//
+"
+\h#functions-path|Path Functions|
+
+The \c{$path.*()} function family contains function that manipulating
+filesystem paths.
+"
+source <functions-path.cli>;
+
+
+// $name.*()
+//
+"
+\h#functions-name|Name Functions|
+
+The \c{$name.*()} function family contains function that operate on target and
+prerequisite names. See also the \l{#functions-target \c{$target.*()} function
+family} for functions that operate on actual targets.
+"
+source <functions-name.cli>;
+
+
+// $target.*()
+//
+"
+\h#functions-target|Target Functions|
+
+The \c{$target.*()} function family contains function that operate on
+targets. See also the \l{#functions-name \c{$name.*()} function family} for
+functions that operate on target (and prerequisite) names.
+"
+source <functions-target.cli>;
+
+
+// $regex.*()
+//
+"
+\h#functions-regex|Regex Functions|
+
+The \c{$regex.*()} function family contains function that provide
+comprehensive regular expression matching and substitution facilities. The
+supported regular expression flavor is ECMAScript (more specifically,
+ECMA-262-based C++11 regular expressions).
+
+In the \c{$regex.*()} functions the substitution escape sequences in the
+format string (the \ci{fmt} argument) are extended with a subset of the Perl
+escape sequences: \c{\\n}, \c{\\u}, \c{\\l}, \c{\\U}, \c{\\L}, \c{\\E},
+\c{\\1} ... \c{\\9}, and \c{\\\\}. Note that the standard ECMAScript escape
+sequences (\c{$1}, \c{$2}, \c{$&}, etc) are still supported.
+
+Note that functions from the \c{$regex.*()} family can only be called fully
+qualified with their family name. For example:
+
+\
+if $regex.match($name, '(.+)-(.+)')
+ ...
+\
+
+"
+source <functions-regex.cli>;
+
+// $json.*()
+//
+"
+\h#functions-json|JSON Functions|
+
+The \c{$json.*()} function family contains function that operate on the JSON
+types: \c{json}, \c{json_array}, and \c{json_object}. For example:
+
+\
+j = [json] one@1 two@abc three@([json] x@1 y@-1)
+
+for m: $j
+{
+ n = $member_name($m)
+ v = $member_value($m)
+
+ info $n $value_type($v) $v
+}
+\
+
+"
+source <functions-json.cli>;
+
+
+// $process.*()
+//
+"
+\h#functions-process|Process Functions|
+"
+source <functions-process.cli>;
+
+
+// $filesystem.*()
+//
+"
+\h#functions-filesystem|Filesystem Functions|
+"
+source <functions-filesystem.cli>;
+
+
+// $project_name.*()
+//
+"
+\h#functions-project_name|Project Name Functions|
+
+The \c{$project_name.*()} function family contains function that operate on
+the \c{project_name} type.
+"
+source <functions-project-name.cli>;
+
+
+// $process_path.*()
+//
+"
+\h#functions-process-path|Process Path Functions|
+
+The \c{$process_path.*()} function family contains function that operate on
+the \c{process_path} type and its extended \c{process_path_ex} variant. These
+types describe a path to an executable that, if necessary, has been found in
+\c{PATH}, completed with an extension, etc. The \c{process_path_ex} variant
+includes additional metadata, such as the stable process name for diagnostics
+and the executable checksum for change tracking.
+"
+source <functions-process-path.cli>;
+
+
+// $target_triplet.*()
+//
+"
+\h#functions-target-triplet|Target Triplet Functions|
+
+The \c{$target_triplet.*()} function family contains function that operate on
+the \c{target_triplet} type that represents the ubiquitous
+\c{\i{cpu}-\i{vendor}-\i{os}} target platform triplet.
+"
+source <functions-target-triplet.cli>;
+
+
+"
+\h1#directives|Directives|
+
+\N{This chapter is a work in progress and is incomplete.}
+
+\h#directives-define|\c{define}|
+
+\
+define <derived>: <base>
+\
+
+Define a new target type \c{<derived>} by inheriting from existing target type
+\c{<base>}. See \l{#targets-types Target Types} for details.
+
+
+\h#directives-include|\c{include}|
+
+\
+include <file>
+include <directory>
+\
+
+Load the specified file (the first form) or \c{buildfile} in the specified
+directory (the second form). In both cases the file is loaded in the scope
+corresponding to its directory. Subsequent inclusions of the same file are
+automatically ignored. See also \l{#directives-source \c{source}}.
+
+
+\h#directives-source|\c{source}|
+
+
+\
+source <file>
+\
+
+Load the specified file in the current scope as if its contents were copied
+and pasted in place of the \c{source} directive. Note that subsequent sourcing
+of the same file in the same scope are not automatically ignored. See also
+\l{#directives-include \c{include}}.
+
\h1#attributes|Attributes|
@@ -5660,7 +6421,7 @@ exe{hello}: cxx{+{f* b*} -{foo bar}}
This is particularly useful if you would like to list the names to include or
exclude in a variable. For example, this is how we can exclude certain files
from compilation but still include them as ordinary file prerequisites (so
-that they are still included into the distribution):
+that they are still included into the source distribution):
\
exc = foo.cxx bar.cxx
@@ -5737,8 +6498,8 @@ patterns/matches that do not already contain an extension. Then the filesystem
search is performed for matching files.
For example, the \c{cxx{\}} target type obtains the default extension from the
-\c{extension} variable. Assuming we have the following line in our
-\c{root.build}:
+\c{extension} variable (see \l{#targets-types Target Types} for background).
+Assuming we have the following line in our \c{root.build}:
\
cxx{*}: extension = cxx
@@ -5762,101 +6523,6 @@ file-based, then the name pattern is returned as is (that is, as an ordinary
name). Project-qualified names are never considered to be patterns.
-\h1#variables|Variables|
-
-\N{This chapter is a work in progress and is incomplete.}
-
-The following variable/value types can currently be used in \c{buildfiles}:
-
-\
-bool
-
-int64
-int64s
-
-uint64
-uint64s
-
-string
-strings
-
-path
-paths
-dir_path
-dir_paths
-
-name
-names
-name_pair
-
-project_name
-target_triplet
-\
-
-Note that while expansions in the target and prerequisite-specific assignments
-happen in the corresponding target and prerequisite contexts, respectively,
-for type/pattern-specific assignments they happen in the scope context. Plus,
-a type/pattern-specific prepend/append is applied at the time of expansion for
-the actual target. For example:
-
-\
-x = s
-
-file{foo}: # target
-{
- x += t # s t
- y = $x y # s t y
-}
-
-file{foo}: file{bar} # prerequisite
-{
- x += p # x t p
- y = $x y # x t p y
-}
-
-file{b*}: # type/pattern
-{
- x += w # <append w>
- y = $x w # <assign s w>
-}
-
-x = S
-
-info $(file{bar}: x) # S w
-info $(file{bar}: y) # s w
-\
-
-
-\h1#directives|Directives|
-
-\N{This chapter is a work in progress and is incomplete.}
-
-\h#directives-include|\c{include}|
-
-\
-include <file>
-include <directory>
-\
-
-Load the specified file (the first form) or \c{buildfile} in the specified
-directory (the second form). In both cases the file is loaded in the scope
-corresponding to its directory. Subsequent inclusions of the same file are
-automatically ignored. See also \l{#directives-source \c{source}}.
-
-
-\h#directives-source|\c{source}|
-
-
-\
-source <file>
-\
-
-Load the specified file in the current scope as if its contents were copied
-and pasted in place of the \c{source} directive. Note that subsequent sourcing
-of the same file in the same scope are not automatically ignored. See also
-\l{#directives-include \c{include}}.
-
-
\h1#module-config|\c{config} Module|
\N{This chapter is a work in progress and is incomplete.}
@@ -6133,29 +6799,53 @@ of the Introduction, the \c{install} module defines the following standard
installation locations:
\
-name default config.* override
----- ------- -----------------
-root install.root
+name default config.install.*
+ (c.i.*) override
+---- ------- ----------------
+root c.i.root
+
+data_root root/ c.i.data_root
+exec_root root/ c.i.exec_root
-data_root root/ install.data_root
-exec_root root/ install.exec_root
+bin exec_root/bin/ c.i.bin
+sbin exec_root/sbin/ c.i.sbin
+lib exec_root/lib/<private>/ c.i.lib
+libexec exec_root/libexec/<private>/<project>/ c.i.libexec
+pkgconfig lib/pkgconfig/ c.i.pkgconfig
-bin exec_root/bin/ install.bin
-sbin exec_root/sbin/ install.sbin
-lib exec_root/lib/<private>/ install.lib
-libexec exec_root/libexec/<private>/<project>/ install.libexec
-pkgconfig lib/pkgconfig/ install.pkgconfig
+etc data_root/etc/ c.i.etc
+include data_root/include/<private>/ c.i.include
+include_arch include/ c.i.include_arch
+share data_root/share/ c.i.share
+data share/<private>/<project>/ c.i.data
+buildfile share/build2/export/<project>/ c.i.buildfile
+
+doc share/doc/<private>/<project>/ c.i.doc
+legal doc/ c.i.legal
+man share/man/ c.i.man
+man<N> man/man<N>/ c.i.man<N>
+\
-etc data_root/etc/ install.etc
-include data_root/include/<private>/ install.include
-share data_root/share/ install.share
-data share/<private>/<project>/ install.data
+The \c{include_arch} location is meant for architecture-specific files, such
+as configuration headers. By default it's the same as \c{include} but can be
+configured by the user to a different value (for example,
+\c{/usr/include/x86_64-linux-gnu/}) for platforms that support multiple
+architectures from the same installation location. This is how one would
+normally use it from a \c{buildfile}:
-doc share/doc/<private>/<project>/ install.doc
-legal doc/ install.legal
-man share/man/ install.man
-man<N> man/man<N>/ install.man<N>
\
+# The configuration header may contain target architecture-specific
+# information so install it into include_arch/ instead of include/.
+#
+h{*}: install = include/libhello/
+h{config}: install = include_arch/libhello/
+\
+
+The \c{buildfile} location is meant for exported buildfiles that can be
+imported by other projects. If a project contains any \c{**.build} buildfiles
+in its \c{build/export/} directory (or \c{**.build2} and \c{build2/export/} in
+the alternative naming scheme), then they are automatically installed into
+this location (recreating subdirectories).
The \c{<project>}, \c{<version>}, and \c{<private>} substitutions in these
\c{config.install.*} values are replaced with the project name, version, and
@@ -6175,7 +6865,9 @@ The private installation subdirectory is specified with the
directory and may include multiple components. For example:
\
-$ b install config.install.root=/usr/local/ config.install.private=hello/
+$ b install \
+ config.install.root=/usr/local/ \
+ config.install.private=hello/
\
\N|If you are relying on your system's dynamic linker defaults to
@@ -6193,6 +6885,153 @@ $ b install \
|
+
+\h#install-reloc|Relocatable Installation|
+
+A relocatable installation can be moved to a directory other than its original
+installation location. Note that the installation should be moved as a whole
+preserving the directory structure under its root (\c{config.install.root}).
+To request a relocatable installation, set the \c{config.install.relocatable}
+variable to \c{true}. For example:
+
+\
+$ b install \
+ config.install.root=/tmp/install \
+ config.install.relocatable=true
+\
+
+A relocatable installation is achieved by using paths relative to one
+filesystem entry within the installation to locate another. Some examples
+include:
+
+\ul|
+
+\li|Paths specified in \c{config.bin.rpath} are made relative using the
+\c{$ORIGIN} (Linux, BSD) or \c{@loader_path} (Mac OS) mechanisms.|
+
+\li|Paths in the generated \c{pkg-config} files are made relative to the
+\c{${pcfiledir\}} built-in variable.|
+
+\li|Paths in the generated installation manifest (\c{config.install.manifest})
+are made relative to the location of the manifest file.||
+
+While these common aspects are handled automatically, if a projects relies on
+knowing its installation location, then it will most likely need to add manual
+support for relocatable installations.
+
+As an example, consider an executable that supports loading plugins and
+requires the plugin installation directory to be embedded into the executable
+during the build. The common way to support relocatable installations for such
+cases is to embed a path relative to the executable and complete it at
+runtime, normally by resolving the executable's path and using its directory
+as a base.
+
+If you would like to always use the relative path, regardless of whether the
+installation is relocatable of not, then you can obtain the library
+installation directory relative to the executable installation directory like
+this:
+
+\
+plugin_dir = $install.resolve($install.lib, $install.bin)
+\
+
+Alternatively, if you would like to continue using absolute paths for
+non-relocatable installations, then you can use something like this:
+
+\
+plugin_dir = $install.resolve( \
+ $install.lib, \
+ ($install.relocatable ? $install.bin : [dir_path] ))
+\
+
+Finally, if you are unable to support relocatable installations, the correct
+way to handle this is to assert this fact in \c{root.build} of your project,
+for example:
+
+\
+assert (!$install.relocatable) 'relocatable installation not supported'
+\
+
+
+\h#install-filter|Installation Filtering|
+
+While project authors determine what gets installed at the \c{buildfile}
+level, the users of the project can further filter the installation using the
+\c{config.install.filter} variable.
+
+The value of this variable is a list of key-value pairs that specify the
+filesystem entries to include or exclude from the installation. For example,
+the following filters will omit installing headers and static libraries
+(notice the quoting of the wildcard).
+
+\
+$ b install config.install.filter='include/@false \"*.a\"@false'
+\
+
+The key in each pair is a file or directory path or a path wildcard pattern.
+If a key is relative and contains a directory component or is a directory,
+then it is treated relative to the corresponding \c{config.install.*}
+location. Otherwise (simple path, normally a pattern), it is matched against
+the leaf of any path. Note that if an absolute path is specified, it should be
+without the \c{config.install.chroot} prefix.
+
+The value in each pair is either \c{true} (include) or \c{false} (exclude).
+The filters are evaluated in the order specified and the first match that is
+found determines the outcome. If no match is found, the default is to
+include. For a directory, while \c{false} means exclude all the sub-paths
+inside this directory, \c{true} does not mean that all the sub-paths will be
+included wholesale. Rather, the matched component of the sub-path is treated
+as included with the rest of the components matched against the following
+sub-filters. For example:
+
+\
+$ b install config.install.filter='
+ include/x86_64-linux-gnu/@true
+ include/x86_64-linux-gnu/details/@false
+ include/@false'
+\
+
+The \c{true} or \c{false} value may be followed by comma and the \c{symlink}
+modifier to only apply to symlink filesystem entries. For example:
+
+\
+$ b config.install.filter='\"*.so\"@false,symlink'
+\
+
+A filter can be negated by specifying \c{!} as the first pair. For example:
+
+\
+$ b install config.install.filter='! include/@false \"*.a\"@false'
+\
+
+Note that the filtering mechanism only affects what gets physically copied to
+the installation directory without affecting what gets built for install or
+the view of what gets installed at the \c{buildfile} level. For example, given
+the \c{include/@false *.a@false} filters, static libraries will still be built
+(unless arranged not to with \c{config.bin.lib}) and the \c{pkg-config} files
+will still end up with \c{-I} options pointing to the header installation
+directory. Note also that this mechanism applies to both \c{install} and
+\c{uninstall} operations.
+
+\N|If you are familiar with the Debian or Fedora packaging, this mechanism is
+somewhat similar to (and can be used for a similar purpose as) the Debian's
+\c{.install} files and Fedora's \c{%files} spec file sections, which are used
+to split the installation into multiple binary packages.|
+
+As another example, the following filters will omit all the
+development-related files (headers, \c{pkg-config} files, static libraries,
+and shared library symlinks; assuming the platform uses the \c{.a}/\c{.so}
+extensions for the libraries):
+
+\
+$ b install config.install.filter='
+ include/@false
+ pkgconfig/@false
+ \"lib/*.a\"@false
+ \"lib/*.so\"@false,symlink'
+\
+
+
\h1#module-version|\c{version} Module|
A project can use any version format as long as it meets the package version
@@ -6201,7 +7040,7 @@ managing projects that conform to the \c{build2} \i{standard version}
format. If you are starting a new project that uses \c{build2}, you are
strongly encouraged to use this versioning scheme. It is based on much thought
and, often painful, experience. If you decide not to follow this advice, you
-are essentially on your own when version management is concerned.
+are essentially on your own where version management is concerned.
The standard \c{build2} project version conforms to \l{http://semver.org
Semantic Versioning} and has the following form:
@@ -6463,7 +7302,7 @@ just not ordered correctly. As a result, we feel that the risks are justified
when the only alternative is manual version management (which is always an
option, nevertheless).
-When we prepare a distribution of a snapshot, the \c{version} module
+When we prepare a source distribution of a snapshot, the \c{version} module
automatically adjusts the package name to include the snapshot information as
well as patches the manifest file in the distribution with the snapshot number
and id (that is, replacing \c{.z} in the version value with the actual
@@ -6655,6 +7494,116 @@ depends: libprint [3.0.0-b.2.1 3.0.0-b.3)
\N{This chapter is a work in progress and is incomplete.}
+\h#module-bin-target-types|Binary Target Types|
+
+The following listing shows the hierarchy of the target types defined by the
+\c{bin} module while the following sections describe each target type in
+detail (\c{target{\}} and \c{file{\}} are standard target types defined by the
+\c{build2} core; see \l{#targets-types Target Types} for details).
+
+\
+ target----------------.
+ | |
+ ... |
+ | |
+ .---------------file------------. lib
+ | | | | | | libul
+ | libue obje bmie hbmie def obj
+liba libua obja bmia hbmia bmi
+libs libus objs bmis hbmis hbmi
+\
+
+
+\h2#module-bin-target-types-lib|\c{lib{\}}, \c{liba{\}}, \c{libs{\}}|
+
+The \c{liba{\}} and \c{libs{\}} target types represent static (archive) and
+shared libraries, respectively.
+
+The \c{lib{\}} target type is a group with the \c{liba{\}} and/or \c{libs{\}}
+members. A rule that encounters a \c{lib{\}} prerequisite may pick a member
+appropriate for the target being built or it may build all the members
+according to the \c{bin.lib} variable. See \l{#intro-lib Library Exportation
+and Versioning} for background.
+
+The \c{lib*{\}} file extensions are normally automatically assigned by the
+matching rules based on the target platform.
+
+
+\h2#module-bin-target-types-libu|\c{libul{\}}, \c{libue{\}}, \c{libua{\}},
+\c{libus{\}}|
+
+The \c{libu*{\}} target types represent utility libraries. Utility libraries
+are static libraries with object files appropriate for linking an executable
+(\c{libue{\}}), static library (\c{libua{\}}), or shared library
+(\c{libus{\}}). Where possible, utility libraries are built in the
+\"thin archive\" mode.
+
+The \c{libul{\}} target type is a group with the \c{libua{\}} and/or
+\c{libus{\}} members. A rule that encounters a \c{libul{\}} prerequisite picks
+a member appropriate for the target being built.
+
+The \c{libu*{\}} file extensions are normally automatically assigned by the
+matching rules based on the target platform.
+
+
+\h2#module-bin-target-types-obj|\c{obj{\}}, \c{obje{\}}, \c{obja{\}},
+\c{objs{\}}|
+
+The \c{obj*{\}} target types represent object files appropriate for linking an
+executable (\c{obje{\}}), static library (\c{obja{\}}), or shared library
+(\c{objs{\}}).
+
+\N|In \c{build2} we use distinct object files for the three types of binaries
+(executable, static library, and shared library). The distinction between
+static and shared libraries is made to accommodate build differences such as
+the need for position-independent code (\c{-fPIC}) in shared libraries. While
+in most cases the same object file can be used for executables and static
+libraries, they are kept separate for consistency and generality.|
+
+The \c{obj{\}} target type is a group with the \c{obje{\}}, and/or
+\c{obja{\}}, and/or \c{objs{\}} members. A rule that encounters an \c{obj{\}}
+prerequisite picks a member appropriate for the target being built.
+
+The \c{obj*{\}} file extensions are normally automatically assigned by the
+matching rules based on the target platform.
+
+
+\h2#module-bin-target-types-bmi|\c{bmi{\}}, \c{bmie{\}}, \c{bmia{\}},
+\c{bmis{\}}|
+
+The \c{bmi*{\}} target types represent binary module interfaces (BMI) for
+C++20 named modules appropriate for linking an executable (\c{bmie{\}}),
+static library (\c{bmia{\}}), or shared library (\c{bmis{\}}).
+
+The \c{bmi{\}} target type is a group with the \c{bmie{\}}, and/or
+\c{bmia{\}}, and/or \c{bmis{\}} members. A rule that encounters an \c{bmi{\}}
+prerequisite picks a member appropriate for the target being built.
+
+The \c{bmi*{\}} file extensions are normally automatically assigned by the
+matching rules based on the target platform.
+
+
+\h2#module-bin-target-types-hbmi|\c{hbmi{\}}, \c{hbmie{\}}, \c{hbmia{\}},
+\c{hbmis{\}}|
+
+The \c{hbmi*{\}} target types represent binary module interfaces (BMI) for
+C++20 header units appropriate for linking an executable (\c{hbmie{\}}),
+static library (\c{hbmia{\}}), or shared library (\c{hbmis{\}}).
+
+The \c{hbmi{\}} target type is a group with the \c{hbmie{\}}, and/or
+\c{hbmia{\}}, and/or \c{hbmis{\}} members. A rule that encounters an
+\c{hbmi{\}} prerequisite picks a member appropriate for the target being
+built.
+
+The \c{hbmi*{\}} file extensions are normally automatically assigned by the
+matching rules based on the target platform.
+
+
+\h2#module-bin-target-types-def|\c{def{\}}|
+
+The \c{def{\}} target type represents Windows module definition files and has
+the fixed default extension \c{.def}.
+
\h1#module-cc|\c{cc} Module|
@@ -6694,6 +7643,11 @@ config.cc.libs
config.cc.internal.scope
cc.internal.scope
+
+config.cc.reprocess
+ cc.reprocess
+
+config.cc.pkgconfig.sysroot
\
Note that the compiler mode options are \"cross-hinted\" between \c{config.c}
@@ -6709,6 +7663,41 @@ $ b config.cxx=\"g++ -m32\"
$ b config.cxx=\"clang++ -stdlib=libc++\"
\
+\h#cc-target-types|C-Common Target Types|
+
+The following listing shows the hierarchy of the target types defined by the
+\c{cc} module while the following sections describe each target type in detail
+(\c{file{\}} is a standard target type defined by the \c{build2} core; see
+\l{#targets-types Target Types} for details). Every \c{cc}-based module (such
+as \c{c} and \c{cxx}) will have these common target types defined in addition
+to the language-specific ones.
+
+\
+.--file--.
+| |
+h pc
+ |
+ pca
+ pcs
+\
+
+\N|While the \c{h{\}} target type represents a C header file, there is hardly
+a C-family compilation without a C header inclusion. As a result, this target
+types is defined by all \c{cc}-based modules.|
+
+For the description of the \c{h{\}} target type refer to \l{#c-target-types-c
+\c{c{\}}, \c{h{\}}} in the C module documentation.
+
+\h2#cc-target-types-pc|\c{pc{\}}, \c{pca{\}}, \c{pcs{\}}|
+
+The \c{pc*{\}} target types represent \c{pkg-config} files. The \c{pc{\}}
+target type represents the common file and has the fixed default extension
+\c{.pc}. The \c{pca{\}} and \c{pcs{\}} target types represent the static and
+shared files and have the fixed default extensions \c{.static.pc} and
+\c{.shared.pc}, respectively. See \l{#cc-import-installed Importation of
+Installed Libraries} for background.
+
+
\h#cc-internal-scope|Compilation Internal Scope|
\N|While this section uses the \c{cxx} module and C++ compilation as an
@@ -6872,6 +7861,9 @@ if ($cxx.target.system == 'mingw32')
That is, we use the \c{.def} file approach for MSVC (including when building
with Clang) and the built-in support (\c{--export-all-symbols}) for MinGW.
+\N|You will likely also want to add the generated \c{.def} file (or the
+blanket \c{*.def}) to your \c{.gitignore} file.|
+
Note that it is also possible to use the \c{.def} file approach for MinGW. In
this case we need to explicitly load the \c{bin.def} module (which should be
done after loading \c{c} or \c{cxx}) and can use the following arrangement:
@@ -6899,6 +7891,169 @@ to the symbol auto-importing support in Windows linkers. Note, however, that
auto-importing only works for functions and not for global variables.
+\h#cc-import-installed|Importation of Installed Libraries|
+
+As discussed in \l{#intro-import Target Importation}, searching for installed
+C/C++ libraries is seamlessly integrated into the general target importation
+mechanism. This section provides more details on the installed library search
+semantics and \c{pkg-config} integration. These details can be particularly
+useful when dealing with libraries that were not built with \c{build2} and
+which often use idiosyncratic \c{pkg-config} file names.
+
+The \c{cc}-based modules use the common installed library search
+implementation with the following semantics. To illustrate the finer points,
+we assume the following import:
+
+\
+import libs = libbar%lib{Xfoo}
+\
+
+\ol|
+
+\li|First, the ordered list of library search directories is obtained by
+combining two lists: the lists of the compiler's system library search
+directories (extracted, for example, with \c{-print-search-dirs} GCC/Clang
+options) and the list of user library search directories (specified, for
+example, with the \c{-L} options in \c{*.loptions}).
+
+The key property of this combined list is that it matches the search semantics
+that would be used by the compiler to find libraries specified with the \c{-l}
+option during linking.|
+
+\li|Given the list obtained in the previous step, a library binary (shared
+and/or static library) is searched for in the correct order and using the
+target platform-appropriate library prefix and extension (for example, \c{lib}
+prefix and the \c{.so}/\c{.a} extensions if targeting Linux).
+
+For example (continuing with the above import and assuming Linux), each
+directory will be checked for the presence of \c{libXfoo.so} and \c{libXfoo.a}
+(where the \c{Xfoo} stem is the imported target name).
+
+If only a shared or static binary is found in a given directory, no further
+directories are checked for the missing variant. Instead, the missing
+variant is assumed to be unavailable.
+
+If neither a shared nor static library is found in a given directory, then
+it is also checked for the presence of the corresponding \c{pkg-config}
+file as in the following step. If such a file is found, then the library is
+assumed to be \i{binless} (header-only, etc).|
+
+\li|If a static and/or shared library is found (or if looking for a binless
+library), the corresponding \c{pkg-config} subdirectory (normally just
+\c{pkgconfig/}) is searched for the library's \c{.pc} file.
+
+More precisely, we first look for the \c{.static.pc} file for a static
+library and for the \c{.shared.pc} file for a shared library falling back
+to the common \c{.pc} if they don't exist.
+
+\N|It is often required to use different options for consuming static and
+shared libraries. While there is the \c{Libs.private} and \c{Cflags.private}
+mechanism in \c{pkg-config}, its semantics is to append options to \c{Libs}
+and \c{Cflags} rather than to provide alternative options. And often the
+required semantics is to provide different options for static and shared
+libraries, such as to provide a macro which indicates whether linking static
+or shared in order to setup symbol exporting.
+
+As a result, in \c{build2} we produce separate \c{.pc} files for static and
+shared libraries in addition to the \"best effort\" common \c{.pc} file for
+compatibility with other build systems. Similarly, when consuming a library
+we first look for the \c{.static.pc} and \c{.shared.pc} files falling back
+to the common \c{.pc} if they are not available.|
+
+To deal with idiosyncrasies in \c{pkg-config} file names, the following base
+names are tried in order, where \ci{name} is the imported target name
+(\c{Xfoo} in the above import), \ci{proj} is the imported project name
+(\c{libbar} in the above import), and \ci{ext} is one of the above-mentioned
+\c{pkg-config} extensions (\c{static.pc}, \c{shared.pc}, or \c{pc}). The
+concrete name tried for the above import is shown in parenthesis as an
+example.
+
+\ol|
+
+\li|\c{lib\i{name}.\i{ext}} (\c{libXfoo.pc})|
+
+\li|\c{\i{name}.\i{ext}} (\c{Xfoo.pc})|
+
+\li|lowercase \c{lib\i{name}.\i{ext}} (\c{libxfoo.pc})|
+
+\li|lowercase \c{\i{name}.\i{ext}} (\c{xfoo.pc})|
+
+\li|\c{\i{proj}.\i{ext}} (\c{libbar.pc}; this test is omitted if not project-qualified)||
+
+||
+
+In particular, the last try (for \c{\i{proj}.\i{ext}}) serves as an escape
+hatch for cases where the \c{.pc} file name does not have anything to do with
+the names of library binaries. The canonical example of this is \c{zlib} which
+names its library binaries \c{libz.so}/\c{libz.a} while its \c{.pc} file \-
+\c{zlib.pc}. To be able to import \c{zlib} that was not built with \c{build2},
+we have to use the following import:
+
+\
+import libs = zlib%lib{z}
+\
+
+Note also that these complex rules (which are unfortunately necessary to deal
+with the lack of any consistency in \c{.pc} file naming) can sometimes produce
+surprising interactions. For example, it may appear that a clearly incorrect
+import nevertheless appears to somehow work, as in the following example:
+
+\
+import libs = zlib%lib{znonsense}
+\
+
+What happens here is that while no library binary is found, \c{zlib.pc} is
+found and as a result the library ends up being considered binless with the
+\c{-lz} (that is found in the \c{Libs} value of \c{zlib.pc}) treated as a
+prerequisite library, resolved using the above algorithm, and linked. In other
+words, in this case we end up with a binless library \c{lib{znonsense\}} that
+depends on \c{lib{z\}} instead of a single \c{lib{z\}} library.
+
+\h2#cc-import-installed-sysroot|Rewriting Installed Libraries System Root (sysroot)|
+
+Sometimes the installed libraries are moved to a different location after the
+installation. This is especially common in embedded development where the code
+is normally cross-compiled and the libraries for the target platform are
+placed into a host directory, called system root or \i{sysroot}, that doesn't
+match where these libraries were originally installed to. For example, the
+libraries might have been installed into \c{/usr/} but on the host machine
+they may reside in \c{/opt/target/usr/}. In this example, \c{/opt/target/} is
+the sysroot.
+
+While such relocations usually do not affect the library headers or binaries,
+they do break the \c{pkg-config}'s \c{.pc} files which often contain \c{-I}
+and \c{-L} options with absolute paths. Continue with the above example, a
+\c{.pc} file as originally installed may contain \c{-I/usr/include} and
+\c{-L/usr/lib} while now, that the libraries have been relocated to
+\c{/opt/target/}, they somehow need to be adjusted to
+\c{-I/opt/target/usr/include} and \c{-L/opt/target/usr/lib}.
+
+While it is possible (and perhaps correct) to accomplish this by fixing the
+\c{.pc} files to match the new location, it is not always possible or easy.
+As a result, \c{build2} provides a mechanism for automatically adjusting the
+system root in the \c{-I} and \c{-L} options extracted from \c{.pc} files.
+
+\N|This functionality is roughly equivalent to that provided with the
+\c{PKG_CONFIG_SYSROOT_DIR} environment variable by the \c{pkg-config}
+utility.|
+
+Specifically, the \c{config.cc.pkgconfig.sysroot} variable can be used to
+specify an alternative system root. When specified, all absolute paths in the
+\c{-I} and \c{-L} options that are not already in this directory will be
+rewritten to start with this sysroot.
+
+\N|Note that this mechanism is a workaround rather than a proper solution since
+it is limited to the \c{-I} and \c{-L} options. In particular, it does not
+handle any other options that may contain absolute paths nor \c{pkg-config}
+variables that may be queried.
+
+As a result, it should only be used for dealing with issues in third-party
+\c{.pc} files that do not handle relocation (for example, using the
+\c{${pcfiledir\}} built-in \c{pkg-config} variable). In particular, for
+\c{build2}-generated \c{.pc} files a \l{#install-reloc relocatable
+installation} should be used instead.|
+
+
\h#cc-gcc|GCC Compiler Toolchain|
The GCC compiler id is \c{gcc}.
@@ -7127,6 +8282,185 @@ config.c.internal.scope
c.internal.scope
\
+\h#c-target-types|C Target Types|
+
+The following listing shows the hierarchy of the target types defined by the
+\c{c} module while the following sections describe each target type in detail
+(\c{file{\}} is a standard target type defined by the \c{build2} core; see
+\l{#targets-types Target Types} for details). See also \l{#cc-target-types
+C-Common Target Types} for target types defined by all the \c{cc}-based
+modules.
+
+\
+.--file--.
+| | |
+c m S
+h
+\
+
+The \c{m{\}} target type represents an Objective-C source file, see \l{c-objc
+Objective-C Compilation} for details.
+
+The \c{S{\}} target type represents an Assembler with C Preprocessor file, see
+\l{c-as-cpp Assembler with C Preprocessor Compilation} for details.
+
+\h2#c-target-types-c|\c{c{\}}, \c{h{\}}|
+
+The \c{c{\}} and \c{h{\}} target types represent C source and header files.
+They have the default extensions \c{.c} and \c{.h}, respectively, which can
+be customized with the \c{extension} variable.
+
+
+\h#c-objc|Objective-C Compilation|
+
+The \c{c} module provides the \c{c.objc} submodule which can be loaded in
+order to register the \c{m{\}} target type and enable Objective-C compilation
+in the \c{C} compile rule. Note that \c{c.objc} must be loaded after the \c{c}
+module and while the \c{m{\}} target type is registered unconditionally,
+compilation is only enabled if the C compiler supports Objective-C for the
+target platform. Typical usage:
+
+\
+# root.build
+#
+using c
+using c.objc
+\
+
+\
+# buildfile
+#
+lib{hello}: {h c}{*}
+lib{hello}: m{*}: include = ($c.target.class == 'macos')
+\
+
+Note also that while there is support for linking Objective-C executables and
+libraries, this is done using the C compiler driver and no attempt is made to
+automatically link any necessary Objective-C runtime library (such as
+\c{-lobjc}).
+
+
+\h#c-as-cpp|Assembler with C Preprocessor Compilation|
+
+The \c{c} module provides the \c{c.as-cpp} submodule which can be loaded in
+order to register the \c{S{\}} target type and enable Assembler with C
+Preprocessor compilation in the \c{C} compile rule. Note that \c{c.as-cpp}
+must be loaded after the \c{c} module and while the \c{S{\}} target type is
+registered unconditionally, compilation is only enabled if the C compiler
+supports Assembler with C Preprocessor compilation. Typical usage:
+
+\
+# root.build
+#
+using c
+using c.as-cpp
+\
+
+\
+# buildfile
+#
+exe{hello}: {h c}{* -hello.c}
+
+# Use C implementation as a fallback if no assembler.
+#
+assembler = ($c.class == 'gcc' && $c.target.cpu == 'x86_64')
+
+exe{hello}: S{hello}: include = $assembler
+exe{hello}: c{hello}: include = (!$assembler)
+\
+
+\
+/* hello.S
+ */
+#ifndef HELLO_RESULT
+# define HELLO_RESULT 0
+#endif
+
+text
+
+.global hello
+hello:
+ /* ... */
+ movq $HELLO_RESULT, %rax
+ ret
+
+#ifdef __ELF__
+.section .note.GNU-stack, \"\", @progbits
+#endif
+\
+
+The default file extension for the \c{S{\}} target type is \c{.S} (capital)
+but that can be customized using the standard mechanisms. For example:
+
+\
+# root.build
+#
+using c
+using c.as-cpp
+
+h{*}: extension = h
+c{*}: extension = c
+S{*}: extension = sx
+\
+
+Note that \c{*.coptions} are passed to the C compiler when compiling Assembler
+with C Preprocessor files because compile options may cause additional
+preprocessor macros to be defined. Plus, some of them (such as \c{-g}) are
+passed (potentially translated) to the underlying assembler. To pass
+additional options when compiling Assembler files use \c{c.poptions} and
+\c{c.coptions}. For example (continuing with the previous example):
+
+\
+if $assembler
+{
+ obj{hello}:
+ {
+ c.poptions += -DHELLO_RESULT=1
+ c.coptions += -Wa,--no-pad-sections
+ }
+}
+\
+
+\h#c-predefs|C Compiler Predefined Macro Extraction|
+
+The \c{c} module provides the \c{c.predefs} submodule which can be loaded in
+order to register a rule that generates a C header with predefined compiler
+macros. Note that the \c{c.predefs} module must be loaded after the \c{c}
+module and the rule will only match with an explicit rule hint. Typical usage:
+
+\
+# root.build
+#
+using c
+using c.predefs
+\
+
+\
+# buildfile
+#
+[rule_hint=c.predefs] h{predefs}:
+\
+
+Note also that the MSVC compiler only supports the predefined macro extraction
+starting from Visual Studio 2019 (16.0; \c{cl.exe} version 19.20). If support
+for earlier versions is required, then you will need to provide a fallback
+implementation appropriate for your project. For example:
+
+\
+[rule_hint=c.predefs] h{predefs}:
+% update
+if ($c.id == 'msvc' && \
+ ($c.version.major < 19 || \
+ ($c.version.major == 19 && $c.version.minor < 20)))
+{{
+ diag c-predefs $>
+
+ cat <<EOF >$path($>)
+ #define _WIN32
+ EOF
+}}
+\
+
\h1#module-cxx|\c{cxx} Module|
@@ -7195,6 +8529,46 @@ config.cxx.translate_include
\
+\h#cxx-target-types|C++ Target Types|
+
+The following listing shows the hierarchy of the target types defined by the
+\c{cxx} module while the following sections describe each target type in
+detail (\c{file{\}} is a standard target type defined by the \c{build2} core;
+see \l{#targets-types Target Types} for details). See also \l{#cc-target-types
+C-Common Target Types} for target types defined by all the \c{cc}-based
+modules.
+
+\
+ .--file--.
+ | |
+cxx mm
+hxx
+ixx
+txx
+mxx
+\
+
+The \c{mm{\}} target type represents an Objective-C++ source file, see
+\l{cxx-objcxx Objective-C++ Compilation} for details.
+
+\h2#cxx-target-types-cxx|\c{cxx{\}}, \c{hxx{\}}, \c{ixx{\}}, \c{txx{\}},
+\c{mxx{\}}|
+
+The \c{cxx{\}}, \c{hxx{\}}, \c{ixx{\}}, \c{txx{\}}, and \c{mxx{\}} target
+types represent C++ source, header, inline, template, and module interface
+files. They have the default extensions \c{.cxx}, \c{.hxx}, \c{.ixx},
+\c{.txx}, and \c{.mxx}, respectively, which can be customized with the
+\c{extension} variable. For example (normally done in \c{root.build}):
+
+\
+using cxx
+
+cxx{*}: extension = cpp
+hxx{*}: extension = hpp
+mxx{*}: extension = cppm
+\
+
+
\h#cxx-modules|C++ Modules Support|
This section describes the build system support for C++ modules.
@@ -7202,7 +8576,9 @@ This section describes the build system support for C++ modules.
\h2#cxx-modules-intro|Modules Introduction|
The goal of this section is to provide a practical introduction to C++ Modules
-and to establish key concepts and terminology.
+and to establish key concepts and terminology. You can skip directly to
+\l{#cxx-modules-build Building Modules} if you are already familiar with this
+topic.
A pre-modules C++ program or library consists of one or more \i{translation
units} which are customarily referred to as C++ source files. Translation
@@ -7253,7 +8629,7 @@ called \i{module interface}, that become \i{visible} once the module is
imported:
\
-import hello.core
+import hello.core;
\
What exactly does \i{visible} mean? To quote the standard: \i{An
@@ -7273,8 +8649,8 @@ module name and its namespace names need not be related, it usually makes
sense to have a parallel naming scheme, as discussed below. Finally, the
\c{import} declaration does not imply any additional visibility for names
declared inside namespaces. Specifically, to access such names we must
-continue using the standard mechanisms, such as qualification or using
-declaration/directive. For example:
+continue using the existing mechanisms, such as qualification or using
+declaration/directive. For example:
\
import hello.core; // Exports hello::say().
@@ -7293,7 +8669,7 @@ link an object file or a library that provides them. In this respect, modules
are similar to headers and as with headers, module's use is not limited to
libraries; they make perfect sense when structuring programs. Furthermore,
a library may also have private or implementation modules that are not
-meant to be consumed by the library's users.
+meant to be imported by the library's consumers.
The producer perspective on modules is predictably more complex. In
pre-modules C++ we only had one kind of translation unit (or source
@@ -7301,6 +8677,11 @@ file). With modules there are three kinds: \i{module interface unit},
\i{module implementation unit}, and the original kind which we will
call a \i{non-module translation unit}.
+\N|There are two additional modular translation units: module interface
+partition and module implementation partition. While partitions are supported,
+they are not covered in this introduction. A link to a complete example
+that uses both types of partitions will be given in the next section.|
+
From the producer's perspective, a module is a collection of module translation
units: one interface unit and zero or more implementation units. A simple
module may consist of just the interface unit that includes implementations
@@ -7311,14 +8692,14 @@ A translation unit is a module interface unit if it contains an \i{exporting
module declaration}:
\
-export module hello.core;
+export module hello;
\
A translation unit is a module implementation unit if it contains a
\i{non-exporting module declaration}:
\
-module hello.core;
+module hello;
\
While module interface units may use the same file extension as normal source
@@ -7330,17 +8711,37 @@ are using some other naming scheme, then perhaps now is a good opportunity to
switch to one of the above. Continuing using the source file extension for
module implementation units appears reasonable and that's what we recommend.
+A modular translation unit (that is, either module interface or
+implementation) that does not start with one of the above module declarations
+must then start with the module introducer:
+
+\
+module;
+
+...
+
+export module hello;
+\
+
+The fragment from the module introducer and until the module declaration is
+called the \i{global module fragment}. Any name declared in the global module
+fragment belongs to the \i{global module}, an implied module containing
+\"old\" or non-modular declarations that don't belong to any named module.
+
A module declaration (exporting or non-exporting) starts a \i{module purview}
that extends until the end of the module translation unit. Any name declared
-in a module's purview \i{belongs} to said module. For example:
+in a module's purview \i{belongs} to the said module. For example:
\
-#include <string> // Not in purview.
+module; // Start of global module fragment.
-export module hello.core; // Start of purview.
+#include <cassert> // Not in purview.
-void
-say_hello (const std::string&); // In purview.
+export module hello; // Start of purview.
+
+import std; // In purview.
+
+void say_hello (const std::string&); // In purview.
\
A name that belongs to a module is \i{invisible} to the module's consumers
@@ -7350,26 +8751,24 @@ ways to accomplish this. We can start the declaration with the \c{export}
specifier, for example:
\
-export module hello.core;
+export module hello;
export enum class volume {quiet, normal, loud};
-export void
-say_hello (const char*, volume);
+export void say_hello (const char*, volume);
\
Alternatively, we can enclose one or more declarations into an \i{exported
group}, for example:
\
-export module hello.core;
+export module hello;
export
{
enum class volume {quiet, normal, loud};
- void
- say_hello (const char*, volume);
+ void say_hello (const char*, volume);
}
\
@@ -7377,42 +8776,33 @@ Finally, if a namespace definition is declared exported, then every name
in its body is exported, for example:
\
-export module hello.core;
+export module hello;
export namespace hello
{
enum class volume {quiet, normal, loud};
- void
- say (const char*, volume);
+ void say_hello (const char*, volume);
}
namespace hello
{
- void
- impl (const char*, volume); // Not exported.
+ void impl (const char*, volume); // Not exported.
}
\
Up until now we've only been talking about names belonging to a module. What
-about the corresponding symbols? For exported names, the resulting symbols
-would be the same as if those names were declared outside of a module's
-purview (or as if no modules were used at all). Non-exported names, on the
-other hand, have \i{module linkage}: their symbols can be resolved from this
-module's units but not from other translation units. They also cannot clash
-with symbols for identical names from other modules (and non-modules). This is
-usually achieved by decorating the non-exported symbols with the module name.
-
-This ownership model has an important backwards compatibility implication: a
-library built with modules enabled can be linked to a program that still uses
-headers. And even the other way around: we can build and use a module for a
-library that was built with headers.
+about the corresponding symbols? All the major C++ compilers have chosen to
+implement the so-called strong ownership model, where for both exported and
+non-exported names, the corresponding symbols are decorated with the module
+name. As a result, they cannot clash with symbols for identical names from
+other named modules or the global module.
What about the preprocessor? Modules do not export preprocessor macros,
only C++ names. A macro defined in the module interface unit cannot affect
the module's consumers. And macros defined by the module's consumers cannot
affect the module interface they are importing. In other words, module
-producers and consumers are isolated from each other when the preprocessor
+producers and consumers are isolated from each other where the preprocessor
is concerned. For example, consider this module interface:
\
@@ -7437,9 +8827,10 @@ import hello;
#endif
\
-This is not to say that the preprocessor cannot be used by either, it just
-doesn't \"leak\" through the module interface. One practical implication of
-this model is the insignificance of the import order.
+This is not to say that the preprocessor cannot be used by either the module
+interface or its consumer, it just that macros don't \"leak\" through the
+module interface. One practical consequence of this model is the
+insignificance of the importation order.
If a module imports another module in its purview, the imported module's
names are not made automatically visible to the consumers of the importing
@@ -7449,10 +8840,9 @@ interface as an example:
\
export module hello;
-import std.core;
+import std;
-export void
-say_hello (const std::string&);
+export std::string formal_hello (const std::string&);
\
And its consumer:
@@ -7463,26 +8853,25 @@ import hello;
int
main ()
{
- say_hello (\"World\");
+ std::string s (format_hello (\"World\"));
}
\
This example will result in a compile error and the diagnostics may
-confusingly indicate that there is no known conversion from a C string to
-\"something\" called \c{std::string}. But with the understanding of the
-difference between \c{import} and \c{#include} the reason should be clear:
-while the module interface \"sees\" \c{std::string} (because it imported its
-module), we (the consumer) do not (since we did not). So the fix is to
-explicitly import \c{std.core}:
+confusingly indicate that there is no member \c{string} in namespace \c{std}.
+But with the understanding of the difference between \c{import} and
+\c{#include} the reason should be clear: while the module interface \"sees\"
+\c{std::string} (because it imported its module), we (the consumer) do not
+(since we did not). So the fix is to explicitly import \c{std}:
\
-import std.core;
+import std;
import hello;
int
main ()
{
- say_hello (\"World\");
+ std::string s (format_hello (\"World\"));
}
\
@@ -7495,14 +8884,13 @@ this is a good design choice is debatable, as discussed below):
\
export module hello;
-export import std.core;
+export import std;
-export void
-say_hello (const std::string&);
+export std::string formal_hello (const std::string&);
\
One way to think of a re-export is \i{as if} an import of a module also
-\"injects\" all the imports said module re-exports, recursively. That's
+\"injects\" all the imports the said module re-exports, recursively. That's
essentially how most compilers implement it.
Module re-export is the mechanism for assembling bigger modules out of
@@ -7523,41 +8911,41 @@ export
\
Besides starting a module purview, a non-exporting module declaration in the
-implementation unit makes non-internal linkage names declared or made visible
-in the \i{interface purview} also visible in the \i{implementation purview}.
-In this sense non-exporting module declaration acts as an extended
-\c{import}. For example:
+implementation unit makes (non-internal linkage) names declared or made
+visible (via import) in the module purview of an interface unit also visible
+in the module purview of the implementation unit. In this sense a
+non-exporting module declaration acts as a special \c{import}. The following
+example illustrates this point:
\
+module;
+
import hello.impl; // Not visible (exports impl()).
-void
-extra_impl (); // Not visible.
+#include <string.h> // Not visible (declares strlen()).
-export module hello.extra; // Start of interface purview.
+export module hello.extra; // Start of module purview (interface).
import hello.core; // Visible (exports core()).
-void
-extra (); // Visible.
+void extra (); // Visible.
-static void
-extra2 (); // Not visible (internal linkage).
+static void extra2 (); // Not visible (internal linkage).
\
And this is the implementation unit:
\
-module hello.extra; // Start of implementation purview.
+module hello.extra; // Start of module purview (implementation).
void
f ()
{
- impl (); // Error.
- extra_impl (); // Error.
- core (); // Ok.
- extra (); // Ok.
- extra2 (); // Error.
+ impl (); // Error.
+ strlen (\"\"); // Error.
+ core (); // Ok.
+ extra (); // Ok.
+ extra2 (); // Error.
}
\
@@ -7567,9 +8955,9 @@ to the module declaration can be.
The final perspective that we consider is that of the build system. From its
point of view the central piece of the module infrastructure is the \i{binary
-module interface}: a binary file that is produced by compiling the module
-interface unit and that is required when compiling any translation unit that
-imports this module as well as the module's implementation units.
+module interface} or BMI: a binary file that is produced by compiling the
+module interface unit and that is required when compiling any translation unit
+that imports this module as well as the module's implementation units.
Then, in a nutshell, the main functionality of a build system when it comes to
modules support is figuring out the order in which all the translation units
@@ -7606,53 +8994,58 @@ compile them, again, on the side.
\h2#cxx-modules-build|Building Modules|
-Compiler support for C++ Modules is still experimental. As a result, it is
-currently only enabled if the C++ standard is set to \c{experimental}. After
-loading the \c{cxx} module we can check if modules are enabled using the
-\c{cxx.features.modules} boolean variable. This is what the relevant
+Compiler support for C++ modules is still experimental, incomplete, and often
+buggy. Also, in \c{build2}, the presence of modules changes the C++
+compilation model in ways that would introduce unnecessary overheads for
+headers-only code. As a result, a project must explicitly enable modules using
+the \c{cxx.features.modules} boolean variable. This is what the relevant
\c{root.build} fragment could look like for a modularized project:
\
-cxx.std = experimental
+cxx.std = latest
+cxx.features.modules = true
using cxx
-assert $cxx.features.modules 'compiler does not support modules'
-
mxx{*}: extension = mxx
cxx{*}: extension = cxx
\
-To support C++ modules the \c{cxx} module (build system) defines several
-additional target types. The \c{mxx{\}} target is a module interface unit.
-As you can see from the above \c{root.build} fragment, in this project we
-are using the \c{.mxx} extension for our module interface files. While
-you can use the same extension as for \c{cxx{\}} (source files), this is
-not recommended since some functionality, such as wildcard patterns, will
-become unusable.
+\N|Note that you must explicitly enable modules in your project even if you
+are only importing other modules, including standard library modules (\c{std}
+or \c{std.compat}).|
+
+To support C++ modules the \c{cxx} build system module defines several
+additional target types. The \c{mxx{\}} target is a module interface unit. As
+you can see from the above \c{root.build} fragment, in this project we are
+using the \c{.mxx} extension for our module interface files. While you can use
+the same extension as for \c{cxx{\}} (source files), this is not recommended
+since some functionality, such as wildcard patterns, will become unusable.
The \c{bmi{\}} group and its \c{bmie{\}}, \c{bmia{\}}, and \c{bmis{\}} members
are used to represent binary module interfaces targets. We normally do not
-need to mention them explicitly in our buildfiles except, perhaps, to specify
-additional, module interface-specific compile options. We will see some
-examples of this below.
+need to mention them explicitly in our \c{buildfiles} except, perhaps, to
+specify additional, module interface-specific compile options.
To build a modularized executable or library we simply list the module
interfaces as its prerequisites, just as we do for source files. As an
example, let's build the \c{hello} program that we have started in the
introduction (you can find the complete project in the
-\l{https://build2.org/pkg/hello Hello Repository} under
-\c{mhello}). Specifically, we assume our project contains the following files:
+\l{https://github.com/build2/cxx20-modules-examples/tree/named-only-import-std
+\c{cxx20-modules-examples}} repository under \c{hello-module}). Specifically,
+we assume our project contains the following files:
\
// file: hello.mxx (module interface)
export module hello;
-import std.core;
+import std;
-export void
-say_hello (const std::string&);
+export namespace hello
+{
+ void say_hello (const std::string_view& name);
+}
\
\
@@ -7660,27 +9053,24 @@ say_hello (const std::string&);
module hello;
-import std.io;
-
-using namespace std;
-
-void
-say_hello (const string& name)
+namespace hello
{
- cout << \"Hello, \" << name << '!' << endl;
+ void say_hello (const std::string_view& n)
+ {
+ std::cout << \"Hello, \" << n << '!' << std::endl;
+ }
}
\
\
-// file: driver.cxx
+// file: main.cxx
-import std.core;
import hello;
int
main ()
{
- say_hello (\"World\");
+ hello::say_hello (\"World\");
}
\
@@ -7688,7 +9078,7 @@ To build a \c{hello} executable from these files we can write the following
\c{buildfile}:
\
-exe{hello}: cxx{driver} {mxx cxx}{hello}
+exe{hello}: cxx{main} {mxx cxx}{hello}
\
Or, if you prefer to use wildcard patterns:
@@ -7697,14 +9087,40 @@ Or, if you prefer to use wildcard patterns:
exe{hello}: {mxx cxx}{*}
\
-Alternatively, we can package the module into a library and then link the
-library to the executable:
+\N|Module partitions, both interface and implementation, are compiled to BMIs
+and as a result must be listed as \c{mxx{\}} prerequisites. See
+\c{hello-partition} in the
+\l{https://github.com/build2/cxx20-modules-examples/tree/named-only-import-std
+\c{cxx20-modules-examples}} repository for a complete example.|
+
+Alternatively, we can place the module into a library and then link the
+library to the executable (see \c{hello-library-module} in the
+\l{https://github.com/build2/cxx20-modules-examples/tree/named-only-import-std
+\c{cxx20-modules-examples}} repository):
\
-exe{hello}: cxx{driver} lib{hello}
+exe{hello}: cxx{main} lib{hello}
lib{hello}: {mxx cxx}{hello}
\
+Note that a library consisting of only module interface units is by default
+always binful (see \l{#intro-lib Library Exportation and Versioning} for
+background) since compiling a module interface always results in an object
+file, even if the module interface does not contain any non-inline/template
+functions or global variables. However, you can explicitly request for such a
+library to be treated as binless:
+
+\
+lib{hello}: mxx{hello}
+{
+ bin.binless = true
+}
+\
+
+\N|Note that if such a binless library has non-inline/template functions or
+global variables, then whether it can used in all situations without causing
+duplicate symbols is platform-dependent.|
+
As you might have surmised from this example, the modules support in
\c{build2} automatically resolves imports to module interface units that are
specified either as direct prerequisites or as prerequisites of library
@@ -7723,9 +9139,11 @@ guess is verified.
The practical implication of this implementation detail is that our module
interface files must embed a portion of a module name, or, more precisely, a
sufficient amount of \"module name tail\" to unambiguously resolve all the
-modules used in a project. Note also that this guesswork is only performed for
+modules used in a project. Note that this guesswork is only performed for
direct module interface prerequisites; for those that come from libraries the
-module names are known and are therefore matched exactly.
+module names are known and are therefore matched exactly. And the guesses are
+always verified before the actual compilation, so misguesses cannot go
+unnoticed.
As an example, let's assume our \c{hello} project had two modules:
\c{hello.core} and \c{hello.extra}. While we could call our interface files
@@ -7760,46 +9178,19 @@ with module names, for example:
mxx{foobar}@./: cxx.module_name = hello
\
-Note also that standard library modules (\c{std} and \c{std.*}) are treated
-specially: they are not fuzzy-matched and they need not be resolvable to
-the corresponding \c{mxx{\}} or \c{bmi{\}} in which case it is assumed
-they will be resolved in an ad hoc way by the compiler. This means that if
-you want to build your own standard library module (for example, because
-your compiler doesn't yet ship one; note that this may not be supported
-by all compilers), then you have to specify the module name explicitly.
-For example:
-
-\
-exe{hello}: cxx{driver} {mxx cxx}{hello} mxx{std-core}
-
-mxx{std-core}@./: cxx.module_name = std.core
-\
+Note also that the standard library modules (\c{std} and \c{std.compat}) are
+treated specially and are resolved in a compiler-specific manner.
When C++ modules are enabled and available, the build system makes sure the
-\c{__cpp_modules} feature test macro is defined. Currently, its value is
-\c{201703} for VC and \c{201704} for GCC and Clang but this will most likely
-change in the future.
+\c{__cpp_modules} feature test macro is defined. However, if the compiler
+version being used does not claim complete modules support, its value may not
+be \c{201907}.
-One major difference between the current C++ modules implementation in VC and
-the other two compilers is the use of the \c{export module} syntax to identify
-the interface units. While both GCC and Clang have adopted this new syntax,
-VC is still using the old one without the \c{export} keyword. We can use the
-\c{__cpp_modules} macro to provide a portable declaration:
-
-\
-#if __cpp_modules >= 201704
-export
-#endif
-module hello;
-\
-
-Note, however, that the modules support in \c{build2} provides temporary
-\"magic\" that allows us to use the new syntax even with VC (don't ask how).
\h2#cxx-modules-symexport|Module Symbols Exporting|
When building a shared library, some platforms (notably Windows) require that
-we explicitly export symbols that must be accessible to the library users.
+we explicitly export symbols that must be accessible to the library consumers.
If you don't need to support such platforms, you can thank your lucky stars
and skip this section.
@@ -7807,20 +9198,26 @@ When using headers, the traditional way of achieving this is via an \"export
macro\" that is used to mark exported APIs, for example:
\
-LIBHELLO_EXPORT void
-say_hello (const string&);
+LIBHELLO_EXPORT void say_hello (const string&);
\
This macro is then appropriately defined (often in a separate \"export
header\") to export symbols when building the shared library and to import
-them when building the library's users.
+them when building the library's consumers (and to nothing when either
+building or consuming the static library).
The introduction of modules changes this in a number of ways, at least as
-implemented by VC (hopefully other compilers will follow suit). While we
-still have to explicitly mark exported symbols in our module interface
-unit, there is no need (and, in fact, no way) to do the same when said
-module is imported. Instead, the compiler automatically treats all
-such explicitly exported symbols (note: symbols, not names) as imported.
+implemented by MSVC and Clang. While we still have to explicitly mark exported
+symbols in our module interface unit, there is no need (and, in fact, no way)
+to do the same when said module is imported. Instead, the compiler
+automatically treats all such explicitly exported symbols (note: symbols, not
+names) as imported.
+
+\N|While the automatic importing may look like the same mechanism as what's
+used to support \l{#cc-auto-symexport Automatic DLL Symbol Exporting}, it
+appears not to be since it also works for global variables, not only
+functions. However, reportedly, it does appear to incur the same additional
+overhead as auto-importing, at least for functions.|
One notable aspect of this new model is the locality of the export macro: it
is only defined when compiling the module interface unit and is not visible to
@@ -7829,20 +9226,19 @@ have a unique per-library name (that \c{LIBHELLO_} prefix) because a header
from one library can be included while building another library.
We can continue using the same export macro and header with modules and, in
-fact, that's the recommended approach when maintaining the dual, header/module
-arrangement for backwards compatibility (discussed below). However, for
-modules-only codebases, we have an opportunity to improve the situation in two
-ways: we can use a single, keyword-like macro instead of a library-specific
-one and we can make the build system manage it for us thus getting rid of the
-export header.
+fact, that's the recommended approach if maintaining the dual, header/module
+arrangement for backwards compatibility. However, for modules-only codebases,
+we have an opportunity to improve the situation in two ways: we can use a
+single, keyword-like macro instead of a library-specific one and we can make
+the build system manage it for us thus getting rid of the export header.
To enable this functionality in \c{build2} we set the
\c{cxx.features.symexport} boolean variable to \c{true} before loading the
\c{cxx} module. For example:
\
-cxx.std = experimental
-
+cxx.std = latest
+cxx.features.modules = true
cxx.features.symexport = true
using cxx
@@ -7858,25 +9254,22 @@ in our module interface units, for example:
\
export module hello;
-import std.core;
+import std;
-export __symexport void
-say_hello (const std::string&);
+export __symexport void say_hello (const std::string&);
\
-As an aside, you may be wondering why can't a module export automatically mean
-a symbol export? While you will normally want to export symbols of all your
+\N|You may be wondering why can't a module export automatically mean a symbol
+export? While you will normally want to export symbols of all your
module-exported names, you may also need to do so for some non-module-exported
ones. For example:
\
export module foo;
-__symexport void
-f_impl ();
+__symexport void f_impl ();
-export __symexport inline void
-f ()
+export __symexport inline void f ()
{
f_impl ();
}
@@ -7885,7 +9278,7 @@ f ()
Furthermore, symbol exporting is a murky area with many limitations and
pitfalls (such as auto-exporting of base classes). As a result, it would not
be unreasonable to expect such an automatic module exporting to only further
-muddy the matter.
+muddy the matter.|
\h2#cxx-modules-install|Modules Installation|
@@ -7914,6 +9307,15 @@ Libs: -L/usr/lib -lhello
cxx.modules = hello.core=/usr/include/hello/core.mxx hello.extra=/usr/include/hello/extra.mxx
\
+\N|The \c{:} character in a module partition name is encoded as \c{..}. For
+example, for \c{hello:core} we would have:
+
+\
+cxx.modules = hello..core=/usr/...
+\
+
+|
+
Additional module properties are specified with variables in the
\c{cxx.module_<property>.<name>} form, for example:
@@ -7944,13 +9346,12 @@ different compared to headers. This section provides basic guidelines for
designing modules. We start with the overall considerations such as module
granularity and partitioning into translation units then continue with the
structure of typical module interface and implementation units. The following
-section discusses practical approaches to modularizing existing code and
-providing dual, header/module interfaces for backwards-compatibility.
+section discusses practical approaches to modularizing existing code.
Unlike headers, the cost of importing modules should be negligible. As a
result, it may be tempting to create \"mega-modules\", for example, one per
library. After all, this is how the standard library is modularized with its
-fairly large \c{std.core} and \c{std.io} modules.
+\c{std} and \c{std.compat} modules.
There is, however, a significant drawback to this choice: every time we make a
change, all consumers of such a mega-module will have to be recompiled,
@@ -7971,11 +9372,58 @@ The sensible approach is then to create modules of conceptually-related and
commonly-used entities possibly complemented with aggregate modules for ease
of importation. This also happens to be generally good design.
-As an example, let's consider an XML library that provides support for both
+As an example, let's consider a JSON library that provides support for both
parsing and serialization. Since it is common for applications to only use one
-of the functionalities, it makes sense to provide the \c{xml.parser} and
-\c{xml.serializer} modules. While it is not too tedious to import both, for
-convenience we could also provide the \c{xml} module that re-exports the two.
+of the functionalities, it makes sense to provide the \c{json.parser} and
+\c{json.serializer} modules. Depending on the representation of JSON we use in
+our library, it will most likely have some shared types so it probably makes
+sense to have the \c{json.types} module that is re-exported by the parser and
+serializer modules. While it is not too tedious to import both \c{json.parser}
+and \c{json.serializer} if both a needed, for convenience we could also
+provide the \c{json} module that re-exports the two. Something along these
+lines:
+
+\
+// types.mxx
+
+export module json.types;
+
+export class json
+{
+ ...
+};
+\
+
+\
+// parser.mxx
+
+export module json.parser;
+
+export import json.types;
+
+export json parse (...);
+\
+
+
+\
+// serializer.mxx
+
+export module json.serializer;
+
+export import json.types;
+
+export ... serialize (const json&);
+\
+
+\
+// json.mxx
+
+export module json;
+
+export import json.types;
+export import json.parser;
+export import json.serializer;
+\
Once we are past selecting an appropriate granularity for our modules, the
next question is how to partition them into translation units. A module can
@@ -7990,9 +9438,10 @@ recompiled. If we keep everything in a single file, then every time we change
the implementation we trigger recompilations that would have been avoided had
the implementation been factored out into a separate unit. Note that a build
system in cooperation with the compiler could theoretically avoid such
-unnecessary recompilations: if the compiler produces identical binary
-interface files when the module interface is unchanged, then the build system
-could detect this and skip recompiling the module's consumers.
+unnecessary recompilations in certain cases: if the compiler produces
+identical binary interface files when the module interface is unchanged, then
+the build system could detect this and skip recompiling the module's
+consumers.
A related issue with single-file modules is the reduction in the build
parallelization opportunities. If the implementation is part of the interface
@@ -8018,12 +9467,12 @@ should be rare.
Once we start writing our first real module the immediate question that
normally comes up is where to put \c{#include} directives and \c{import}
declarations and in what order. To recap, a module unit, both interface and
-implementation, is split into two parts: before the module declaration which
-obeys the usual or \"old\" translation unit rules and after the module
-declaration which is the module purview. Inside the module purview all
-non-exported declarations have module linkage which means their symbols are
-invisible to any other module (including the global module). With this
-understanding, consider the following module interface:
+implementation, is split into two parts: before the module declaration, called
+the global module fragment, which obeys the usual or \"old\" translation unit
+rules and after the module declaration which is the module purview. Inside the
+module purview all declarations have their symbols invisible to any other
+module (including the global module). With this understanding, consider the
+following module interface:
\
export module hello;
@@ -8049,6 +9498,8 @@ following module interface that uses an export header (which presumably sets
up symbols exporting macros) as well as an inline file:
\
+module;
+
#include <string>
export module hello;
@@ -8067,30 +9518,33 @@ A note on inline/template files: in header-based projects we could include
additional headers in those files, for example, if the included declarations
are only needed in the implementation. For the reasons just discussed, this
does not work with modules and we have to move all the includes into the
-interface file, before the module purview. On the other hand, with modules, it
-is safe to use namespace-level using-directives (for example, \c{using
-namespace std;}) in inline/template files (and, with care, even in the
-interface file).
+interface file, into the global module fragment. On the other hand, with
+modules, it is safe to use namespace-level using-directives (for example,
+\c{using namespace std;}) in inline/template files (and, with care, even in
+the interface file).
What about imports, where should we import other modules? Again, to recap,
unlike a header inclusion, an \c{import} declaration only makes exported names
-visible without redeclaring them. As result, in module implementation
-units, it doesn't really matter where we place imports, in or out of the
-module purview. There are, however, two differences when it comes to module
-interface units: only imports in the purview are visible to implementation
-units and we can only re-export an imported module from the purview.
+visible without redeclaring them. As result, in module implementation units,
+it doesn't really matter where we place imports, in the module purview or the
+global module fragment. There are, however, two differences when it comes to
+module interface units: only imports in the purview are visible to
+implementation units and we can only re-export an imported module from the
+purview.
The guideline is then for interface units to import in the module purview
unless there is a good reason not to make the import visible to the
implementation units. And for implementation units to always import in the
-purview for consistency. For example:
+purview for simplicity. For example:
\
+module;
+
#include <cassert>
export module hello;
-import std.core;
+import std;
#include <libhello/export.hxx>
@@ -8108,6 +9562,8 @@ unit template:
\
// Module interface unit.
+module; // Start of global module fragment.
+
<header includes>
export module <name>; // Start of module purview.
@@ -8126,6 +9582,8 @@ As well as the module implementation unit template:
\
// Module implementation unit.
+module; // Start of global module fragment.
+
<header includes>
module <name>; // Start of module purview.
@@ -8139,7 +9597,8 @@ Let's now discuss module naming. Module names are in a separate \"name plane\"
and do not collide with namespace, type, or function names. Also, as mentioned
earlier, the standard does not assign a hierarchical meaning to module names
though it is customary to assume module \c{hello.core} is a submodule of
-\c{hello} and importing the latter also imports the former.
+\c{hello} and, unless stated explicitly otherwise, importing the latter also
+imports the former.
It is important to choose good names for public modules (that is, modules
packaged into libraries and used by a wide range of consumers) since changing
@@ -8150,7 +9609,7 @@ worth coming up with a consistent naming scheme here as well.
The general guideline is to start names of public modules with the library's
namespace name followed by a name describing the module's functionality. In
particular, if a module is dedicated to a single class (or, more generally,
-has a single primary entity), then it makes sense to use its name as the
+has a single primary entity), then it makes sense to use that name as the
module name's last component.
As a concrete example, consider \c{libbutl} (the \c{build2} utility library):
@@ -8163,27 +9622,25 @@ the \c{butl::string_parser} namespace with the corresponding module called
When is it a good idea to re-export a module? The two straightforward cases
are when we are building an aggregate module out of submodules, for example,
-\c{xml} out of \c{xml.parser} and \c{xml.serializer}, or when one module
-extends or supersedes another, for example, as \c{std.core} extends
-\c{std.fundamental}. It is also clear that there is no need to re-export a
+\c{json} out of \c{json.parser} and \c{json.serializer}, or when one module
+extends or supersedes another, for example, as \c{json.parser} extends
+\c{json.types}. It is also clear that there is no need to re-export a
module that we only use in the implementation. The case when we use a module
in our interface is, however, a lot less clear cut.
But before considering the last case in more detail, let's understand the
issue with re-export. In other words, why not simply re-export any module we
import in our interface? In essence, re-export implicitly injects another
-module import anywhere our module is imported. If we re-export \c{std.core}
+module import anywhere our module is imported. If we re-export \c{std}
then consumers of our module will also automatically \"see\" all the names
-exported by \c{std.core}. They can then start using names from \c{std} without
-explicitly importing \c{std.core} and everything will compile until one day
+exported by \c{std}. They can then start using names from \c{std} without
+explicitly importing \c{std} and everything will compile until one day
they no longer need to import our module or we no longer need to import
-\c{std.core}. In a sense, re-export becomes part of our interface and it is
+\c{std}. In a sense, re-export becomes part of our interface and it is
generally good design to keep interfaces minimal.
And so, at the outset, the guideline is then to only re-export the minimum
-necessary. This, by the way, is the reason why it may make sense to divide
-\c{std.core} into submodules such as \c{std.core.string}, \c{std.core.vector},
-etc.
+necessary.
Let's now discuss a few concrete examples to get a sense of when re-export
might or might not be appropriate. Unfortunately, there does not seem to be a
@@ -8195,17 +9652,16 @@ interface:
\
export module hello;
-import std.core;
+import std;
export namespace hello
{
- void say (const std::string&);
+ std::string format_hello (const std::string&);
}
\
-Should we re-export \c{std.core} (or, \c{std.core.string}) in this case? Most
-likely not. If consumers of our module want to use \c{std::string} in order to
-pass an argument to our function, then it is natural to expect them to
+Should we re-export \c{std} in this case? Most likely not. If consumers of our
+module want to refer to \c{std::string}, then it is natural to expect them to
explicitly import the necessary module. In a sense, this is analogous to
scoping: nobody expects to be able to use just \c{string} (without \c{std::})
because of \c{using namespace hello;}.
@@ -8219,7 +9675,7 @@ Let's now consider a more interesting case (inspired by real events):
\
export module small_vector;
-import std.core;
+import std;
template <typename T, std::size_t N>
export class small_vector: public std::vector<T, ...>
@@ -8247,10 +9703,10 @@ implementation re-uses the comparison operators provided by \c{std::vector}
(via implicit to-base conversion) but they aren't visible.
There is a palpable difference between the two cases: the first merely uses
-\c{std.core} interface while the second is \i{based on} and, in a sense,
-\i{extends} it which feels like a stronger relationship. Re-exporting
-\c{std.core} (or, better yet, \c{std.core.vector}, should it become available)
-does not seem unreasonable.
+\c{std} interface while the second is \i{based on} and, in a sense,
+\i{extends} it which feels like a stronger relationship. Re-exporting \c{std}
+(or, better yet, \c{std.vector}, if it were available) seems less
+unreasonable.
Note also that there is no re-export of headers nor header inclusion
visibility in the implementation units. Specifically, in the previous example,
@@ -8264,113 +9720,17 @@ incur some development overhead compared to the old, headers-only approach.
\h2#cxx-modules-existing|Modularizing Existing Code|
The aim of this section is to provide practical guidelines to modularizing
-existing codebases as well as supporting the dual, header/module interface for
-backwards-compatibility.
+existing codebases.
Predictably, a well modularized (in the general sense) set of headers makes
conversion to C++ modules easier. Inclusion cycles will be particularly hard
to deal with (C++ modules do not allow circular interface dependencies).
-Furthermore, as we will see below, if you plan to provide the dual
-header/module interface, then having a one-to-one header to module mapping
-will simplify this task. As a result, it may make sense to spend some time
-cleaning and re-organizing your headers prior to attempting modularization.
-
-Let's first discuss why the modularization approach illustrated by the
-following example does not generally work:
-
-\
-export module hello;
-
-export
-{
-#include \"hello.hxx\"
-}
-\
-
-There are several issue that usually make this unworkable. Firstly, the header
-we are trying to export most likely includes other headers. For example, our
-\c{hello.hxx} may include \c{<string>} and we have already discussed why
-including it in the module purview, let alone exporting its names, is a bad
-idea. Secondly, the included header may declare more names than what should be
-exported, for example, some implementation details. In fact, it may declare
-names with internal linkage (uncommon for headers but not impossible) which
-are illegal to export. Finally, the header may define macros which will no
-longer be visible to the consumers.
-
-Sometimes, however, this can be the only approach available (for example, if
-trying to non-intrusively modularize a third-party library). It is possible to
-work around the first issue by \i{pre-including} outside of the module purview
-headers that should not be exported. Here we rely on the fact that the second
-inclusion of the same header will be ignored. For example:
+Having a one-to-one header to module mapping will simplify this task. As a
+result, it may make sense to spend some time cleaning and re-organizing your
+headers prior to attempting modularization.
-\
-#include <string> // Pre-include to suppress inclusion below.
-
-export module hello;
-
-export
-{
-#include \"hello.hxx\"
-}
-\
-
-Needless to say this approach is very brittle and usually requires that you
-place all the inter-related headers into a single module. As a result, its use
-is best limited to exploratory modularization and early prototyping.
-
-When starting modularization of a codebase there are two decisions we have to
-make at the outset: the level of the C++ modules support we can assume and the
-level of backwards compatibility we need to provide.
-
-The two modules support levels we distinguish are just modules and modules
-with the modularized standard library. The choice we have to make then is
-whether to support the standard library only as headers, only as modules, or
-both. Note that some compiler/standard library combinations may not be usable
-in some of these modes.
-
-The possible backwards compatibility levels are \i{modules-only} (consumption
-via headers is no longer supported), \i{modules-or-headers} (consumption
-either via headers or modules), and \i{modules-and-headers} (as the previous
-case but with support for consuming a library built with modules via headers
-and vice versa).
-
-What kind of situations call for the last level? We may need to continue
-offering the library as headers if we have a large number of existing
-consumers that cannot possibly be all modularized at once (or even ever). So
-the situation we may end up in is a mixture of consumers trying to use the
-same build of our library with some of them using modules and some \-
-headers. The case where we may want to consume a library built with headers
-via modules is not as far fetched as it may seem: the library might have been
-built with an older version of the compiler (for example, it was installed
-from a distribution's package) while the consumer is being built with a
-compiler version that supports modules. Note also that as discussed earlier
-the modules ownership semantics supports both kinds of such \"cross-usage\".
-
-Generally, compiler implementations do not support mixing inclusion and
-importation of the same entities in the same translation unit. This makes
-migration tricky if you plan to use the modularized standard library because
-of its pervasive use. There are two plausible strategies to handling this
-aspect of migration: If you are planning to consume the standard library
-exclusively as modules, then it may make sense to first change your entire
-codebase to do that. Simply replace all the standard library header inclusions
-with importation of the relevant \c{std.*} modules.
-
-The alternative strategy is to first complete the modularization of our entire
-project (as discussed next) while continuing consuming the standard library as
-headers. Once this is done, we can normally switch to using the modularized
-standard library quite easily. The reason for waiting until the complete
-modularization is to eliminate header inclusions between components which
-would often result in conflicting styles of the standard library consumption.
-
-Note also that due to the lack of header re-export and include visibility
-support discussed earlier, it may make perfect sense to only support the
-modularized standard library when modules are enabled even when providing
-backwards compatibility with headers. In fact, if all the compiler/standard
-library implementations that your project caters to support the modularized
-standard library, then there is little sense not to impose such a restriction.
-
-The overall strategy for modularizing our own components is to identify and
-modularize inter-dependent sets of headers one at a time starting from the
+The recommended strategy for modularizing our own components is to identify
+and modularize inter-dependent sets of headers one at a time starting from the
lower-level components. This way any newly modularized set will only depend on
the already modularized ones. After converting each set we can switch its
consumers to using imports keeping our entire project buildable and usable.
@@ -8384,402 +9744,86 @@ example, it's not uncommon to end up importing the module in its
implementation unit which is not something that all the compilers can handle
gracefully.
-Let's now explore how we can provide the various levels of backwards
-compatibility discussed above. Here we rely on two feature test macros to
-determine the available modules support level: \c{__cpp_modules} (modules are
-available) and \c{__cpp_lib_modules} (standard library modules are available,
-assumes \c{__cpp_modules} is also defined).
-
-If backwards compatibility is not necessary (the \i{modules-only} level), then
-we can use the module interface and implementation unit templates presented
-earlier and follow the above guidelines. If we continue consuming the standard
-library as headers, then we don't need to change anything in this area. If we
-only want to support the modularized standard library, then we simply replace
-the standard library header inclusions with the corresponding module
-imports. If we want to support both ways, then we can use the following
-templates. The module interface unit template:
-
-\
-// C includes, if any.
-
-#ifndef __cpp_lib_modules
-<std includes>
-#endif
-
-// Other includes, if any.
-
-export module <name>;
-
-#ifdef __cpp_lib_modules
-<std imports>
-#endif
-
-<module interface>
-\
-
-The module implementation unit template:
-
-\
-// C includes, if any.
-
-#ifndef __cpp_lib_modules
-<std includes>
-
-<extra std includes>
-#endif
-
-// Other includes, if any.
-
-module <name>;
-
-#ifdef __cpp_lib_modules
-<extra std imports> // Only additional to interface.
-#endif
-
-<module implementation>
-\
-
-For example:
-
-\
-// hello.mxx (module interface)
-
-#ifndef __cpp_lib_modules
-#include <string>
-#endif
-
-export module hello;
-
-#ifdef __cpp_lib_modules
-import std.core;
-#endif
-
-export void say_hello (const std::string& name);
-\
-
-\
-// hello.cxx (module implementation)
-
-#ifndef __cpp_lib_modules
-#include <string>
-
-#include <iostream>
-#endif
-
-module hello;
-
-#ifdef __cpp_lib_modules
-import std.io;
-#endif
-
-using namespace std;
-
-void say_hello (const string& n)
-{
- cout << \"Hello, \" << n << '!' << endl;
-}
-\
-
-If we need support for symbol exporting in this setup (that is, we are
-building a library and need to support Windows), then we can use the
-\c{__symexport} mechanism discussed earlier, for example:
-
-\
-// hello.mxx (module interface)
-
-...
-
-export __symexport void say_hello (const std::string& name);
-\
-
-The consumer code in the \i{modules-only} setup is straightforward: they
-simply import the desired modules.
-
-To support consumption via headers when modules are unavailable (the
-\i{modules-or-headers} level) we can use the following setup. Here we also
-support the dual header/modules consumption for the standard library (if this
-is not required, replace \c{#ifndef __cpp_lib_modules} with \c{#ifndef
-__cpp_modules} and remove \c{#ifdef __cpp_lib_modules}). The module interface
-unit template:
-
-\
-#ifndef __cpp_modules
-#pragma once
-#endif
-
-// C includes, if any.
-
-#ifndef __cpp_lib_modules
-<std includes>
-#endif
-
-// Other includes, if any.
-
-#ifdef __cpp_modules
-export module <name>;
-
-#ifdef __cpp_lib_modules
-<std imports>
-#endif
-#endif
-
-<module interface>
-\
-
-The module implementation unit template:
-
-\
-#ifndef __cpp_modules
-#include <module interface file>
-#endif
-
-// C includes, if any.
-
-#ifndef __cpp_lib_modules
-<std includes>
-
-<extra std includes>
-#endif
-
-// Other includes, if any
-
-#ifdef __cpp_modules
-module <name>;
-
-#ifdef __cpp_lib_modules
-<extra std imports> // Only additional to interface.
-#endif
-#endif
-
-<module implementation>
-\
-
-Notice the need to repeat \c{<std includes>} in the implementation file due to
-the lack of include visibility discussed above. This is necessary when modules
-are enabled but the standard library is not modularized since in this case the
-implementation does not \"see\" any of the headers included in the interface.
-
-Besides these templates we will most likely also need an export header that
-appropriately defines a module export macro depending on whether modules are
-used or not. This is also the place where we can handle symbol exporting. For
-example, here is what it could look like for our \c{libhello} library:
-
-\
-// export.hxx (module and symbol export)
-
-#pragma once
+If our module needs to \"export\" macros then the recommended approach is to
+simply provide an additional header that the consumer includes. While it might
+be tempting to also wrap the module import into this header, some may prefer
+to explicitly import the module and include the header, especially if the
+macros may not be needed by all consumers. This way we can also keep the
+header macro-only which means it can be included freely, in or out of module
+purviews.
-#ifdef __cpp_modules
-# define LIBHELLO_MODEXPORT export
-#else
-# define LIBHELLO_MODEXPORT
-#endif
-#if defined(LIBHELLO_SHARED_BUILD)
-# ifdef _WIN32
-# define LIBHELLO_SYMEXPORT __declspec(dllexport)
-# else
-# define LIBHELLO_SYMEXPORT
-# endif
-#elif defined(LIBHELLO_SHARED)
-# ifdef _WIN32
-# define LIBHELLO_SYMEXPORT __declspec(dllimport)
-# else
-# define LIBHELLO_SYMEXPORT
-# endif
-#else
-# define LIBHELLO_SYMEXPORT
-#endif
-\
+\h#cxx-objcxx|Objective-C++ Compilation|
-And this is the module that uses it and provides the dual header/module
-support:
+The \c{cxx} module provides the \c{cxx.objcxx} submodule which can be loaded
+in order to register the \c{mm{\}} target type and enable Objective-C++
+compilation in the \c{C++} compile rule. Note that \c{cxx.objcxx} must be
+loaded after the \c{cxx} module and while the \c{mm{\}} target type is
+registered unconditionally, compilation is only enabled if the C++ compiler
+supports Objective-C++ for the target platform. Typical usage:
\
-// hello.mxx (module interface)
-
-#ifndef __cpp_modules
-#pragma once
-#endif
-
-#ifndef __cpp_lib_modules
-#include <string>
-#endif
-
-#ifdef __cpp_modules
-export module hello;
-
-#ifdef __cpp_lib_modules
-import std.core;
-#endif
-#endif
-
-#include <libhello/export.hxx>
-
-LIBHELLO_MODEXPORT namespace hello
-{
- LIBHELLO_SYMEXPORT void say (const std::string& name);
-}
+# root.build
+#
+using cxx
+using cxx.objcxx
\
\
-// hello.cxx (module implementation)
-
-#ifndef __cpp_modules
-#include <libhello/hello.mxx>
-#endif
-
-#ifndef __cpp_lib_modules
-#include <string>
-
-#include <iostream>
-#endif
-
-#ifdef __cpp_modules
-module hello;
-
-#ifdef __cpp_lib_modules
-import std.io;
-#endif
-#endif
-
-using namespace std;
-
-namespace hello
-{
- void say (const string& n)
- {
- cout << \"Hello, \" << n << '!' << endl;
- }
-}
+# buildfile
+#
+lib{hello}: {hxx cxx}{*}
+lib{hello}: mm{*}: include = ($cxx.target.class == 'macos')
\
-The consumer code in the \i{modules-or-headers} setup has to use either
-inclusion or importation depending on the modules support availability, for
-example:
+Note also that while there is support for linking Objective-C++ executables
+and libraries, this is done using the C++ compiler driver and no attempt is
+made to automatically link any necessary Objective-C runtime library (such as
+\c{-lobjc}).
-\
-#ifdef __cpp_modules
-import hello;
-#else
-#include <libhello/hello.mxx>
-#endif
-\
-Predictably, the final backwards compatibility level (\i{modules-and-headers})
-is the most onerous to support. Here existing consumers have to continue
-working with the modularized version of our library which means we have to
-retain all the existing header files. We also cannot assume that just because
-modules are available they are used (a consumer may still prefer headers),
-which means we cannot rely on (only) the \c{__cpp_modules} and
-\c{__cpp_lib_modules} macros to make the decisions.
+\h#cxx-predefs|C++ Compiler Predefined Macro Extraction|
-One way to arrange this is to retain the headers and adjust them according to
-the \i{modules-or-headers} template but with one important difference: instead
-of using the standard module macros we use our custom ones (and we can also
-have unconditional \c{#pragma once}). For example:
+The \c{cxx} module provides the \c{cxx.predefs} submodule which can be loaded
+in order to register a rule that generates a C++ header with predefined
+compiler macros. Note that the \c{cxx.predefs} module must be loaded after the
+\c{cxx} module and the rule will only match with an explicit rule
+hint. Typical usage:
\
-// hello.hxx (module header)
-
-#pragma once
-
-#ifndef LIBHELLO_LIB_MODULES
-#include <string>
-#endif
-
-#ifdef LIBHELLO_MODULES
-export module hello;
-
-#ifdef LIBHELLO_LIB_MODULES
-import std.core;
-#endif
-#endif
-
-#include <libhello/export.hxx>
-
-LIBHELLO_MODEXPORT namespace hello
-{
- LIBHELLO_SYMEXPORT void say (const std::string& name);
-}
+# root.build
+#
+using cxx
+using cxx.predefs
\
-Now if this header is included (for example, by an existing consumer) then
-none of the \c{LIBHELLO_*MODULES} macros will be defined and the header will
-act as, well, a plain old header. Note that we will also need to make the
-equivalent change in the export header.
-
-We also provide the module interface files which appropriately define the two
-custom macros and then simply includes the corresponding headers:
-
\
-// hello.mxx (module interface)
-
-#ifdef __cpp_modules
-#define LIBHELLO_MODULES
-#endif
-
-#ifdef __cpp_lib_modules
-#define LIBHELLO_LIB_MODULES
-#endif
-
-#include <libhello/hello.hxx>
+# buildfile
+#
+[rule_hint=cxx.predefs] hxx{predefs}:
\
-The module implementation unit can remain unchanged. In particular, we
-continue including \c{hello.mxx} if modules support is unavailable. However,
-if you find the use of different macros in the header and source files
-confusing, then instead it can be adjusted as follows (note also that now we
-are including \c{hello.hxx}):
+Note also that the MSVC compiler only supports the predefined macro extraction
+starting from Visual Studio 2019 (16.0; \c{cl.exe} version 19.20). If support
+for earlier versions is required, then you will need to provide a fallback
+implementation appropriate for your project. For example:
\
-// hello.cxx (module implementation)
-
-#ifdef __cpp_modules
-#define LIBHELLO_MODULES
-#endif
-
-#ifdef __cpp_lib_modules
-#define LIBHELLO_LIB_MODULES
-#endif
-
-#ifndef LIBHELLO_MODULES
-#include <libhello/hello.hxx>
-#endif
-
-#ifndef LIBHELLO_LIB_MODULES
-#include <string>
-
-#include <iostream>
-#endif
-
-#ifdef LIBHELLO_MODULES
-module hello;
-
-#ifdef LIBHELLO_LIB_MODULES
-import std.io;
-#endif
-#endif
+[rule_hint=cxx.predefs] hxx{predefs}:
+% update
+if ($cxx.id == 'msvc' && \
+ ($cxx.version.major < 19 || \
+ ($cxx.version.major == 19 && $cxx.version.minor < 20)))
+{{
+ diag c++-predefs $>
-...
+ cat <<EOF >$path($>)
+ #define _WIN32
+ #define __cplusplus 201402L
+ EOF
+}}
\
-In this case it may also make sense to factor the \c{LIBHELLO_*MODULES} macro
-definitions into a common header.
-
-In the \i{modules-and-headers} setup the existing consumers that would like to
-continue using headers don't require any changes. And for those that would
-like to use modules if available the arrangement is the same as for the
-\i{modules-or-headers} compatibility level.
-
-If our module needs to \"export\" macros then the recommended approach is to
-simply provide an additional header that the consumer includes. While it might
-be tempting to also wrap the module import into this header, some may prefer
-to explicitly import the module and include the header, especially if the
-macros may not be needed by all consumers. This way we can also keep the
-header macro-only which means it can be included freely, in or out of module
-purviews.
-
\h1#module-in|\c{in} Module|
@@ -9090,11 +10134,11 @@ by searching in the \c{PATH} environment variable.
By convention, \c{bash} module libraries should use the \c{lib} name prefix,
for example, \c{libhello}. If there is also a native library (that is, one
written in C/C++) that provides the same functionality (or the \c{bash}
-library is a language binding for said library), then it is customary to add
-the \c{.bash} extension to the \c{bash} library name, for example,
+library is a language binding for the said library), then it is customary to
+add the \c{.bash} extension to the \c{bash} library name, for example,
\c{libhello.bash}. Note that in this case the top-level subdirectory within
-the project is expected to be called without the \c{bash} extension,
-for example, \c{libhello}.
+the project is expected to be called without the \c{bash} extension, for
+example, \c{libhello}.
Modules can be \i{private} or \i{public}. Private modules are implementation
details of a specific project and are not expected to be imported from other
@@ -9141,4 +10185,498 @@ corresponding \c{in{\}} and one or more \c{bash{\}} prerequisites as well as
\c{bash{\}} targets that have the corresponding \c{in{\}} prerequisite (if you
need to preprocess a script that does not depend on any modules, you can use
the \c{in} module's rule).
+
+
+\h1#json-dump|Appendix A \- JSON Dump Format|
+
+This appendix describes the machine-readable, JSON-based build system state
+dump format that can be requested with the \c{--dump-format=json-v0.1} build
+system driver option (see \l{b(1)} for details).
+
+The format is specified in terms of the serialized representation of C++
+\c{struct} instances. See \l{b.xhtml#json-output JSON OUTPUT} for details on
+the overall properties of this format and the semantics of the \c{struct}
+serialization.
+
+\N|This format is currently unstable (thus the temporary \c{-v0.1} suffix)
+and may be changed in ways other than as described in \l{b.xhtml#json-output
+JSON OUTPUT}. In case of such changes the format version will be incremented
+to allow detecting incompatibilities but no support for older versions is
+guaranteed.|
+
+The build system state can be dumped after the load phase (\c{--dump=load}),
+once the build state has been loaded, and/or after the match phase
+(\c{--dump=match}), after rules have been matched to targets to execute the
+desired action. The JSON format differs depending on after which phase it is
+produced. After the load phase the format aims to describe the
+action-independent state, essentially as specified in the \c{buildfiles}.
+While after the match phase it aims to describe the state for executing the
+specified action, as determined by the rules that have been matched. The
+former state would be more appropriate, for example, for an IDE that tries to
+use \c{buildfiles} as project files. While the latter state could be used to
+determine the actual build graph for a certain action, for example, in order
+to infer which executable targets are considered tests by the \c{test}
+operation.
+
+While it's possible to dump the build state as a byproduct of executing an
+action (for example, performing an update), it's often desirable to only dump
+the build state and do it as quickly as possible. For such cases the
+recommended option combinations are as follows (see the \c{--load-only} and
+\c{--match-only} documentation for details):
+
+\
+$ b --load-only --dump=load --dump-format=json-v0.1 .../dir/
+
+$ b --match-only --dump=match --dump-format=json-v0.1 .../dir/
+$ b --match-only --dump=match --dump-format=json-v0.1 .../dir/type{name}
+\
+
+\N|Note that a match dump for a large project can produce a large amount of
+data, especially for the \c{update} operation (tens and even hundreds of
+megabytes is not uncommon). To reduce this size it is possible to limit the
+dump to specific scopes and/or targets with the \c{--dump-scope} and
+\c{--dump-target} options.|
+
+The complete dump (that is, not of a specific scope or target) is a tree of
+nested scope objects (see \l{#intro-dirs-scopes Output Directories and Scopes}
+for background). The scope object has the serialized representation of the
+following C++ \c{struct} \c{scope}. It is the same for both load and match
+dumps except for the type of the \c{targets} member:
+
+\
+struct scope
+{
+ string out_path;
+ optional<string> src_path;
+
+ vector<variable> variables; // Non-type/pattern scope variables.
+
+ vector<scope> scopes; // Immediate children.
+
+ vector<loaded_target|matched_target> targets;
+};
+\
+
+For example (parts of the output are omitted for brevity):
+
+\N|The actual output is produced unindented to reduce the size.|
+
+\
+$ cd /tmp
+$ bdep new hello
+$ cd hello
+$ bdep new -C @gcc cc
+$ b --load-only --dump=load --dump-format=json-v0.1
+{
+ \"out_path\": \"\",
+ \"variables\": [ ... ],
+ \"scopes\": [
+ {
+ \"out_path\": \"/tmp/hello-gcc\",
+ \"variables\": [ ... ],
+ \"scopes\": [
+ {
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello\",
+ \"variables\": [ ... ],
+ \"scopes\": [
+ {
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello/hello\",
+ \"variables\": [ ... ],
+ \"targets\": [ ... ]
+ }
+ ],
+ \"targets\": [ ... ]
+ }
+ ],
+ \"targets\": [ ... ]
+ }
+ ]
+}
+\
+
+The \c{out_path} member is relative to the parent scope. It is empty for the
+special global scope, which is the root of the tree. The \c{src_path} member
+is absent if it is the same as \c{out_path} (in source build or scope outside
+of project).
+
+\N|For the match dump, targets that have not been matched for the specified
+action are omitted.|
+
+In the load dump, the target object has the serialized representation of the
+following C++ \c{struct} \c{loaded_target}:
+
+\
+struct loaded_target
+{
+ string name; // Relative quoted/qualified name.
+ string display_name; // Relative display name.
+ string type; // Target type.
+ optional<string> group; // Absolute quoted/qualified group target.
+
+ vector<variable> variables; // Target variables.
+
+ vector<prerequisite> prerequisites;
+};
+\
+
+For example (continuing with the previous \c{hello} setup):
+
+\
+{
+ \"out_path\": \"\",
+ \"scopes\": [
+ {
+ \"out_path\": \"/tmp/hello-gcc\",
+ \"scopes\": [
+ {
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello\",
+ \"scopes\": [
+ {
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello/hello\",
+ \"targets\": [
+ {
+ \"name\": \"exe{hello}\",
+ \"display_name\": \"exe{hello}\",
+ \"type\": \"exe\",
+ \"prerequisites\": [
+ {
+ \"name\": \"cxx{hello}\",
+ \"type\": \"cxx\"
+ },
+ {
+ \"name\": \"testscript{testscript}\",
+ \"type\": \"testscript\"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+\
+
+The target \c{name} member is the target name that is qualified with the
+extension (if applicable and known) and, if required, is quoted so that it can
+be passed back to the build system driver on the command line. The
+\c{display_name} member is unqualified and unquoted. Note that both the target
+\c{name} and \c{display_name} members are normally relative to the containing
+scope (if any).
+
+The prerequisite object has the serialized representation of the following C++
+\c{struct} \c{prerequisite}:
+
+\
+struct prerequisite
+{
+ string name; // Quoted/qualified name.
+ string type;
+ vector<variable> variables; // Prerequisite variables.
+};
+\
+
+The prerequisite \c{name} member is normally relative to the containing scope.
+
+In the match dump, the target object has the serialized representation of the
+following C++ \c{struct} \c{matched_target}:
+
+\
+struct matched_target
+{
+ string name;
+ string display_name;
+ string type;
+ optional<string> group;
+
+ optional<path> path; // Absent if not path target, not assigned.
+
+ vector<variable> variables;
+
+ optional<operation_state> outer_operation; // null if not matched.
+ operation_state inner_operation; // null if not matched.
+};
+\
+
+For example (outer scopes removed for brevity):
+
+\
+$ b --match-only --dump=match --dump-format=json-v0.1
+{
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello/hello\",
+ \"targets\": [
+ {
+ \"name\": \"/tmp/hello/hello/cxx{hello.cxx}@./\",
+ \"display_name\": \"/tmp/hello/hello/cxx{hello}@./\",
+ \"type\": \"cxx\",
+ \"path\": \"/tmp/hello/hello/hello.cxx\",
+ \"inner_operation\": {
+ \"rule\": \"build.file\",
+ \"state\": \"unchanged\"
+ }
+ },
+ {
+ \"name\": \"obje{hello.o}\",
+ \"display_name\": \"obje{hello}\",
+ \"type\": \"obje\",
+ \"group\": \"/tmp/hello-gcc/hello/hello/obj{hello}\",
+ \"path\": \"/tmp/hello-gcc/hello/hello/hello.o\",
+ \"inner_operation\": {
+ \"rule\": \"cxx.compile\",
+ \"prerequisite_targets\": [
+ {
+ \"name\": \"/tmp/hello/hello/cxx{hello.cxx}@./\",
+ \"type\": \"cxx\"
+ },
+ {
+ \"name\": \"/usr/include/c++/12/h{iostream.}\",
+ \"type\": \"h\"
+ },
+ ...
+ ]
+ }
+ },
+ {
+ \"name\": \"exe{hello.}\",
+ \"display_name\": \"exe{hello}\",
+ \"type\": \"exe\",
+ \"path\": \"/tmp/hello-gcc/hello/hello/hello\",
+ \"inner_operation\": {
+ \"rule\": \"cxx.link\",
+ \"prerequisite_targets\": [
+ {
+ \"name\": \"/tmp/hello-gcc/hello/hello/obje{hello.o}\",
+ \"type\": \"obje\"
+ }
+ ]
+ }
+ }
+ ]
+}
+\
+
+The first four members in \c{matched_target} have the same semantics as in
+\c{loaded_target}.
+
+The \c{outer_operation} member is only present if the action has an outer
+operation. For example, when performing \c{update-for-test}, \c{test} is the
+outer operation while \c{update} is the inner operation.
+
+The operation state object has the serialized representation of the following
+C++ \c{struct} \c{operation_state}:
+
+\
+struct operation_state
+{
+ string rule; // null if direct recipe match.
+
+ optional<string> state; // One of unchanged|changed|group.
+
+ vector<variable> variables; // Rule variables.
+
+ vector<prerequisite_target> prerequisite_targets;
+};
+\
+
+The \c{rule} member is the matched rule name. The \c{state} member is the
+target state, if known after match. The \c{prerequisite_targets} array is a
+subset of prerequisites resolved to targets that are in effect for this
+action. The matched rule may add additional targets, for example, dynamically
+extracted additional dependencies, like \c{/usr/include/c++/12/h{iostream.\}}
+in the above listing.
+
+The prerequisite target object has the serialized representation of the
+following C++ \c{struct} \c{prerequisite_target}:
+
+\
+struct prerequisite_target
+{
+ string name; // Absolute quoted/qualified target name.
+ string type;
+ bool adhoc;
+};
+\
+
+The \c{variables} array in the scope, target, prerequisite, and prerequisite
+target objects contains scope, target, prerequisite, and rule variables,
+respectively.
+
+The variable object has the serialized representation of the following C++
+\c{struct} \c{variable}:
+
+\
+struct variable
+{
+ string name;
+ optional<string> type;
+ json_value value; // null|boolean|number|string|object|array
+};
+\
+
+For example:
+
+\
+{
+ \"out_path\": \"\",
+ \"variables\": [
+ {
+ \"name\": \"build.show_progress\",
+ \"type\": \"bool\",
+ \"value\": true
+ },
+ {
+ \"name\": \"build.verbosity\",
+ \"type\": \"uint64\",
+ \"value\": 1
+ },
+ ...
+ ],
+ \"scopes\": [
+ {
+ \"out_path\": \"/tmp/hello-gcc\",
+ \"scopes\": [
+ {
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello\",
+ \"scopes\": [
+ {
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello/hello\",
+ \"variables\": [
+ {
+ \"name\": \"out_base\",
+ \"type\": \"dir_path\",
+ \"value\": \"/tmp/hello-gcc/hello/hello\"
+ },
+ {
+ \"name\": \"src_base\",
+ \"type\": \"dir_path\",
+ \"value\": \"/tmp/hello/hello\"
+ },
+ {
+ \"name\": \"cxx.poptions\",
+ \"type\": \"strings\",
+ \"value\": [
+ \"-I/tmp/hello-gcc/hello\",
+ \"-I/tmp/hello\"
+ ]
+ },
+ {
+ \"name\": \"libs\",
+ \"value\": \"/tmp/hello-gcc/libhello/libhello/lib{hello}\"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+\
+
+The \c{type} member is absent if the variable value is untyped.
+
+The \c{value} member contains the variable value in a suitable JSON
+representation. Specifically:
+
+\ul|
+
+\li|\c{null} values are represented as JSON \c{null}.|
+
+\li|\c{bool} values are represented as JSON \c{boolean}.|
+
+\li|\c{int64} and \c{uint64} values are represented as JSON \c{number}.|
+
+\li|\c{string}, \c{path}, \c{dir_path} values are represented as JSON
+ \c{string}.|
+
+\li|Untyped simple name values are represented as JSON \c{string}.|
+
+\li|Pairs of above values are represented as JSON objects with the \c{first}
+ and \c{second} members corresponding to the pair elements.|
+
+\li|Untyped complex name values are serialized as target names and represented
+ as JSON \c{string}.|
+
+\li|Containers of above values are represented as JSON arrays corresponding to
+ the container elements.|
+
+\li|An empty value is represented as an empty JSON object if it's a typed
+ pair, as an empty JSON array if it's a typed container or is untyped, and
+ as an empty string otherwise.||
+
+One expected use-case for the match dump is to determine the set of targets
+for which a given action is applicable. For example, we may want to determine
+all the executables in a project that can be tested with the \c{test}
+operation in order to present this list to the user in an IDE plugin or
+some such. To further illuminate the problem, consider the following
+\c{buildfile} which declares a number of executable targets, some are
+tests and some are not:
+
+\
+exe{hello1}: ... testscript # Test because of testscript prerequisite.
+
+exe{hello2}: test = true # Test because of test=true.
+
+exe{hello3}: ... testscript # Not a test because of test=false.
+{
+ test = false
+}
+\
+
+As can be seen, trying to infer this information is not straightforward and
+doing so manually by examining prerequisites, variables, etc., while possible,
+will be complex and likely brittle. Instead, the recommended approach is to
+use the match dump and base the decision on the \c{state} target object
+member. Specifically, a rule which matched the target but determined that
+nothing needs to be done for this target, returns the special \c{noop}
+recipe. The \c{build2} core recognizes this situation and sets such target's
+state to \c{unchanged} during match. Here is what the match dump will look
+like for the above three executables:
+
+\
+$ b --match-only --dump=match --dump-format=json-v0.1 test
+{
+ \"out_path\": \"hello\",
+ \"src_path\": \"/tmp/hello/hello\",
+ \"targets\": [
+ {
+ \"name\": \"exe{hello1.}\",
+ \"display_name\": \"exe{hello1}\",
+ \"type\": \"exe\",
+ \"path\": \"/tmp/hello-gcc/hello/hello/hello1\",
+ \"inner_operation\": {
+ \"rule\": \"test\"
+ }
+ },
+ {
+ \"name\": \"exe{hello2.}\",
+ \"display_name\": \"exe{hello2}\",
+ \"type\": \"exe\",
+ \"path\": \"/tmp/hello-gcc/hello/hello/hello2\",
+ \"inner_operation\": {
+ \"rule\": \"test\"
+ }
+ },
+ {
+ \"name\": \"exe{hello3}\",
+ \"display_name\": \"exe{hello3}\",
+ \"type\": \"exe\",
+ \"inner_operation\": {
+ \"rule\": \"test\",
+ \"state\": \"unchanged\"
+ }
+ }
+ ]
+}
+\
+
"
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 817837b..c539903 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -622,15 +622,27 @@ By convention, the testscript file should be called either \c{testscript} if
you only have one or have the \c{.testscript} extension, for example,
\c{basics.testscript}. The \c{test} module registers the \c{testscript{\}}
target type to be used for testscript files. We don't have to use explicit
-target type for the \c{testscript} file.
+target type for the \c{testscript} file. For example:
+
+\
+exe{hello}: testscript{basics advanced}
+\
A testscript prerequisite can be specified for any target. For example, if
-our directory contains a bunch of shell scripts that we want to test together,
+our directory contains a bunch of executables that we want to test together,
then it makes sense to specify the testscript prerequisite for the directory
target:
\
-./: testscript{basics}
+./: testscript
+\
+
+Similarly, the same testscript can be used to test multiple targets. For
+example:
+
+\
+exe{hello}: testscript{basics advanced}
+exe{hello-lite}: testscript{basics}
\
During variable lookup if a variable is not found in one of the testscript
@@ -744,6 +756,65 @@ as expected.
\N|The \c{test.redirects}, \c{test.cleanups}, and \c{$*} variables are of the
special \c{cmdline} type, see \l{#lexical Lexical Structure} for details.|
+The special \c{test.*} variables make it fairly easy to arrange the testing of
+a single executable. What if we need to run multiple executables from a single
+testscript file? For example, we may have a pair of executables, such as
+\c{reader} and \c{writer}, that must be tested together. Or we may have a
+number of test executables that all require a common setup, for example,
+cryptographic key generation, which we would like not to repeating for each
+test. While it is possible to achieve this with target-specific variables
+similar to \c{test}, things will be less automatic. In particular, there
+will be no automatic translation of target names to paths and we will have
+to do it manually. For example:
+
+\
+# buildfile
+
+./: exe{reader}: cxx{reader} ...
+./: exe{writer}: cxx{writer} ...
+
+./: testscript
+{
+ reader = exe{reader}
+ writer = exe{writer}
+}
+\
+
+\
+# testscript
+
+# Translate targets to paths.
+#
+reader = $path($reader)
+writer = $path($writer)
+
+: pipe
+:
+$writer | $reader
+
+: file
+:
+$writer output;
+$reader output
+\
+
+\N|Strictly speaking, for local executables, there is no need to pass the
+target names from \c{buildfile} to \c{testscript} and instead we could just
+list them literally in \c{testscript}. In particular, this could be an
+attractive approach if we have a large number of such executables. For
+example:
+
+\
+# testscript
+
+$path(exe{test1}) : test1
+$path(exe{test2}) : test2
+$path(exe{test3}) : test3
+...
+\
+
+|
+
Another pre-defined variable is \c{test.target}. It is used to specify the
test target platform when cross-testing (for example, when running Windows
test on Linux under Wine). Normally, you would set it in your
@@ -967,6 +1038,9 @@ the teardown commands are executed sequentially and in the order specified.
Again, if any of them fail, the group execution is terminated and the group is
considered to have failed.
+\N|Currently, the only way to run several executables serially is to place
+them into a single compound test. See \l{#syntax-test Test} for details.|
+
As an example, consider the following version of \c{basics.testscript}:
\
@@ -1418,6 +1492,7 @@ while potentially spanning several physical lines. The \c{-line} suffix
here signifies a \i{logical line}, for example, a command line plus its
here-document fragments.
+
\h#syntax-grammar|Grammar|
The complete grammar of the Testscript language is presented next with the
@@ -1474,33 +1549,58 @@ test:
+(variable-line|command-like)
variable-like:
- variable-line|variable-if
+ variable-line|variable-flow
variable-line:
<variable-name> ('='|'+='|'=+') value-attributes? <value> ';'?
value-attributes: '[' <key-value-pairs> ']'
+variable-flow:
+ variable-if|variable-for|variable-while
+
variable-if:
('if'|'if!') command-line
- variable-if-body
+ variable-flow-body
*variable-elif
?variable-else
- 'end'
+ 'end' ';'?
variable-elif:
('elif'|'elif!') command-line
- variable-if-body
+ variable-flow-body
variable-else:
'else'
- variable-if-body
+ variable-flow-body
-variable-if-body:
+variable-flow-body:
*variable-like
+variable-for:
+ variable-for-args|variable-for-stream
+
+variable-for-args:
+ 'for' <variable-name> element-attributes? ':' \
+ value-attributes? <value>
+ variable-flow-body
+ 'end' ';'?
+
+element-attributes: value-attributes
+
+variable-for-stream:
+ (command-pipe '|')? \
+ 'for' (<opt>|stdin)* <variable-name> element-attributes? (stdin)*
+ variable-flow-body
+ 'end' ';'?
+
+variable-while:
+ 'while' command-line
+ variable-flow-body
+ 'end' ';'?
+
command-like:
- command-line|command-if
+ command-line|command-flow
command-line: command-expr (';'|(':' <text>))?
*here-document
@@ -1513,24 +1613,47 @@ command: <path>(' '+(<arg>|redirect|cleanup))* command-exit?
command-exit: ('=='|'!=') <exit-status>
+command-flow:
+ command-if|command-for|command-while
+
command-if:
('if'|'if!') command-line
- command-if-body
+ command-flow-body
*command-elif
?command-else
'end' (';'|(':' <text>))?
command-elif:
('elif'|'elif!') command-line
- command-if-body
+ command-flow-body
command-else:
'else'
- command-if-body
+ command-flow-body
-command-if-body:
+command-flow-body:
*(variable-line|command-like)
+command-for:
+ command-for-args|command-for-stream
+
+command-for-args:
+ 'for' <variable-name> element-attributes? ':' \
+ value-attributes? <value>
+ command-flow-body
+ 'end' (';'|(':' <text>))?
+
+command-for-stream:
+ (command-pipe '|')? \
+ 'for' (<opt>|stdin)* <variable-name> element-attributes? (stdin)*
+ command-flow-body
+ 'end' (';'|(':' <text>))?
+
+command-while:
+ 'while' command-line
+ command-flow-body
+ 'end' (';'|(':' <text>))?
+
redirect: stdin|stdout|stderr
stdin: '0'?(in-redirect)
@@ -1563,6 +1686,12 @@ description:
+(':' <text>)
\
+Note that the only purpose of having separate (from the command flow control
+constructs) variable-only flow control constructs is to remove the error-prone
+requirement of having to specify \c{+} and \c{-} prefixes in group
+setup/teardown.
+
+
\h#syntax-script|Script|
\
@@ -1573,6 +1702,7 @@ script:
A testscript file is an implicit group scope (see \l{#model Model and
Execution} for details).
+
\h#syntax-scope|Scope|
\
@@ -1622,6 +1752,7 @@ the scopes in an \c{if-else} chain are alternative implementations of the same
group/test (thus the single description). If at least one of them is a group
scope, then all the others are treated as groups as well.
+
\h#syntax-directive|Directive|
\
@@ -1635,7 +1766,7 @@ variable is assigned. You can, however, use variables assigned in the
buildfile. For example:
\
-include common-$(cxx.target.class).testscript
+.include common-$(cxx.target.class).testscript
\
\h2#syntax-directive-include|Include|
@@ -1654,6 +1785,7 @@ this scope should not be included again. The implementation is not required to
handle links when determining if two paths are to the same file. Relative
paths are assumed to be relative to the including testscript file.
+
\h#syntax-setup-teardown|Setup and Teardown|
\
@@ -1667,11 +1799,12 @@ setup-line: '+' command-like
tdown-line: '-' command-like
\
-Note that variable assignments (including \c{variable-if}) do not use the
+Note that variable assignments (including \c{variable-flow}) do not use the
\c{'+'} and \c{'-'} prefixes. A standalone (not part of a test) variable
assignment is automatically treated as a setup if no tests have yet been
encountered in this scope and as a teardown otherwise.
+
\h#syntax-test|Test|
\
@@ -1690,11 +1823,16 @@ cat <'verbose = true' >=$conf;
test1 $conf
\
+\N|As discussed in \l{#model Model and Execution}, tests are executed in
+parallel. Currently, the only way to run several executables serially is to
+place them into a single compound test.|
+
+
\h#syntax-variable|Variable|
\
variable-like:
- variable-line|variable-if
+ variable-line|variable-flow
variable-line:
<variable-name> ('='|'+='|'=+') value-attributes? <value> ';'?
@@ -1713,25 +1851,26 @@ echo $args # foo bar fox baz
The value can only be followed by \c{;} inside a test to signal the test
continuation.
+
\h#syntax-variable-if|Variable-If|
\
variable-if:
('if'|'if!') command-line
- variable-if-body
+ variable-flow-body
*variable-elif
?variable-else
- 'end'
+ 'end' ';'?
variable-elif:
('elif'|'elif!') command-line
- variable-if-body
+ variable-flow-body
variable-else:
'else'
- variable-if-body
+ variable-flow-body
-variable-if-body:
+variable-flow-body:
*variable-like
\
@@ -1755,15 +1894,90 @@ with a ternary operator is often more concise:
slash = ($cxx.target.class == 'windows' ? \\\\ : /)
\
-Note also that the only purpose of having a separate (from \c{command-if})
-variable-only if-block is to remove the error-prone requirement of having to
-specify \c{+} and \c{-} prefixes in group setup/teardown.
+
+\h#syntax-variable-for|Variable-For|
+
+\
+variable-for:
+ variable-for-args|variable-for-stream
+
+variable-for-args:
+ 'for' <variable-name> element-attributes? ':' \
+ value-attributes? <value>
+ variable-flow-body
+ 'end' ';'?
+
+variable-for-stream:
+ (command-pipe '|')? \
+ 'for' (<opt>|stdin)* <variable-name> element-attributes? (stdin)*
+ variable-flow-body
+ 'end' ';'?
+
+variable-flow-body:
+ *variable-like
+\
+
+A group of variables can be set in a loop while iterating over elements of a
+list. The iteration semantics is the same as in \c{command-for}. For example:
+
+\
+uvalues =
+for v: $values
+ uvalues += $string.ucase($v)
+end
+\
+
+Another example:
+
+\
+uvalues =
+cat values.txt | for -n v
+ uvalues += $string.ucase($v)
+end
+\
+
+Or using the \c{stdin} redirect:
+
+\
+uvalues =
+for -n v <=values.txt
+ uvalues += $string.ucase($v)
+end
+\
+
+
+\h#syntax-variable-while|Variable-While|
+
+\
+variable-while:
+ 'while' command-line
+ variable-flow-body
+ 'end' ';'?
+
+variable-flow-body:
+ *variable-like
+\
+
+A group of variables can be set in a loop while the condition evaluates to
+\c{true}. The condition \c{command-line} semantics is the same as in
+\c{scope-if}. For example:
+
+\
+uvalues =
+i = [uint64] 0
+n = $size($values)
+while ($i != $n)
+ uvalues += $string.ucase($values[$i])
+ i += 1
+end
+\
+
\h#syntax-command|Command|
\
command-like:
- command-line|command-if
+ command-line|command-flow
command-line: command-expr (';'|(':' <text>))?
*here-document
@@ -1778,7 +1992,7 @@ command-exit: ('=='|'!=') <exit-status>
\
A command line is a command expression. If it appears directly (as opposed to
-inside \c{command-if}) in a test, then it can be followed by \c{;} to signal
+inside \c{command-flow}) in a test, then it can be followed by \c{;} to signal
the test continuation or by \c{:} and the trailing description.
A command expression can combine several command pipes with logical AND and OR
@@ -1803,25 +2017,26 @@ to succeed (0 exit code). The logical result of executing a command is
therefore a boolean value which is used in the higher-level constructs (pipe
and expression).
+
\h#syntax-command-if|Command-If|
\
command-if:
('if'|'if!') command-line
- command-if-body
+ command-flow-body
*command-elif
?command-else
'end' (';'|(':' <text>))?
command-elif:
('elif'|'elif!') command-line
- command-if-body
+ command-flow-body
command-else:
'else'
- command-if-body
+ command-flow-body
-command-if-body:
+command-flow-body:
*(variable-line|command-like)
\
@@ -1841,6 +2056,108 @@ end;
test1 $foo
\
+
+\h#syntax-command-for|Command-For|
+
+\
+command-for:
+ command-for-args|command-for-stream
+
+command-for-args:
+ 'for' <variable-name> element-attributes? ':' \
+ value-attributes? <value>
+ command-flow-body
+ 'end' (';'|(':' <text>))?
+
+command-for-stream:
+ (command-pipe '|')? \
+ 'for' (<opt>|stdin)* <variable-name> element-attributes? (stdin)*
+ command-flow-body
+ 'end' (';'|(':' <text>))?
+
+command-flow-body:
+ *(variable-line|command-like)
+\
+
+A group of commands can be executed in a loop while iterating over elements of
+a list and setting the specified variable (called \i{loop variable}) to the
+corresponding element on each iteration. At the end of the iteration the loop
+variable contains the value of the last element, if any. Note that in a
+compound test, commands inside \c{command-for} must not end with
+\c{;}. Rather, \c{;} may follow \c{end}.
+
+The \c{for} loop has two forms: In the first form the list is specified as
+arguments. Similar to the \c{for} loop in the Buildfile language, it can
+contain variable expansions, function calls, evaluation contexts, and/or
+literal values. For example:
+
+\
+for v: $values
+ test1 $v
+end;
+test2
+\
+
+In the second form the list is read from the \c{stdin} input. The input data
+is split into elements either at whitespaces (default) or newlines, which can
+be controlled with the \c{-n|--newline} and \c{-w|--whitespace} options.
+Overall, this form supports the same set of options as the \l{#builtins-set
+\c{set}} pseudo-builtin. For example:
+
+\
+cat values.txt | for -n v
+ test1 $v
+end
+\
+
+Or using the \c{stdin} redirect:
+
+\
+for -n v <=values.txt
+ test1 $v
+end
+\
+
+Both forms can include value attributes enclosed in \c{[]} to be applied to
+each element, again similar to the \l{#builtins-set \c{set}} pseudo-builtin.
+
+
+\h#syntax-command-while|Command-While|
+
+\
+command-while:
+ 'while' command-line
+ command-flow-body
+ 'end' (';'|(':' <text>))?
+
+command-flow-body:
+ *(variable-line|command-like)
+\
+
+A group of commands can be executed in a loop while a condition evaluates to
+\c{true}. The condition \c{command-line} semantics is the same as in
+\c{scope-if}. Note that in a compound test, commands inside \c{command-while}
+must not end with \c{;}. Rather, \c{;} may follow \c{end}. For example:
+
+\
+i = [uint64] 0;
+n = $size($values);
+while ($i != $n)
+ test1 ($values[$i])
+ i += 1
+end;
+test2
+\
+
+Another example:
+
+\
+while test -f $file
+ test1 $file
+end
+\
+
+
\h#syntax-redirect|Redirect|
\
@@ -1969,6 +2286,7 @@ Similar to the input redirects, an output here-document redirect must be
specified literally on the command line. See \l{#syntax-here-document Here
Document} for details.
+
\h#syntax-here-document|Here-Document|
\
@@ -2459,6 +2777,11 @@ env - --unset=FOO -- $*
Terminate the command if it fails to complete within the specified number
of seconds. See also the \l{#builtins-timeout \c{timeout}} builtin.|
+\li|\n\c{-s|--timeout-success}
+
+ Assume the command terminated due to the timeout specified with the
+ \c{-t|--timeout} option to have succeeded.|
+
\li|\n\c{-c|--cwd <dir>}
Change the command's working directory.|
@@ -2531,6 +2854,56 @@ false
Do nothing and terminate normally with the 1 exit code (indicating failure).
+\h#builtins-find|\c{find}|
+
+\
+find <start-path>... [<expression>]
+\
+
+Search for filesystem entries in a filesystem hierarchy. Traverse filesystem
+hierarchies from each \i{start-path} specified on the command line, evaluate
+for each filesystem entry the boolean \i{expression} consisting of the
+options-like arguments called \i{primaries}, and print the filesystem entry
+path if it evaluates to \c{true}, one path per line. The primaries are
+combined into the expression with an implicit logical AND operator. The empty
+expression always evaluates to \c{true}.
+
+Note that the implementation deviates from POSIX in a number of ways. It only
+supports a small subset of primaries and doesn't support compound expressions,
+negations, logical OR and (explicit) AND operators, and the \c{-type} primary
+values other than \c{f}, \c{d}, and \c{l}. It, however, supports the
+\c{-mindepth} and \c{-maxdepth} primaries which are not specified by POSIX but
+are supported by the major \c{find} utility implementations.
+
+The following primaries are supported:
+
+\dl|
+
+\li|\n\c{-name <pattern>}
+
+ Evaluates to \c{true} if a filesystem entry base name matches the specified
+ wildcard pattern.|
+
+\li|\n\c{-type <type>}
+
+ Evaluates to \c{true} if a filesystem entry type matches the specified type:
+ \c{f} for a regular file, \c{d} for a directory, and \c{l} for a symbolic
+ link.|
+
+\li|\n\c{-mindepth <depth>}
+
+ Evaluates to \c{true} if a filesystem entry directory level is not less than
+ the specified depth. The level of the \i{start-path} entries specified on
+ the command line is 0.|
+
+\li|\n\c{-maxdepth <depth>}
+
+ Evaluates to \c{true} if a filesystem entry directory level is not greater
+ than the specified depth. The level of the \i{start-path} entries specified
+ on the command line is 0. Note that the implementation is smart enough not
+ to traverse a directory when the maximum depth is reached.||
+
+
\h#builtins-ln|\c{ln}|
\
@@ -2778,7 +3151,7 @@ are supported.
\h#builtins-set|\c{set}|
\
-set [-e] [-n|-w] [<attr>] <var>
+set [-e] [-n|-w] <var> [<attr>]
\
Set variable from the \c{stdin} input.
@@ -2815,7 +3188,7 @@ If the \i{attr} argument is specified, then it must contain a list of value
attributes enclosed in \c{[]}, for example:
\
-sed -e 's/foo/bar/' input | set [string] x
+sed -e 's/foo/bar/' input | set x [string]
\
Note that this is also the only way to set a variable with a computed name,
@@ -2823,7 +3196,7 @@ for example:
\
foo = FOO
-set [null] $foo <-
+set $foo [null] <-
\
\dl|