Repository: conan-io/conan
Pull request #17445 (2024-12-10 15:21:39)

Failing test for unset conf
Changelog: Bugfix: Fix issues with unsetting some types of confs
Docs: Omit

This makes `unset` confs and confs never written behave the same when try are queried with a `choices` argument

This came about as investigation from https://github.com/conan-io/conan/pull/17416 


Patch:
diff --git a/conan/tools/cmake/cmakedeps/templates/macros.py b/conan/tools/cmake/cmakedeps/templates/macros.py
index b5c0e185b..db804b8de 100644
--- a/conan/tools/cmake/cmakedeps/templates/macros.py
+++ b/conan/tools/cmake/cmakedeps/templates/macros.py
@@ -65,8 +65,7 @@ class MacrosTemplate(CMakeDepsFileTemplate):
                                   NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
                      set(CMAKE_FIND_LIBRARY_SUFFIXES ${_OLD_CMAKE_FIND_LIBRARY_SUFFIXES})
                      if(NOT CONAN_SHARED_FOUND_LIBRARY)
-                       message(STATUS "Cannot locate shared library: ${_LIBRARY_NAME}")
-                       message(DEBUG "DLL library not found, creating UNKNOWN IMPORTED target")
+                       message(DEBUG "DLL library not found, creating UNKNOWN IMPORTED target, searched for: ${_LIBRARY_NAME}")
                        if(NOT TARGET ${_LIB_NAME})
                           add_library(${_LIB_NAME} UNKNOWN IMPORTED)
                        endif()
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:
diff --git a/conans/model/conf.py b/conans/model/conf.py
index 9140be00a..a9664b891 100644
--- a/conans/model/conf.py
+++ b/conans/model/conf.py
@@ -322,7 +322,7 @@ class Conf:
         conf_value = self._values.get(conf_name)
         if conf_value:
             v = conf_value.value
-            if choices is not None and v not in choices:
+            if choices is not None and v not in choices and v is not None:
                 raise ConanException(f"Unknown value '{v}' for '{conf_name}'")
             # Some smart conversions
             if check_type is bool and not isinstance(v, bool):


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):
     """
diff --git a/test/unittests/model/test_conf.py b/test/unittests/model/test_conf.py
index f0d155a74..841a87bf7 100644
--- a/test/unittests/model/test_conf.py
+++ b/test/unittests/model/test_conf.py
@@ -326,3 +326,22 @@ def test_conf_scope_patterns_bad(scope, conf, assert_message):
         c.loads(final_conf)
         c.validate()
     assert assert_message in str(exc_info.value)
+
+
+@pytest.mark.parametrize("choices", [None, ["Foo", "Bar"]])
+def test_unset_basic_same_behaviour(choices):
+    c = ConfDefinition()
+    assert c.get("user.company.cpu:jobs", choices=choices) is None
+
+    c2 = ConfDefinition()
+    c2.loads("user.company.cpu:jobs=!")
+    assert c2.get("user.company.cpu:jobs", choices=choices) is None
+
+    c3 = ConfDefinition()
+    c3.loads("user.company.cpu:jobs=4")
+
+    c4 = ConfDefinition()
+    c4.loads("user.company.cpu:jobs=!")
+    c3.update_conf_definition(c4)
+
+    assert c3.get("user.company.cpu:jobs", choices=choices) is None