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

[dvelop2] user extending Conan generated CMakePresets #13090

Merged
Merged
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
42 changes: 40 additions & 2 deletions conan/tools/cmake/presets.py
Original file line number Diff line number Diff line change
@@ -200,10 +200,10 @@ def write_cmake_presets(conanfile, toolchain_file, generator, cache_variables,

preset_content = json.dumps(data, indent=4)
save(preset_path, preset_content)
_save_cmake_user_presets(conanfile, preset_path, user_presets_path)
_save_cmake_user_presets(conanfile, preset_path, user_presets_path, preset_prefix, data)


def _save_cmake_user_presets(conanfile, preset_path, user_presets_path):
def _save_cmake_user_presets(conanfile, preset_path, user_presets_path, preset_prefix, preset_data):
if not user_presets_path:
return

@@ -223,20 +223,58 @@ def _save_cmake_user_presets(conanfile, preset_path, user_presets_path):
return

# It uses schema version 4 unless it is forced to 2
inherited_user = {}
if os.path.basename(user_presets_path) != "CMakeUserPresets.json":
inherited_user = _collect_user_inherits(output_dir, preset_prefix)

if not os.path.exists(user_presets_path):
data = {"version": _schema_version(conanfile, default=4),
"vendor": {"conan": dict()}}
for preset, inherits in inherited_user.items():
for i in inherits:
data.setdefault(preset, []).append({"name": i})
else:
data = json.loads(load(user_presets_path))
if "conan" not in data.get("vendor", {}):
# The file is not ours, we cannot overwrite it
return

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

data = json.dumps(data, indent=4)
save(user_presets_path, data)


def _collect_user_inherits(output_dir, preset_prefix):
# Collect all the existing targets in the user files, to create empty conan- presets
# so things doesn't break for multi-platform, when inherits don't exist
collected_targets = {}
types = "configurePresets", "buildPresets", "testPresets"
for file in ("CMakePresets.json", "CMakeUserPresests.json"):
user_file = os.path.join(output_dir, file)
if os.path.exists(user_file):
user_json = json.loads(load(user_file))
for preset_type in types:
for preset in user_json.get(preset_type, []):
inherits = preset.get("inherits", [])
if isinstance(inherits, str):
inherits = [inherits]
conan_inherits = [i for i in inherits if i.startswith(preset_prefix)]
if conan_inherits:
collected_targets.setdefault(preset_type, []).extend(conan_inherits)
return collected_targets


def _clean_user_inherits(data, preset_data):
for preset_type in "configurePresets", "buildPresets", "testPresets":
presets = preset_data.get(preset_type, [])
presets_names = [p["name"] for p in presets]
other = data.get(preset_type, [])
other[:] = [p for p in other if p["name"] not in presets_names]


def _get_already_existing_preset_index(name, presets):
"""Get the index of a Preset with a given name, this is used to replace it with updated contents
"""
93 changes: 93 additions & 0 deletions conans/test/functional/toolchains/cmake/test_presets_inherit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import platform
import textwrap

import pytest

from conans.test.utils.tools import TestClient


@pytest.mark.tool("cmake", "3.23")
def test_cmake_presets_with_user_presets_file():
""" Test the integration of the generated one with a user root CMakePresets.json
"""
c = TestClient()
c.run("new cmake_exe -d name=foo -d version=1.0")
conanfile = c.load("conanfile.py")
conanfile = conanfile.replace("tc = CMakeToolchain(self)",
"tc = CMakeToolchain(self)\n"
" tc.user_presets_path = 'ConanPresets.json'\n"
" tc.presets_prefix = 'conan'\n")
cmake_presets = textwrap.dedent("""
{
"version": 4,
"include": ["./ConanPresets.json"],
"configurePresets": [
{
"name": "default",
"displayName": "multi config",
"inherits": "conan-default"
},
{
"name": "release",
"displayName": "release single config",
"inherits": "conan-release"
},
{
"name": "debug",
"displayName": "debug single config",
"inherits": "conan-debug"
}
],
"buildPresets": [
{
"name": "multi-release",
"configurePreset": "default",
"configuration": "Release",
"inherits": "conan-release"
},
{
"name": "multi-debug",
"configurePreset": "default",
"configuration": "Debug",
"inherits": "conan-debug"
},
{
"name": "release",
"configurePreset": "release",
"configuration": "Release",
"inherits": "conan-release"
},
{
"name": "debug",
"configurePreset": "debug",
"configuration": "Debug",
"inherits": "conan-debug"
}
]
}""")
c.save({"conanfile.py": conanfile,
"CMakePresets.json": cmake_presets})

c.run(f"install . ")
c.run(f"install . -s build_type=Debug")

if platform.system() != "Windows":
c.run_command("cmake --preset debug")
c.run_command("cmake --build --preset debug")
c.run_command("./build/Debug/foo")
else:
c.run_command("cmake --preset default")
c.run_command("cmake --build --preset multi-debug")
c.run_command("build\\Debug\\foo")

assert "Hello World Debug!" in c.out

if platform.system() != "Windows":
c.run_command("cmake --preset release")
c.run_command("cmake --build --preset release")
c.run_command("./build/Release/foo")
else:
c.run_command("cmake --build --preset multi-release")
c.run_command("build\\Release\\foo")

assert "Hello World Release!" in c.out