Repository: conan-io/conan

Issue:
[bug] cmake_find_package_multi: create lowercase config file
<!-- What is your suggestion? Please be as specific as possible! -->

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).

Currently, `cmake_find_package_multi` generator seems to create the "case sensitive" version of config files: `NameConfig.cmake`

I would suggest to create instead the lower case version: `name-config.cmake` (maybe only if name is already lower case).

Why?

Because when a file like `name-config.cmake` is available, both `find_package(Name)` and `find_package(name)` work (even `find_package(NAME)` works I guess).

If `nameConfig.cmake` is available, only `find_package(name)` works, not `find_package(Name)`. Therefore this file may not properly mimic official upstream's config file.

**Example with protobuf**:

see those errors in https://github.com/conan-io/conan-center-index/pull/2037#issuecomment-725712839

conanfile:

```python
    def package_info(self):
        ...
        self.cpp_info.filenames["cmake_find_package_multi"] = "protobuf"
        ...
```

CMakeLists.txt of test_package:

```cmake
...
# this should work with official config file, even if find_package(protobuf CONFIG REQUIRED)
# should be prefered (or find_package(Protobuf) if relying on official module file)
find_package(Protobuf CONFIG REQUIRED)
...
```

Generared cmake files, and errors when `find_package(Protobuf CONFIG)` is called:

```
protobuf/3.9.1 (test package): Generator cmake_find_package_multi created protobufConfigVersion.cmake
protobuf/3.9.1 (test package): Generator cmake_find_package_multi created protobufTarget-debug.cmake
protobuf/3.9.1 (test package): Generator cmake_find_package_multi created protobufTargets.cmake
protobuf/3.9.1 (test package): Generator cmake_find_package_multi created protobufConfig.cmake
...
CMake Error at CMakeLists.txt:7 (find_package):
  Could not find a package configuration file provided by "Protobuf" with any
  of the following names:

    ProtobufConfig.cmake
    protobuf-config.cmake
```


Discussion:
Oh, this is interesting. We added ``self.cpp_info.filenames["cmake_find_package_multi"] = "protobuf"`` explicitly to the model because it seemed necessary to match the upstream casing of the file. If lowercase always work, this isn't necessary anymore for ``cmake_find_package_multi``?

Are you sure it is always case insensitive, for all CMake versions and in all platforms and OSs? Is it possible that it is only case-insensitive in some systems like Windows?
> Oh, this is interesting. We added self.cpp_info.filenames["cmake_find_package_multi"] = "protobuf" explicitly to the model because it seemed necessary to match the upstream casing of the file. **If lowercase always work, this isn't necessary anymore for cmake_find_package_multi?**

Sorry I don't understand.

if `self.cpp_info.filenames["cmake_find_package_multi"] = "protobuf"`, with `cmake_find_package_multi` generator, `find_package(protobuf CONFIG)` works, but not `find_package(Protobuf CONFIG)` while it should.

From https://cmake.org/cmake/help/latest/command/find_package.html

> Config mode search attempts to locate a configuration file provided by the package to be found. A cache entry called \<PackageName\>_DIR is created to hold the directory containing the file. By default the command searches for a package with the name \<PackageName\>. If the NAMES option is given the names following it are used instead of \<PackageName\>. **The command searches for a file called \<PackageName\>Config.cmake or \<lower-case-package-name\>-config.cmake for each name specified**. A replacement set of possible configuration file names may be given using the CONFIGS option. The search procedure is specified below. Once found, the configuration file is read and processed by CMake. Since the file is provided by the package it already knows the location of package contents. The full path to the configuration file is stored in the cmake variable \<PackageName\>_CONFIG.

This is why I recommend to create:

- `<PackageName>Config.cmake` if `self.cpp_info.filenames["cmake_find_package_multi"]` has at least one uppercase character.
- `<packagename>-config.cmake` if `self.cpp_info.filenames["cmake_find_package_multi"]` is lowercase.

Resolving patch:
diff --git a/conans/client/generators/cmake_find_package_multi.py b/conans/client/generators/cmake_find_package_multi.py
--- a/conans/client/generators/cmake_find_package_multi.py
+++ b/conans/client/generators/cmake_find_package_multi.py
@@ -278,9 +278,9 @@ def content(self):
             pkg_public_deps_filenames = [self._get_filename(self.deps_build_info[it[0]]) for it in
                                          public_deps]
             config_version = self.config_version_template.format(version=pkg_version)
-            ret["{}ConfigVersion.cmake".format(pkg_filename)] = config_version
+            ret[self._config_version_filename(pkg_filename)] = config_version
             if not cpp_info.components:
-                ret["{}Config.cmake".format(pkg_filename)] = self._config(
+                ret[self._config_filename(pkg_filename)] = self._config(
                     filename=pkg_filename,
                     name=pkg_findname,
                     version=cpp_info.version,
@@ -331,9 +331,21 @@ def content(self):
                     conan_message=CMakeFindPackageCommonMacros.conan_message,
                     configs=self._configurations
                 )
-                ret["{}Config.cmake".format(pkg_filename)] = target_config
+                ret[self._config_filename(pkg_filename)] = target_config
         return ret
 
+    def _config_filename(self, pkg_filename):
+        if pkg_filename == pkg_filename.lower():
+            return "{}-config.cmake".format(pkg_filename)
+        else:
+            return "{}Config.cmake".format(pkg_filename)
+
+    def _config_version_filename(self, pkg_filename):
+        if pkg_filename == pkg_filename.lower():
+            return "{}-config-version.cmake".format(pkg_filename)
+        else:
+            return "{}ConfigVersion.cmake".format(pkg_filename)
+
     def _config(self, filename, name, version, public_deps_names):
         # Builds the XXXConfig.cmake file for one package
 


Test patch:
diff --git a/conans/test/functional/generators/cmake_find_package_multi_test.py b/conans/test/functional/generators/cmake_find_package_multi_test.py
--- a/conans/test/functional/generators/cmake_find_package_multi_test.py
+++ b/conans/test/functional/generators/cmake_find_package_multi_test.py
@@ -57,7 +57,7 @@ def test_native_export_multi(self):
                 # Test that we are using find_dependency with the NO_MODULE option
                 # to skip finding first possible FindBye somewhere
                 self.assertIn("find_dependency(hello REQUIRED NO_MODULE)",
-                              load(os.path.join(c.current_folder, "byeConfig.cmake")))
+                              load(os.path.join(c.current_folder, "bye-config.cmake")))
 
                 if platform.system() == "Windows":
                     c.run_command('cmake .. -G "Visual Studio 15 Win64"')
@@ -315,29 +315,31 @@ def build(self):
 
         client.save({"conanfile.py": conanfile, "CMakeLists.txt": cmakelists})
         client.run("install .")
-        os.unlink(os.path.join(client.current_folder, "helloConfigVersion.cmake"))
+        os.unlink(os.path.join(client.current_folder, "hello-config-version.cmake"))
         exit_code = client.run("build .", assert_error=True)
         self.assertNotEqual(0, exit_code)
 
     @parameterized.expand([
-        ("find_package(hello 1.0)", False, True),
-        ("find_package(hello 1.1)", False, True),
-        ("find_package(hello 1.2)", False, False),
-        ("find_package(hello 1.0 EXACT)", False, False),
-        ("find_package(hello 1.1 EXACT)", False, True),
-        ("find_package(hello 1.2 EXACT)", False, False),
-        ("find_package(hello 0.1)", False, False),
-        ("find_package(hello 2.0)", False, False),
-        ("find_package(hello 1.0 REQUIRED)", False, True),
-        ("find_package(hello 1.1 REQUIRED)", False, True),
-        ("find_package(hello 1.2 REQUIRED)", True, False),
-        ("find_package(hello 1.0 EXACT REQUIRED)", True, False),
-        ("find_package(hello 1.1 EXACT REQUIRED)", False, True),
-        ("find_package(hello 1.2 EXACT REQUIRED)", True, False),
-        ("find_package(hello 0.1 REQUIRED)", True, False),
-        ("find_package(hello 2.0 REQUIRED)", True, False)
+        ("hello", "1.0", "", False, True),
+        ("Hello", "1.0", "", False, True),
+        ("HELLO", "1.0", "", False, True),
+        ("hello", "1.1", "", False, True),
+        ("hello", "1.2", "", False, False),
+        ("hello", "1.0", "EXACT", False, False),
+        ("hello", "1.1", "EXACT", False, True),
+        ("hello", "1.2", "EXACT", False, False),
+        ("hello", "0.1", "", False, False),
+        ("hello", "2.0", "", False, False),
+        ("hello", "1.0", "REQUIRED", False, True),
+        ("hello", "1.1", "REQUIRED", False, True),
+        ("hello", "1.2", "REQUIRED", True, False),
+        ("hello", "1.0", "EXACT REQUIRED", True, False),
+        ("hello", "1.1", "EXACT REQUIRED", False, True),
+        ("hello", "1.2", "EXACT REQUIRED", True, False),
+        ("hello", "0.1", "REQUIRED", True, False),
+        ("hello", "2.0", "REQUIRED", True, False)
     ])
-    def test_version(self, find_package_string, cmake_fails, package_found):
+    def test_version(self, name, version, params, cmake_fails, package_found):
         client = TestClient()
         client.run("new hello/1.1 -s")
         client.run("create .")
@@ -345,9 +347,9 @@ def test_version(self, find_package_string, cmake_fails, package_found):
         cmakelists = textwrap.dedent("""
             cmake_minimum_required(VERSION 3.1)
             project(consumer)
-            {find_package_string}
-            message(STATUS "hello found: ${{hello_FOUND}}")
-            """).format(find_package_string=find_package_string)
+            find_package({name} {version} {params})
+            message(STATUS "hello found: ${{{name}_FOUND}}")
+            """).format(name=name, version=version, params=params)
 
         conanfile = textwrap.dedent("""
             from conans import ConanFile, CMake
diff --git a/conans/test/functional/generators/components/propagate_specific_components_test.py b/conans/test/functional/generators/components/propagate_specific_components_test.py
--- a/conans/test/functional/generators/components/propagate_specific_components_test.py
+++ b/conans/test/functional/generators/components/propagate_specific_components_test.py
@@ -56,7 +56,7 @@ def test_cmake_find_package(self):
     def test_cmake_find_package_multi(self):
         t = TestClient(cache_folder=self.cache_folder)
         t.run('install middle/version@ -g cmake_find_package_multi')
-        content = t.load('middleConfig.cmake')
+        content = t.load('middle-config.cmake')
         self.assertIn("find_dependency(top REQUIRED NO_MODULE)", content)
         self.assertIn("find_package(top REQUIRED NO_MODULE)", content)
 
diff --git a/conans/test/unittests/client/generators/cmake_find_package_multi_test.py b/conans/test/unittests/client/generators/cmake_find_package_multi_test.py
--- a/conans/test/unittests/client/generators/cmake_find_package_multi_test.py
+++ b/conans/test/unittests/client/generators/cmake_find_package_multi_test.py
@@ -24,5 +24,5 @@ def test_cmake_find_package_multi_version(self):
 
         generator = CMakeFindPackageMultiGenerator(conanfile)
         content = generator.content
-        config_version = content["my_pkgConfigVersion.cmake"]
+        config_version = content["my_pkg-config-version.cmake"]
         self.assertIn('set(PACKAGE_VERSION "0.1")', config_version)