Repository: conan-io/conan

Issue:
[question] using `boost/system` recipe with system-wide config
<!-- What is your question? Please be as specific as possible! -->

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).

Hi! My question is about `conan` recipes for system packages (like [gtk/system](https://conan.io/center/gtk) or [xorg/system](https://conan.io/center/xorg) from CCI). In our ecosystem we want to use `boost/system` (custom one) together with `boost/1.7?` from CCI (we need a transparent switching between them in the root of dependency tree). We use `CMakeDeps` generator. So before build it generates (among other things) `Boost-config.cmake`, which is very hard to reproduce in `boost/system` . 

- Before `1.36` we use the following smart crutch which exploits a bug (?) in generator. Our recipe declare package `boost/system` with filename (in latest terms `cmake_file_name`) `SystemBoost` and with name (in latest terms `cmake_target_name`) `Boost`. When somebody (e.g. `A`) wants to depend on boost, `CMakeDeps` generates **unused** `SystemBoost-config.cmake` and call `find_package(Boost ... ) ` in `A-config.cmake`. This `find_package` uses system-wide `FindBoost.cmake` or config from `boost-cmake` project and works perfectly. 
- In `1.36` this bug was fixed. ([the method](https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/cmakedeps.py#L698) was introduced). So the crutch is not working now.
- Could you elaborate this situation?

There are my thoughts about it:
- I like the idea to use system-wide config in the specific case of system boost.
- Generating conan's `Boost-config.cmake` to emulate a system one seems very hard. In CCI the only way to do this is using `PkgConfig`, but Boost has no official `.pc` file AFAIK.
- Maybe `CMakeDeps` can take a system-wide boost config somehow (copying or symlinking it instead of generate its own) in this specific case?
- Maybe conan can legitimate the crutch described above? 

Thanks in advance!


Discussion:
Hi @alex-700,

Reading your particular case it does not look to me to abuse a bug or behavior of a generator to use it that way. I think the changes introduced in the generator are improvements to make the integration with CMake simpler and improve syntax in recipes.

However, I see the point on better supporting "system" recipes and this is something we would consider in the design of the graph for Conan 2.0.

Meanwhile, I believe Conan has different points of customization that could be used to overcome this situation. For example, if you are already using a custom recipe for `boost/system`, you could avoid the generator and use the system-wide `FindBoost.cmake` directly. If you remove the generator from the recipe maybe that is enough to make the trick.

Also, you could use the `generate()` method in the recipe and remove the files that the generator creates or rename them as desired (see the example with `generate()` method here: https://docs.conan.io/en/latest/reference/conanfile/tools/cmake.html#cmakedeps)

I hope I understood the question correctly and that you can make it work with Conan 1.36 as well. Thank you!
@danimtb thanks for the answer!
> I think the changes introduced in the generator are improvements to make the integration with CMake simpler and improve syntax in recipes.

Agree. I thought about having a possibility to have different strings for `<string1>-config.cmake` and `find_dependency(<string2> ... )` in dependants. This feature (bug) was dropped in `1.36`. Yeap, this feature is odd usually, but it was a core for crutch described above.

> For example, if you are already using a custom recipe for boost/system, you could avoid the generator and use the system-wide FindBoost.cmake directly. If you remove the generator from the recipe maybe that is enough to make the trick.

It seems, that I didn't get this option. In which recipe do you suggest to drop `generator`? Our `boost/system` recipe has no `generator`s. But other packages, which depends on boost, have. And I can not remove `generator` there, because I need it for other dependencies. 

> Also, you could use the generate() method in the recipe and remove the files that the generator creates or rename them as desired (see the example with generate() method here: https://docs.conan.io/en/latest/reference/conanfile/tools/cmake.html#cmakedeps)

I thought about it. But this solution force me to fix `generate` methods for **all transitive** dependants of `boost`. It doesn't look good. Specifically in the case of overriding `boost` dependency for some of CCI packages.
@memsharded , can I ask you to comment this too? 
Hi @alex-700,

Thanks for the answer, now I understand better your issue. I believe you could still make the trick using the new cpp_info properties approach, right?

Adding something like this `self.cpp_info.set_property("cmake_file_name", "SystemBoost")` to your `boost/system` recipe should work in the same way.
@danimtb No, it does not. I have already set this property before. There is exhaustive list of `cpp_info` attributes from our `system/boost` recipe.
```yaml
filenames:
  CMakeDeps: "SystemBoost"
  cmake_find_package: "SystemBoost"
  cmake_find_package_multi: "SystemBoost"
cmake_file_name: "SystemBoost"
```

>  I thought about having a possibility to have different strings for `<string1>-config.cmake` and `find_dependency(<string2> ... )` in dependants.

In terms of my cited description, `<string1>` and `<string2>` in `conan 1.36` are taken from one place (and so always the same), namely `self.get_filename` from [here](https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/cmakedeps.py#L557). The call of this method for `<string1>` is [here](https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/cmakedeps.py#L590) and for `<string2>` is [here](https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/cmakedeps.py#L729) or [here](https://github.com/conan-io/conan/blob/develop/conan/tools/cmake/cmakedeps.py#L712). 
I have created a test using this reduced version or the recipe:

```python
from conans import ConanFile

class Conan(ConanFile):
    name = "boost"
    version = "system"
    settings = "os", "compiler", "build_type", "arch"

    def package_info(self):
        self.cpp_info.set_property("cmake_file_name", "SystemBoost")
```

Running the following commands I see this:

```bash
$ conan --version
Conan version 1.36.0
$ conan create conanfile.py
...
$ conan install boost/system@ -g CMakeDeps
...
$ ls
SystemBoost-release-x86_64-data.cmake  SystemBoostConfig.cmake  SystemBoostConfigVersion.cmake  SystemBoostTarget-release.cmake  SystemBoostTargets.cmake  cmakedeps_macros.cmake  conanbuildinfo.txt  conanfile.py
```
So it seems to be working as expected only with the `set_property("cmake_file_name", "SystemBoost")`.

--------------------------------

With the additional information of your example I see the same result with this conanfile:

```python
from conans import ConanFile

class Conan(ConanFile):
    name = "boost"
    version = "system"
    settings = "os", "compiler", "build_type", "arch"

    def package_info(self):
        self.cpp_info.filenames["CMakeDeps"] = "SystemBoost"
        self.cpp_info.filenames["cmake_find_package"] = "SystemBoost"
        self.cpp_info.filenames["cmake_find_package_multi"] = "SystemBoost"
        self.cpp_info.set_property("cmake_file_name", "SystemBoost")
```

```bash
$ conan --version
Conan version 1.36.0
$ conan create conanfile.py
...
$ conan install boost/system@ -g CMakeDeps
...
$ ls
SystemBoost-release-x86_64-data.cmake  SystemBoostConfig.cmake  SystemBoostConfigVersion.cmake  SystemBoostTarget-release.cmake  SystemBoostTargets.cmake  cmakedeps_macros.cmake  conanbuildinfo.txt  conanfile.py
```
In this reproducer we need also the second package (e.g. A) which depends on Boost. And we need to look inside conan-generated  `AConfig.cmake`. The expected behaviour of this `boost/system`: `AConfig.cmake` contains `find_dependency(Boost ...)`. But the actual behaviour: there is `find_dependency(SystemBoost...)`.
ok, you are right. That is currently not supported as it is out of the use case for the different cpp_info properties. We need to take this into account for "system" recipes like the one you suggest.
@lasote can I ask your opinion (as an expert in `CMakeDeps` generator :) ) about this question?

It is sad that only existing workaround is using `cmake_find_package_multi` generator, where this bug (or feature) can be exploited.
Hi, I would like to step back to the original pain and check I got it. So you have CCI packages depending on the Boost recipe of CCI, right?
And you want a way to replace the Boost "node" and link with the system one, correct?
So you are creating a recipe "boost/system" and declaring an override requirement in the consumer project BUT you cannot get the packages depending on boost to link against system one but against "boost/system" one. Am I correct?

EDIT: But not only for the final consumer (that might be patched writing a generate() method) but also when you want to build from sources any of the intermediate nodes (depending on boost)?
> Hi, I would like to step back to the original pain and check I got it. So you have CCI packages depending on the Boost recipe of CCI, right?
And you want a way to replace the Boost "node" and link with the system one, correct?

This described situation is similar to mine (and more interesting), but not the same.

> So you are creating a recipe "boost/system" and declaring an override requirement in the consumer project BUT you cannot get the packages depending on boost to link against system one but against "boost/system" one.

This is almost correct. The main part here that this `boost/system` recipe is not "honest" one in the terms of `conan` model.

My real case is following.

The simplified dependency tree is following:
```
A -> B -> C -> D -> boost
```
A, B, C, D is our packages and I can change them as I want. They used `CMakeDeps` generator, but after conan=1.36, I switch them back into `cmake_find_package_multi` to exploit the bug described above.

I want to have a possibility change `boost` from CCI one to system one and vice-versa in A (via `('boost/...', 'override')` ) (as you said). I have a magic `SystemBoost` recipe, which act like `boost/system`, but exploit a bug (feature) in `conan` and instead of declaring all `conan` properties honestly, declare only `cmake_file_name: "SystemBoost"`. `cmake_find_package_multi` generator creates **unused** `SystemBoostConfig.cmake` and in other conan-generated files (for example `BConfig.cmake` during build of A) we can get `find_package(boost ...)` instead of `find_package(SystemBoost ...)` and this will be expected behavior (to properly setup boost via system `FindBoost.cmake`). 
Ok, thanks I think I got it right.
Well, the `bug/feature` you describe is clearly a bug and will not come back, hahaha. But we could think about introducing a property to skip both the generation of the `SystemBoost-config.cmake` files and also the dependant `find_dependency`.
Something like `set_property("cmake_skip_config", True)` but I need to discuss it with the team because it might introduce some complex situations like invisible transitive dependencies when the skipped node is not a final node and maybe others.
Just to re-confirm, would that feature help?

EDIT: oh, wait, it is not valid. You need the `find_dependency` to do `find_dependency("boost")`... well I will discuss a solution in that direction and will report back.
> Just to re-confirm, would that feature help?

Yes, I think, that it will help.

But just for clarification: in terms of this feature we will skip generation of `boost-config.cmake` (not `SystemBoostConfig.cmake`, this name is the part of our crutch to implement the idea). 

> You need the `find_dependency` to do `find_dependency("boost")`

yeap, and it will be :) `self.name` in the `SystemBoost` recipe is `boost` (and always was), and if I don't declare `cmake_file_name` it will work as expected.
Hi, good news. We discussed it and will try to provide something for the next release.

Resolving patch:
diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py
--- a/conan/tools/cmake/cmakedeps/cmakedeps.py
+++ b/conan/tools/cmake/cmakedeps/cmakedeps.py
@@ -60,6 +60,10 @@ def content(self):
             if dep.is_build_context and dep.ref.name not in self.build_context_activated:
                 continue
 
+            if dep.new_cpp_info.get_property("skip_deps_file", "CMakeDeps"):
+                # Skip the generation of config files for this node, it will be located externally
+                continue
+
             config_version = ConfigVersionTemplate(self, require, dep)
             ret[config_version.filename] = config_version.render()
 


Test patch:
diff --git a/conans/test/integration/toolchains/cmake/__init__.py b/conans/test/integration/toolchains/cmake/__init__.py
new file mode 100644
diff --git a/conans/test/integration/toolchains/cmake/cmakedeps/__init__.py b/conans/test/integration/toolchains/cmake/cmakedeps/__init__.py
new file mode 100644
diff --git a/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py
new file mode 100644
--- /dev/null
+++ b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py
@@ -0,0 +1,39 @@
+import os
+
+from conans.test.assets.genconanfile import GenConanfile
+from conans.test.utils.tools import TestClient
+
+
+def test_package_from_system():
+    """
+    If a node declares "system_package" property, the CMakeDeps generator will skip generating
+    the -config.cmake and the other files for that node but will keep the "find_dependency" for
+    the nodes depending on it. That will cause that cmake looks for the config files elsewhere
+    https://github.com/conan-io/conan/issues/8919"""
+    client = TestClient()
+    dep2 = str(GenConanfile().with_name("dep2").with_version("1.0")
+               .with_settings("os", "arch", "build_type", "compiler"))
+    dep2 += """
+    def package_info(self):
+        self.cpp_info.set_property("skip_deps_file", True)
+        self.cpp_info.set_property("cmake_file_name", "custom_dep2")
+
+    """
+    client.save({"conanfile.py": dep2})
+    client.run("create .")
+
+    dep1 = GenConanfile().with_name("dep1").with_version("1.0").with_require("dep2/1.0")\
+                         .with_settings("os", "arch", "build_type", "compiler")
+    client.save({"conanfile.py": dep1})
+    client.run("create .")
+
+    consumer = GenConanfile().with_name("consumer").with_version("1.0").\
+        with_require("dep1/1.0").with_generator("CMakeDeps").\
+        with_settings("os", "arch", "build_type", "compiler")
+    client.save({"conanfile.py": consumer})
+    client.run("install .")
+    assert os.path.exists(os.path.join(client.current_folder, "dep1-config.cmake"))
+    assert not os.path.exists(os.path.join(client.current_folder, "dep2-config.cmake"))
+    assert not os.path.exists(os.path.join(client.current_folder, "custom_dep2-config.cmake"))
+    contents = client.load("dep1-release-x86_64-data.cmake")
+    assert 'set(dep1_FIND_DEPENDENCY_NAMES ${dep1_FIND_DEPENDENCY_NAMES} custom_dep2)' in contents
diff --git a/conans/test/unittests/tools/cmake/test_cmakedeps.py b/conans/test/unittests/tools/cmake/test_cmakedeps.py
--- a/conans/test/unittests/tools/cmake/test_cmakedeps.py
+++ b/conans/test/unittests/tools/cmake/test_cmakedeps.py
@@ -171,4 +171,3 @@ def test_component_name_same_package():
         data_cmake = files["mypkg-release-x86-data.cmake"]
         assert 'set(mypkg_mypkg_INCLUDE_DIRS_RELEASE ' \
                '"${mypkg_PACKAGE_FOLDER_RELEASE}/includedirs1")' in data_cmake
-