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.pull/5028/head
parent
e9bd7d49bd
commit
d894c48660
26 changed files with 643 additions and 0 deletions
@ -0,0 +1,196 @@ |
|||||||
|
--- |
||||||
|
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 |
||||||
|
|
||||||
|
#### `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 |
@ -0,0 +1,8 @@ |
|||||||
|
## New `sourceset` module |
||||||
|
|
||||||
|
A new module, `sourceset`, was added to help building many binaries |
||||||
|
from the same source files. Source sets associate source files and |
||||||
|
dependencies to keys in a `configuration_data` object or a dictionary; |
||||||
|
they then take multiple `configuration_data` objects or dictionaries, |
||||||
|
and compute the set of source files and dependencies for each of those |
||||||
|
configurations. |
@ -0,0 +1,190 @@ |
|||||||
|
# Copyright 2019 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from collections import namedtuple |
||||||
|
from .. import mesonlib |
||||||
|
from ..mesonlib import listify |
||||||
|
from . import ExtensionModule |
||||||
|
from ..interpreterbase import ( |
||||||
|
noPosargs, noKwargs, permittedKwargs, |
||||||
|
InterpreterObject, MutableInterpreterObject, ObjectHolder, |
||||||
|
InterpreterException, InvalidArguments, InvalidCode, FeatureNew, |
||||||
|
) |
||||||
|
from ..interpreter import ( |
||||||
|
GeneratedListHolder, CustomTargetHolder, |
||||||
|
CustomTargetIndexHolder |
||||||
|
) |
||||||
|
|
||||||
|
SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps') |
||||||
|
SourceFiles = namedtuple('SourceFiles', 'sources dependencies') |
||||||
|
|
||||||
|
class SourceSetHolder(MutableInterpreterObject, ObjectHolder): |
||||||
|
def __init__(self, environment, subdir): |
||||||
|
MutableInterpreterObject.__init__(self) |
||||||
|
ObjectHolder.__init__(self, list()) |
||||||
|
self.environment = environment |
||||||
|
self.subdir = subdir |
||||||
|
self.frozen = False |
||||||
|
self.methods.update({ |
||||||
|
'add': self.add_method, |
||||||
|
'add_all': self.add_all_method, |
||||||
|
'all_sources': self.all_sources_method, |
||||||
|
'apply': self.apply_method, |
||||||
|
}) |
||||||
|
|
||||||
|
def check_source_files(self, arg, allow_deps): |
||||||
|
sources = [] |
||||||
|
deps = [] |
||||||
|
for x in arg: |
||||||
|
if isinstance(x, (str, mesonlib.File, |
||||||
|
GeneratedListHolder, CustomTargetHolder, |
||||||
|
CustomTargetIndexHolder)): |
||||||
|
sources.append(x) |
||||||
|
elif hasattr(x, 'found'): |
||||||
|
if not allow_deps: |
||||||
|
msg = 'Dependencies are not allowed in the if_false argument.' |
||||||
|
raise InvalidArguments(msg) |
||||||
|
deps.append(x) |
||||||
|
else: |
||||||
|
msg = 'Sources must be strings or file-like objects.' |
||||||
|
raise InvalidArguments(msg) |
||||||
|
mesonlib.check_direntry_issues(sources) |
||||||
|
return sources, deps |
||||||
|
|
||||||
|
def check_conditions(self, arg): |
||||||
|
keys = [] |
||||||
|
deps = [] |
||||||
|
for x in listify(arg): |
||||||
|
if isinstance(x, str): |
||||||
|
keys.append(x) |
||||||
|
elif hasattr(x, 'found'): |
||||||
|
deps.append(x) |
||||||
|
else: |
||||||
|
raise InvalidArguments('Conditions must be strings or dependency object') |
||||||
|
return keys, deps |
||||||
|
|
||||||
|
@permittedKwargs(['when', 'if_false', 'if_true']) |
||||||
|
def add_method(self, args, kwargs): |
||||||
|
if self.frozen: |
||||||
|
raise InvalidCode('Tried to use \'add\' after querying the source set') |
||||||
|
when = listify(kwargs.get('when', [])) |
||||||
|
if_true = listify(kwargs.get('if_true', [])) |
||||||
|
if_false = listify(kwargs.get('if_false', [])) |
||||||
|
if not when and not if_true and not if_false: |
||||||
|
if_true = args |
||||||
|
elif args: |
||||||
|
raise InterpreterException('add called with both positional and keyword arguments') |
||||||
|
keys, dependencies = self.check_conditions(when) |
||||||
|
sources, extra_deps = self.check_source_files(if_true, True) |
||||||
|
if_false, _ = self.check_source_files(if_false, False) |
||||||
|
self.held_object.append(SourceSetRule(keys, sources, if_false, [], dependencies, extra_deps)) |
||||||
|
|
||||||
|
@permittedKwargs(['when', 'if_true']) |
||||||
|
def add_all_method(self, args, kwargs): |
||||||
|
if self.frozen: |
||||||
|
raise InvalidCode('Tried to use \'add_all\' after querying the source set') |
||||||
|
when = listify(kwargs.get('when', [])) |
||||||
|
if_true = listify(kwargs.get('if_true', [])) |
||||||
|
if not when and not if_true: |
||||||
|
if_true = args |
||||||
|
elif args: |
||||||
|
raise InterpreterException('add_all called with both positional and keyword arguments') |
||||||
|
keys, dependencies = self.check_conditions(when) |
||||||
|
for s in if_true: |
||||||
|
if not isinstance(s, SourceSetHolder): |
||||||
|
raise InvalidCode('Arguments to \'add_all\' after the first must be source sets') |
||||||
|
s.frozen = True |
||||||
|
self.held_object.append(SourceSetRule(keys, [], [], if_true, dependencies, [])) |
||||||
|
|
||||||
|
def collect(self, enabled_fn, all_sources, into=None): |
||||||
|
if not into: |
||||||
|
into = SourceFiles(set(), set()) |
||||||
|
for entry in self.held_object: |
||||||
|
if all(x.found() for x in entry.dependencies) and \ |
||||||
|
all(enabled_fn(key) for key in entry.keys): |
||||||
|
into.sources.update(entry.sources) |
||||||
|
into.dependencies.update(entry.dependencies) |
||||||
|
into.dependencies.update(entry.extra_deps) |
||||||
|
for ss in entry.sourcesets: |
||||||
|
ss.collect(enabled_fn, all_sources, into) |
||||||
|
if not all_sources: |
||||||
|
continue |
||||||
|
into.sources.update(entry.if_false) |
||||||
|
return into |
||||||
|
|
||||||
|
@noKwargs |
||||||
|
@noPosargs |
||||||
|
def all_sources_method(self, args, kwargs): |
||||||
|
self.frozen = True |
||||||
|
files = self.collect(lambda x: True, True) |
||||||
|
return list(files.sources) |
||||||
|
|
||||||
|
@permittedKwargs(['strict']) |
||||||
|
def apply_method(self, args, kwargs): |
||||||
|
if len(args) != 1: |
||||||
|
raise InterpreterException('Apply takes exactly one argument') |
||||||
|
config_data = args[0] |
||||||
|
self.frozen = True |
||||||
|
strict = kwargs.get('strict', True) |
||||||
|
if isinstance(config_data, dict): |
||||||
|
def _get_from_config_data(key): |
||||||
|
if strict and key not in config_data: |
||||||
|
raise InterpreterException('Entry %s not in configuration dictionary.' % key) |
||||||
|
return config_data.get(key, False) |
||||||
|
else: |
||||||
|
config_cache = dict() |
||||||
|
|
||||||
|
def _get_from_config_data(key): |
||||||
|
nonlocal config_cache |
||||||
|
if key not in config_cache: |
||||||
|
args = [key] if strict else [key, False] |
||||||
|
config_cache[key] = config_data.get_method(args, {}) |
||||||
|
return config_cache[key] |
||||||
|
|
||||||
|
files = self.collect(_get_from_config_data, False) |
||||||
|
res = SourceFilesHolder(files) |
||||||
|
return res |
||||||
|
|
||||||
|
class SourceFilesHolder(InterpreterObject, ObjectHolder): |
||||||
|
def __init__(self, files): |
||||||
|
InterpreterObject.__init__(self) |
||||||
|
ObjectHolder.__init__(self, files) |
||||||
|
self.methods.update({ |
||||||
|
'sources': self.sources_method, |
||||||
|
'dependencies': self.dependencies_method, |
||||||
|
}) |
||||||
|
|
||||||
|
@noPosargs |
||||||
|
@noKwargs |
||||||
|
def sources_method(self, args, kwargs): |
||||||
|
return list(self.held_object.sources) |
||||||
|
|
||||||
|
@noPosargs |
||||||
|
@noKwargs |
||||||
|
def dependencies_method(self, args, kwargs): |
||||||
|
return list(self.held_object.dependencies) |
||||||
|
|
||||||
|
class SourceSetModule(ExtensionModule): |
||||||
|
@FeatureNew('SourceSet module', '0.51.0') |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
super().__init__(*args, **kwargs) |
||||||
|
self.snippets.add('source_set') |
||||||
|
|
||||||
|
@noKwargs |
||||||
|
@noPosargs |
||||||
|
def source_set(self, interpreter, state, args, kwargs): |
||||||
|
return SourceSetHolder(interpreter.environment, interpreter.subdir) |
||||||
|
|
||||||
|
def initialize(*args, **kwargs): |
||||||
|
return SourceSetModule(*args, **kwargs) |
@ -0,0 +1,8 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include "all.h" |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
if (p) abort(); |
||||||
|
f(); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
extern void f(void); |
||||||
|
extern void g(void); |
||||||
|
extern void h(void); |
||||||
|
extern void undefined(void); |
||||||
|
|
||||||
|
/* No extern here to get a common symbol */ |
||||||
|
void (*p)(void); |
@ -0,0 +1,5 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void f(void) |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void g(void) |
||||||
|
{ |
||||||
|
h(); |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
project('a', 'c') |
||||||
|
|
||||||
|
good = declare_dependency(link_with: static_library('good', 'g.c')) |
||||||
|
bad = declare_dependency(link_args: 'nonexistent.a') |
||||||
|
not_found = dependency('invalid', required: false) |
||||||
|
|
||||||
|
source_set = import('sourceset') |
||||||
|
|
||||||
|
sources = source_set.source_set() |
||||||
|
sources.add(when: 'YES', if_false: ['nope.c']) |
||||||
|
sources.add(when: 'YES1', if_true: files('a.c')) |
||||||
|
subdir('subdir') |
||||||
|
sources.add(when: 'NO', if_true: 'nope.c', if_false: ['f.c']) |
||||||
|
sources.add(when: 'NO', if_true: bad, if_false: ['f.c']) |
||||||
|
|
||||||
|
sources.add(when: 'YES2', if_true: good) |
||||||
|
|
||||||
|
# dependencies as conditions |
||||||
|
sources.add(when: not_found, if_true: 'nope.c') |
||||||
|
|
||||||
|
# test add_all |
||||||
|
sources2 = source_set.source_set() |
||||||
|
sources2.add(when: 'YES1', if_true: 'nope.c') |
||||||
|
sources.add_all(when: 'NO', if_true: sources2) |
||||||
|
|
||||||
|
# test duplicate items |
||||||
|
sources.add(when: 'YES1', if_true: files('a.c')) |
||||||
|
|
||||||
|
conf1 = configuration_data() |
||||||
|
conf1.set10('YES', true) |
||||||
|
conf1.set10('YES1', true) |
||||||
|
conf1.set10('YES2', false) |
||||||
|
conf1.set10('NO', false) |
||||||
|
result1 = sources.apply(conf1) |
||||||
|
|
||||||
|
conf2 = configuration_data() |
||||||
|
conf2.set10('YES', true) |
||||||
|
conf2.set10('YES1', false) |
||||||
|
conf2.set10('YES2', true) |
||||||
|
conf2.set10('NO', false) |
||||||
|
result2 = sources.apply(conf2) |
||||||
|
|
||||||
|
# Each target will recompile the objects |
||||||
|
executable('first', sources: result1.sources(), dependencies: result1.dependencies()) |
||||||
|
executable('second', sources: result2.sources(), dependencies: result2.dependencies()) |
||||||
|
|
||||||
|
# All target will use the same object files |
||||||
|
if meson.is_unity() |
||||||
|
message('Skipping extraction test because this is a Unity build.') |
||||||
|
else |
||||||
|
all_objs = static_library('all_objs', sources.all_sources()) |
||||||
|
executable('first_via_lib', objects: all_objs.extract_objects(result1.sources()), dependencies: result1.dependencies()) |
||||||
|
executable('second_via_lib', objects: all_objs.extract_objects(result2.sources()), dependencies: result2.dependencies()) |
||||||
|
endif |
@ -0,0 +1,3 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void (*p)(void) = undefined; |
@ -0,0 +1,13 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void h(void) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
if (p) abort(); |
||||||
|
f(); |
||||||
|
g(); |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
sources.add(when: ['YES2', good], if_true: [ files('b.c') ]) |
@ -0,0 +1,8 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include "all.h" |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
if (p) abort(); |
||||||
|
f(); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
extern void f(void); |
||||||
|
extern void g(void); |
||||||
|
extern void h(void); |
||||||
|
extern void undefined(void); |
||||||
|
|
||||||
|
/* No extern here to get a common symbol */ |
||||||
|
void (*p)(void); |
@ -0,0 +1,5 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void f(void) |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void g(void) |
||||||
|
{ |
||||||
|
h(); |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
project('a', 'c') |
||||||
|
|
||||||
|
good = declare_dependency(link_with: static_library('good', 'g.c')) |
||||||
|
bad = declare_dependency(link_args: 'nonexistent.a') |
||||||
|
not_found = dependency('invalid', required: false) |
||||||
|
|
||||||
|
source_set = import('sourceset') |
||||||
|
|
||||||
|
sources = source_set.source_set() |
||||||
|
sources.add(when: 'YES', if_false: ['nope.c']) |
||||||
|
sources.add(when: 'YES1', if_true: files('a.c')) |
||||||
|
subdir('subdir') |
||||||
|
sources.add(when: 'NO', if_true: 'nope.c', if_false: ['f.c']) |
||||||
|
sources.add(when: 'NO', if_true: bad, if_false: ['f.c']) |
||||||
|
|
||||||
|
sources.add(when: 'YES2', if_true: good) |
||||||
|
|
||||||
|
# dependencies as conditions |
||||||
|
sources.add(when: not_found, if_true: 'nope.c') |
||||||
|
|
||||||
|
# test add_all |
||||||
|
sources2 = source_set.source_set() |
||||||
|
sources2.add(when: 'YES1', if_true: 'nope.c') |
||||||
|
sources.add_all(when: 'NO', if_true: sources2) |
||||||
|
|
||||||
|
# test duplicate items |
||||||
|
sources.add(when: 'YES1', if_true: files('a.c')) |
||||||
|
|
||||||
|
conf1 = { |
||||||
|
'YES': true, |
||||||
|
'YES1': true, |
||||||
|
'YES2': false, |
||||||
|
'NO': false, |
||||||
|
} |
||||||
|
result1 = sources.apply(conf1) |
||||||
|
|
||||||
|
conf2 = { |
||||||
|
'YES': true, |
||||||
|
'YES1': false, |
||||||
|
'YES2': true, |
||||||
|
'NO': false, |
||||||
|
} |
||||||
|
result2 = sources.apply(conf2) |
||||||
|
|
||||||
|
# Each target will recompile the objects |
||||||
|
executable('first', sources: result1.sources(), dependencies: result1.dependencies()) |
||||||
|
executable('second', sources: result2.sources(), dependencies: result2.dependencies()) |
||||||
|
|
||||||
|
# All target will use the same object files |
||||||
|
if meson.is_unity() |
||||||
|
message('Skipping extraction test because this is a Unity build.') |
||||||
|
else |
||||||
|
all_objs = static_library('all_objs', sources.all_sources()) |
||||||
|
executable('first_via_lib', objects: all_objs.extract_objects(result1.sources()), dependencies: result1.dependencies()) |
||||||
|
executable('second_via_lib', objects: all_objs.extract_objects(result2.sources()), dependencies: result2.dependencies()) |
||||||
|
endif |
@ -0,0 +1,3 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void (*p)(void) = undefined; |
@ -0,0 +1,13 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void h(void) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
if (p) abort(); |
||||||
|
f(); |
||||||
|
g(); |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
sources.add(when: ['YES2', good], if_true: [ files('b.c') ]) |
@ -0,0 +1,7 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
f(); |
||||||
|
g(); |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
extern void f(void); |
||||||
|
extern void g(void); |
@ -0,0 +1,5 @@ |
|||||||
|
#! /usr/bin/env python3 |
||||||
|
|
||||||
|
import sys |
||||||
|
from shutil import copyfile |
||||||
|
copyfile(*sys.argv[1:]) |
@ -0,0 +1,5 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void f(void) |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
#include "all.h" |
||||||
|
|
||||||
|
void g(void) |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
# Try using sourceset with various kinds of generated sources |
||||||
|
|
||||||
|
project('a', 'c') |
||||||
|
|
||||||
|
cp = find_program('cp.py') |
||||||
|
|
||||||
|
source_set = import('sourceset') |
||||||
|
sources = source_set.source_set() |
||||||
|
|
||||||
|
a_c = custom_target('gen-custom-target', |
||||||
|
input: 'a.c', output: 'out_a.c', |
||||||
|
command: [cp, '@INPUT@', '@OUTPUT@']) |
||||||
|
sources.add(when: 'YES', if_true: a_c) |
||||||
|
sources.add(when: 'YES', if_true: a_c[0]) |
||||||
|
|
||||||
|
f_c = configure_file(input: 'f.c', output: 'out_f.c', copy: true) |
||||||
|
sources.add(when: 'YES', if_true: f_c) |
||||||
|
sources.add(when: 'YES', if_true: f_c) |
||||||
|
|
||||||
|
gen = generator(cp, output: 'out_@PLAINNAME@', arguments: ['@INPUT@', '@OUTPUT@']) |
||||||
|
g_c = gen.process(files('g.c')) |
||||||
|
sources.add(when: 'YES', if_true: g_c) |
||||||
|
sources.add(when: 'YES', if_true: g_c) |
||||||
|
|
||||||
|
conf1 = { 'YES': true, } |
||||||
|
result1 = sources.apply(conf1) |
||||||
|
|
||||||
|
executable('first', sources: result1.sources(), dependencies: result1.dependencies()) |
Loading…
Reference in new issue