From a2f94ca18b237ccaf072474a1a4405a996e84395 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 31 May 2020 22:27:14 +0200 Subject: 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. --- mesonbuild/cmake/__init__.py | 5 +- mesonbuild/cmake/common.py | 95 ++++++++++++++++++++++++++++++++++ mesonbuild/cmake/interpreter.py | 21 +++++--- mesonbuild/interpreter.py | 12 ++++- mesonbuild/modules/cmake.py | 110 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 223 insertions(+), 20 deletions(-) diff --git a/mesonbuild/cmake/__init__.py b/mesonbuild/cmake/__init__.py index 01cc3f90b..db7aefd6f 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 e7da0d798..4510b5d5a 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 a5bf545d5..57e6e1d63 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 b8d4fec75..740b0bcea 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 6c4098baa..dcbeda864 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) -- cgit v1.2.3 From 08e838a235f5b1207694463dbd619dcebdc4eb61 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 31 May 2020 22:30:13 +0200 Subject: cmake: added test case --- test cases/cmake/19 advanced options/main.cpp | 18 +++++++++++++ test cases/cmake/19 advanced options/meson.build | 24 +++++++++++++++++ .../subprojects/cmOpts/CMakeLists.txt | 18 +++++++++++++ .../subprojects/cmOpts/cmMod.cpp | 31 ++++++++++++++++++++++ .../subprojects/cmOpts/cmMod.hpp | 14 ++++++++++ .../subprojects/cmOpts/cmTest.cpp | 25 +++++++++++++++++ .../subprojects/cmOpts/cmTest.hpp | 3 +++ .../subprojects/cmOpts/main.cpp | 10 +++++++ test cases/cmake/19 advanced options/test.json | 8 ++++++ 9 files changed, 151 insertions(+) create mode 100644 test cases/cmake/19 advanced options/main.cpp create mode 100644 test cases/cmake/19 advanced options/meson.build create mode 100644 test cases/cmake/19 advanced options/subprojects/cmOpts/CMakeLists.txt create mode 100644 test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.cpp create mode 100644 test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.hpp create mode 100644 test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.cpp create mode 100644 test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.hpp create mode 100644 test cases/cmake/19 advanced options/subprojects/cmOpts/main.cpp create mode 100644 test cases/cmake/19 advanced options/test.json diff --git a/test cases/cmake/19 advanced options/main.cpp b/test cases/cmake/19 advanced options/main.cpp new file mode 100644 index 000000000..6a071cc21 --- /dev/null +++ b/test cases/cmake/19 advanced options/main.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +using namespace std; + +int main(void) { + cmModClass obj("Hello"); + cout << obj.getStr() << endl; + + int v1 = obj.getInt(); + int v2 = getTestInt(); + if (v1 != ((1 + v2) * 2)) { + cerr << "Number test failed" << endl; + return 1; + } + return 0; +} diff --git a/test cases/cmake/19 advanced options/meson.build b/test cases/cmake/19 advanced options/meson.build new file mode 100644 index 000000000..7e2d4e810 --- /dev/null +++ b/test cases/cmake/19 advanced options/meson.build @@ -0,0 +1,24 @@ +project('cmake_set_opt', ['c', 'cpp']) + +cm = import('cmake') +opts = cm.subproject_options() + +opts.add_cmake_defines({'SOME_CMAKE_VAR': 'something', 'SOME_OTHER_VAR': true}) + +opts.set_override_option('cpp_std', 'c++11') # Global is C++11 +opts.set_override_option('cpp_std', 'c++14', target: 'cmModLib++') # Override it with C++14 for cmModLib++ + +opts.append_compile_args('cpp', '-DMESON_GLOBAL_FLAG=1') +opts.append_compile_args('cpp', ['-DMESON_SPECIAL_FLAG1=1', ['-DMESON_SPECIAL_FLAG2=1']], target: 'cmModLib++') +opts.append_compile_args('cpp', '-DMESON_MAGIC_INT=42', target: 'cmModLib++') +opts.append_compile_args('cpp', [[[['-DMESON_MAGIC_INT=20']]]], target: 'cmTestLib') + +opts.set_install(false) +opts.set_install(true, target: 'testEXE') + +sp = cm.subproject('cmOpts', options: opts) +dep1 = sp.dependency('cmModLib++') +dep2 = sp.dependency('cmTestLib') + +exe1 = executable('main', ['main.cpp'], dependencies: [dep1, dep2]) +test('test1', exe1) diff --git a/test cases/cmake/19 advanced options/subprojects/cmOpts/CMakeLists.txt b/test cases/cmake/19 advanced options/subprojects/cmOpts/CMakeLists.txt new file mode 100644 index 000000000..584841e87 --- /dev/null +++ b/test cases/cmake/19 advanced options/subprojects/cmOpts/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.7) + +project(CmOpts) + +set(CMAKE_CXX_STANDARD 98) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(NOT "${SOME_CMAKE_VAR}" STREQUAL "something") + message(FATAL_ERROR "Setting the CMake var failed") +endif() + +add_library(cmModLib++ STATIC cmMod.cpp) +add_library(cmTestLib STATIC cmTest.cpp) +add_executable(testEXE main.cpp) + +target_link_libraries(testEXE cmModLib++) + +install(TARGETS cmTestLib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) diff --git a/test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.cpp b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.cpp new file mode 100644 index 000000000..7651b60a1 --- /dev/null +++ b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.cpp @@ -0,0 +1,31 @@ +#include "cmMod.hpp" + +using namespace std; + +#if __cplusplus < 201402L +#error "At least C++14 is required" +#endif + +#ifndef MESON_GLOBAL_FLAG +#error "MESON_GLOBAL_FLAG was not set" +#endif + +#ifndef MESON_SPECIAL_FLAG1 +#error "MESON_SPECIAL_FLAG1 was not set" +#endif + +#ifndef MESON_SPECIAL_FLAG2 +#error "MESON_SPECIAL_FLAG2 was not set" +#endif + +cmModClass::cmModClass(string foo) { + str = foo + " World"; +} + +string cmModClass::getStr() const { + return str; +} + +int cmModClass::getInt() const { + return MESON_MAGIC_INT; +} diff --git a/test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.hpp b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.hpp new file mode 100644 index 000000000..074893657 --- /dev/null +++ b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmMod.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +class cmModClass { +private: + std::string str; + +public: + cmModClass(std::string foo); + + std::string getStr() const; + int getInt() const; +}; diff --git a/test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.cpp b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.cpp new file mode 100644 index 000000000..a00cdcd95 --- /dev/null +++ b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.cpp @@ -0,0 +1,25 @@ +#include "cmTest.hpp" + +#if __cplusplus < 201103L +#error "At least C++11 is required" +#endif + +#if __cplusplus >= 201402L +#error "At most C++11 is required" +#endif + +#ifndef MESON_GLOBAL_FLAG +#error "MESON_GLOBAL_FLAG was not set" +#endif + +#ifdef MESON_SPECIAL_FLAG1 +#error "MESON_SPECIAL_FLAG1 *was* set" +#endif + +#ifdef MESON_SPECIAL_FLAG2 +#error "MESON_SPECIAL_FLAG2 *was* set" +#endif + +int getTestInt() { + return MESON_MAGIC_INT; +} diff --git a/test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.hpp b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.hpp new file mode 100644 index 000000000..5a3bf7b49 --- /dev/null +++ b/test cases/cmake/19 advanced options/subprojects/cmOpts/cmTest.hpp @@ -0,0 +1,3 @@ +#pragma once + +int getTestInt(); diff --git a/test cases/cmake/19 advanced options/subprojects/cmOpts/main.cpp b/test cases/cmake/19 advanced options/subprojects/cmOpts/main.cpp new file mode 100644 index 000000000..497d1cee0 --- /dev/null +++ b/test cases/cmake/19 advanced options/subprojects/cmOpts/main.cpp @@ -0,0 +1,10 @@ +#include +#include "cmMod.hpp" + +using namespace std; + +int main(void) { + cmModClass obj("Hello (LIB TEST)"); + cout << obj.getStr() << endl; + return 0; +} diff --git a/test cases/cmake/19 advanced options/test.json b/test cases/cmake/19 advanced options/test.json new file mode 100644 index 000000000..e2d9c051f --- /dev/null +++ b/test cases/cmake/19 advanced options/test.json @@ -0,0 +1,8 @@ +{ + "installed": [ + {"type": "exe", "file": "usr/bin/cm_testEXE"} + ], + "tools": { + "cmake": ">=3.11" + } +} -- cgit v1.2.3 From ede2cd556c0568dabcb8826fe87bd00c376b797e Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 31 May 2020 22:30:24 +0200 Subject: cmake: added docs --- docs/markdown/CMake-module.md | 77 +++++++++++++++++++++++++++++++++++++++-- docs/markdown/snippets/cmake.md | 17 +++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 docs/markdown/snippets/cmake.md diff --git a/docs/markdown/CMake-module.md b/docs/markdown/CMake-module.md index 7103608fe..fc6157ed7 100644 --- a/docs/markdown/CMake-module.md +++ b/docs/markdown/CMake-module.md @@ -48,8 +48,6 @@ The `subproject` method is almost identical to the normal meson `subproject` function. The only difference is that a CMake project instead of a meson project is configured. -Also, project specific CMake options can be added with the `cmake_options` key. - The returned `sub_proj` supports the same options as a "normal" subproject. Meson automatically detects CMake build targets, which can be accessed with the methods listed [below](#subproject-object). @@ -87,6 +85,49 @@ It should be noted that not all projects are guaranteed to work. The safest approach would still be to create a `meson.build` for the subprojects in question. +### Configuration options + +*New in meson 0.55.0* + +Meson also supports passing configuration options to CMake and overriding +certain build details extracted from the CMake subproject. + +```meson +cmake = import('cmake') +opt_var = cmake.subproject_options() + +# Call CMake with `-DSOME_OTHER_VAR=ON` +opt_var.add_cmake_defines({'SOME_OTHER_VAR': true}) + +# Globally override the C++ standard to c++11 +opt_var.set_override_option('cpp_std', 'c++11') + +# Override the previous global C++ standard +# with c++14 only for the CMake target someLib +opt_var.set_override_option('cpp_std', 'c++14', target: 'someLib') + +sub_pro = cmake.subproject('someLibProject', options: opt_var) + +# Further changes to opt_var have no effect +``` + +See [the CMake options object](#cmake-options-object) for a complete reference +of all supported functions. + +The CMake configuration options object is very similar to the +[configuration data object](Reference-manual.md#configuration-data-object) object +returned by [`configuration_data`](Reference-manual.md#configuration_data). It +is generated by the `subproject_options` function + +All configuration options have to be set *before* the subproject is configured +and must be passed to the `subproject` method via the `options` key. Altering +the configuration object won't have any effect on previous `cmake.subproject` +calls. + +In earlier meson versions CMake command-line parameters could be set with the +`cmake_options` kwarg. However, this feature is deprecated since 0.55.0 and only +kept for compatibility. It will not work together with the `options` kwarg. + ### `subproject` object This object is returned by the `subproject` function described above @@ -103,7 +144,37 @@ and supports the following methods: the subproject. Usually `dependency()` or `target()` should be preferred to extract build targets. - `found` returns true if the subproject is available, otherwise false - *new in in 0.53.2* + *new in meson 0.53.2* + +### `cmake options` object + +This object is returned by the `subproject_options()` function and consumed by +the `options` kwarg of the `subproject` function. The following methods are +supported: + + - `add_cmake_defines({'opt1': val1, ...})` add additional CMake commandline defines + - `set_override_option(opt, val)` set specific [build options](Build-options.md) + for targets. This will effectively add `opt=val` to the `override_options` + array of the [build target](Reference-manual.md#executable) + - `set_install(bool)` override wether targets should be installed or not + - `append_compile_args(lang, arg1, ...)` append compile flags for a specific + language to the targets + - `append_link_args(arg1, ...)` append linger args to the targets + - `clear()` reset all data in the `cmake options` object + +The methods `set_override_option`, `set_install`, `append_compile_args` and +`append_link_args` support the optional `target` kwarg. If specified, the set +options affect the specific target. The effect of the option is global for the +subproject otherwise. + +If, for instance, `opt_var.set_install(false)` is called, no target will be +installed regardless of what is set by CMake. However, it is still possible to +install specific targets (here `foo`) by setting the `target` kwarg: +`opt_var.set_install(true, target: 'foo')` + +Options that are not set won't affect the generated subproject. So, if for +instance, `set_install` was not called then the values extracted from CMake will +be used. ## CMake configuration files diff --git a/docs/markdown/snippets/cmake.md b/docs/markdown/snippets/cmake.md new file mode 100644 index 000000000..16da78e7a --- /dev/null +++ b/docs/markdown/snippets/cmake.md @@ -0,0 +1,17 @@ +## Configure CMake subprojects with meson.subproject_options + +Meson now supports passing configuration options to CMake and overriding +certain build details extracted from the CMake subproject. + +The new CMake configuration options object is very similar to the +[configuration data object](Reference-manual.md#configuration-data-object) object +returned by [`configuration_data`](Reference-manual.md#configuration_data). It +is generated by the `subproject_options` function + +All configuration options have to be set *before* the subproject is configured +and must be passed to the `subproject` method via the `options` key. Altering +the configuration object won't have any effect on previous `cmake.subproject` +calls. + +**Note:** The `cmake_options` kwarg for the `subproject` function is now +deprecated since it is replaced by the new `options` system. -- cgit v1.2.3 From 0e98a7679a658b2a53a5de578f202a022c69f0c1 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Mon, 1 Jun 2020 12:25:05 +0200 Subject: cmake: Skip MSVC like compilers, since C++11 is not supported --- test cases/cmake/19 advanced options/meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test cases/cmake/19 advanced options/meson.build b/test cases/cmake/19 advanced options/meson.build index 7e2d4e810..6332ca4b7 100644 --- a/test cases/cmake/19 advanced options/meson.build +++ b/test cases/cmake/19 advanced options/meson.build @@ -1,5 +1,10 @@ project('cmake_set_opt', ['c', 'cpp']) +comp = meson.get_compiler('cpp') +if comp.get_argument_syntax() == 'msvc' + error('MESON_SKIP_TEST: MSVC is not supported because it does not support C++11') +endif + cm = import('cmake') opts = cm.subproject_options() -- cgit v1.2.3