Repository: conan-io/conan
Pull request: build-folder-vars user.value
Changelog: Feature: Add ``build_folder_vars=["const.myvalue"]`` to create presets for user "myvalue" arbitrary values.
Docs: https://github.com/conan-io/docs/pull/3800

Close https://github.com/conan-io/conan/issues/16557

Problem statement:
[question] preset name != profile name
### What is your question?

For my conan profile "Release" the cmake preset ends up being "conan-release", and for "Debug" it ends up being "conan-debug". This is fine! But for "DebugASAN", it also ends up being "conan-debug" and not "conan-debugasan".

Is there some easy way I can control the preset name from the profile? Or always have it be the same?

Sorry, if this has been asked before; most things I could find where outdated or referred to Conan1.

### Have you read the CONTRIBUTING guide?

- [X] I've read the CONTRIBUTING guide

Hints from issue discussion:
Hi @h-2 

Thanks for your question.

The profile file name is never used in the preset itself, if your profile file is named "Release", it is a coincidence. The presets names are taken:
- by default from the ``build_type`` setting
- It can be customized with the ``self.folders.build_folder_vars`` as ``self.folders.build_folder_vars = ["settings.os", "options.shared"]`` in recipes
- It can also be customized with the ``tools.cmake.cmake_layout:build_folder_vars`` conf from profiles.

So if your ``asan`` is something like an option in your profile, it should be easy to get it into the preset name, is this the case?
Thank you for the quick reply and the explanations.

> So if your asan is something like an option in your profile, it should be easy to get it into the preset name, is this the case?

Well, it changes the cxx_flags, but I wouldnt want to add those to the name...

Is there a way I can just do `tools.cmake.cmake_layout:build_folder_vars = profile_name` or even set it to a fixed string?


I have tried adding:
```
tools.cmake.cmake_layout:build_folder_vars = [ "profile_name" ]
```
But it doesn't work:
```
conanfile.txt: ERROR: Traceback (most recent call last):
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conans/client/generators/__init__.py", line 97, in write_generators
    generator.generate()
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/toolchain/toolchain.py", line 269, in generate
    write_cmake_presets(self._conanfile, toolchain, self.generator, cache_variables,
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/presets.py", line 19, in write_cmake_presets
    preset_path, preset_data = _CMakePresets.generate(conanfile, toolchain_file, generator,
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/presets.py", line 72, in generate
    data = _CMakePresets._contents(conanfile, toolchain_file, cache_variables, generator,
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/presets.py", line 98, in _contents
    conf = _CMakePresets._configure_preset(conanfile, generator, cache_variables, toolchain_file,
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/presets.py", line 115, in _configure_preset
    name = _CMakePresets._configure_preset_name(conanfile, multiconfig)
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/presets.py", line 228, in _configure_preset_name
    custom_conf, user_defined_build = get_build_folder_custom_vars(conanfile)
  File "/opt/prepare/corpus/.venv/lib/python3.10/site-packages/conan/tools/cmake/layout.py", line 84, in get_build_folder_custom_vars
    group, var = s.split(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
```

I am also not really sure how this is related to the layout of the build folder at all. It's just about the naming of the preset 🤔 
> tools.cmake.cmake_layout:build_folder_vars = profile_name

It is not possible at this moment. Note that profile name is not even a single file, as there can be (and it is common) to use multiple profiles like ``-pr=profile1 -pr=profile2 -pr=profile3``.  This would be very challenging to implement, as the profile names are not really stored and propagated, they are evaluated very early and only the ``settings``, ``options``, ``conf`` values are propagating down to the recipes and CMake helpers.

The best approach for these cases is usually to abstract the concept of sanitizers, with custom settings ore options like in https://docs.conan.io/2/reference/binary_model/extending.html#extending-the-binary-model.
Because the binary compatibility might be affected by them, so a explicit model of the sanitizer beyond the low level ``cxxflags`` is beneficial to manage different ``package_id`` binaries for the binaries with sanitization and binaries without. Or you don't want to manage this difference in your binaries?

Commit message: Update conan/tools/cmake/layout.py

Co-authored-by: Abril Rincón Blanco <git@rinconblanco.es>

Patch to review:
diff --git a/conan/tools/cmake/layout.py b/conan/tools/cmake/layout.py
index dad6ce31270..a30364f52ac 100644
--- a/conan/tools/cmake/layout.py
+++ b/conan/tools/cmake/layout.py
@@ -94,9 +94,11 @@ def get_build_folder_custom_vars(conanfile):
                     tmp = "{}_{}".format(var, value)
         elif group == "self":
             tmp = getattr(conanfile, var, None)
+        elif group == "user":
+            tmp = var
         else:
             raise ConanException("Invalid 'tools.cmake.cmake_layout:build_folder_vars' value, it has"
-                                 " to start with 'settings.' or 'options.': {}".format(s))
+                                 " to start with 'settings.', 'options.' or '.user': {}".format(s))
         if tmp:
             ret.append(tmp.lower())
 
diff --git a/test/integration/toolchains/cmake/test_cmaketoolchain.py b/test/integration/toolchains/cmake/test_cmaketoolchain.py
index dee27284e79..af884767ba6 100644
--- a/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -1278,6 +1278,32 @@ def layout(self):
     assert "conan-windows-pkg-0.1-debug" in presets
 
 
+def test_build_folder_vars_constants_user():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout
+
+        class Conan(ConanFile):
+            name = "dep"
+            version = "0.1"
+            settings = "os", "build_type"
+            generators = "CMakeToolchain"
+
+            def layout(self):
+                cmake_layout(self)
+        """)
+    c.save({"conanfile.py": conanfile})
+    conf = "tools.cmake.cmake_layout:build_folder_vars='[\"user.myvalue\"]'"
+    settings = " -s os=FreeBSD -s arch=armv8 -s build_type=Debug"
+    c.run("install . -c={} {}".format(conf, settings))
+    assert "cmake --preset conan-myvalue-debug" in c.out
+    assert os.path.exists(os.path.join(c.current_folder, "build", "myvalue", "Debug"))
+    presets = load(os.path.join(c.current_folder,
+                                "build/myvalue/Debug/generators/CMakePresets.json"))
+    assert "conan-myvalue-debug" in presets
+
+
 def test_extra_flags():
     client = TestClient()
     conanfile = textwrap.dedent("""


Code review comments:
diff_hunk: @@ -1278,6 +1278,32 @@ def layout(self):
     assert "conan-windows-pkg-0.1-debug" in presets
 
 
+def test_build_folder_vars_constants_user():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout
+
+        class Conan(ConanFile):
+            name = "dep"
+            version = "0.1"
+            settings = "os", "build_type"
+            generators = "CMakeToolchain"
+
+            def layout(self):
+                cmake_layout(self)
+        """)
+    c.save({"conanfile.py": conanfile})
+    conf = "tools.cmake.cmake_layout:build_folder_vars='[\"user.myvalue\"]'"
path: test/integration/toolchains/cmake/test_cmaketoolchain.py
text: @user1:
I would be ok with this but I was thinking, why don't we use a user conf to set the value for this?
Like:

```
conan install . -c=tools.cmake.cmake_layout:build_folder_vars='["user.whatever:myvar"]' -s build_type=Debug -c user.whatever:myvar=pepito
```

For me this feels more aligned with the rest of things that we can set via `build_folder_vars` that are variable names that are later evaluated to get the value, not the values directly. 

@author:
That is true, I thought about it, but I checked most of the existing configurations, it didn't seem any of them made much sense, so it should be only ``user.``, not ``tools.`` or ``core.``, and then it was just extra verbosity to define a constant. I actually thought to call it ``constant.myvalue`` to differentiate from ``conf`` ``user.`` values.

@user2:
I think calling it `constant.` is a great idea!

@user1:
Some ideas...

```
param.myvalue
const.myvalue
prop.myvalue
var.myvalue
value.myvalue
```

Maybe we can use `:` instead of `.` ? like `const:myvalue`

Also what about using a symbol preceding the value?

```
@user3
::myvalue
:myvalue
#myvalue
```


@author:
I'd vote for ``const.myvalue``.

The syntax would be more homogenous: 
```
build_folder_vars = [settings.os, options.shared, self.name, const.myvalue]
```


@user1:
I think `const.myvalue` is good enough 👍

@user2:
👍 for const

@author:
Done