diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2022-07-11 11:45:28 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2022-07-18 11:23:40 +0200 |
commit | 68069bf5fd906f548dbc12909254e8b17263b151 (patch) | |
tree | db8300c755116f8613e92c6c519f01945c3e2b20 /doc | |
parent | 334ff0a070031eac2e08839aa4c97666941bc653 (diff) |
Document new depends value format and functionality
Diffstat (limited to 'doc')
-rw-r--r-- | doc/manual.cli | 883 |
1 files changed, 821 insertions, 62 deletions
diff --git a/doc/manual.cli b/doc/manual.cli index 0ef08cf..02b65fc 100644 --- a/doc/manual.cli +++ b/doc/manual.cli @@ -384,6 +384,350 @@ are treated the same): || +\h1#package-skeleton|Package Build System Skeleton| + +There are situations where \c{bpkg} may need to evaluate \c{buildfile} +expressions and fragments before committing to a particular version of the +package and therefore before actually unpacking anything. For example, +\c{bpkg} may need to evaluate a condition in the conditional dependency or it +may need to negotiate a configuration among several dependents of a package +which requires it to know this package's configuration variable types and +default values. + +To solve this chicken and egg of a problem, \c{bpkg} includes a minimal subset +of the build system files along with the package's standard metadata (name, +version, etc) into the repository metadata (\l{#manifest-package-list-pkg +\c{packages.manifest}}). This subset is called the package build system +skeleton, or just package skeleton for short, and includes the +\c{build/bootstrap.build} and \c{build/root.build} files (or their alternative +naming scheme variants) as well as any files that may be sources by +\c{root.build}. + +The inclusion of \c{build/bootstrap.build} and \c{build/root.build} (if +present) as well as any \c{build/config/*.build} (or their alternative naming +scheme variants) is automatic. However, if \c{root.build} sources any files +other than \c{build/config/*.build}, then they must be specified explicitly in +the package manifest using the \l{#manifest-package-build-file \c{build-file}} +value. + +Inside these buildfiles the skeleton load can be distinguished from normal +load by examining the \c{build.mode} variable, which is set to \c{skeleton} +during the skeleton load. In particular, this variable must be used to omit +loading of build system modules that are neither built-in nor standard +pre-installed and which are therefore listed as a package dependencies. Such +modules are not yet available during the skeleton load. For example: + +\ +# root.build + +using cxx # Ok, built-in module. +using autoconf # Ok, standard pre-installed module. + +if ($build.mode != 'skeleton') + using hello +\ + +The \c{build.mode} variable can also be used to omit parts of \c{root.build} +that are expensive to evaluate and which are only necessary during the actual +build. Here is a realistic example: + +\ +# root.build + +... + +using cxx + +# Determine the GCC plugin directory. But omit doing it during the +# skeleton load. +# +if ($build.mode != 'skeleton') +{ + if ($cxx.id != 'gcc') + fail 'this project can only be built with GCC' + + # If plugin support is disabled, then -print-file-name will print + # the name we have passed (the real plugin directory will always + # be absolute). + # + plugin_dir = [dir_path] \ + $process.run($cxx.path -print-file-name=plugin) + + if (\"$plugin_dir\" == plugin) + fail \"$recall($cxx.path) does not support plugins\" + + plugin_dir = $normalize($plugin_dir) +} +\ + + +\h1#dep-config-negotiation|Dependency Configuration Negotiation| + +In \c{bpkg}, a dependent package may specify a desired configuration for a +dependency package. Because there could be multiple such dependents, \c{bpkg} +needs to come up with a dependency configuration that is acceptable to all of +them. This process is called the dependency configuration negotiation. + +The desired dependency configuration is specified as part of the +\l{#manifest-package-depends \c{depends}} manifest value and can be expressed +as either a single \c{require} clause of as a pair of \c{prefer}/\c{accept} +clauses. + +The \c{require} clause is essentially a shortcut for specifying the +\c{prefer}/\c{accept} clauses where the \c{accept} condition simply verifies +all the variable values assigned in the \c{prefer} clause. It is, however, +further restricted to the common case of only setting \c{bool} variables and +only to \c{true} to allow additional optimizations during the configuration +negotiation. The remainder of this section only deals with the general +\c{prefer}/\c{accept} semantics. + +While the exact format of \c{prefer}/\c{accept} is described as part of the +\l{#manifest-package-depends \c{depends}} manifest value, for this section it +is sufficient to know that the \c{prefer} clause is an arbitrary \c{buildfile} +fragment that is expected to set one or more dependency configuration +variables to the values preferred by this dependent while the \c{accept} +clause is a \c{buildfile} eval context expression that should evaluate to +\c{true} or \c{false} indicating whether the dependency configuration values +it is evaluated on are acceptable to this dependent. For example: + +\ +libfoo ^1.0.0 +{ + # We prefer the cache but can work without it. + # We need the buffer of at least 4KB. + # + prefer + { + config.libfoo.cache = true + + config.libfoo.buffer = ($config.libfoo.buffer < 4096 \ + ? 4096 \ + : $config.libfoo.buffer) + } + + accept ($config.libfoo.buffer >= 4096) +} +\ + +The configuration negotiation algorithm can be summarized as cooperative +refinement. Specifically, whenever a \c{prefer} clause of a dependent changes +any configuration value, all other dependent's \c{prefer} clauses are +re-evaluated. This process continues until there are no more changes +(success), one of the \c{accept} clauses returned \c{false} (failure), or the +process starts \"yo-yo'ing\" between two or more configurations (failure). + +The dependents are expected to cooperate by not overriding \"better\" values +that were set by other dependents. Consider the following two \c{prefer} +clauses: + +\ +prefer +{ + config.libfoo.buffer = 4096 +} + +prefer +{ + config.libfoo.buffer = ($config.libfoo.buffer < 4096 \ + ? 4096 \ + : $config.libfoo.buffer) +} +\ + +The first version is non-cooperative and should only be used if this dependent +requires the buffer to be exactly 4KB. The second version is cooperative: it +will increase the buffer to the minimum required by this dependent but will +respect values above 4KB. + +One case where we don't need to worry about this is when setting the +configuration variable to the \"best\" possible value. One common example of +this is setting a \c{bool} configuration to \c{true}. + +With a few exceptions discussed below, a dependent must always re-set the +configuration variable, even if to the better value. For example, the +following is an incorrect attempt at the above cooperative \c{prefer} clause: + +\ +prefer +{ + if ($config.libfoo.buffer < 4096) # Incorrect. + config.libfoo.buffer = 4096 +} +\ + +The problem with the above attempt is that the default value could be greater +than 4KB, in which case \c{bpkg} will have no idea that there is a dependent +relying on this configuration value. + +Before each \c{prefer} clause re-evaluation, values that were first set by +this dependent are reset to their defaults thus allowing the dependent to +change its mind, for instance, in response to other configuration changes. +For example: + +\ +# While we have no preference about the cache, if enabled/disabled, +# we need a bigger/smaller buffer. +# +prefer +{ + min_buffer = ($config.libfoo.cache ? 8192 : 4096) + + config.libfoo.buffer = ($config.libfoo.buffer < $min_buffer \ + ? $min_buffer \ + : $config.libfoo.buffer) +} + +accept ($config.libfoo.buffer >= ($config.libfoo.cache ? 8192 : 4096)) +\ + +The interesting case to consider in the above example is when +\c{config.libfoo.cache} changes from \c{true} to \c{false}: without the reset +to defaults semantics the \c{prefer} clause would have kept the buffer at 8KB +(since it's greater than the 4KB minimum). + +\N|Currently \c{accept} is always evaluated after \c{prefer} and temporary +variables (like \c{min_buffer} in the above example) set in \c{prefer} are +visible in \c{accept}. But it's best not to rely on this in case this changes +in the future. For example, we may try harder to resolve the \"yo-yo'ing\" +case mentioned above by checking if one of the alternating configurations +are acceptable to everyone without re-evaluation. + +This is also the reason why we need a separate \c{accept} in the first place. +Plus, it allows for more advanced configuration techniques where we may need +to have an acceptance criteria but no preferences.| + +Configuration variables that are set by the dependent in the \c{prefer} clause +are visible in the subsequent clauses as well as in the subsequent \c{depends} +values of this dependent. Configuration variables that are not set, however, +are only visible until the immediately following \c{reflect} clause. For +example, in the above listing, \c{config.libfoo.cache} would still be visible +in the \c{reflect} clause if it were to follow \c{accept} but no further. As a +result, if we need to make decisions based on configuration variables that we +have no preference about, they need to be saved in the \c{prefer} clause. For +example: + +\ +depends: +\\ +libfoo ^1.0.0 +{ + # We have no preference about the cache but need to + # observe its value. + # + prefer + { + } + + accept (true) + + reflect + { + config.hello.libfoo_cache = $config.libfoo.cache + } +} +\\ + +depends: libbar ^1.0.0 ? ($config.hello.libfoo_cache) +\ + +It is possible to determine the origin of the configuration variable value +using the \c{$config.origin()} function. It returns either \c{undefined} if +the variable is undefined (only possible if it has no default value), +\c{default} if the variable has the default value from the \c{config} +directive in \c{root.build}, \c{buildfile} if the value is from a +\c{buildfile}, normally \c{config.build}, or \c{override} if the value is a +command line override (that is, user configuration). For example, this is how +we could use it if we only wanted to change the default value (notice that +it's the variable's name and not its \c{$}-expansion that we pass to +\c{$config.origin()}): + +\ +prefer +{ + config.libfoo.buffer = ( \ + $config.origin(config.libfoo.buffer) == 'default' \ + ? 4096 \ + : $config.libfoo.buffer) +} +\ + +The following sub-sections discuss a number of more advanced configuration +techniques that are based on the functionality described in this section. + + +\h#dep-config-prefer-x-accept-xy|Prefer X but Accept X or Y| + +Consider a configuration variable that is a choice between several mutually +exclusive values, for example, user interface backends that could be, say, +\c{cli}, \c{gui}, or \c{none}. In such situations it's common to prefer one +value but being able to work with some subset of them. For example, we could +prefer \c{gui} but were also able to make do with \c{cli} but not with +\c{none}. Here is how we could express such a configuration: + +\ +libfoo ^1.0.0 +{ + # We prefer `gui`, can also work with `cli` but not `none`. + # + prefer + { + config.libfoo.ui = ( \ + $config.origin(config.libfoo.ui) == 'default' || \ + ($config.liba.ui != 'gui' && $config.liba.ui != 'cli') \ + ? 'gui' \ + : $config.liba.ui) + } + + accept ($config.liba.ui == 'gui' || $config.liba.ui == 'cli') +} +\ + +\h#dep-config-use-if-enabled|Use If Enabled| + +Sometimes we may want to use a feature if it is enabled by someone else but +not enable it ourselves. For example, the feature might be expensive and our +use of it tangential, but if it's enabled anyway, then we might as well take +advantage of it. Here is how we could express such a configuration: + +\ +libfoo ^1.0.0 +{ + # Use config.libfoo.x only if enabled by someone else. + # + prefer + { + } + + accept (true) + + reflect + { + config.hello.libfoo_x = $config.libfoo.x + } +} +\ + +\h#dep-config-disable-default|Disable If Enabled by Default| + +Sometimes we may want to disable a feature that is enabled by default provided +that nobody else needs it. For example, the feature might be expensive and we +would prefer to avoid paying the cost if we are the only ones using this +dependency. Here is how we could express such a configuration: + +\ +libfoo ^1.0.0 +{ + prefer + { + if ($config.origin(config.libfoo.x) == 'default') + config.libfoo.x = false + } + + accept (true) +} +\ + + \h1#manifests|Manifests| This chapter describes the general manifest file format as well as the @@ -517,9 +861,9 @@ Expressed as a C-string, the value in the above example is: \N|Originally, the multi-line mode was entered if \c{:} after the name were -followed by \c{\\} and a newline but on the same line. While this syntax is -still recognized for backwards compatibility, it is deprecated and will be -discontinued in the future.| +immediately followed by \c{\\} and a newline but on the same line. While this +syntax is still recognized for backwards compatibility, it is deprecated and +will be discontinued in the future.| Note that in the multi-line mode we can still use newline escaping to split long lines, for example: @@ -722,8 +1066,8 @@ license: <licenses> [; <comment>] [build-warning-email]: <email> [; <comment>] [build-error-email]: <email> [; <comment>] -[depends]: [*][?] <alternatives> [; <comment>] -[requires]: [*][?] [<alternatives>] [; <comment>] +[depends]: [*] <alternatives> [; <comment>] +[requires]: [*] <alternatives> [; <comment>] [tests]: [*] <name> [<version-constraint>] [examples]: [*] <name> [<version-constraint>] @@ -1156,17 +1500,102 @@ build error notifications are sent to this email. \h2#manifest-package-depends|\c{depends}| \ -[depends]: [*][?] <alternatives> [; <comment>] +[depends]: [*] <alternatives> [; <comment>] +\ + +Single-line form: -<alternatives> = <dependency> [ '|' <dependency>]* +\ +<alternatives> = <alternative> [ '|' <alternative>]* +<alternative> = <dependencies> ['?' <enable-cond>] [<reflect-var>] +<dependencies> = <dependency> | \ + '{' <dependency> [<dependency>]* '}' [<version-constraint>] <dependency> = <name> [<version-constraint>] +<enable-cond> = '(' <buildfile-eval-expr> ')' +<reflect-var> = <config-var> '=' <value> +\ + +Multi-line form: + +\ +<alternatives> = + <alternative>[ + '|' + <alternative>]* + +<alternative> = + <dependencies> + '{' + [ + 'enable' <enable-cond> + ] + + [ + 'require' + '{' + <buildfile-fragment> + '}' + + ] | [ + + 'prefer' + '{' + <buildfile-fragment> + '}' + + 'accept' <accept-cond> + ] + + [ + 'reflect' + '{' + <buildfile-fragment> + '}' + ] + '}' + +<accept-cond> = '(' <buildfile-eval-expr> ')' +\ + +The dependency packages. The most common form of a dependency is a package +name followed by the optional version constraint. For example: + +\ +depends: libhello ^1.0.0 +\ + +See \l{#package-version-constraint Package Version Constraint} for the format +and semantics of the version constraint. Instead of a concrete value, the +version in the constraint can also be specified in terms of the dependent +package's version (that is, its \l{#manifest-package-version \c{version}} +value) using the special \c{$} value. This mechanism is primarily useful when +developing related packages that should track each other's versions exactly or +closely. For example: + +\ +name: sqlite3 +version: 3.18.2 +depends: libsqlite3 == $ +\ + +If multiple packages are specified within a single \c{depends} value, they +must be grouped with \c{{\}}. This can be useful if the packages share a +version constraint. The group constraint applies to all the packages in +the group that do not have their own constraint. For example: + +\ +depends: { libboost-any libboost-log libboost-uuid ~1.77.1 } ~1.77.0 \ -The dependency packages. If the \c{depends} value starts with \c{*}, then -it is a \i{build-time} dependency. Otherwise it is \i{run-time}. +If the \c{depends} value starts with \c{*}, then it is a \i{build-time} +dependency. Otherwise it is \i{run-time}. For example: + +\ +depends: * byacc >= 20210619 +\ \N|Most of the build-time dependencies are expected to be tools such as code -generator, so you can think of \c{*} as the executable mark printed by +generators, so you can think of \c{*} as the executable mark printed by \c{ls}. An important difference between the two kinds of dependencies is that in case of cross-compilation a build-time dependency must be built for the host machine, not the target. Build system modules are also build-time @@ -1174,87 +1603,413 @@ dependencies.| Two special build-time dependency names are recognized and checked in an ad hoc manner: \c{build2} (the \c{build2} build system) and \c{bpkg} (the -\c{build2} package manager). This allows us to specify the required build -system and package manager versions, for example: +\c{build2} package manager). This allows us to specify the minimum required +build system and package manager versions, for example: \ -depends: * build2 >= 0.6.0 -depends: * bpkg >= 0.6.0 +depends: * build2 >= 0.15.0 +depends: * bpkg >= 0.15.0 \ -Each \c{depends} value can specify multiple packages with the \i{OR} -semantics. While multiple \c{depends} values are used to specify multiple -packages with the \i{AND} semantics. A value that starts with \c{?} is a -conditional dependency. Whether such a dependency will be in effect can -only be determined at the package configuration time. It is recommended that -you provide a comment for each conditional dependency as an aid to the user. -For example: +\N|If you are developing or packaging a project that uses features from the +not yet released (staged) version of the \c{build2} toolchain, then you can +use the pre-release version in the constraint. For example: \ -depends: libz -depends: libfoo ~1.2.0 ; Only works with libfoo 1.2.*. -depends: libgnutls >= 1.2.3 | libopenssl >= 2.3.4 -depends: ? libboost-regex >= 1.52.0 ; Only if no C++11 <regex>. -depends: ? libqtcore >= 5.0.0 ; Only if GUI is enabled. +depends: * build2 >= 0.16.0- +depends: * bpkg >= 0.16.0- \ -It is recommended that you specify unconditional dependencies first with -simple (no alternatives) dependencies leading each set. +| -See \l{#package-version-constraint Package Version Constraint} for the format -and semantics of the optional version constraint. Instead of a concrete value, -the version in the constraint can also be specified in terms of the dependent -package's version (that is, its \l{#manifest-package-version \c{version}} -value) using the special \c{$} value. This mechanism is primarily useful when -developing related packages that should track each other's versions exactly or -closely. For example: +A dependency can be conditional, that is, it is only enabled if a certain +condition is met. For example: \ -name: sqlite3 -version: 3.18.2 -depends: libsqlite3 == $ +depends: libposix-getopt ^1.0.0 ? ($cxx.target.class == 'windows') +\ + +The condition after \c{?} inside \c{()} is a \c{buildfile} eval context +expression that should evaluate to \c{true} or \c{false}, as if it were +specified in the \c{buildfile} \c{if} directive (see \l{b#intro-lang-expand +Expansion and Quoting} and \l{b#intro-if-else Conditions (\c{if-else})} for +details). + +The condition expression is evaluated after loading the package build system +skeleton, that is, after loading its \c{root.build} (see \l{#package-skeleton +Package Build System Skeleton} for details). As a result, variable values set +by build system modules that are loaded in \c{root.build} as well as package's +configuration (including previously reflected; see below) or computed values +can be referenced in dependency conditions. For example, given the following +\c{root.build}: + +\ +# root.build + +... + +using cxx + +# MinGW ships POSIX <getopt.h>. +# +need_getopt = ($cxx.target.class == 'windows' && \ + $cxx.target.system != 'mingw32') + +config [bool] config.hello.regex ?= false +\ + +We could have the following conditional dependencies: + +\ +depends: libposix-getopt ^1.0.0 ? ($need_getop) ; Windows && !MinGW. +depends: libposix-regex ^1.0.0 ? ($config.hello.regex && \ + $cxx.target.class == 'windows') +\ + +The first \c{depends} value in the above example also shows the use of an +optional comment. It's a good idea to provide it if the condition is not +sufficiently self-explanatory. + +A dependency can \"reflect\" configuration variables to the subsequent +\c{depends} values and to the package configuration. This can be used to +signal whether a conditional dependency is enabled or which dependency +alternative was selected (see below). The single-line form of \c{depends} can +only reflect one configuration variable. For example: + +\ +depends: libposix-regex ^1.0.0 \ + ? ($cxx.target.class == 'windows') \ + config.hello.external_regex=true +\ + +\ +# root.build + +... + +using cxx + +config [bool] config.hello.external_regex ?= false +\ + +\ +# buildfile + +libs = + +if $config.hello.external_regex + import libs += libposix-regex%lib{posix-regex} + +exe{hello}: ... $libs +\ + +In the above example, if the \c{hello} package is built on Windows, then the +dependency on \c{libposix-regex} will be enabled and the package will be +configured with \c{config.hello.external_regex=true}. This is used in the +\c{buildfile} to decide whether to import \c{libposix-regex}. While in this +example it would have probably been easier to just duplicate the check for +Windows in the \c{buildfile} (or, better yet, factor this check to +\c{root.build} and share the result via a computed variable between +\c{manifest} and \c{buildfile}), the reflect mechanism is the only way to +communicate the selected dependency alternative (discussed next). + +\N|An attempt to set a reflected configuration variable that is overridden by +the user is an error. In a sense, configuration variables that are used to +reflect information should be treated as the package's implementation details +if the package management is involved. If, however, the package is configured +without \c{bpkg}'s involvement, then these variables could reasonably be +provided as user configuration. + +If you feel the need to allow a reflected configuration variable to also +potentially be supplied as user configuration, then it's probably a good sign +that you should turn things around: make the variable only user-configurable +and use the enable condition instead of reflect. Alternatively, you could try +to recognize and handle user overrides with the help of the +\c{$config.origin()} function discussed in \l{#dep-config-negotiation +Dependency Configuration Negotiation}.| + +While multiple \c{depends} values are used to specify multiple packages with +the \i{AND} semantics, inside \c{depends} we can specify multiple packages (or +groups of packages) with the \i{OR} semantics, called dependency +alternatives. For example: + \ +depends: libmysqlclient >= 5.0.3 | libmariadb ^10.2.2 +\ + +When selecting an alternative, \c{bpkg} only considers packages that are +either already present in the build configuration or are selected as +dependencies by other packages, picking the first alternative with a +satisfactory version constraint and an acceptable configuration. As a result, +the order of alternatives expresses a preference. If, however, this does not +yield a suitable alternative, then \c{bpkg} fails asking the user to make the +selection. + +For example, if the package with the above dependency is called \c{libhello} +and we build it in a configuration that already has both \c{libmysqlclient} +and \c{libmariadb}, then \c{bpkg} will select \c{libmysqlclient}, provided the +existing version satisfies the version constraint. If, however, there are no +existing packages in the build configuration and we attempt to build just +\c{libhello}, then \c{bpkg} will fail asking the user to pick one of the +alternatives. If we wanted to make \c{bpkg} select \c{libmariadb} we could +run: + +\ +$ bpkg build libhello ?libmariadb +\ + +\N|While \c{bpkg}'s refusal to automatically pick an alternative that would +require building a new package may at first seem unfriendly to the user, +practical experience shows that such extra user-friendliness would rarely +justifies the potential confusion that it may cause. + +Also note that it's not only the user that can pick a certain alternative but +also a dependent package. Continue with the above example, if we had \c{hello} +that depended on \c{libhello} but only supported MariaDB (or provided a +configuration variable to explicitly select the database), then we could +have the following in its \c{manifest}: + +\ +depends: libmariadb ; Select MariaDB in libhello. +depends: libhello ^1.0.0 +\ + +| + +Dependency alternatives can be combined with all the other features discussed +above: groups, conditional dependencies, and reflect. As mentioned earlier, +reflect is the only way to communicate the selection to subsequent \c{depends} +values and package configuration. For example: + +\ +depends: libmysqlclient >= 5.0.3 config.hello.db='mysql' | \ + libmariadb ^10.2.2 ? ($cxx.target.class != 'windows') \ + config.hello.db='mariadb' + +depends: libz ^1.2.1100 ? ($config.hello.db == 'mysql') +\ + +If an alternative is conditional and the condition evaluates to \c{false}, +then this alternative is not considered. If all but one alternative are +disabled due to conditions, then this becomes an ordinary dependency. If all +the alternatives are disabled due to conditions, then the entire dependency +is disabled. For example: + +\ +depends: libmysqlclient >= 5.0.3 ? ($config.hello.db == 'mysql') | \ + libmariadb ^10.2.2 ? ($config.hello.db == 'mariadb') +\ + +While there is no need to use the dependency alternatives in the above example +(since the alternatives are mutually exclusive), it makes for good +documentation of intent. + +Besides as a single line, the \c{depends} value can also be specified in a +multi-line form which, besides potentially better readability, provides +additional functionality. In the multi-line form, each dependency alternative +occupies a separate line and \c{|} can be specified either at the end of +the dependency alternative line or on a separate line. For example: + +\ +depends: +\\ +libmysqlclient >= 5.0.3 ? ($config.hello.db == 'mysql') | +libmariadb ^10.2.2 ? ($config.hello.db == 'mariadb') +\\ +\ + +A dependency alternative can be optionally followed by a block containing a +number of clauses. The \c{enable} clause is the alternative way to specify the +condition for a conditional dependency while the \c{reflect} clause is the +alternative way to specify the reflected configuration variable. The block may +also contain \c{#}-style comments, similar to \c{buildfile}. For example: + +\ +depends: +\\ +libmysqlclient >= 5.0.3 +{ + reflect + { + config.hello.db = 'mysql' + } +} +| +libmariadb ^10.2.2 +{ + # TODO: MariaDB support on Windows. + # + enable ($cxx.target.class != 'windows') + + reflect + { + config.hello.db = 'mariadb' + } +} +\\ +\ + +While the \c{enable} clause is essentially the same as its inline \c{?} +variant, the \c{reflect} clause is an arbitrary \c{buildfile} fragment that +can have more complex logic and assign multiple configuration variables. For +example: + +\ +libmariadb ^10.2.2 +{ + reflect + { + if ($cxx.target.class == 'windows') + config.hello.db = 'mariadb-windows' + else + config.hello.db = 'mariadb-posix' + } +} +\ + +The multi-line form also allows us to express our preferences and requirements +for the dependency configuration. If all we need is to set one or more +\c{bool} configuration variables to \c{true} (which usually translates to +enabling one or more features), then we can use the \c{require} clause. For +example: + +\ +libmariadb ^10.2.2 +{ + require + { + config.libmariadb.cache = true + + if ($cxx.target.class != 'windows') + config.libmariadb.tls = true + } +} +\ + +For more complex dependency configurations we use the \c{prefer} and +\c{accept} clauses. The \c{prefer} clause can set configuration variables of +any type and to any value in order to express the package's preferred +configuration while the \c{accept} condition evaluates whether any given +configuration is acceptable. For example: + +\ +libmariadb ^10.2.2 +{ + # We prefer the cache but can work without it. + # We need the buffer of at least 4KB. + # + prefer + { + config.libmariadb.cache = true + + config.libmariadb.buffer = ($config.libmariadb.buffer < 4096 \ + ? 4096 \ + : $config.libmariadb.buffer) + } + + accept ($config.libmariadb.buffer >= 4096) +} +\ + +The \c{require} and \c{prefer} clauses are arbitrary \c{buildfile} fragments +similar to \c{reflect} while the \c{accept} clause is a \c{buildfile} eval +context expression that should evaluate to \c{true} or \c{false}, similar to +\c{enable}. + +Given the \c{require} and \c{prefer}/\c{accept} clauses of all the dependents +of a particular dependency, \c{bpkg} tries to negotiates a configuration +acceptable to all of them as described in \l{#dep-config-negotiation +Dependency Configuration Negotiation}. + +All the clauses are evaluated in the specified order, that is, \c{enable}, +then \c{require} or \c{prefer}/\c{accept}, and finally \c{reflect}, with the +(negotiated, in case of \c{prefer}) configuration values set by preceding +clauses available for examination by the subsequent clauses in this +\c{depends} value as well as in all the subsequent ones. For example: + +\ +depends: +\\ +libmariadb ^10.2.2 +{ + prefer + { + config.libmariadb.cache = true + + config.libmariadb.buffer = ($config.libmariadb.buffer < 4096 \ + ? 4096 \ + : $config.libmariadb.buffer) + } + + accept ($config.libmariadb.buffer >= 4096) + + reflect + { + config.hello.buffer = $config.libmariadb.buffer + } +} +\\ + +depends: liblru ^1.0.0 ? ($config.libmariadb.cache) +\ + +The above example also highlights the difference between the +\c{require}/\c{prefer} and \c{reflect} clauses that is easy to mix up: in +\c{require}/\c{prefer} we set the dependency's while in \c{reflect} we set the +dependent's configuration variables. \h2#manifest-package-requires|\c{requires}| \ -[requires]: [*][?] [<alternatives>] [; <comment>] +[requires]: [*] <alternatives> [; <comment>] -<alternatives> = <requirement> [ '|' <requirement>]* -<requirement> = <id> | <dependency> -<id> = <name> +<alternatives> = <alternative> [ '|' <alternative>]* +<alternative> = <requirements> ['?' [<enable-cond>]] [<reflect-var>] +<requirements> = [<requirement>] | \ + '{' <requirement> [<requirement>]* '}' [<version-constraint>] +<requirement> = <name> [<version-constraint>] +<enable-cond> = '(' <buildfile-eval-expr> ')' +<reflect-var> = <config-var> '=' <value> \ The package requirements (other than other packages). Such requirements are -normally checked during package configuration by the build system and the only -purpose of capturing them in the manifest is for documentation. Similar to -\c{depends}, a value that starts with \c{*} is a build-time requirement and/or -with \c{?} is a conditional requirement. For example: +normally checked in an ad hoc way during package configuration by its +\c{buildfiles} and the primary purpose of capturing them in the manifest is +for documentation. However, there are some special requirements that are +recognized by the tooling (see below). For example: \ -requires: linux | windows | macos requires: c++11 -requires: ? ; VC 15 or later if targeting Windows. -requires: ? ; libc++ if using Clang on Mac OS. +requires: linux | windows | macos +requires: libc++ ? ($macos) ; libc++ if using Clang on Mac OS. \ -Notice that in the last two cases the id is omitted altogether with only the -comment specifying the requirement. - -Note that \c{requires} should also be used to specify dependencies on external -libraries, that is, the ones not packaged or not in the repository. In this -case it may make sense to also specify the version constraint. For example: +The format of the \c{requires} value is similar to +\l{#manifest-package-depends \c{depends}} with the following differences. The +requirement name (with or without version constraint) can mean anything (but +must still be a valid package name). Only the \c{enable} and \c{reflect} +clauses are permitted. There is a simplified syntax with either the +requirement or enable condition or both being empty and where the comment +carries all the information (and is thus mandatory). For example: \ -requires: zlib >= 1.2.0 ; Most systems already have it or get from zlib.net. +requires: ; X11 libs. +requires: ? ($windows) ; Only 64-bit. +requires: ? ; Only 64-bit if on Windows. +requires: x86_64 ? ; Only if on Windows. \ -It is recommended that you specify unconditional requirements first with -simple (no alternatives) requirements leading each set. +Note that \c{requires} can also be used to specify dependencies on system +libraries, that is, the ones not to be packaged. In this case it may make +sense to also specify the version constraint. For example: + +\ +requires: libx11 >= 1.7.2 +\ -To assist automated processing, the following pre-defined ids should be used -for the common requirements: +To assist potential future automated processing, the following pre-defined +requirement names should be used for the common requirements: \ c++98 @@ -1271,6 +2026,8 @@ posix linux macos freebsd +openbsd +netbsd windows \ @@ -1280,7 +2037,8 @@ clang[_X.Y] ; For example: clang_6, clang_3.4, clang_3.4.1 msvc[_N.U] ; For example: msvc_14, msvc_15.3 \ -The following pre-defined ids are recognized by automated build bots: +The following pre-defined requirement names are recognized by automated build +bots: \ bootstrap @@ -1500,7 +2258,8 @@ The contents of the mandatory \c{bootstrap.build} file, optional \c{root.build} file, and additional files included by \c{root.build}, or their alternative naming scheme variants (\c{bootstrap.build2}, etc). Packages with the alternative naming scheme should use the \c{*-build2} values instead of -\c{*-build}. +\c{*-build}. See \l{#package-skeleton Package Build System Skeleton} for +background. These files must reside in the package's \c{build/} subdirectory and have the \c{.build} extension (or their alternative names). They can be provided either |