Repository: conan-io/conan
Pull request #17430 (2024-12-08 21:05:58)

Fix broken `cpp_info.location` deduction due to unsanitized regex
Using `grpc` with `-c tools.cmake.cmakedeps:new=will_break_next` currently fails with:
> ConanException: grpc/1.67.1: Cannot obtain 'location' for library 'grpc++_alts' in ['/home/martin/.conan2/p/b/grpcad62ea78a9235/p/lib']. You can specify 'cpp_info.location' directly or report in github.com/conan-io/conan/issues if you think it should have been deduced correctly.


This PR fixes the location detection failure due to the unsanitized `+` symbols in the library name.

valgur: The location detection could be improved further, still. It should fall back to the fuzzy regex only if an exact match is not found.
> grpc/1.67.1: WARN: There were several matches for Lib grpc: ['lib/libgrpc.so.44.0.0', 'lib/libgrpc_authorization_provider.so.1.67.1', 'lib/libgrpc_plugin_support.so.1.67.1', 'lib/libgrpc_unsecure.so.44.0.0']

> grpc/1.67.1: WARN: There were several matches for Lib grpc++: ['lib/libgrpc++.so.1.67.1', 'lib/libgrpc++_alts.so.1.67.1', 'lib/libgrpc++_error_details.so.1.67.1', 'lib/libgrpc++_reflection.so.1.67.1', 'lib/libgrpc++_unsecure.so.1.67.1']
valgur: Unrelated, but too minor for a separate issue - using `grpc` with the new CMakeDeps still fails after this fix with
```
  File "/home/martin/libs/conan/conan/tools/cmake/cmakedeps2/target_configuration.py", line 169, in _get_cmake_lib
    self._conanfile.output.warning("frameworks not supported yet in new CMakeDeps generator")
    ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'ConanFileInterface' object has no attribute 'output'
```
memsharded: > Unrelated, but too minor for a separate issue - using grpc with the new CMakeDeps still fails after this fix with

Agree, if you want you can just replace that with a ``ConanOutput(scope=str(self.conanfile)).warning()`` or something like that, this doesn't require test coverage yet, as it is just a temporary warning until the frameworks are implemented.

Patch:
diff --git a/conan/tools/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py
index e887a828a..15b95930a 100644
--- a/conan/tools/cmake/cmakedeps2/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps2/target_configuration.py
@@ -4,6 +4,7 @@ import textwrap
 import jinja2
 from jinja2 import Template
 
+from conan.api.output import ConanOutput
 from conan.errors import ConanException
 from conans.client.graph.graph import CONTEXT_BUILD, CONTEXT_HOST
 from conans.model.pkg_type import PackageType
@@ -166,7 +167,7 @@ class TargetConfigurationTemplate2:
                   "system_libs": system_libs}
 
         if info.frameworks:
-            self._conanfile.output.warning("frameworks not supported yet in new CMakeDeps generator")
+            ConanOutput(scope=str(self._conanfile)).warning("frameworks not supported yet in new CMakeDeps generator")
 
         if info.libs:
             if len(info.libs) != 1:
diff --git a/conans/model/build_info.py b/conans/model/build_info.py
index dc180a96d..98ea5aae6 100644
--- a/conans/model/build_info.py
+++ b/conans/model/build_info.py
@@ -550,9 +550,11 @@ class _Component:
             elif ext == ".dll":
                 dll_location = _find_matching(bindirs, libname)
         else:
-            regex_static = re.compile(rf"(?:lib)?{libname}(?:[._-].+)?\.(?:a|lib)")
-            regex_shared = re.compile(rf"(?:lib)?{libname}(?:[._-].+)?\.(?:so|dylib)")
-            regex_dll = re.compile(rf"(?:.+)?({libname}|{component_name})(?:.+)?\.dll")
+            lib_sanitized = re.escape(libname)
+            component_sanitized = re.escape(component_name)
+            regex_static = re.compile(rf"(?:lib)?{lib_sanitized}(?:[._-].+)?\.(?:a|lib)")
+            regex_shared = re.compile(rf"(?:lib)?{lib_sanitized}(?:[._-].+)?\.(?:so|dylib)")
+            regex_dll = re.compile(rf".*(?:{lib_sanitized}|{component_sanitized}).*\.dll")
             static_location = _find_matching(libdirs, regex_static)
             shared_location = _find_matching(libdirs, regex_shared)
             if static_location or not shared_location:


Test patch:
diff --git a/test/unittests/model/build_info/test_deduce_locations.py b/test/unittests/model/build_info/test_deduce_locations.py
index 33dfd1b2a..5a3d0a883 100644
--- a/test/unittests/model/build_info/test_deduce_locations.py
+++ b/test/unittests/model/build_info/test_deduce_locations.py
@@ -68,7 +68,8 @@ def test_deduce_shared_link_locations(conanfile):
 @pytest.mark.parametrize("lib_name, libs", [
     ("liblog4cxx.so.15.2.0", ["log4cxx"]),
     ("libapr-1.0.dylib", ["apr-1"]),
-    ("libapr-1.so.0.7.4", ["apr-1"])
+    ("libapr-1.so.0.7.4", ["apr-1"]),
+    ("libgrpc++_alts.so.1.67.1", ["grpc++_alts"])
 ])
 def test_complex_deduce_locations_shared(lib_name, libs, conanfile):
     """