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

presets relativize paths #16015

Merged
merged 10 commits into from
Apr 15, 2024
7 changes: 2 additions & 5 deletions conan/tools/cmake/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,8 @@ def configure(self, variables=None, build_script_folder=None, cli_args=None,
if self._generator:
arg_list.append('-G "{}"'.format(self._generator))
if self._toolchain_file:
if os.path.isabs(self._toolchain_file):
toolpath = self._toolchain_file
else:
toolpath = os.path.join(generator_folder, self._toolchain_file)
arg_list.append('-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolpath.replace("\\", "/")))
toolpath = self._toolchain_file.replace("\\", "/")
arg_list.append('-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolpath))
if self._conanfile.package_folder:
pkg_folder = self._conanfile.package_folder.replace("\\", "/")
arg_list.append('-DCMAKE_INSTALL_PREFIX="{}"'.format(pkg_folder))
Expand Down
40 changes: 23 additions & 17 deletions conan/tools/cmake/presets.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import json
import os
import platform
import textwrap

from conan.api.output import ConanOutput
from conan.api.output import ConanOutput, Color
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
Expand All @@ -28,6 +29,11 @@ class _CMakePresets:
@staticmethod
def generate(conanfile, toolchain_file, generator, cache_variables, preset_prefix, buildenv, runenv,
cmake_executable):
toolchain_file = os.path.abspath(os.path.join(conanfile.generators_folder, toolchain_file))
try: # Make it relative to the build dir if possible
toolchain_file = os.path.relpath(toolchain_file, conanfile.build_folder)
except ValueError:
pass
cache_variables = cache_variables or {}
if platform.system() == "Windows" and generator == "MinGW Makefiles":
if "CMAKE_SH" not in cache_variables:
Expand Down Expand Up @@ -74,7 +80,7 @@ def generate(conanfile, toolchain_file, generator, cache_variables, preset_prefi

preset_content = json.dumps(data, indent=4)
save(preset_path, preset_content)
ConanOutput(str(conanfile)).info("CMakeToolchain generated: CMakePresets.json")
ConanOutput(str(conanfile)).info(f"CMakeToolchain generated: {preset_path}")
return preset_path, data

@staticmethod
Expand Down Expand Up @@ -172,13 +178,11 @@ def _format_val(val):
except:
is_consumer = False
if is_consumer:
conanfile.output.info(
f"Preset '{name}' added to CMakePresets.json. Invoke it manually using "
f"'cmake --preset {name}' if using CMake>=3.23")
conanfile.output.info(f"If your CMake version is not compatible with "
f"CMakePresets (<3.23) call cmake like: 'cmake <path> "
f"-G {_format_val(generator)} {add_toolchain_cache}"
f"{cache_variables_info}'")
msg = textwrap.dedent(f"""\
CMakeToolchain: Preset '{name}' added to CMakePresets.json.
(cmake>=3.23) cmake --preset {name}
(cmake<3.23) cmake <path> -G {_format_val(generator)} {add_toolchain_cache} {cache_variables_info}""")
conanfile.output.info(msg, fg=Color.CYAN)
return ret

@staticmethod
Expand Down Expand Up @@ -283,14 +287,15 @@ def generate(conanfile, preset_path, user_presets_path, preset_prefix, preset_da

if inherited_user:
_IncludingPresets._clean_user_inherits(data, preset_data)
data = _IncludingPresets._append_user_preset_path(data, preset_path)

try: # Make it relative to the CMakeUserPresets.json if possible
preset_path = os.path.relpath(preset_path, output_dir)
except ValueError:
pass
data = _IncludingPresets._append_user_preset_path(data, preset_path, output_dir)

data = json.dumps(data, indent=4)
try:
presets_path = os.path.relpath(user_presets_path, conanfile.generators_folder)
except ValueError: # in Windows this fails if in another drive
presets_path = user_presets_path
ConanOutput(str(conanfile)).info(f"CMakeToolchain generated: {presets_path}")
ConanOutput(str(conanfile)).info(f"CMakeToolchain generated: {user_presets_path}")
save(user_presets_path, data)

@staticmethod
Expand Down Expand Up @@ -326,15 +331,16 @@ def _clean_user_inherits(data, preset_data):
other[:] = [p for p in other if p["name"] not in presets_names]

@staticmethod
def _append_user_preset_path(data, preset_path):
def _append_user_preset_path(data, preset_path, output_dir):
""" - Appends a 'include' to preset_path if the schema supports it.
- Otherwise it merges to "data" all the configurePresets, buildPresets etc from the
read preset_path.
"""
if "include" not in data:
data["include"] = []
# Clear the folders that have been deleted
data["include"] = [i for i in data.get("include", []) if os.path.exists(i)]
data["include"] = [i for i in data.get("include", [])
if os.path.exists(os.path.join(output_dir, i))]
if preset_path not in data["include"]:
data["include"].append(preset_path)
return data
Expand Down
10 changes: 5 additions & 5 deletions conan/tools/cmake/toolchain/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,16 @@ def generate(self):
check_duplicated_generator(self, self._conanfile)
toolchain_file = self._conanfile.conf.get("tools.cmake.cmaketoolchain:toolchain_file")
if toolchain_file is None: # The main toolchain file generated only if user dont define
save(os.path.join(self._conanfile.generators_folder, self.filename), self.content)
ConanOutput(str(self._conanfile)).info(f"CMakeToolchain generated: {self.filename}")
toolchain_file = self.filename
save(os.path.join(self._conanfile.generators_folder, toolchain_file), self.content)
ConanOutput(str(self._conanfile)).info(f"CMakeToolchain generated: {toolchain_file}")
# If we're using Intel oneAPI, we need to generate the environment file and run it
if self._conanfile.settings.get_safe("compiler") == "intel-cc":
IntelCC(self._conanfile).generate()
# Generators like Ninja or NMake requires an active vcvars
elif self.generator is not None and "Visual" not in self.generator:
VCVars(self._conanfile).generate()
toolchain = os.path.abspath(os.path.join(self._conanfile.generators_folder,
toolchain_file or self.filename))

cache_variables = {}
for name, value in self.cache_variables.items():
if isinstance(value, bool):
Expand Down Expand Up @@ -266,7 +266,7 @@ def generate(self):

cmake_executable = self._conanfile.conf.get("tools.cmake:cmake_program", None) or self._find_cmake_exe()

write_cmake_presets(self._conanfile, toolchain, self.generator, cache_variables,
write_cmake_presets(self._conanfile, toolchain_file, self.generator, cache_variables,
self.user_presets_path, self.presets_prefix, buildenv, runenv,
cmake_executable)

Expand Down
22 changes: 6 additions & 16 deletions conans/test/functional/toolchains/cmake/test_cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,9 @@ def test_toolchain_win(self, compiler, build_type, runtime, version, cppstd, arc
}
options = {"shared": shared}
save(self.client.cache.new_config_path, "tools.build:jobs=1")
install_out = self._run_build(settings, options)

# FIXME: Hardcoded VS version and partial toolset check
toolchain_path = os.path.join(self.client.current_folder, "build",
"conan_toolchain.cmake").replace("\\", "/")
self._run_build(settings, options)
self.assertIn('cmake -G "Visual Studio 15 2017" '
'-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolchain_path), self.client.out)
'-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"', self.client.out)
if toolset == "v140":
self.assertIn("Microsoft Visual Studio 14.0", self.client.out)
else:
Expand Down Expand Up @@ -312,12 +308,10 @@ def test_toolchain_mingw_win(self, build_type, libcxx, version, cppstd, arch, sh
"build_type": build_type,
}
options = {"shared": shared}
install_out = self._run_build(settings, options)
self._run_build(settings, options)
self.assertIn("The C compiler identification is GNU", self.client.out)
toolchain_path = os.path.join(self.client.current_folder, "build",
"conan_toolchain.cmake").replace("\\", "/")
self.assertIn('cmake -G "MinGW Makefiles" '
'-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolchain_path), self.client.out)
'-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"', self.client.out)
assert '-DCMAKE_SH="CMAKE_SH-NOTFOUND"' in self.client.out

def _verify_out(marker=">>"):
Expand Down Expand Up @@ -370,10 +364,8 @@ def test_toolchain_linux(self, build_type, cppstd, arch, libcxx, shared):
"arch": arch,
"build_type": build_type}
self._run_build(settings, {"shared": shared})
toolchain_path = os.path.join(self.client.current_folder, "build",
"conan_toolchain.cmake").replace("\\", "/")
self.assertIn('cmake -G "Unix Makefiles" '
'-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolchain_path), self.client.out)
'-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"', self.client.out)

extensions_str = "ON" if "gnu" in cppstd else "OFF"
arch_str = "-m32" if arch == "x86" else "-m64"
Expand Down Expand Up @@ -430,10 +422,8 @@ def test_toolchain_apple(self, build_type, cppstd, shared):
"build_type": build_type}
self._run_build(settings, {"shared": shared})

toolchain_path = os.path.join(self.client.current_folder, "build",
"conan_toolchain.cmake").replace("\\", "/")
self.assertIn('cmake -G "Unix Makefiles" '
'-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolchain_path), self.client.out)
'-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"', self.client.out)

extensions_str = "OFF" if cppstd else ""
vals = {"CMAKE_CXX_STANDARD": cppstd,
Expand Down
14 changes: 7 additions & 7 deletions conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ def test_cmake_presets_multiple_settings_single_config():
assert os.path.exists(user_presets_path)
user_presets = json.loads(load(user_presets_path))
assert len(user_presets["include"]) == 1
presets = json.loads(load(user_presets["include"][0]))
presets = json.loads(client.load(user_presets["include"][0]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 1
assert len(presets["testPresets"]) == 1
Expand All @@ -710,7 +710,7 @@ def test_cmake_presets_multiple_settings_single_config():
assert os.path.exists(user_presets_path)
user_presets = json.loads(load(user_presets_path))
assert len(user_presets["include"]) == 2
presets = json.loads(load(user_presets["include"][0]))
presets = json.loads(client.load(user_presets["include"][0]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 1
assert len(presets["testPresets"]) == 1
Expand All @@ -720,7 +720,7 @@ def test_cmake_presets_multiple_settings_single_config():
assert presets["testPresets"][0]["name"] == "conan-apple-clang-12.0-gnu17-release"
assert presets["testPresets"][0]["configurePreset"] == "conan-apple-clang-12.0-gnu17-release"

presets = json.loads(load(user_presets["include"][1]))
presets = json.loads(client.load(user_presets["include"][1]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 1
assert len(presets["testPresets"]) == 1
Expand All @@ -741,7 +741,7 @@ def test_cmake_presets_multiple_settings_single_config():
user_presets = json.loads(load(user_presets_path))
# The [0] is the apple-clang 12 the [1] is the apple-clang 13
assert len(user_presets["include"]) == 3
presets = json.loads(load(user_presets["include"][2]))
presets = json.loads(client.load(user_presets["include"][2]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 1
assert len(presets["testPresets"]) == 1
Expand Down Expand Up @@ -887,7 +887,7 @@ def test_cmake_presets_multiple_settings_multi_config():
assert os.path.exists(user_presets_path)
user_presets = json.loads(load(user_presets_path))
assert len(user_presets["include"]) == 1
presets = json.loads(load(user_presets["include"][0]))
presets = json.loads(client.load(user_presets["include"][0]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 1
assert len(presets["testPresets"]) == 1
Expand All @@ -904,7 +904,7 @@ def test_cmake_presets_multiple_settings_multi_config():
assert os.path.exists(user_presets_path)
user_presets = json.loads(load(user_presets_path))
assert len(user_presets["include"]) == 1
presets = json.loads(load(user_presets["include"][0]))
presets = json.loads(client.load(user_presets["include"][0]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 2
assert len(presets["testPresets"]) == 2
Expand All @@ -928,7 +928,7 @@ def test_cmake_presets_multiple_settings_multi_config():
user_presets = json.loads(load(user_presets_path))
# The [0] is the msvc dynamic/14 the [1] is the static/17
assert len(user_presets["include"]) == 2
presets = json.loads(load(user_presets["include"][1]))
presets = json.loads(client.load(user_presets["include"][1]))
assert len(presets["configurePresets"]) == 1
assert len(presets["buildPresets"]) == 1
assert len(presets["testPresets"]) == 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def test_clang_visual_studio_generator(self, client):
'-c tools.cmake.cmaketoolchain:generator="{}"'.format(generator))
assert 'cmake -G "{}"'.format(generator) in client.out
assert "MSVC-like command-line" in client.out
assert "main __clang_major__16" in client.out
assert "main __clang_major__17" in client.out
# Check this! Clang compiler in Windows is reporting MSC_VER and MSVC_LANG!
assert "main _MSC_VER193" in client.out
assert "main _MSVC_LANG2017" in client.out
Expand Down
4 changes: 1 addition & 3 deletions conans/test/functional/toolchains/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ def build(self):
client = TestClient()
client.save({"conanfile.py": conanfile})
client.run("build .", assert_error=True) # No CMakeLists.txt
toolchain_path = os.path.join(client.current_folder,
"conan_toolchain.cmake").replace("\\", "/")
self.assertIn('-DCMAKE_TOOLCHAIN_FILE="{}"'.format(toolchain_path), client.out)
self.assertIn('-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"', client.out)
self.assertIn("ERROR: conanfile.py: Error in build() method", client.out)

@pytest.mark.tool("visual_studio")
Expand Down