Repository: conan-io/conan

Issue:
[question] Why is CMAKE_FIND_PACKAGE_PREFER_CONFIG=ON by default?
### Environment details

* Operating System+version: Ubuntu2
* Compiler+version: irrelevant
* Conan version: 1.60.1
* Python version: 3.8.10


### Steps to reproduce

CMakeToolchain generator sets by default `set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)`.
This causes issues during different builds. E.g. QT provides some own cmake modules

Ref: https://github.com/qt/qtbase/tree/fa522eb8becba3243aae8bee191f6f60672434f2/cmake

Since conan sets `set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)`, cmake won't consider those modules first and the build will fail (e.g. since the original clang ClangConfig.cmake is found first). 
This enforces users to actively set `tc.variables["CMAKE_FIND_PACKAGE_PREFER_CONFIG"]="ON"`, or the global conf `tools.cmake.cmaketoolchain:find_package_prefer_config=False`. 

Is this intended?

I will create a test for this. 

### Logs

_No response_
CMakeConfigDeps: issues when there is a built-in CMake module
The current implementation of `CMakeConfigDeps` will prioritise modules by default if they exist



https://github.com/conan-io/conan-center-index/issues/28208


Discussion:
I have created a test for it in my fork (I did not want to spam your repo with another PR).

Ref: https://github.com/thorsten-klein/conan/compare/develop...thorsten-klein:conan:issue-14550 

What would actually be your preferred way, how I can provide this test?
In another case if setting explicitly to `OFF` by default, then cmake seems to prefer MODULES.

This happens for example in tensorflow-lite.
Ref: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/CMakeLists.txt#L53
and: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/CMakeLists.txt#L149

What I can see: when `CMAKE_FIND_PACKAGE_PREFER_CONFIG=OFF` then the tensorflow-own module is found first when doing `find_package(fft2d)`. When I set the variable to `ON`, it will find the correct `find_package(fft2d)`.
But setting to `ON` by default breaks other builds (see above).

So as a result: In any case setting `CMAKE_FIND_PACKAGE_PREFER_CONFIG` will change the cmake default behavior (described in https://cmake.org/cmake/help/latest/command/find_package.html#config-mode-search-procedure). 

I would suggest to leave this variable unchanged by default.
The conf could be extended to support "None" (default), so it will be handled as follows:

- `tools.cmake.cmaketoolchain:find_package_prefer_config=False`: `set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF)`
- `tools.cmake.cmaketoolchain:find_package_prefer_config=True`: `set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)`
- `tools.cmake.cmaketoolchain:find_package_prefer_config=None` (default): `# do not change value of CMAKE_FIND_PACKAGE_PREFER_CONFIG`

Hi @thorsten-klein 

Thanks for your report, and for the test you provided, that helps to understand issues.

There is one thing that needs attention in the test, however. If this is happening only in ``editable`` mode, then the ``hello`` package needs to define the ``layout()`` correctly. It is importing ``cmake_layout()`` but not using it, and even if it was used, the default ``cmake_layout()`` does not provide any default for ``builddirs``, so it would be necessary to define the ``self.cpp.build.builddirs``.

The default for ``set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)`` was discussed some time ago, and even if it is not exactly the CMake default this one seemed to make lot of sense:
- Conan generates by default Config files
- Many third party packages, including tons of open source libraries in ConanCenter have undefined ``find_package(something)``, that will default to find modules first.
- Some third party packages have system libraries (zlib, openssl and many more), with default find modules somewhere, in the CMake installation, for example.
- That means, that many packages would default to find, link and use an outdated system version of zlib, openssl, instead of the provided Conan package that created the ``xxxx-config.cmake`` file, because of this (historical) default precendence of modules. It is know than modules is a legacy approach, that has been superseded by config files, that are always recommended better than find modules.

So Conan ``CMakeToolchain`` decided to default to this, to avoid many serious problems (including security concerns, like linking an older openssl), even if it is not the default, and added several mechanisms to change it:
- ``tools.cmake.cmaketoolchain:find_package_prefer_config``
- The ``CMakeToolchain`` can be customize too for an individual project that might want to don't use this, with the idiom:
```python
def generate(self):
    tc = CMakeToolchain(self)
    tc.blocks["find_paths"].values["find_package_prefer_config"] = "OFF"
    tc.generate()
```



Those `XXX-config.cmake` are only generated when using CMakeDeps generator, right? 
We are using `CMakeToolchain` generator and in this case none are created.

I do not understand in which case a wrong OpenSSL is taken. Is there any test for your described scenario? I would like to understand better. 
I have created a PR where I add further tests for this `find_package_prefer_config` (#14597). 
It seems that it works as expected.

What I now understand better:

A cmake project may `PREPEND`/`APPEND` paths to `CMAKE_MODULE_PATH`. 
`CMAKE_MODULE_PATH` paths are always considered first before `CMAKE_PREFIX_PATH`. 
Therefore conan ensure that the `cpp_info.builddirs` are prepended to `CMAKE_MODULE_PATH` to ensure that the conan package is always considered. `CMAKE_PREFIX_PATH` alone would not be enough since it would not be considered if `CMAKE_MODULE_PATH` was set in the project.

A cmake project may `PREPEND`/`APPEND` paths to `CMAKE_PREFIX_PATH`. 
cmake is checking for any file `<CMAKE_PREFIX_PATH>/helloConfig.cmake` before it is iterating over the default sub-folders (Ref: cmake search-procedure). Therefore conan must ensure that each `cpp_info.builddirs` is prepended to `CMAKE_PREFIX_PATH` as well.

Unfortunately this means, that conan users must explicitly specify each individual directory as `cpp_info.builddirs` (or conan would need some magic in order to search all `Find*.cmake` and `*[Cc]onfig.cmake` in the package_folder and add the paths automatically).

This has finally evolved as follows:
- The new ``CMakeConfigDeps`` generator only generates ``xxx-config.cmake`` files. It will not support/create ``find_xxx.cmake`` modules anymore. 
- The new ``CMakeConfigDeps`` generator didn't initially add CMAKE_FIND_PACKAGE_PREFER_CONFIG. It has got a few reports and complaints about issues where users CMakeLists.txt with something as ``find_package(Boost REQUIRED)`` was failing because it happened that CMake prioritized its own ``findBoost.cmake`` internal module, finding an old Boost installed in the system.
- As such, it makes sense that this generator defines always CMAKE_FIND_PACKAGE_PREFER_CONFIG=ON
- It has been introduced in https://github.com/conan-io/conan/pull/18832 for next Conan 2.20





Patch:
diff --git a/conan/tools/cmake/cmakedeps2/cmakedeps.py b/conan/tools/cmake/cmakedeps2/cmakedeps.py
index d6b33b8cc89..47475130609 100644
--- a/conan/tools/cmake/cmakedeps2/cmakedeps.py
+++ b/conan/tools/cmake/cmakedeps2/cmakedeps.py
@@ -197,6 +197,8 @@ def _get_cmake_paths(self, requirements, dirs_name):
 
     def generate(self):
         template = textwrap.dedent("""\
+        set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
+
         {% for pkg_name, folder in pkg_paths.items() %}
         set({{pkg_name}}_DIR "{{folder}}")
         {% endfor %}


Test patch:
diff --git a/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py b/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
index 0f329d469de..c88f6cc257e 100644
--- a/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
+++ b/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
@@ -34,6 +34,7 @@ class PkgConan(ConanFile):
     c.save({"conanfile.py": conanfile}, clean_first=True)
     c.run(f"install . -c tools.cmake.cmakedeps:new={new_value}")
     cmake_paths = c.load("conan_cmakedeps_paths.cmake")
+    assert "set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)" in cmake_paths
     assert re.search(r"list\(PREPEND CMAKE_PROGRAM_PATH \".*/bin\"", cmake_paths)  # default
     assert re.search(r"list\(PREPEND CMAKE_LIBRARY_PATH \".*/mylib\"", cmake_paths)
     assert re.search(r"list\(PREPEND CMAKE_INCLUDE_PATH \".*/myincludes\"", cmake_paths)
@@ -150,6 +151,7 @@ class PkgConan(ConanFile):
     assert ('set(liba_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/full_deploy/'
             'host/liba/1.0/includea" )') in liba_targets
 
+
 def test_cmakeconfigdeps_recipe():
     c = TestClient()
     conanfile = textwrap.dedent("""
@@ -257,6 +259,7 @@ def package_info(self):
     assert "CMakeConfigDeps: cmake_set_interface_link_directories deprecated and invalid. " \
            "The package 'package_info()' must correctly define the (CPS) information" in c.out
 
+
 def test_consuming_cpp_info_with_components_dependency_from_same_package():
     c = TestClient()
     conanfile = textwrap.dedent("""
@@ -360,7 +363,7 @@ def test(self):
         """)
     c.save({"dependent/conanfile.py": dependent_conanfile,
             "main/conanfile.py": conanfile,
-            "main/test_package/conanfile.py":test_package})
+            "main/test_package/conanfile.py": test_package})
     c.run("create ./dependent/ --name=dependent --version=0.1 "
           f"-c tools.cmake.cmakedeps:new={new_value}")
     c.run(f"create ./main/ --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new={new_value}")
@@ -422,7 +425,7 @@ def test(self):
 
     c.save({"automake/conanfile.py": automake,
             "libtool/conanfile.py": conanfile,
-            "libtool/test_package/conanfile.py":test_package})
+            "libtool/test_package/conanfile.py": test_package})
     c.run("create automake")
     c.run(f"create libtool -c tools.cmake.cmakedeps:new={new_value}")
     targets = c.load("libtool/test_package/libtool-Targets-release.cmake")
@@ -474,7 +477,7 @@ def test(self):
 
     c.save({"automake/conanfile.py": automake,
             "libtool/conanfile.py": conanfile,
-            "libtool/test_package/conanfile.py":test_package})
+            "libtool/test_package/conanfile.py": test_package})
     c.run("create automake")
     c.run(f"create libtool -c tools.cmake.cmakedeps:new={new_value}")
     targets = c.load("libtool/test_package/libtool-Targets-release.cmake")
@@ -494,7 +497,8 @@ class Dep(ConanFile):
             version = "1.0"
             settings = "os", "compiler", "build_type", "arch"
             def package_info(self):
-                self.cpp_info.components["mycomp"].set_property("cmake_target_name", "dep::mycomponent")
+                self.cpp_info.components["mycomp"].set_property("cmake_target_name",
+                                                                "dep::mycomponent")
         """),
              "conanfile.py": textwrap.dedent("""
              from conan import ConanFile
@@ -509,7 +513,8 @@ class Pkg(ConanFile):
                 def generate(self):
                     deps = CMakeDeps(self)
                     deps.set_property("dep", "cmake_target_aliases", ["alias", "dep::other_name"])
-                    deps.set_property("dep::mycomp", "cmake_target_aliases", ["component_alias", "dep::my_aliased_component"])
+                    deps.set_property("dep::mycomp", "cmake_target_aliases",
+                                      ["component_alias", "dep::my_aliased_component"])
                     deps.generate()
              """)})
     tc.run("create dep")