Repository: conan-io/conan
Pull request: Fix/cmakedeps tool no build type
Changelog: Bugfix: CMakeDeps generate files for ``tool_requires`` with the same ``build_type`` as the "host" context.
Docs: Omit

Close https://github.com/conan-io/conan/issues/13209

Problem statement:
[bug] CMakeDeps.generate() fails when `build_context_activated` is used
### Environment details

* Windows 10:
* Compiler: MSVC 2022
* Conan version: 1.58.0
* Python version: 3.7.5


### Steps to reproduce

1. have this in the `conanfile.py`
```
    tool_requires = ["clang-style-tools-integration/2.3.1@us/release"]
    
    def generate(self):
        deps = CMakeDeps(self)
        deps.build_context_activated = ["clang-style-tools-integration"]
        deps.generate()
```
2. the potentially relevant part of the `conanfile.py` of the `clang-style-tools-integration` package:
```
    def package_info(self):
        # to be used with the cmake_find_package and cmake_find_package_multi Conan generators
        self.cpp_info.build_modules.append("FindClangStyleToolsIntegration.cmake")
        # to be used with the newer CMakeDeps generator
        self.cpp_info.set_property('cmake_build_modules', ["FindClangStyleToolsIntegration.cmake"])
        self.cpp_info.set_property("cmake_find_mode", "config")
```
3. run `conan install ..`

When using the same package as "private" requires instead of as `tool_requires` and without the `deps.build_context_activated =` it works without any other changes so i assume the recipe is basically ok.

### Logs

```
conanfile.py (fusiongui/3b6cca5-dirty): Calling generate()
conanfile.py (fusiongui/3b6cca5-dirty): Preset 'relwithdebinfo' added to CMakePresets.json. Invoke it manually using 'cmake --preset relwithdebinfo'
conanfile.py (fusiongui/3b6cca5-dirty): If your CMake version is not compatible with CMakePresets (<3.19) call cmake like: 'cmake <path> -G Ninja -DCMAKE_TOOLCHAIN_FILE=C:\Users\bauepete\workspaces\fusiongui_cloned\fusion\build_conantest\conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=RelWithDebInfo'
ERROR: conanfile.py (fusiongui/3b6cca5-dirty): Error in generate() method, line 44
        deps.generate()
        AttributeError: 'NoneType' object has no attribute 'lower'
```

Hints from issue discussion:
Hi @petermbauer 

Thanks for your detailed report.

It seems you are missing ``deps.build_context_build_modules = ["xxxx"]``
The ``build_context_activated`` is meant for generating the config.cmake files, but activating their build-modules is also opt-in. This is because in-package scripts might easily conflict between build and host context, so it is better to do it explicitly to avoid potential errors.
Hi @memsharded , thanks for the quick reply!
I have it in my file but left it out intentionally since it is not required to reproduce the error. So same error when i add it.
That is really unexpected, and I checked, there are tests in the test suite that explicitly test that this is working, and I wrote a new test too, it worked with that. There might be something else there that is causing that error. Maybe if you could define env-var CONAN_VERBOSE_TRACEBACK to display a larger traceback, that could help. Or trying to post a bit more complete conanfiles, like there might be some detail, for example the ``build_context_build_modules`` is a relevant detail you ommitted.
Ah, thanks for pointing me to the `CONAN_VERBOSE_TRACEBACK`, i was already looking for such a setting in  https://docs.conan.io/1/mastering/logging.html.
Here is the larger traceback:
```
...
Installing (downloading, building) binaries...
clang-style-tools-integration/2.3.1@us/release: Already installed!
conanfile.py (someapp/None): Applying build-requirement: clang-style-tools-integration/2.3.1@us/release
conanfile.py (someapp/None): Generator txt created conanbuildinfo.txt
conanfile.py (someapp/None): Calling generate()
ERROR: Traceback (most recent call last):
  File "C:\Users\someuser\workspaces\someapp\theapp\.venv\lib\site-packages\conans\errors.py", line 34, in conanfile_exception_formatter
    yield
  File "C:\Users\someuser\workspaces\someapp\theapp\.venv\lib\site-packages\conans\client\generators\__init__.py", line 259, in write_toolchain
    conanfile.generate()
  File "C:\Users\someuser\workspaces\someapp\theapp\conanfile.py", line 15, in generate
    deps.generate()
  File "C:\Users\someuser\workspaces\someapp\theapp\.venv\lib\site-packages\conan\tools\cmake\cmakedeps\cmakedeps.py", line 48, in generate
    generator_files = self.content
  File "C:\Users\someuser\workspaces\someapp\theapp\.venv\lib\site-packages\conan\tools\cmake\cmakedeps\cmakedeps.py", line 90, in content
    self._generate_files(require, dep, ret, find_module_mode=False)
  File "C:\Users\someuser\workspaces\someapp\theapp\.venv\lib\site-packages\conan\tools\cmake\cmakedeps\cmakedeps.py", line 103, in _generate_files
    ret[data_target.filename] = data_target.render()
  File "C:\Users\someuser\workspaces\someapp\theapp\.venv\lib\site-packages\conan\tools\cmake\cmakedeps\templates\target_data.py", line 20, in filename
    data_fname += "{}-{}".format(self.file_name, self.configuration.lower())
AttributeError: 'NoneType' object has no attribute 'lower'
```


This is the minimal conanfile.py to reproduce it:
```Python
from conan import ConanFile
from conan.tools.cmake import CMakeDeps


class SomeAppConan(ConanFile):
    name = "someapp"
    settings = ("os", "compiler", "build_type", "arch")

    tool_requires = ["clang-style-tools-integration/2.3.1@us/release"]

    def generate(self):
        deps = CMakeDeps(self)
        deps.build_context_activated = ["clang-style-tools-integration"]
        deps.build_context_build_modules = ["clang-style-tools-integration"]
        deps.generate()
```
Here is the conanfile.py of the dependency:
```
from conans import ConanFile


class CodingStyleToolsIntegration(ConanFile):
    name = "clang-style-tools-integration"
    no_copy_source = True

    scm = {
        'type': 'git',
        'url': 'auto',
        'revision': 'auto'
    }

    def build(self):
        pass

    def package(self):
        for pattern in [".clang-*", "*.cmake", "scripts/*"]:
            self.copy(pattern)

    def deploy(self):
        self.copy(".clang-*")

    def package_info(self):
        self.env_info.CODING_STYLE_TOOLS_INTEGRATION_DIR = self.package_folder
        # to be used with the cmake_find_package and cmake_find_package_multi Conan generators
        self.cpp_info.build_modules.append("FindClangStyleToolsIntegration.cmake")
        # to be used with the newer CMakeDeps generator
        self.cpp_info.set_property('cmake_build_modules', ["FindClangStyleToolsIntegration.cmake"])
        self.cpp_info.set_property("cmake_find_mode", "config")
```
Quick hint for 2.0: https://github.com/conan-io/conan/pull/13226. This makes the traces more naturally to appear with ``-vv`` or ``-vvv``, that will probably be easier to guess than using such env-var.
Thanks for the details, I managed to reproduce with them.

I am submitting a potential fix in https://github.com/conan-io/conan/pull/13260 for 1.60, but not sure if it will work, sounds a bit risky.

In the meantime, the issue was caused because missing ``build_type`` in the "build" profile. If you define it in your build profile or just pass ``-s:b build_type=Release``, that will workaround the issue.
I have some bad news, the changes in https://github.com/conan-io/conan/pull/13260 required to explicitly change a test that was capturing the behavior that the build context should have its own different build_type. I am concerned that the proposed change would be breaking users relying on this. While I think this could be changed in 2.0, aligning host and build contexts behavior, I don't think it is possible to do it in 1.X safely. The alternatives would be:

- Workaround the issue with a "build" profile that declares the ``build_type``
- Propose a different workaround specific for 1.X that falls back to the host ``build_type`` only when the expectation is that it would otherwise crash
Thanks for taking care of this, no problem to set a `build_type` for the "build" profile in my case.
Ok, excellent, I'll move the PR #13260 to 2.0 then, thanks!

Commit message: fixing for 2.0

Patch to review:
diff --git a/conan/tools/cmake/cmakedeps/templates/__init__.py b/conan/tools/cmake/cmakedeps/templates/__init__.py
index dbf9c0dc668..fb785c59ef0 100644
--- a/conan/tools/cmake/cmakedeps/templates/__init__.py
+++ b/conan/tools/cmake/cmakedeps/templates/__init__.py
@@ -60,18 +60,11 @@ def filename(self):
 
     @property
     def configuration(self):
-        if not self.require.build:
-            return self.cmakedeps.configuration \
-                if self.cmakedeps.configuration else None
-        else:
-            return self.conanfile.settings_build.get_safe("build_type")
+        return self.cmakedeps.configuration
 
     @property
     def arch(self):
-        if not self.require.build:
-            return self.cmakedeps.arch if self.cmakedeps.arch else None
-        else:
-            return self.conanfile.settings_build.get_safe("arch")
+        return self.cmakedeps.arch
 
     @property
     def config_suffix(self):
diff --git a/conans/__init__.py b/conans/__init__.py
index c49b248976a..cb1d464001f 100644
--- a/conans/__init__.py
+++ b/conans/__init__.py
@@ -2,4 +2,4 @@
 REVISIONS = "revisions"  # Only when enabled in config, not by default look at server_launcher.py
 OAUTH_TOKEN = "oauth_token"
 
-__version__ = '2.0.0-dev'
+__version__ = '2.0.1-dev'
diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py
index ba6729e74da..d3001e94a3d 100644
--- a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py
+++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps.py
@@ -341,7 +341,7 @@ def build(self):
 
 
 def test_custom_configuration(client):
-    """  The configuration may differ from the build context and the host context"""
+    """  The configuration in the build context is still the same than the host context"""
     conanfile = textwrap.dedent("""
        from conan import ConanFile
        from conan.tools.cmake import CMakeDeps
@@ -366,12 +366,12 @@ def generate(self):
     client.run("install . -pr:h default -s:b build_type=RelWithDebInfo"
                " -pr:b default -s:b arch=x86 --build missing")
     curdir = client.current_folder
-    data_name_context_build = "liba_build-relwithdebinfo-x86-data.cmake"
+    data_name_context_build = f"liba_build-debug-{host_arch}-data.cmake"
     data_name_context_host = f"liba-debug-{host_arch}-data.cmake"
     assert os.path.exists(os.path.join(curdir, data_name_context_build))
     assert os.path.exists(os.path.join(curdir, data_name_context_host))
 
-    assert "set(liba_build_INCLUDE_DIRS_RELWITHDEBINFO" in \
+    assert "set(liba_build_INCLUDE_DIRS_DEBUG" in \
            open(os.path.join(curdir, data_name_context_build)).read()
     assert "set(liba_INCLUDE_DIRS_DEBUG" in \
            open(os.path.join(curdir, data_name_context_host)).read()
diff --git a/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py
index 333b7d2a2b7..45add87f6ce 100644
--- a/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py
+++ b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py
@@ -468,6 +468,38 @@ def build(self):
     # The debug binaries are missing, so adding --build=missing
     c.run("create example -pr:b=default -pr:h=default -s:h build_type=Debug --build=missing "
           "--build=example")
+
     # listed as both requires and build_requires
     c.assert_listed_require({"example/1.0": "Cache"})
     c.assert_listed_require({"example/1.0": "Cache"}, build=True)
+
+
+def test_using_package_module():
+    """
+    This crashed, because the profile "build" didn't have "build_type"
+    https://github.com/conan-io/conan/issues/13209
+    """
+    c = TestClient()
+    c.save({"conanfile.py": GenConanfile("tool", "0.1")})
+    c.run("create .")
+
+    consumer = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import CMakeDeps
+        class Pkg(ConanFile):
+            name = "pkg"
+            version = "0.1"
+            settings = "os", "compiler", "build_type", "arch"
+            tool_requires = "tool/0.1"
+
+            def generate(self):
+                deps = CMakeDeps(self)
+                deps.build_context_activated = ["tool"]
+                deps.build_context_build_modules = ["tool"]
+                deps.generate()
+        """)
+    c.save({"conanfile.py": consumer,
+            "profile_build": "[settings]\nos=Windows"}, clean_first=True)
+    c.run("create . -pr:b=profile_build")
+    # it doesn't crash anymore, it used to crash
+    assert "pkg/0.1: Created package" in c.out


Code review comments:
diff_hunk: @@ -2,4 +2,4 @@
 REVISIONS = "revisions"  # Only when enabled in config, not by default look at server_launcher.py
 OAUTH_TOKEN = "oauth_token"
 
-__version__ = '2.0.0-dev'
+__version__ = '2.0.1-dev'
path: conans/__init__.py
text: @user1:
```suggestion
__version__ = '2.0.0-dev'
```