Repository: conan-io/conan
Pull request #12925 (2023-01-18 11:29:58)

fixes for cmake_layout single-config + build_folder_vars
Changelog: Bugfix: Fix ``cmake_layout`` for single-config configurations when defining ``build_folder_vars``
Docs: https://github.com/conan-io/docs/pull/XXXX

Close https://github.com/conan-io/conan/issues/12918



Patch:
diff --git a/conan/tools/cmake/layout.py b/conan/tools/cmake/layout.py
index abddc047f..959ae93e5 100644
--- a/conan/tools/cmake/layout.py
+++ b/conan/tools/cmake/layout.py
@@ -23,14 +23,11 @@ def cmake_layout(conanfile, generator=None, src_folder=".", build_folder="build"
 
     build_folder = build_folder if not subproject else os.path.join(subproject, build_folder)
     config_build_folder, user_defined_build = get_build_folder_custom_vars(conanfile)
-
     if config_build_folder:
-        conanfile.folders.build = os.path.join(build_folder, config_build_folder)
-    else:
-        if not multi and not user_defined_build:
-            conanfile.folders.build = os.path.join(build_folder, build_type)
-        else:
-            conanfile.folders.build = build_folder
+        build_folder = os.path.join(build_folder, config_build_folder)
+    if not multi and not user_defined_build:
+        build_folder = os.path.join(build_folder, build_type)
+    conanfile.folders.build = build_folder
 
     conanfile.folders.generators = os.path.join(conanfile.folders.build, "generators")
 


Test patch:
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
index e172d3927..c4ae2e602 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -587,7 +587,7 @@ def test_cmake_presets_multiple_settings_single_config():
                "-s compiler.version=12.0 -s compiler.cppstd=gnu17"
     client.run("install . {} {}".format(settings, settings_layout))
     assert os.path.exists(os.path.join(client.current_folder, "build", "apple-clang-12.0-gnu17",
-                                       "generators"))
+                                       "Release", "generators"))
     assert os.path.exists(user_presets_path)
     user_presets = json.loads(load(user_presets_path))
     assert len(user_presets["include"]) == 1
@@ -601,27 +601,33 @@ def test_cmake_presets_multiple_settings_single_config():
     assert presets["testPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
     assert presets["testPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
 
-    # If we create the "Debug" one, it has the same toolchain and preset file, that is
-    # always multiconfig
+    # If we create the "Debug" one, it will be appended
     client.run("install . {} -s build_type=Debug {}".format(settings, settings_layout))
-    assert os.path.exists(os.path.join(client.current_folder, "build", "apple-clang-12.0-gnu17", "generators"))
+    assert os.path.exists(os.path.join(client.current_folder, "build", "apple-clang-12.0-gnu17",
+                                       "Release", "generators"))
     assert os.path.exists(user_presets_path)
+    print(load(user_presets_path))
     user_presets = json.loads(load(user_presets_path))
-    assert len(user_presets["include"]) == 1
+    assert len(user_presets["include"]) == 2
     presets = json.loads(load(user_presets["include"][0]))
-    assert len(presets["configurePresets"]) == 2
-    assert len(presets["buildPresets"]) == 2
-    assert len(presets["testPresets"]) == 2
+    assert len(presets["configurePresets"]) == 1
+    assert len(presets["buildPresets"]) == 1
+    assert len(presets["testPresets"]) == 1
     assert presets["configurePresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
-    assert presets["configurePresets"][1]["name"] == "apple-clang-12.0-gnu17-debug"
     assert presets["buildPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
-    assert presets["buildPresets"][1]["name"] == "apple-clang-12.0-gnu17-debug"
     assert presets["buildPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
-    assert presets["buildPresets"][1]["configurePreset"] == "apple-clang-12.0-gnu17-debug"
     assert presets["testPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
-    assert presets["testPresets"][1]["name"] == "apple-clang-12.0-gnu17-debug"
     assert presets["testPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
-    assert presets["testPresets"][1]["configurePreset"] == "apple-clang-12.0-gnu17-debug"
+
+    presets = json.loads(load(user_presets["include"][1]))
+    assert len(presets["configurePresets"]) == 1
+    assert len(presets["buildPresets"]) == 1
+    assert len(presets["testPresets"]) == 1
+    assert presets["configurePresets"][0]["name"] == "apple-clang-12.0-gnu17-debug"
+    assert presets["buildPresets"][0]["name"] == "apple-clang-12.0-gnu17-debug"
+    assert presets["buildPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-debug"
+    assert presets["testPresets"][0]["name"] == "apple-clang-12.0-gnu17-debug"
+    assert presets["testPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-debug"
 
     # But If we change, for example, the cppstd and the compiler version, the toolchain
     # and presets will be different, but it will be appended to the UserPresets.json
@@ -629,12 +635,12 @@ def test_cmake_presets_multiple_settings_single_config():
                "-s compiler.version=13 -s compiler.cppstd=gnu20"
     client.run("install . {} {}".format(settings, settings_layout))
     assert os.path.exists(os.path.join(client.current_folder, "build", "apple-clang-13-gnu20",
-                                       "generators"))
+                                       "Release", "generators"))
     assert os.path.exists(user_presets_path)
     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"]) == 2
-    presets = json.loads(load(user_presets["include"][1]))
+    assert len(user_presets["include"]) == 3
+    presets = json.loads(load(user_presets["include"][2]))
     assert len(presets["configurePresets"]) == 1
     assert len(presets["buildPresets"]) == 1
     assert len(presets["testPresets"]) == 1
@@ -681,8 +687,12 @@ def test_cmake_presets_duplicated_install(multiconfig):
         settings += '-c tools.cmake.cmaketoolchain:generator="Multi-Config"'
     client.run("install . {}".format(settings))
     client.run("install . {}".format(settings))
-    presets_path = os.path.join(client.current_folder, "build", "gcc-5", "generators",
-                                "CMakePresets.json")
+    if multiconfig:
+        presets_path = os.path.join(client.current_folder, "build", "gcc-5", "generators",
+                                    "CMakePresets.json")
+    else:
+        presets_path = os.path.join(client.current_folder, "build", "gcc-5", "Release", "generators",
+                                    "CMakePresets.json")
     assert os.path.exists(presets_path)
     contents = json.loads(load(presets_path))
     assert len(contents["buildPresets"]) == 1
diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
index 7f2bac7fb..c308a6279 100644
--- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -583,7 +583,8 @@ def test_user_presets_version2():
 
             """)
     client.save({"conanfile.py": conanfile, "CMakeLists.txt": "foo"})
-    configs = ["-c tools.cmake.cmaketoolchain.presets:max_schema_version=2 ",
+    configs = ["-c tools.cmake.cmaketoolchain:generator=Ninja",
+               "-c tools.cmake.cmaketoolchain.presets:max_schema_version=2 ",
                "-c tools.cmake.cmake_layout:build_folder_vars='[\"settings.compiler.cppstd\"]'"]
     client.run("install . {} -s compiler.cppstd=14".format(" ".join(configs)))
     client.run("install . {} -s compiler.cppstd=17".format(" ".join(configs)))
@@ -591,10 +592,10 @@ def test_user_presets_version2():
     presets = json.loads(client.load("CMakeUserPresets.json"))
     assert len(presets["configurePresets"]) == 2
     assert presets["version"] == 2
-    assert "build/14/generators/conan_toolchain.cmake" \
+    assert "build/14/Release/generators/conan_toolchain.cmake" \
            in presets["configurePresets"][0]["cacheVariables"]["CMAKE_TOOLCHAIN_FILE"].replace("\\",
                                                                                                "/")
-    assert "build/17/generators/conan_toolchain.cmake" \
+    assert "build/17/Release/generators/conan_toolchain.cmake" \
            in presets["configurePresets"][1]["cacheVariables"]["CMAKE_TOOLCHAIN_FILE"].replace("\\",
                                                                                                "/")
 
@@ -751,7 +752,7 @@ def test_presets_ninja_msvc(arch, arch_toolset):
            "-s compiler.runtime_type=Release"
     client.run("install . {} -s compiler.cppstd=14 {} -s arch={}".format(" ".join(configs), msvc, arch))
 
-    presets = json.loads(client.load("build/14/generators/CMakePresets.json"))
+    presets = json.loads(client.load("build/14/Release/generators/CMakePresets.json"))
 
     toolset_value = {"x86_64": "host=x86_64", "x86": "x86"}.get(arch_toolset)
     arch_value = {"x86_64": "x64", "x86": "x86"}.get(arch)
@@ -779,7 +780,7 @@ def test_presets_ninja_msvc(arch, arch_toolset):
 
     client.run(
         "install . {} -s compiler.cppstd=14 {} -s arch={}".format(" ".join(configs), msvc, arch))
-    presets = json.loads(client.load("build/14/generators/CMakePresets.json"))
+    presets = json.loads(client.load("build/14/Release/generators/CMakePresets.json"))
     assert "architecture" in presets["configurePresets"][0]
     assert "toolset" not in presets["configurePresets"][0]
 
@@ -900,6 +901,11 @@ def test_cmake_layout_toolchain_folder():
           "-c tools.cmake.cmake_layout:build_folder_vars='[\"settings.arch\", \"settings.build_type\"]'")
     assert os.path.exists(os.path.join(c.current_folder,
                                        "build/x86-debug/generators/conan_toolchain.cmake"))
+    c.run("install . -s os=Linux -s compiler=gcc -s compiler.version=7 -s build_type=Debug "
+          "-s compiler.libcxx=libstdc++11 -s arch=x86 "
+          "-c tools.cmake.cmake_layout:build_folder_vars='[\"settings.os\"]'")
+    assert os.path.exists(os.path.join(c.current_folder,
+                                       "build/linux/Debug/generators/conan_toolchain.cmake"))
 
 
 def test_set_linker_scripts():