Skip to content
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

Define toolset and architecture in CMakePresets.json for VS to avoid mismatches #15215

Merged
merged 8 commits into from
Dec 15, 2023
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
22 changes: 12 additions & 10 deletions conan/tools/cmake/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from conan.api.output import ConanOutput
from conan.tools.cmake.layout import get_build_folder_custom_vars
from conan.tools.cmake.toolchain.blocks import GenericSystemBlock
from conan.tools.cmake.utils import is_multi_configuration
from conan.tools.microsoft import is_msvc
from conans.client.graph.graph import RECIPE_CONSUMER
Expand Down Expand Up @@ -118,22 +119,23 @@ def _configure_preset(conanfile, generator, cache_variables, toolchain_file, mul
"generator": generator,
"cacheVariables": cache_variables,
}

if buildenv:
ret["environment"] = buildenv

if "Ninja" in generator and is_msvc(conanfile):
toolset_arch = conanfile.conf.get("tools.cmake.cmaketoolchain:toolset_arch")
if toolset_arch:
toolset_arch = "host={}".format(toolset_arch)
if is_msvc(conanfile):
# We can force the generator Visual even if it is Ninja, to define the toolset
toolset = GenericSystemBlock.get_toolset("Visual", conanfile)
# It seems "external" strategy is enough, as it is defined by toolchain
if toolset:
ret["toolset"] = {
"value": toolset_arch,
"value": toolset,
"strategy": "external"
}
arch = {"x86": "x86",
"x86_64": "x64",
"armv7": "ARM",
"armv8": "ARM64"}.get(conanfile.settings.get_safe("arch"))

arch = GenericSystemBlock.get_generator_platform("Visual", conanfile)
# https://learn.microsoft.com/en-us/cpp/build/cmake-presets-vs
if generator and "Ninja" in generator and arch == "Win32":
arch = "x86" # for command line, it is not Win32, it is x86
if arch:
ret["architecture"] = {
"value": arch,
Expand Down
18 changes: 10 additions & 8 deletions conan/tools/cmake/toolchain/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,14 +681,15 @@ class GenericSystemBlock(Block):
{% endif %}
""")

def _get_toolset(self, generator):
@staticmethod
def get_toolset(generator, conanfile):
toolset = None
if generator is None or ("Visual" not in generator and "Xcode" not in generator):
return None
settings = self._conanfile.settings
settings = conanfile.settings
compiler = settings.get_safe("compiler")
if compiler == "intel-cc":
return IntelCC(self._conanfile).ms_toolset
return IntelCC(conanfile).ms_toolset
elif compiler == "msvc":
toolset = settings.get_safe("compiler.toolset")
if toolset is None:
Expand All @@ -706,14 +707,15 @@ def _get_toolset(self, generator):
else:
raise ConanException("CMakeToolchain with compiler=clang and a CMake "
"'Visual Studio' generator requires VS16 or VS17")
toolset_arch = self._conanfile.conf.get("tools.cmake.cmaketoolchain:toolset_arch")
toolset_arch = conanfile.conf.get("tools.cmake.cmaketoolchain:toolset_arch")
if toolset_arch is not None:
toolset_arch = "host={}".format(toolset_arch)
toolset = toolset_arch if toolset is None else "{},{}".format(toolset, toolset_arch)
return toolset

def _get_generator_platform(self, generator):
settings = self._conanfile.settings
@staticmethod
def get_generator_platform(generator, conanfile):
settings = conanfile.settings
# Returns the generator platform to be used by CMake
compiler = settings.get_safe("compiler")
arch = settings.get_safe("arch")
Expand Down Expand Up @@ -816,8 +818,8 @@ def _get_winsdk_version(self, system_version, generator_platform):

def context(self):
generator = self._toolchain.generator
generator_platform = self._get_generator_platform(generator)
toolset = self._get_toolset(generator)
generator_platform = self.get_generator_platform(generator, self._conanfile)
toolset = self.get_toolset(generator, self._conanfile)
system_name, system_version, system_processor = self._get_cross_build()

# This is handled by the tools.apple:sdk_path and CMAKE_OSX_SYSROOT in Apple
Expand Down
33 changes: 33 additions & 0 deletions conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,10 @@ def test_cmake_presets_with_conanfile_txt():
c.run_command("ctest --preset conan-debug")
c.run_command("./build/Debug/foo")
else:
c.run_command("cmake --preset conan-default")
# this second called used to fail
# https://github.com/conan-io/conan/issues/13792, CMake ignoring toolchain architecture
# and toolset
c.run_command("cmake --preset conan-default")
c.run_command("cmake --build --preset conan-debug")
c.run_command("ctest --preset conan-debug")
Expand All @@ -975,6 +979,35 @@ def test_cmake_presets_with_conanfile_txt():
assert "Hello World Release!" in c.out


@pytest.mark.skipif(platform.system() != "Windows", reason="Needs windows")
@pytest.mark.tool("ninja")
@pytest.mark.tool("cmake", "3.23")
def test_cmake_presets_with_conanfile_txt_ninja():
c = TestClient()

c.run("new cmake_exe -d name=foo -d version=1.0")
os.unlink(os.path.join(c.current_folder, "conanfile.py"))
c.save({"conanfile.txt": textwrap.dedent("""
[generators]
CMakeToolchain

[layout]
cmake_layout
""")})

conf = "-c tools.cmake.cmaketoolchain:generator=Ninja"
c.run(f"install . {conf}")
c.run(f"install . -s build_type=Debug {conf}")

c.run_command("build\\Release\\generators\\conanbuild.bat && cmake --preset conan-release")
c.run_command("build\\Release\\generators\\conanbuild.bat && cmake --preset conan-release")
c.run_command("build\\Release\\generators\\conanbuild.bat && cmake --build --preset conan-release")
c.run_command("build\\Release\\generators\\conanbuild.bat && ctest --preset conan-release")
c.run_command("build\\Release\\foo")

assert "Hello World Release!" in c.out


def test_cmake_presets_not_forbidden_build_type():
client = TestClient(path_with_spaces=False)
client.run("new cmake_exe -d name=hello -d version=0.1")
Expand Down
23 changes: 15 additions & 8 deletions conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,8 @@ def layout(self):
assert "/" not in presets["include"]


@pytest.mark.parametrize("arch, arch_toolset", [("x86", "x86_64"), ("x86_64", "x86_64")])
@pytest.mark.parametrize("arch, arch_toolset", [("x86", "x86_64"), ("x86_64", "x86_64"),
("x86", "x86"), ("x86_64", "x86")])
def test_presets_ninja_msvc(arch, arch_toolset):
client = TestClient()
conanfile = textwrap.dedent("""
Expand All @@ -860,9 +861,8 @@ def layout(self):

presets = json.loads(client.load("build/14/Release/generators/CMakePresets.json"))

toolset_value = {"x86_64": "host=x86_64", "x86": "x86"}.get(arch_toolset)
toolset_value = {"x86_64": "v141,host=x86_64", "x86": "v141,host=x86"}.get(arch_toolset)
arch_value = {"x86_64": "x64", "x86": "x86"}.get(arch)

assert presets["configurePresets"][0]["architecture"]["value"] == arch_value
assert presets["configurePresets"][0]["architecture"]["strategy"] == "external"
assert presets["configurePresets"][0]["toolset"]["value"] == toolset_value
Expand All @@ -875,20 +875,27 @@ def layout(self):
client.run(
"install . {} -s compiler.cppstd=14 {} -s arch={}".format(" ".join(configs), msvc, arch))

toolset_value = {"x86_64": "v141,host=x86_64", "x86": "v141,host=x86"}.get(arch_toolset)
arch_value = {"x86_64": "x64", "x86": "Win32"}.get(arch) # NOTE: Win32 is different!!
presets = json.loads(client.load("build/14/generators/CMakePresets.json"))
assert "architecture" not in presets["configurePresets"][0]
assert "toolset" not in presets["configurePresets"][0]
assert presets["configurePresets"][0]["architecture"]["value"] == arch_value
assert presets["configurePresets"][0]["architecture"]["strategy"] == "external"
assert presets["configurePresets"][0]["toolset"]["value"] == toolset_value
assert presets["configurePresets"][0]["toolset"]["strategy"] == "external"

# No toolset defined in conf, no value
rmdir(os.path.join(client.current_folder, "build"))
configs = ["-c tools.cmake.cmake_layout:build_folder_vars='[\"settings.compiler.cppstd\"]'",
"-c tools.cmake.cmaketoolchain:generator=Ninja"]

client.run(
"install . {} -s compiler.cppstd=14 {} -s arch={}".format(" ".join(configs), msvc, arch))
presets = json.loads(client.load("build/14/Release/generators/CMakePresets.json"))
assert "architecture" in presets["configurePresets"][0]
assert "toolset" not in presets["configurePresets"][0]
toolset_value = {"x86_64": "v141", "x86": "v141"}.get(arch_toolset)
arch_value = {"x86_64": "x64", "x86": "x86"}.get(arch)
assert presets["configurePresets"][0]["architecture"]["value"] == arch_value
assert presets["configurePresets"][0]["architecture"]["strategy"] == "external"
assert presets["configurePresets"][0]["toolset"]["value"] == toolset_value
assert presets["configurePresets"][0]["toolset"]["strategy"] == "external"


def test_pkg_config_block():
Expand Down