Repository: conan-io/conan

Issue:
[question] Packaging prebuilt projects with existing FooConfing.cmake and Generating Multiple CMake Presets  
### What is your question?

Hello,

I apologize for my lack of English,
We use conan to package manage our projects that use cmake and are used in various operating systems (macos, linux, android) and architectures (arm64, x86_64, x86, armv7, armv6). We can only use Conan for already compiled binaries because we need complex build systems to compile projects and even have to use docker for armv6 because the GCC version we use is extremely old (4.2.3) and the toolchain can only run on x86 Linux.

While developing the projects, we wrote a script that loads these packages with conan for all architectures and operating systems and creates a CMakePresets.json containing separate presets for each configuration. The presets contain their own FOO_DIR cache values.

The problem is that we need to rewrite this script for each project, and we cannot use CMakeToolChain or CMakeDeps because the presets they create are for only one configuration.

I wonder if there is a way to force CMakeToolChain and CMakeDeps to create presets for multiple configurations?

Also we don't want to edit our CMake files for conan and we dont want to use conan as a build tool because cmake do this excellently and we are not comfortable to depend on conan entirely even if its self hosted. 
### Have you read the CONTRIBUTING guide?

- [x] I've read the CONTRIBUTING guide


Discussion:
Hi @sufuk 

> I apologize for my lack of English,

Not an issue! 🙂 

> I wonder if there is a way to force CMakeToolChain and CMakeDeps to create presets for multiple configurations?

I am not sure what is the issue. ``CMakeToolchain`` and ``CMakeDeps`` are already multi-config generators, they can handle multiple configurations without overwriting each other, and they can generate multiple folders for the builds and multiple different presets. 

You can see in https://docs.conan.io/2/examples/tools/cmake/cmake_toolchain/build_project_cmake_presets.html, how it works for multi-config, but the same approach also works for more configurations, by defining the conf ``tools.cmake.cmake_layout:build_folder_vars`` (it can also be defined as ``self.folders.build_folder_vars`` in the recipe), check for example https://docs.conan.io/2/reference/tools/cmake/cmake_layout.html

> Also we don't want to edit our CMake files for conan and we dont want to use conan as a build tool because cmake do this excellently and we are not comfortable to depend on conan entirely even if its self hosted.

Sure, Conan is not a build system, it is not a build tool, it just wraps and call Conan when necessary. Having a ``conanfile.py`` with a ``build()`` method that calls ``cmake.build()`` and it is still doing the same old ``cmake --build ...`` that you would do by hand. And it also works for the developers doing ``conan install + cmake --preset= ...``.

All the ``CMakeToolchain`` + ``CMakeDeps`` integration is designed to work without having to modify at all any ``CMakeLists.txt``.


Conan can also work with xxx-config.cmake files inside packages, with some limitations (mostly due to cmake), please check: https://docs.conan.io/2/examples/tools/cmake/cmake_toolchain/use_package_config_cmake.html


So I'd say that things should work as you desire. If there is something that is not behaving like you expect, the best would be to put a simple and minimal reproducible example in a repo, so we can check it, reproduce and advice. Like some toy example ``conanfile.py`` recipes and the commands necessary to reproduce.


Hello @memsharded 

Thank you for your response,
I will try again with your recommendations and keep update the thread.
Hello again @memsharded 
It seems conan overrides configure presets every time when conan install is called. Is there a way to bypass this ?

`# Conan generated presets should have only 1 configurePreset, no more, overwrite it`
[https://github.com/conan-io/conan/blob/7e0f025fd7a4b134241ae8d3d353118795bd6c19/conan/tools/cmake/presets.py#L180C2-L192C80](url)
The ``CMakePresets.json`` shouldn't be overriden if the right ``layout()`` definition is provided (or it should only be overriden when modeled as such). If the layout definition is correct, new presets will be added/appended to the existing ones, not overriding them.

The ``CMakeUserPresets.json`` might be overriden to point to the actual ``CMakePresets.json``, but only if the ``CMakeUserPresets.json`` doesn't exist or was created by Conan (``conan`` vendor field).

So I am not sure what could be happening, we would need to have something we could reproduce to understand the issue. Thanks!
> The `CMakePresets.json` shouldn't be overriden if the right `layout()` definition is provided (or it should only be overriden when modeled as such). If the layout definition is correct, new presets will be added/appended to the existing ones, not overriding them.
> 
> The `CMakeUserPresets.json` might be overriden to point to the actual `CMakePresets.json`, but only if the `CMakeUserPresets.json` doesn't exist or was created by Conan (`conan` vendor field).
> 
> So I am not sure what could be happening, we would need to have something we could reproduce to understand the issue. Thanks!

Ok I will try to make an reproducable example just to be sure My generator is not in Mutli config gens 
![image](https://github.com/conan-io/conan/assets/32745693/2c8b18ed-ed8c-4535-8af8-5e224d2ba5cb)
is this affect this?
It depends, if you are in Windows with msvc, by default it will be multi-config, because CMake Visual Studio generator is multi-config.

This is why the reproducible case is important, clarifying the files used, the commands, and also the inputs (OS, compiler), and reporting on the full output...
Hello @memsharded it turns out key is using tools.cmake.cmake_layout:build_folder_vars="['settings.os', 'settings.arch']"
That way Any of the configureations (CMakePresets.json)doesn't overwrite each other, and I dont do anything special for CMakeUserPresets.json but it doesnt overwritten it appends new configurations just like I wanted. Now I can 
configure my projects in same works space for android-(armv7, arm64, x86, x86_64), linux, macos.

I have only one question it not a problem I fixed it for me in conanfie.py generator method, but is there any way to use relative paths for for example toolchainFile in the CMakePresets.json and include paths for presets in User Preset file?

> t turns out key is using tools.cmake.cmake_layout:build_folder_vars="['settings.os', 'settings.arch']"
That way Any of the configureations (CMakePresets.json)doesn't overwrite each other, and I dont do anything special for CMakeUserPresets.json but it doesnt overwritten it appends new configurations just like I wanted. Now I can
configure my projects in same works space for android-(armv7, arm64, x86, x86_64), linux, macos.

Excellent, I am happy that my suggestion above (``tools.cmake.cmake_layout:build_folder_vars (it can also be defined as self.folders.build_folder_vars``) helped, thanks for the follow up!

> I have only one question it not a problem I fixed it for me in conanfie.py generator method, but is there any way to use relative paths for for example toolchainFile in the CMakePresets.json and include paths for presets in User Preset file?

Yes, we try to make paths relative whenever possible, to allow some relocatibility. I am not sure why these paths are not relative,  maybe it doesn't work, but we can give it another try.
> > t turns out key is using tools.cmake.cmake_layout:build_folder_vars="['settings.os', 'settings.arch']"
> > That way Any of the configureations (CMakePresets.json)doesn't overwrite each other, and I dont do anything special for CMakeUserPresets.json but it doesnt overwritten it appends new configurations just like I wanted. Now I can
> > configure my projects in same works space for android-(armv7, arm64, x86, x86_64), linux, macos.
> 
> Excellent, I am happy that my suggestion above (`tools.cmake.cmake_layout:build_folder_vars (it can also be defined as self.folders.build_folder_vars`) helped, thanks for the follow up!
> 
> > I have only one question it not a problem I fixed it for me in conanfie.py generator method, but is there any way to use relative paths for for example toolchainFile in the CMakePresets.json and include paths for presets in User Preset file?
> 
> Yes, we try to make paths relative whenever possible, to allow some relocatibility. I am not sure why these paths are not relative, maybe it doesn't work, but we can give it another try.

Hello @memsharded thanks four your quick response again with absolute paths It is impossible to call conan install in the host and built project in the docker container(Mounted conan cache of host and project dir), but with relative paths it works quite good, I didn't see any problem,



Patch:
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index 3ac9760fb3c..02a80b39cd7 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -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))
diff --git a/conan/tools/cmake/presets.py b/conan/tools/cmake/presets.py
index c6fd71bd851..d50bfdea228 100644
--- a/conan/tools/cmake/presets.py
+++ b/conan/tools/cmake/presets.py
@@ -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
@@ -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:
@@ -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
@@ -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
@@ -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
@@ -326,7 +331,7 @@ 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.
@@ -334,7 +339,8 @@ def _append_user_preset_path(data, 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
diff --git a/conan/tools/cmake/toolchain/toolchain.py b/conan/tools/cmake/toolchain/toolchain.py
index 8391f93790c..22e8c38013e 100644
--- a/conan/tools/cmake/toolchain/toolchain.py
+++ b/conan/tools/cmake/toolchain/toolchain.py
@@ -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):
@@ -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)
 


Test patch:
diff --git a/conans/test/functional/toolchains/cmake/test_cmake.py b/conans/test/functional/toolchains/cmake/test_cmake.py
index d98610e4ade..7743cc9464a 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake.py
@@ -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:
@@ -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=">>"):
@@ -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"
@@ -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,
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
index 2463c2f9710..17ded05c24f 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py
index c2fcd265e39..1261ea73189 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py
@@ -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
diff --git a/conans/test/functional/toolchains/test_basic.py b/conans/test/functional/toolchains/test_basic.py
index 54fad8c3605..314b44e7907 100644
--- a/conans/test/functional/toolchains/test_basic.py
+++ b/conans/test/functional/toolchains/test_basic.py
@@ -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")