aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2020-05-31 22:27:14 +0200
committerDaniel Mensinger <daniel@mensinger-ka.de>2020-06-05 11:45:05 +0200
commita2f94ca18b237ccaf072474a1a4405a996e84395 (patch)
tree749b4894faec0b23d22bad79acbb04a45bad4976
parent2e30afb23b0f34308c747b1e799b66a2f992d398 (diff)
downloadmeson-a2f94ca18b237ccaf072474a1a4405a996e84395.zip
meson-a2f94ca18b237ccaf072474a1a4405a996e84395.tar.gz
meson-a2f94ca18b237ccaf072474a1a4405a996e84395.tar.bz2
cmake: Add more advanced subproject configuration options
This is done with the new cmake subprojects options object that is similar to the already exisiting configuration data object. It is consumed by the new `options` kwarg of the cmake.subproject function.
-rw-r--r--mesonbuild/cmake/__init__.py5
-rw-r--r--mesonbuild/cmake/common.py95
-rw-r--r--mesonbuild/cmake/interpreter.py21
-rw-r--r--mesonbuild/interpreter.py12
-rw-r--r--mesonbuild/modules/cmake.py110
5 files changed, 223 insertions, 20 deletions
diff --git a/mesonbuild/cmake/__init__.py b/mesonbuild/cmake/__init__.py
index 01cc3f9..db7aefd 100644
--- a/mesonbuild/cmake/__init__.py
+++ b/mesonbuild/cmake/__init__.py
@@ -24,11 +24,14 @@ __all__ = [
'CMakeTarget',
'CMakeTraceLine',
'CMakeTraceParser',
+ 'SingleTargetOptions',
+ 'TargetOptions',
'parse_generator_expressions',
'language_map',
+ 'cmake_defines_to_args',
]
-from .common import CMakeException
+from .common import CMakeException, SingleTargetOptions, TargetOptions, cmake_defines_to_args
from .client import CMakeClient
from .executor import CMakeExecutor
from .fileapi import CMakeFileAPI
diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py
index e7da0d7..4510b5d 100644
--- a/mesonbuild/cmake/common.py
+++ b/mesonbuild/cmake/common.py
@@ -60,6 +60,26 @@ def _flags_to_list(raw: str) -> T.List[str]:
res = list(filter(lambda x: len(x) > 0, res))
return res
+def cmake_defines_to_args(raw: T.Any, permissive: bool = False) -> T.List[str]:
+ res = [] # type: T.List[str]
+ if not isinstance(raw, list):
+ raw = [raw]
+
+ for i in raw:
+ if not isinstance(i, dict):
+ raise MesonException('Invalid CMake defines. Expected a dict, but got a {}'.format(type(i).__name__))
+ for key, val in i.items():
+ assert isinstance(key, str)
+ if isinstance(val, (str, int, float)):
+ res += ['-D{}={}'.format(key, val)]
+ elif isinstance(val, bool):
+ val_str = 'ON' if val else 'OFF'
+ res += ['-D{}={}'.format(key, val_str)]
+ else:
+ raise MesonException('Type "{}" of "{}" is not supported as for a CMake define value'.format(type(val).__name__, key))
+
+ return res
+
class CMakeFileGroup:
def __init__(self, data: dict):
self.defines = data.get('defines', '')
@@ -163,3 +183,78 @@ class CMakeConfiguration:
mlog.log('Project {}:'.format(idx))
with mlog.nested():
i.log()
+
+class SingleTargetOptions:
+ def __init__(self) -> None:
+ self.opts = {} # type: T.Dict[str, str]
+ self.lang_args = {} # type: T.Dict[str, T.List[str]]
+ self.link_args = [] # type: T.List[str]
+ self.install = 'preserve'
+
+ def set_opt(self, opt: str, val: str) -> None:
+ self.opts[opt] = val
+
+ def append_args(self, lang: str, args: T.List[str]) -> None:
+ if lang not in self.lang_args:
+ self.lang_args[lang] = []
+ self.lang_args[lang] += args
+
+ def append_link_args(self, args: T.List[str]) -> None:
+ self.link_args += args
+
+ def set_install(self, install: bool) -> None:
+ self.install = 'true' if install else 'false'
+
+ def get_override_options(self, initial: T.List[str]) -> T.List[str]:
+ res = [] # type: T.List[str]
+ for i in initial:
+ opt = i[:i.find('=')]
+ if opt not in self.opts:
+ res += [i]
+ res += ['{}={}'.format(k, v) for k, v in self.opts.items()]
+ return res
+
+ def get_compile_args(self, lang: str, initial: T.List[str]) -> T.List[str]:
+ if lang in self.lang_args:
+ return initial + self.lang_args[lang]
+ return initial
+
+ def get_link_args(self, initial: T.List[str]) -> T.List[str]:
+ return initial + self.link_args
+
+ def get_install(self, initial: bool) -> bool:
+ return {'preserve': initial, 'true': True, 'false': False}[self.install]
+
+class TargetOptions:
+ def __init__(self) -> None:
+ self.global_options = SingleTargetOptions()
+ self.target_options = {} # type: T.Dict[str, SingleTargetOptions]
+
+ def __getitem__(self, tgt: str) -> SingleTargetOptions:
+ if tgt not in self.target_options:
+ self.target_options[tgt] = SingleTargetOptions()
+ return self.target_options[tgt]
+
+ def get_override_options(self, tgt: str, initial: T.List[str]) -> T.List[str]:
+ initial = self.global_options.get_override_options(initial)
+ if tgt in self.target_options:
+ initial = self.target_options[tgt].get_override_options(initial)
+ return initial
+
+ def get_compile_args(self, tgt: str, lang: str, initial: T.List[str]) -> T.List[str]:
+ initial = self.global_options.get_compile_args(lang, initial)
+ if tgt in self.target_options:
+ initial = self.target_options[tgt].get_compile_args(lang, initial)
+ return initial
+
+ def get_link_args(self, tgt: str, initial: T.List[str]) -> T.List[str]:
+ initial = self.global_options.get_link_args(initial)
+ if tgt in self.target_options:
+ initial = self.target_options[tgt].get_link_args(initial)
+ return initial
+
+ def get_install(self, tgt: str, initial: bool) -> bool:
+ initial = self.global_options.get_install(initial)
+ if tgt in self.target_options:
+ initial = self.target_options[tgt].get_install(initial)
+ return initial
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index a5bf545..57e6e1d 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -17,7 +17,7 @@
import pkg_resources
-from .common import CMakeException, CMakeTarget
+from .common import CMakeException, CMakeTarget, TargetOptions
from .client import CMakeClient, RequestCMakeInputs, RequestConfigure, RequestCompute, RequestCodeModel
from .fileapi import CMakeFileAPI
from .executor import CMakeExecutor
@@ -994,7 +994,7 @@ class CMakeInterpreter:
mlog.log('CMake project', mlog.bold(self.project_name), 'has', mlog.bold(str(len(self.targets) + len(self.custom_targets))), 'build targets.')
- def pretend_to_be_meson(self) -> CodeBlockNode:
+ def pretend_to_be_meson(self, options: TargetOptions) -> CodeBlockNode:
if not self.project_name:
raise CMakeException('CMakeInterpreter was not analysed')
@@ -1158,21 +1158,26 @@ class CMakeInterpreter:
dep_var = '{}_dep'.format(tgt.name)
tgt_var = tgt.name
+ install_tgt = options.get_install(tgt.cmake_name, tgt.install)
+
# Generate target kwargs
tgt_kwargs = {
- 'build_by_default': tgt.install,
- 'link_args': tgt.link_flags + tgt.link_libraries,
+ 'build_by_default': install_tgt,
+ 'link_args': options.get_link_args(tgt.cmake_name, tgt.link_flags + tgt.link_libraries),
'link_with': link_with,
'include_directories': id_node(inc_var),
- 'install': tgt.install,
- 'install_dir': tgt.install_dir,
- 'override_options': tgt.override_options,
+ 'install': install_tgt,
+ 'override_options': options.get_override_options(tgt.cmake_name, tgt.override_options),
'objects': [method(x, 'extract_all_objects') for x in objec_libs],
}
+ # Only set if installed and only override if it is set
+ if install_tgt and tgt.install_dir:
+ tgt_kwargs['install_dir'] = tgt.install_dir
+
# Handle compiler args
for key, val in tgt.compile_opts.items():
- tgt_kwargs['{}_args'.format(key)] = val
+ tgt_kwargs['{}_args'.format(key)] = options.get_compile_args(tgt.cmake_name, key, val)
# Handle -fPCI, etc
if tgt_func == 'executable':
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index b8d4fec..740b0bc 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2440,7 +2440,7 @@ class Interpreter(InterpreterBase):
if isinstance(item, build.CustomTarget):
return CustomTargetHolder(item, self)
- elif isinstance(item, (int, str, bool, Disabler)) or item is None:
+ elif isinstance(item, (int, str, bool, Disabler, InterpreterObject)) or item is None:
return item
elif isinstance(item, build.Executable):
return ExecutableHolder(item, self)
@@ -2851,13 +2851,21 @@ external dependencies (including libraries) must go to "dependencies".''')
with mlog.nested():
new_build = self.build.copy()
prefix = self.coredata.builtins['prefix'].value
+
+ from .modules.cmake import CMakeSubprojectOptions
+ options = kwargs.get('options', CMakeSubprojectOptions())
+ if not isinstance(options, CMakeSubprojectOptions):
+ raise InterpreterException('"options" kwarg must be CMakeSubprojectOptions'
+ ' object (created by cmake.subproject_options())')
+
cmake_options = mesonlib.stringlistify(kwargs.get('cmake_options', []))
+ cmake_options += options.cmake_options
cm_int = CMakeInterpreter(new_build, subdir, subdir_abs, prefix, new_build.environment, self.backend)
cm_int.initialise(cmake_options)
cm_int.analyse()
# Generate a meson ast and execute it with the normal do_subproject_meson
- ast = cm_int.pretend_to_be_meson()
+ ast = cm_int.pretend_to_be_meson(options.target_options)
mlog.log()
with mlog.nested():
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index 6c4098b..dcbeda8 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -14,12 +14,28 @@
import re
import os, os.path, pathlib
import shutil
+import typing as T
from . import ExtensionModule, ModuleReturnValue
from .. import build, dependencies, mesonlib, mlog
-from ..interpreterbase import permittedKwargs, FeatureNew, stringArgs, InterpreterObject, ObjectHolder, noPosargs
+from ..cmake import SingleTargetOptions, TargetOptions, cmake_defines_to_args
from ..interpreter import ConfigurationDataHolder, InterpreterException, SubprojectHolder
+from ..interpreterbase import (
+ InterpreterObject,
+ ObjectHolder,
+
+ FeatureNew,
+ FeatureNewKwargs,
+ FeatureDeprecatedKwargs,
+
+ stringArgs,
+ permittedKwargs,
+ noPosargs,
+ noKwargs,
+
+ InvalidArguments,
+)
COMPATIBILITIES = ['AnyNewerVersion', 'SameMajorVersion', 'SameMinorVersion', 'ExactVersion']
@@ -82,42 +98,107 @@ class CMakeSubprojectHolder(InterpreterObject, ObjectHolder):
assert(all([x in res for x in ['inc', 'src', 'dep', 'tgt', 'func']]))
return res
- @permittedKwargs({})
+ @noKwargs
+ @stringArgs
def get_variable(self, args, kwargs):
return self.held_object.get_variable_method(args, kwargs)
- @permittedKwargs({})
+ @noKwargs
+ @stringArgs
def dependency(self, args, kwargs):
info = self._args_to_info(args)
return self.get_variable([info['dep']], kwargs)
- @permittedKwargs({})
+ @noKwargs
+ @stringArgs
def include_directories(self, args, kwargs):
info = self._args_to_info(args)
return self.get_variable([info['inc']], kwargs)
- @permittedKwargs({})
+ @noKwargs
+ @stringArgs
def target(self, args, kwargs):
info = self._args_to_info(args)
return self.get_variable([info['tgt']], kwargs)
- @permittedKwargs({})
+ @noKwargs
+ @stringArgs
def target_type(self, args, kwargs):
info = self._args_to_info(args)
return info['func']
@noPosargs
- @permittedKwargs({})
+ @noKwargs
def target_list(self, args, kwargs):
return self.held_object.cm_interpreter.target_list()
@noPosargs
- @permittedKwargs({})
+ @noKwargs
@FeatureNew('CMakeSubproject.found()', '0.53.2')
def found_method(self, args, kwargs):
return self.held_object is not None
+class CMakeSubprojectOptions(InterpreterObject):
+ def __init__(self) -> None:
+ super().__init__()
+ self.cmake_options = [] # type: T.List[str]
+ self.target_options = TargetOptions()
+
+ self.methods.update(
+ {
+ 'add_cmake_defines': self.add_cmake_defines,
+ 'set_override_option': self.set_override_option,
+ 'set_install': self.set_install,
+ 'append_compile_args': self.append_compile_args,
+ 'append_link_args': self.append_link_args,
+ 'clear': self.clear,
+ }
+ )
+
+ def _get_opts(self, kwargs: dict) -> SingleTargetOptions:
+ if 'target' in kwargs:
+ return self.target_options[kwargs['target']]
+ return self.target_options.global_options
+
+ @noKwargs
+ def add_cmake_defines(self, args, kwargs) -> None:
+ self.cmake_options += cmake_defines_to_args(args)
+
+ @stringArgs
+ @permittedKwargs({'target'})
+ def set_override_option(self, args, kwargs) -> None:
+ if len(args) != 2:
+ raise InvalidArguments('set_override_option takes exactly 2 positional arguments')
+ self._get_opts(kwargs).set_opt(args[0], args[1])
+
+ @permittedKwargs({'target'})
+ def set_install(self, args, kwargs) -> None:
+ if len(args) != 1 or not isinstance(args[0], bool):
+ raise InvalidArguments('set_install takes exactly 1 boolean argument')
+ self._get_opts(kwargs).set_install(args[0])
+
+ @stringArgs
+ @permittedKwargs({'target'})
+ def append_compile_args(self, args, kwargs) -> None:
+ if len(args) < 2:
+ raise InvalidArguments('append_compile_args takes at least 2 positional arguments')
+ self._get_opts(kwargs).append_args(args[0], args[1:])
+
+ @stringArgs
+ @permittedKwargs({'target'})
+ def append_link_args(self, args, kwargs) -> None:
+ if not args:
+ raise InvalidArguments('append_link_args takes at least 1 positional argument')
+ self._get_opts(kwargs).append_link_args(args)
+
+ @noPosargs
+ @noKwargs
+ def clear(self, args, kwargs) -> None:
+ self.cmake_options.clear()
+ self.target_options = TargetOptions()
+
+
class CmakeModule(ExtensionModule):
cmake_detected = False
cmake_root = None
@@ -287,16 +368,27 @@ class CmakeModule(ExtensionModule):
return res
@FeatureNew('subproject', '0.51.0')
- @permittedKwargs({'cmake_options', 'required'})
+ @FeatureNewKwargs('subproject', '0.55.0', ['options'])
+ @FeatureDeprecatedKwargs('subproject', '0.55.0', ['cmake_options'])
+ @permittedKwargs({'cmake_options', 'required', 'options'})
@stringArgs
def subproject(self, interpreter, state, args, kwargs):
if len(args) != 1:
raise InterpreterException('Subproject takes exactly one argument')
+ if 'cmake_options' in kwargs and 'options' in kwargs:
+ raise InterpreterException('"options" cannot be used together with "cmake_options"')
dirname = args[0]
subp = interpreter.do_subproject(dirname, 'cmake', kwargs)
if not subp.held_object:
return subp
return CMakeSubprojectHolder(subp, dirname)
+ @FeatureNew('subproject_options', '0.55.0')
+ @noKwargs
+ @noPosargs
+ def subproject_options(self, state, args, kwargs) -> ModuleReturnValue:
+ opts = CMakeSubprojectOptions()
+ return ModuleReturnValue(opts, [])
+
def initialize(*args, **kwargs):
return CmakeModule(*args, **kwargs)