Skip to content

Commit

Permalink
Make all Meson level options overridable per subproject.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpakkane committed Feb 13, 2025
1 parent ea678ed commit eaf1735
Show file tree
Hide file tree
Showing 63 changed files with 1,971 additions and 829 deletions.
37 changes: 37 additions & 0 deletions docs/markdown/Configuring-a-build-directory.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,40 @@ by invoking [`meson configure`](Commands.md#configure) with the
project source directory or the path to the root `meson.build`. In
this case, Meson will print the default values of all options similar
to the example output from above.

## Per project subproject options rewrite (Since 1.8)

A common requirement when building large projects with many
subprojects is to build some (or all) subprojects with project options
that are different from the "main project". This has been sort of
possible in a limited way but is now natively supported. Per project
options can be added, changed and removed at runtime using the command
line, in other words, without editing existing `meson.build` files.

Starting with version 1.8 you can specify per-project option settings.
These can be specified for every top level (i.e. not project) options.
Suppose you have a project that has a single subproject called
`numbercruncher` that does heavy computation. During development you
want to build that subproject with optimizations enabled but your main
project without optimizations. This can be done by specifying a custom
value to the given subproject:

meson configure -Dnumbercruncher:optimization=3

Another case might be that you want to build with errors as warnings,
but some subproject does not support it. To configure `werror` per
subproject you can do:

meson configure -Dwerror=true -Dnaughty:werror=false

You can also specify a different value on the top level project. For
example you could enable optimizations on all subprojects but not the
top level project:

meson configure -Doptimization=2 -D:optimization=0

Note the colon after the second `D`.

Subproject specific values can be removed with -U

meson configure -Usubproject:optionnname
19 changes: 19 additions & 0 deletions docs/markdown/snippets/optionrefactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Per project subproject options rewrite

You can now define per-subproject values for all shared configuration
options. As an example you might want to enable optimizations on only
one subproject:

meson configure -Dnumbercruncher:optimization=3

Subproject specific values can be removed with -U

meson configure -Unumbercruncher:optimization

This is a major change in how options are handled, and the
implementation will evolve over the next few releases of Meson. If
this change causes an error in your builds, please [report an issue on
GitHub](https://github.com/mesonbuild/meson/issues/new).

We have tried to keep backwards compatibility as much as possible, but
this may lead to some build breakage.
3 changes: 2 additions & 1 deletion mesonbuild/ast/introspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ def _str_list(node: T.Any) -> T.Optional[T.List[str]]:

def_opts = self.flatten_args(kwargs.get('default_options', []))
_project_default_options = mesonlib.stringlistify(def_opts)
self.project_default_options = cdata.create_options_dict(_project_default_options, self.subproject)
string_dict = cdata.create_options_dict(_project_default_options, self.subproject)
self.project_default_options = {OptionKey(s): v for s, v in string_dict.items()}
self.default_options.update(self.project_default_options)
self.coredata.set_default_options(self.default_options, self.subproject, self.environment)

Expand Down
47 changes: 34 additions & 13 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from .. import mlog
from ..compilers import LANGUAGES_USING_LDFLAGS, detect, lang_suffixes
from ..mesonlib import (
File, MachineChoice, MesonException, OrderedSet,
File, MachineChoice, MesonException, MesonBugException, OrderedSet,
ExecutableSerialisation, EnvironmentException,
classify_unity_sources, get_compiler_for_source
)
Expand Down Expand Up @@ -424,7 +424,7 @@ def generate_unity_files(self, target: build.BuildTarget, unity_src: str) -> T.L
abs_files: T.List[str] = []
result: T.List[mesonlib.File] = []
compsrcs = classify_unity_sources(target.compilers.values(), unity_src)
unity_size = target.get_option(OptionKey('unity_size'))
unity_size = self.get_target_option(target, 'unity_size')
assert isinstance(unity_size, int), 'for mypy'

def init_language_file(suffix: str, unity_file_number: int) -> T.TextIO:
Expand Down Expand Up @@ -878,7 +878,8 @@ def object_filename_from_source(self, target: build.BuildTarget, compiler: Compi
object_suffix = machine.get_object_suffix()
# For the TASKING compiler, in case of LTO or prelinking the object suffix has to be .mil
if compiler.get_id() == 'tasking':
if target.get_option(OptionKey('b_lto')) or (isinstance(target, build.StaticLibrary) and target.prelink):
use_lto = self.get_target_option(target, 'b_lto')
if use_lto or (isinstance(target, build.StaticLibrary) and target.prelink):
if not source.rsplit('.', 1)[1] in lang_suffixes['c']:
if isinstance(target, build.StaticLibrary) and not target.prelink:
raise EnvironmentException('Tried using MIL linking for a static library with a assembly file. This can only be done if the static library is prelinked or disable \'b_lto\'.')
Expand Down Expand Up @@ -925,10 +926,10 @@ def _determine_ext_objs(self, extobj: 'build.ExtractedObjects') -> T.List[str]:
# With unity builds, sources don't map directly to objects,
# we only support extracting all the objects in this mode,
# so just return all object files.
if extobj.target.is_unity:
if self.is_unity(extobj.target):
compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources)
sources = []
unity_size = extobj.target.get_option(OptionKey('unity_size'))
unity_size = self.get_target_option(extobj.target, 'unity_size')
assert isinstance(unity_size, int), 'for mypy'

for comp, srcs in compsrcs.items():
Expand Down Expand Up @@ -981,7 +982,7 @@ def create_msvc_pch_implementation(self, target: build.BuildTarget, lang: str, p

def target_uses_pch(self, target: build.BuildTarget) -> bool:
try:
return T.cast('bool', target.get_option(OptionKey('b_pch')))
return T.cast('bool', self.get_target_option(target, 'b_pch'))
except (KeyError, AttributeError):
return False

Expand All @@ -1007,30 +1008,29 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com
# starting from hard-coded defaults followed by build options and so on.
commands = compiler.compiler_args()

copt_proxy = target.get_options()
# First, the trivial ones that are impossible to override.
#
# Add -nostdinc/-nostdinc++ if needed; can't be overridden
commands += self.get_no_stdlib_args(target, compiler)
# Add things like /NOLOGO or -pipe; usually can't be overridden
commands += compiler.get_always_args()
# warning_level is a string, but mypy can't determine that
commands += compiler.get_warn_args(T.cast('str', target.get_option(OptionKey('warning_level'))))
commands += compiler.get_warn_args(T.cast('str', self.get_target_option(target, 'warning_level')))
# Add -Werror if werror=true is set in the build options set on the
# command-line or default_options inside project(). This only sets the
# action to be done for warnings if/when they are emitted, so it's ok
# to set it after or get_warn_args().
if target.get_option(OptionKey('werror')):
if self.get_target_option(target, 'werror'):
commands += compiler.get_werror_args()
# Add compile args for c_* or cpp_* build options set on the
# command-line or default_options inside project().
commands += compiler.get_option_compile_args(copt_proxy)
commands += compiler.get_option_compile_args(target, self.environment, target.subproject)

optimization = target.get_option(OptionKey('optimization'))
optimization = self.get_target_option(target, 'optimization')
assert isinstance(optimization, str), 'for mypy'
commands += compiler.get_optimization_args(optimization)

debug = target.get_option(OptionKey('debug'))
debug = self.get_target_option(target, 'debug')
assert isinstance(debug, bool), 'for mypy'
commands += compiler.get_debug_args(debug)

Expand Down Expand Up @@ -1755,7 +1755,7 @@ def generate_target_install(self, d: InstallData) -> None:
# TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more
# fine-grained stripping of static archives.
can_strip = not isinstance(t, build.StaticLibrary)
should_strip = can_strip and t.get_option(OptionKey('strip'))
should_strip = can_strip and self.get_target_option(t, 'strip')
assert isinstance(should_strip, bool), 'for mypy'
# Install primary build output (library/executable/jar, etc)
# Done separately because of strip/aliases/rpath
Expand Down Expand Up @@ -2092,3 +2092,24 @@ def compile_target_to_generator(self, target: build.CompileTarget) -> build.Gene
all_sources = T.cast('_ALL_SOURCES_TYPE', target.sources) + T.cast('_ALL_SOURCES_TYPE', target.generated)
return self.compiler_to_generator(target, target.compiler, all_sources,
target.output_templ, target.depends)

def is_unity(self, target: build.BuildTarget) -> bool:
if isinstance(target, build.CompileTarget):
return False
val = self.get_target_option(target, 'unity')
if val == 'on':
return True
if val == 'off':
return False
if val == 'subprojects':
return target.subproject != ''
raise MesonException(f'Internal error: invalid option type for "unity": {val}')

def get_target_option(self, target: build.BuildTarget, name: T.Union[str, OptionKey]) -> T.Union[str, int, bool, T.List[str]]:
if isinstance(name, str):
key = OptionKey(name, subproject=target.subproject)
elif isinstance(name, OptionKey):
key = name
else:
raise MesonBugException('Internal error: invalid option type.')
return self.environment.coredata.get_option_for_target(target, key)
Loading

0 comments on commit eaf1735

Please sign in to comment.