new module "sourceset" to match source file lists against configuration data
In QEMU a single set of source files is built against many different
configurations in order to generate many executable. Each executable
includes a different but overlapping subset of the source files; some
of the files are compiled separately for each output, others are
compiled just once.
Using Makefiles, this is achieved with a complicated mechanism involving
a combination of non-recursive and recursive make; Meson can do better,
but because there are hundreds of such conditional rules, it's important
to keep meson.build files brief and easy to follow. Therefore, this
commit adds a new module to satisfy this use case while preserving
Meson's declarative nature.
Configurations are mapped to a configuration_data object, and a new
"source set" object is used to store all the rules, and then retrieve
the desired set of sources together with their dependencies.
The test case shows how extract_objects can be used to satisfy both
cases, i.e. when the object files are shared across targets and when
they have to be separate. In the real-world case, a project would use
two source set objects for the two cases and then do
"executable(..., sources: ... , objects: ...)". The next commit
adds such an example.
6 years ago
|
|
|
---
|
|
|
|
short-description: Source set module
|
|
|
|
authors:
|
|
|
|
- name: Paolo Bonzini
|
|
|
|
email: pbonzini@redhat.com
|
|
|
|
years: [2019]
|
|
|
|
...
|
|
|
|
|
|
|
|
# Source set module
|
|
|
|
|
|
|
|
This module provides support for building many targets against a single set
|
|
|
|
of files; the choice of which files to include in each target depends on the
|
|
|
|
contents of a dictionary or a `configuration_data` object. The module can
|
|
|
|
be loaded with:
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
ssmod = import('sourceset')
|
|
|
|
```
|
|
|
|
|
|
|
|
A simple example of using the module looks like this:
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
ss = ssmod.source_set()
|
|
|
|
# Include main.c unconditionally
|
|
|
|
ss.add(files('main.c'))
|
|
|
|
# Include a.c if configuration key FEATURE1 is true
|
|
|
|
ss.add(when: 'FEATURE1', if_true: files('a.c'))
|
|
|
|
# Include zlib.c if the zlib dependency was found, and link zlib
|
|
|
|
# in the executable
|
|
|
|
ss.add(when: zlib, if_true: files('zlib.c'))
|
|
|
|
# many more rules here...
|
|
|
|
ssconfig = ss.apply(config)
|
|
|
|
executable('exe', sources: ssconfig.sources(),
|
|
|
|
dependencies: ssconfig.dependencies())
|
|
|
|
```
|
|
|
|
|
|
|
|
and it would be equivalent to
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
sources = files('main.c')
|
|
|
|
dependencies = []
|
|
|
|
if config['FEATURE1'] then
|
|
|
|
sources += [files('a.c')]
|
|
|
|
endif
|
|
|
|
if zlib.found() then
|
|
|
|
sources += [files('zlib.c')]
|
|
|
|
dependencies += [zlib]
|
|
|
|
endif
|
|
|
|
# many more "if"s here...
|
|
|
|
executable('exe', sources: sources, dependencies: dependencies())
|
|
|
|
```
|
|
|
|
|
|
|
|
Sourcesets can be used with a single invocation of the `apply` method,
|
|
|
|
similar to the example above, but the module is especially useful
|
|
|
|
when multiple executables are generated by applying the same rules to
|
|
|
|
many different configurations.
|
|
|
|
|
|
|
|
*Added 0.51.0*
|
|
|
|
|
|
|
|
## Functions
|
|
|
|
|
|
|
|
### `source_set()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
ssmod.source_set()
|
|
|
|
```
|
|
|
|
|
|
|
|
Create and return a new source set object.
|
|
|
|
|
|
|
|
**Returns**: a [source set][`source_set` object]
|
|
|
|
|
|
|
|
## `source_set` object
|
|
|
|
|
|
|
|
The `source_set` object provides methods to add files to a source set and
|
|
|
|
to query it. The source set becomes immutable after any method but `add`
|
|
|
|
is called.
|
|
|
|
|
|
|
|
### Methods
|
|
|
|
|
|
|
|
#### `add()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
source_set.add([when: varnames_and_deps],
|
|
|
|
[if_true: sources_and_deps],
|
|
|
|
[if_false: list_of_alt_sources])
|
|
|
|
source_set.add(sources_and_deps)
|
|
|
|
```
|
|
|
|
|
|
|
|
Add a *rule* to a source set. A rule determines the conditions under which
|
|
|
|
some source files or dependency objects are included in a build configuration.
|
|
|
|
All source files must be present in the source tree or they can be created
|
|
|
|
in the build tree via `configure_file`, `custom_target` or `generator`.
|
|
|
|
|
|
|
|
`varnames_and_deps` is a list of conditions for the rule, which can be
|
|
|
|
either strings or dependency objects (a dependency object is anything that
|
|
|
|
has a `found()` method). If *all* the strings evaluate to true and all
|
|
|
|
dependencies are found, the rule will evaluate to true; `apply()`
|
|
|
|
will then include the contents of the `if_true` keyword argument in its
|
|
|
|
result. Otherwise, that is if any of the strings in the positional
|
|
|
|
arguments evaluate to false or any dependency is not found, `apply()`
|
|
|
|
will instead use the contents of the `if_false` keyword argument.
|
|
|
|
|
|
|
|
Dependencies can also appear in `sources_and_deps`. In this case, a
|
|
|
|
missing dependency will simply be ignored and will *not* disable the rule,
|
|
|
|
similar to how the `dependencies` keyword argument works in build targets.
|
|
|
|
|
|
|
|
**Note**: It is generally better to avoid mixing source sets and disablers.
|
|
|
|
This is because disablers will cause the rule to be dropped altogether,
|
|
|
|
and the `list_of_alt_sources` would not be taken into account anymore.
|
|
|
|
|
|
|
|
#### `add_all()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
source_set.add_all(when: varnames_and_deps,
|
|
|
|
if_true: [source_set1, source_set2, ...])
|
|
|
|
source_set.add_all(source_set1, source_set2, ...)
|
|
|
|
```
|
|
|
|
|
|
|
|
Add one or more source sets to another.
|
|
|
|
|
|
|
|
For each source set listed in the arguments, `apply()` will
|
|
|
|
consider their rules only if the conditions in `varnames_and_deps` are
|
|
|
|
evaluated positively. For example, the following:
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
sources_b = ssmod.source_set()
|
|
|
|
sources_b.add(when: 'HAVE_A', if_true: 'file.c')
|
|
|
|
sources = ssmod.source_set()
|
|
|
|
sources.add_all(when: 'HAVE_B', if_true: sources_b)
|
|
|
|
```
|
|
|
|
|
|
|
|
is equivalent to:
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
sources = ssmod.source_set()
|
|
|
|
sources.add(when: ['HAVE_A', 'HAVE_B'], if_true: 'file.c')
|
|
|
|
```
|
|
|
|
|
|
|
|
#### `all_sources()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
list source_set.all_sources(...)
|
|
|
|
```
|
|
|
|
|
|
|
|
Returns a list of all sources that were placed in the source set using
|
|
|
|
`add` (including nested source sets) and that do not have a not-found
|
|
|
|
dependency. If a rule has a not-found dependency, only the `if_false`
|
|
|
|
sources are included (if any).
|
|
|
|
|
|
|
|
**Returns**: a list of file objects
|
|
|
|
|
|
|
|
#### `all_dependencies()` *(since 0.52.0)*
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
list source_set.all_dependencies(...)
|
|
|
|
```
|
|
|
|
|
|
|
|
Returns a list of all dependencies that were placed in the source set
|
|
|
|
using `add` (including nested source sets) and that were found.
|
|
|
|
|
|
|
|
**Returns**: a list of dependencies
|
|
|
|
|
new module "sourceset" to match source file lists against configuration data
In QEMU a single set of source files is built against many different
configurations in order to generate many executable. Each executable
includes a different but overlapping subset of the source files; some
of the files are compiled separately for each output, others are
compiled just once.
Using Makefiles, this is achieved with a complicated mechanism involving
a combination of non-recursive and recursive make; Meson can do better,
but because there are hundreds of such conditional rules, it's important
to keep meson.build files brief and easy to follow. Therefore, this
commit adds a new module to satisfy this use case while preserving
Meson's declarative nature.
Configurations are mapped to a configuration_data object, and a new
"source set" object is used to store all the rules, and then retrieve
the desired set of sources together with their dependencies.
The test case shows how extract_objects can be used to satisfy both
cases, i.e. when the object files are shared across targets and when
they have to be separate. In the real-world case, a project would use
two source set objects for the two cases and then do
"executable(..., sources: ... , objects: ...)". The next commit
adds such an example.
6 years ago
|
|
|
#### `apply()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
source_files source_set.apply(conf_data[, strict: false])
|
|
|
|
```
|
|
|
|
|
|
|
|
Match the source set against a dictionary or a `configuration_data` object
|
|
|
|
and return a *source configuration* object. A source configuration object
|
|
|
|
allows you to retrieve the sources and dependencies for a specific configuration.
|
|
|
|
|
|
|
|
By default, all the variables that were specified in the rules have to
|
|
|
|
be present in `conf_data`. However, in some cases the convention is
|
|
|
|
that `false` configuration symbols are absent in `conf_data`; this is
|
|
|
|
the case for example when the configuration was loaded from a Kconfig file.
|
|
|
|
In that case you can specify the `strict: false` keyword argument, which
|
|
|
|
will treat absent variables as false.
|
|
|
|
|
|
|
|
**Returns**: a [source configuration][`source_configuration` object]
|
|
|
|
|
|
|
|
## `source_configuration` object
|
|
|
|
|
|
|
|
The `source_configuration` object provides methods to query the result of an
|
|
|
|
`apply` operation on a source set.
|
|
|
|
|
|
|
|
### Methods
|
|
|
|
|
|
|
|
#### `sources()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
source_config.sources()
|
|
|
|
```
|
|
|
|
|
|
|
|
Return the source files corresponding to the applied configuration.
|
|
|
|
|
|
|
|
**Returns**: a list of file objects
|
|
|
|
|
|
|
|
#### `dependencies()`
|
|
|
|
|
|
|
|
``` meson
|
|
|
|
source_config.dependencies()
|
|
|
|
```
|
|
|
|
|
|
|
|
Return the dependencies corresponding to the applied configuration.
|
|
|
|
|
|
|
|
**Returns**: a list of dependency objects
|