Skip to content

Added support for tools.cmake.cmaketoolchain:extra_variables #16242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions conan/tools/cmake/toolchain/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,46 @@ def context(self):
"winsdk_version": winsdk_version,
"gen_platform_sdk_version": gen_platform_sdk_version}

class ExtraVariablesBlock(Block):
template = textwrap.dedent(r"""
{% if extra_variables %}
{% for key, value in extra_variables.items() %}
set({{ key }} {{ value }})
{% endfor %}
{% endif %}
""")

CMAKE_CACHE_TYPES = ["BOOL","FILEPATH", "PATH", "STRING", "INTERNAL"]

def get_exact_type(self, key, value):
if isinstance(value, str):
return f"\"{value}\""
elif isinstance(value, (int, float)):
return value
elif isinstance(value, dict):
var_value = self.get_exact_type(key, value.get("value"))
is_cache = value.get("cache")
if is_cache:
if not isinstance(is_cache, bool):
raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables "cache" must be a boolean (True/False)')
var_type = value.get("type")
if not var_type:
raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables needs "type" defined for cache variable "{key}"')
if var_type not in self.CMAKE_CACHE_TYPES:
raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables invalid type "{var_type}" for cache variable "{key}". Possible types: {", ".join(self.CMAKE_CACHE_TYPES)}')
# Set docstring as variable name if not defined
docstring = value.get("docstring") or key
return f"{var_value} CACHE {var_type} \"{docstring}\""
else:
return var_value

def context(self):
# Reading configuration from "tools.cmake.cmaketoolchain:extra_variables"
extra_variables = self._conanfile.conf.get("tools.cmake.cmaketoolchain:extra_variables", default={}, check_type=dict)
parsed_extra_variables = {}
for key, value in extra_variables.items():
parsed_extra_variables[key] = self.get_exact_type(key, value)
return {"extra_variables": parsed_extra_variables}

class OutputDirsBlock(Block):

Expand Down
3 changes: 2 additions & 1 deletion conan/tools/cmake/toolchain/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from conan.tools.build import use_win_mingw
from conan.tools.cmake.presets import write_cmake_presets
from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
from conan.tools.cmake.toolchain.blocks import ToolchainBlocks, UserToolchain, GenericSystemBlock, \
from conan.tools.cmake.toolchain.blocks import ExtraVariablesBlock, ToolchainBlocks, UserToolchain, GenericSystemBlock, \
AndroidSystemBlock, AppleSystemBlock, FPicBlock, ArchitectureBlock, GLibCXXBlock, VSRuntimeBlock, \
CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, PkgConfigBlock, \
SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock, CompilersBlock, LinkerScriptsBlock, \
Expand Down Expand Up @@ -165,6 +165,7 @@ def __init__(self, conanfile, generator=None):
("parallel", ParallelBlock),
("extra_flags", ExtraFlagsBlock),
("cmake_flags_init", CMakeFlagsInitBlock),
("extra_variables", ExtraVariablesBlock),
("try_compile", TryCompileBlock),
("find_paths", FindFiles),
("pkg_config", PkgConfigBlock),
Expand Down
1 change: 1 addition & 0 deletions conans/model/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"tools.cmake.cmaketoolchain:toolset_arch": "Toolset architecture to be used as part of CMAKE_GENERATOR_TOOLSET in CMakeToolchain",
"tools.cmake.cmaketoolchain:toolset_cuda": "(Experimental) Path to a CUDA toolset to use, or version if installed at the system level",
"tools.cmake.cmaketoolchain:presets_environment": "String to define wether to add or not the environment section to the CMake presets. Empty by default, will generate the environment section in CMakePresets. Can take values: 'disabled'.",
"tools.cmake.cmaketoolchain:extra_variables": "Dictionary with variables to be injected in CMakeToolchain (potential override of CMakeToolchain defined variables)",
"tools.cmake.cmake_layout:build_folder_vars": "Settings and Options that will produce a different build folder and different CMake presets names",
"tools.cmake.cmake_layout:build_folder": "(Experimental) Allow configuring the base folder of the build for local builds",
"tools.cmake.cmake_layout:test_folder": "(Experimental) Allow configuring the base folder of the build for test_package",
Expand Down
57 changes: 56 additions & 1 deletion test/integration/toolchains/cmake/test_cmaketoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1501,7 +1501,6 @@ def layout(self):
presets = json.loads(c.load(user_presets["include"][0]))
assert os.path.isabs(presets["configurePresets"][0]["toolchainFile"])


def test_output_dirs_gnudirs_local_default():
# https://github.com/conan-io/conan/issues/14733
c = TestClient()
Expand Down Expand Up @@ -1575,3 +1574,59 @@ def _assert_install(out):
c.run("build .")
_assert_install(c.out)
assert "CMAKE_INSTALL_PREFIX" not in c.out


def test_toolchain_extra_variables():
windows_profile = textwrap.dedent("""
[settings]
os=Windows
arch=x86_64
[conf]
tools.cmake.cmaketoolchain:extra_variables={'CMAKE_GENERATOR_INSTANCE': '${GENERATOR_INSTANCE}/buildTools/', 'FOO': '42' }
""")

client = TestClient()
client.save({"conanfile.txt": "[generators]\nCMakeToolchain",
"windows": windows_profile})

# Test passing extra_variables from pro ile
client.run("install . --profile:host=windows")
toolchain = client.load("conan_toolchain.cmake")
assert 'set(CMAKE_GENERATOR_INSTANCE "${GENERATOR_INSTANCE}/buildTools/")' in toolchain
assert 'set(FOO "42")' in toolchain

# Test input from command line passing dict between doble quotes
client.run(textwrap.dedent(r"""
install . -c tools.cmake.cmaketoolchain:extra_variables="{'CMAKE_GENERATOR_INSTANCE': '${GENERATOR_INSTANCE}/buildTools/', 'FOO': 42.2, 'DICT': {'value': 1}, 'CACHE_VAR': {'value': 'hello world', 'cache': True, 'type': 'BOOL', 'docstring': 'test variable'}}"
""")
)

toolchain = client.load("conan_toolchain.cmake")
assert 'set(CMAKE_GENERATOR_INSTANCE "${GENERATOR_INSTANCE}/buildTools/")' in toolchain
assert 'set(FOO 42.2)' in toolchain
assert 'set(DICT 1)' in toolchain
assert 'set(CACHE_VAR "hello world" CACHE BOOL "test variable")' in toolchain


client.run(textwrap.dedent("""
install . -c tools.cmake.cmaketoolchain:extra_variables="{'invalid': {'value': 'hello world', 'cache': 'true'}}"
""") , assert_error=True)
assert 'tools.cmake.cmaketoolchain:extra_variables "cache" must be a boolean (True/False)' in client.out

# Test invalid cache variable
client.run(textwrap.dedent("""
install . -c tools.cmake.cmaketoolchain:extra_variables="{'invalid': {'value': 'hello world', 'cache': True}}"
""") , assert_error=True)
assert 'tools.cmake.cmaketoolchain:extra_variables needs "type" defined for cache variable "invalid"' in client.out

client.run(textwrap.dedent("""
install . -c tools.cmake.cmaketoolchain:extra_variables="{'invalid': {'value': 'hello world', 'cache': True, 'type': 'INVALID_TYPE'}}"
""") , assert_error=True)
assert 'tools.cmake.cmaketoolchain:extra_variables invalid type "INVALID_TYPE" for cache variable "invalid". Possible types: BOOL, FILEPATH, PATH, STRING, INTERNAL' in client.out

client.run(textwrap.dedent("""
install . -c tools.cmake.cmaketoolchain:extra_variables="{'CACHE_VAR_DEFAULT_DOC': {'value': 'hello world', 'cache': True, 'type': 'PATH'}}"
"""))
toolchain = client.load("conan_toolchain.cmake")
assert 'set(CACHE_VAR_DEFAULT_DOC "hello world" CACHE PATH "CACHE_VAR_DEFAULT_DOC")' in toolchain