Skip to content

Commit

Permalink
Feature/cmake presets custom settings (#11308)
Browse files Browse the repository at this point in the history
* kind of working

* Added test, pending try windows

* Changed layout generator folder

* asserts windows cppstd

* Fix test

* Fixed win test

* win again

* win passing test and more complete

* Support for options

* Fix win test

* Review and not append if no option

* False is valid
  • Loading branch information
lasote committed May 23, 2022
1 parent 2508192 commit 98d1954
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 28 deletions.
4 changes: 2 additions & 2 deletions conan/tools/cmake/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def __init__(self, conanfile):
self._conanfile = conanfile

cmake_presets = load_cmake_presets(conanfile.generators_folder)
configure_preset = get_configure_preset(cmake_presets,
conanfile.settings.get_safe("build_type"))
configure_preset = get_configure_preset(cmake_presets, conanfile)

self._generator = configure_preset["generator"]
self._toolchain_file = configure_preset["toolchainFile"]
self._cache_variables = configure_preset["cacheVariables"]
Expand Down
36 changes: 33 additions & 3 deletions conan/tools/cmake/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,19 @@ def cmake_layout(conanfile, generator=None, src_folder="."):
build_type = str(conanfile.settings.build_type)
except ConanException:
raise ConanException("'build_type' setting not defined, it is necessary for cmake_layout()")

suffix = get_build_folder_vars_suffix(conanfile)
if multi:
conanfile.folders.build = "build"
else:
build_type = build_type.lower()
conanfile.folders.build = "cmake-build-{}".format(build_type)
conanfile.folders.build = "cmake-build-{}".format(str(build_type).lower())

if suffix:
conanfile.folders.build += "-{}".format(suffix)

conanfile.folders.generators = os.path.join("build" if not suffix else "build-{}".format(suffix),
"generators")

conanfile.folders.generators = os.path.join("build", "generators")
conanfile.cpp.source.includedirs = ["include"]

if multi:
Expand All @@ -34,3 +40,27 @@ def cmake_layout(conanfile, generator=None, src_folder="."):
else:
conanfile.cpp.build.libdirs = ["."]
conanfile.cpp.build.bindirs = ["."]


def get_build_folder_vars_suffix(conanfile):

build_vars = conanfile.conf.get("tools.cmake.cmake_layout.build_folder_vars",
default=[], check_type=list)
ret = []
for s in build_vars:
tmp = None
if s.startswith("settings."):
_, var = s.split("settings.", 1)
tmp = conanfile.settings.get_safe(var)
elif s.startswith("options."):
_, var = s.split("options.", 1)
value = conanfile.options.get_safe(var)
if value is not None:
tmp = "{}_{}".format(var, value)
else:
raise ConanException("Invalid 'tools.cmake.cmake_layout.build_folder_vars' value, it has"
" to start with 'settings.' or 'options.': {}".format(s))
if tmp:
ret.append(tmp.lower())

return "-".join(ret)
63 changes: 44 additions & 19 deletions conan/tools/cmake/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,51 @@
import os
import platform

from conan.tools.cmake.layout import get_build_folder_vars_suffix
from conan.tools.cmake.utils import is_multi_configuration
from conans.errors import ConanException
from conans.util.files import save, load


def _add_build_preset(conanfile, multiconfig):
build_type = conanfile.settings.get_safe("build_type")
configure_preset_name = _configure_preset_name(build_type, multiconfig)
ret = {"name": build_type,
configure_preset_name = _configure_preset_name(conanfile, multiconfig)
ret = {"name": _build_preset_name(conanfile),
"configurePreset": configure_preset_name}
if multiconfig:
ret["configuration"] = build_type
return ret


def _configure_preset_name(build_type, multiconfig):
return "default" if not build_type or multiconfig else build_type
def _build_preset_name(conanfile):
build_type = conanfile.settings.get_safe("build_type")
suffix = get_build_folder_vars_suffix(conanfile)
if suffix:
if build_type:
return "{}-{}".format(build_type, suffix)
else:
return suffix
return build_type or "default"


def _configure_preset_name(conanfile, multiconfig):
build_type = conanfile.settings.get_safe("build_type")
suffix = get_build_folder_vars_suffix(conanfile)
base = "default" if multiconfig or not build_type else build_type
if suffix:
return "{}-{}".format(base, suffix)
return base


def _add_configure_preset(conanfile, generator, cache_variables, toolchain_file, multiconfig):
build_type = conanfile.settings.get_safe("build_type")
name = _configure_preset_name(build_type, multiconfig)
if not multiconfig:
name = _configure_preset_name(conanfile, multiconfig)
if not multiconfig and build_type:
cache_variables["CMAKE_BUILD_TYPE"] = build_type
ret = {
"name": name,
"displayName": "{} Config".format(name.capitalize()),
"description": "{} configure using '{}' generator".format(name.capitalize(), generator),
"displayName": "'{}' config".format(name),
"description": "'{}' configure using '{}' generator".format(name, generator),
"generator": generator,
"cacheVariables": cache_variables,
"toolchainFile": toolchain_file,
Expand Down Expand Up @@ -68,21 +85,23 @@ def write_cmake_presets(conanfile, toolchain_file, generator):

preset_path = os.path.join(conanfile.generators_folder, "CMakePresets.json")
multiconfig = is_multi_configuration(generator)
build_type = conanfile.settings.get_safe("build_type")

if os.path.exists(preset_path):
# We append the new configuration making sure that we don't overwrite it
data = json.loads(load(preset_path))
if multiconfig:
build_presets = data["buildPresets"]
already_exist = any([b["configuration"] for b in build_presets if b == build_type])
build_preset_name = _build_preset_name(conanfile)
already_exist = any([b["configuration"]
for b in build_presets if b == build_preset_name])
if not already_exist:
data["buildPresets"].append(_add_build_preset(conanfile, multiconfig))
else:
configure_presets = data["configurePresets"]
configure_preset_name = _configure_preset_name(conanfile, multiconfig)
already_exist = any([c["name"]
for c in configure_presets
if c["name"] == _configure_preset_name(build_type, multiconfig)])
if c["name"] == configure_preset_name])
if not already_exist:
conf_preset = _add_configure_preset(conanfile, generator, cache_variables,
toolchain_file, multiconfig)
Expand All @@ -100,29 +119,35 @@ def write_cmake_presets(conanfile, toolchain_file, generator):
user_presets_path = os.path.join(conanfile.source_folder, "CMakeUserPresets.json")
if not os.path.exists(user_presets_path):
data = {"version": 4, "include": [preset_path]}
data = json.dumps(data, indent=4)
save(user_presets_path, data)
else:
data = json.loads(load(user_presets_path))
if preset_path not in data["include"]:
data["include"].append(preset_path)

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


def load_cmake_presets(folder):
tmp = load(os.path.join(folder, "CMakePresets.json"))
return json.loads(tmp)


def get_configure_preset(cmake_presets, build_type):

# Do we find a preset for the current build type?
def get_configure_preset(cmake_presets, conanfile):
expected_name = _configure_preset_name(conanfile, multiconfig=False)
# Do we find a preset for the current configuration?
for preset in cmake_presets["configurePresets"]:
if preset["name"] == build_type:
if preset["name"] == expected_name:
return preset

expected_name = _configure_preset_name(conanfile, multiconfig=True)
# In case of multi-config generator or None build_type
for preset in cmake_presets["configurePresets"]:
if preset["name"] == "default":
if preset["name"] == expected_name:
return preset

# FIXME: Might be an issue if someone perform several conan install that involves different
# CMake generators (multi and single config). Would be impossible to determine which
# is the correct configurePreset because the generator IS in the configure preset.

raise ConanException("Not available configurePreset for the build_type {}".format(build_type))
raise ConanException("Not available configurePreset, expected name is {}".format(expected_name))
1 change: 1 addition & 0 deletions conans/model/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"tools.cmake.cmaketoolchain:system_version": "Define CMAKE_SYSTEM_VERSION in CMakeToolchain",
"tools.cmake.cmaketoolchain:system_processor": "Define CMAKE_SYSTEM_PROCESSOR in CMakeToolchain",
"tools.env.virtualenv:auto_use": "Automatically activate virtualenv file generation",
"tools.cmake.cmake_layout.build_folder_vars": "Settings and Options that will produce a different build folder and different CMake presets names",
"tools.files.download:retry": "Number of retries in case of failure when downloading",
"tools.files.download:retry_wait": "Seconds to wait between download attempts",
"tools.gnu:make_program": "Indicate path to make program",
Expand Down

0 comments on commit 98d1954

Please sign in to comment.