Repository: conan-io/conan
Pull request #17045 (2024-09-23 17:42:38)

[BazelDeps][bugfix] Windows and shared libraries
Changelog: Bugfix: BazelDeps did not find DLL files as Conan does not model them in the Windows platform.
Docs: omit
Close: https://github.com/conan-io/conan/issues/16691



Patch:
diff --git a/conan/tools/google/bazeldeps.py b/conan/tools/google/bazeldeps.py
index 4cb213e88..916e13f42 100644
--- a/conan/tools/google/bazeldeps.py
+++ b/conan/tools/google/bazeldeps.py
@@ -10,8 +10,8 @@ from conan.internal import check_duplicated_generator
 from conans.model.dependencies import get_transitive_requires
 from conans.util.files import save
 
-_BazelTargetInfo = namedtuple("DepInfo", ['repository_name', 'name', 'requires', 'cpp_info'])
-_LibInfo = namedtuple("LibInfo", ['name', 'is_shared', 'lib_path', 'interface_lib_path'])
+_BazelTargetInfo = namedtuple("DepInfo", ['repository_name', 'name', 'ref_name', 'requires', 'cpp_info'])
+_LibInfo = namedtuple("LibInfo", ['name', 'is_shared', 'lib_path', 'import_lib_path'])
 
 
 def _get_name_with_namespace(namespace, name):
@@ -72,18 +72,19 @@ def _get_requirements(conanfile, build_context_activated):
         yield require, dep
 
 
-def _get_libs(dep, cpp_info=None) -> list:
+def _get_libs(dep, cpp_info=None, reference_name=None) -> list:
     """
     Get the static/shared library paths
 
     :param dep: normally a <ConanFileInterface obj>
     :param cpp_info: <CppInfo obj> of the component.
+    :param reference_name: <str> Package/Component's reference name. ``None`` by default.
     :return: list of tuples per static/shared library ->
-             [(lib_name, is_shared, library_path, interface_library_path)]
+             [(name, is_shared, lib_path, import_lib_path)]
              Note: ``library_path`` could be both static and shared ones in case of UNIX systems.
                     Windows would have:
-                        * shared: library_path as DLL, and interface_library_path as LIB
-                        * static: library_path as LIB, and interface_library_path as None
+                        * shared: library_path as DLL, and import_library_path as LIB
+                        * static: library_path as LIB, and import_library_path as None
     """
     def _is_shared():
         """
@@ -93,22 +94,52 @@ def _get_libs(dep, cpp_info=None) -> list:
         return {"shared-library": True,
                 "static-library": False}.get(str(dep.package_type), default_value)
 
-    def _save_lib_path(lib_, lib_path_):
+    def _save_lib_path(file_name, file_path):
         """Add each lib with its full library path"""
-        formatted_path = lib_path_.replace("\\", "/")
-        _, ext_ = os.path.splitext(formatted_path)
-        if is_shared and ext_ == ".lib":  # Windows interface library
-            interface_lib_paths[lib_] = formatted_path
-        else:
-            lib_paths[lib_] = formatted_path
+        name, ext = file_name.split('.', maxsplit=1)  # ext could be .if.lib
+        formatted_path = file_path.replace("\\", "/")
+        lib_name = None
+        # Users may not name their libraries in a conventional way. For example, directly
+        # use the basename of the lib file as lib name, e.g., cpp_info.libs = ["liblib1.a"]
+        # Issue related: https://github.com/conan-io/conan/issues/11331
+        if file_name in libs:  # let's ensure that it has any extension
+            lib_name = file_name
+        elif name in libs:
+            lib_name = name
+        elif name.startswith("lib"):
+            short_name = name[3:]  # libpkg -> pkg
+            if short_name in libs:
+                lib_name = short_name
+        # FIXME: CPS will be in charge of defining correctly this part. At the moment, we can
+        #        only guess the name of the DLL binary as it does not follow any Conan pattern.
+        if ext == "dll" or ext.endswith(".dll"):
+            if lib_name:
+                shared_windows_libs[lib_name] = formatted_path
+            elif total_libs_number == 1 and libs[0] not in shared_windows_libs:
+                shared_windows_libs[libs[0]] = formatted_path
+            else:  # let's cross the fingers... This is the last chance.
+                for lib in libs:
+                    if ref_name in name and ref_name in lib and lib not in shared_windows_libs:
+                        shared_windows_libs[lib] = formatted_path
+                        break
+        elif lib_name is not None:
+            lib_paths[lib_name] = formatted_path
 
     cpp_info = cpp_info or dep.cpp_info
+    libs = cpp_info.libs[:]  # copying the values
+    if not libs:  # no libraries declared
+        return []
     is_shared = _is_shared()
     libdirs = cpp_info.libdirs
     bindirs = cpp_info.bindirs if is_shared else []  # just want to get shared libraries
-    libs = cpp_info.libs[:]  # copying the values
+    ref_name = reference_name or dep.ref.name
+    if hasattr(cpp_info, "aggregated_components"):
+        # Global cpp_info
+        total_libs_number = len(cpp_info.aggregated_components().libs)
+    else:
+        total_libs_number = len(libs)
     lib_paths = {}
-    interface_lib_paths = {}
+    shared_windows_libs = {}
     for libdir in set(libdirs + bindirs):
         if not os.path.exists(libdir):
             continue
@@ -117,35 +148,22 @@ def _get_libs(dep, cpp_info=None) -> list:
             full_path = os.path.join(libdir, f)
             if not os.path.isfile(full_path):  # Make sure that directories are excluded
                 continue
-            name, ext = os.path.splitext(f)
-            # Users may not name their libraries in a conventional way. For example, directly
-            # use the basename of the lib file as lib name, e.g., cpp_info.libs = ["liblib1.a"]
-            # Issue related: https://github.com/conan-io/conan/issues/11331
-            if ext and f in libs:  # let's ensure that it has any extension
+            _, ext = os.path.splitext(f)
+            if is_shared and ext in (".so", ".dylib", ".lib", ".dll"):
+                _save_lib_path(f, full_path)
+            elif not is_shared and ext in (".a", ".lib"):
                 _save_lib_path(f, full_path)
-                continue
-            if name not in libs and name.startswith("lib"):
-                name = name[3:]  # libpkg -> pkg
-            # FIXME: Should it read a conf variable to know unexpected extensions?
-            if (is_shared and ext in (".so", ".dylib", ".lib", ".dll")) or \
-               (not is_shared and ext in (".a", ".lib")):
-                if name in libs:
-                    _save_lib_path(name, full_path)
-                    continue
-                else:  # last chance: some cases the name could be pkg.if instead of pkg
-                    name = name.split(".", maxsplit=1)[0]
-                    if name in libs:
-                        _save_lib_path(name, full_path)
 
     libraries = []
-    for lib, lib_path in lib_paths.items():
-        interface_lib_path = None
-        if lib_path.endswith(".dll"):
-            if lib not in interface_lib_paths:
+    for name, lib_path in lib_paths.items():
+        import_lib_path = None
+        if is_shared and os.path.splitext(lib_path)[1] == ".lib":
+            if name not in shared_windows_libs:
                 raise ConanException(f"Windows needs a .lib for link-time and .dll for runtime."
                                      f" Only found {lib_path}")
-            interface_lib_path = interface_lib_paths.pop(lib)
-        libraries.append((lib, is_shared, lib_path, interface_lib_path))
+            import_lib_path = lib_path  # .lib
+            lib_path = shared_windows_libs.pop(name)  # .dll
+        libraries.append((name, is_shared, lib_path, import_lib_path))
     # TODO: Would we want to manage the cases where DLLs are provided by the system?
     return libraries
 
@@ -345,8 +363,8 @@ class _BazelBUILDGenerator:
         {% else %}
         static_library = "{{ lib_info.lib_path }}",
         {% endif %}
-        {% if lib_info.interface_lib_path %}
-        interface_library = "{{ lib_info.interface_lib_path }}",
+        {% if lib_info.import_lib_path %}
+        interface_library = "{{ lib_info.import_lib_path }}",
         {% endif %}
     )
     {% endfor %}
@@ -498,12 +516,12 @@ class _BazelBUILDGenerator:
                 libs_info = []
                 bindirs = [_relativize_path(bindir, package_folder_path)
                            for bindir in cpp_info.bindirs]
-                for (lib, is_shared, lib_path, interface_lib_path) in libs:
+                for (lib, is_shared, lib_path, import_lib_path) in libs:
                     # Bazel needs to relativize each path
                     libs_info.append(
                         _LibInfo(lib, is_shared,
                                  _relativize_path(lib_path, package_folder_path),
-                                 _relativize_path(interface_lib_path, package_folder_path))
+                                 _relativize_path(import_lib_path, package_folder_path))
                     )
                 ret.update({
                     "libs": libs_info,
@@ -599,7 +617,8 @@ class _InfoGenerator:
             comp_requires_names = self._get_cpp_info_requires_names(cpp_info)
             comp_name = _get_component_name(self._dep, comp_ref_name)
             # Save each component information
-            components_info.append(_BazelTargetInfo(None, comp_name, comp_requires_names, cpp_info))
+            components_info.append(_BazelTargetInfo(None, comp_name, comp_ref_name,
+                                                    comp_requires_names, cpp_info))
         return components_info
 
     @property
@@ -622,7 +641,8 @@ class _InfoGenerator:
                 for req in self._transitive_reqs.values()
             ]
         cpp_info = self._dep.cpp_info
-        return _BazelTargetInfo(repository_name, pkg_name, requires, cpp_info)
+        return _BazelTargetInfo(repository_name, pkg_name, _get_package_reference_name(self._dep),
+                                requires, cpp_info)
 
 
 class BazelDeps:
diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py
index 284a05b20..a2834789a 100644
--- a/conans/client/graph/graph.py
+++ b/conans/client/graph/graph.py
@@ -135,7 +135,7 @@ class Node(object):
             if require.build and (self.context == CONTEXT_HOST or  # switch context
                                   require.ref.version != self.ref.version):  # or different version
                 pass
-            elif require.visible is False and require.ref.version != self.ref.version:
+            elif require.visible is False: #  and require.ref.version != self.ref.version:
                 # Experimental, to support repackaging of openssl previous versions FIPS plugins
                 pass  # An invisible require doesn't conflict with itself
             else:
@@ -177,15 +177,17 @@ class Node(object):
         source_node = dependant.src
         return source_node.check_downstream_exists(down_require) or result
 
-    def check_loops(self, new_node):
+    def check_loops(self, new_node, count=0):
         if self.ref == new_node.ref and self.context == new_node.context:
-            return self
+            if count >= 1:
+                return self
+            count += 1
         if not self.dependants:
             return
         assert len(self.dependants) == 1
         dependant = self.dependants[0]
         source_node = dependant.src
-        return source_node.check_loops(new_node)
+        return source_node.check_loops(new_node, count)
 
     @property
     def package_id(self):


Test patch:
diff --git a/test/integration/build_requires/test_install_test_build_require.py b/test/integration/build_requires/test_install_test_build_require.py
index 148dfd2a3..8892dc032 100644
--- a/test/integration/build_requires/test_install_test_build_require.py
+++ b/test/integration/build_requires/test_install_test_build_require.py
@@ -222,3 +222,37 @@ def test_bootstrap_other_architecture():
     assert package1["depends"] == []
     assert package2["package_id"] == linux_pkg_id
     assert package2["depends"] == [win_pkg_id]
+
+
+def test_bootstrap_cc():
+    # https://github.com/conan-io/conan/issues/16758
+    cc = textwrap.dedent("""
+        from conan import ConanFile
+
+        class CcConanfile(ConanFile):
+            name = "cc"
+            version = "1.0"
+            options = {"bootstrap": [True, False]}
+            default_options = {"bootstrap": True}
+
+            def build_requirements(self):
+                if self.options.bootstrap:
+                    self.tool_requires("cc/1.0", options={"*:bootstrap": False})
+        """)
+    foo = GenConanfile("foo", "1.0").with_tool_requirement("cc/1.0")
+    c = TestClient()
+    c.save({"cc/conanfile.py": cc,
+            "foo/conanfile.py": foo})
+    c.run("create cc --build-require")
+    # Both are build-requires
+    c.assert_listed_binary({"cc/1.0": ("826727aac60b5956d1df5121a3921f26a6984f15", "Build")},
+                           build=True)
+    c.assert_listed_binary({"cc/1.0": ("96ae1d965ce6f2e2256ac0cfc3a35dcd4860c389", "Build")},
+                           build=True)
+    # This works fine, being able to build the 2 different binaries both bootstrapped and not
+    c.run("list cc/1.0:*")
+    assert "bootstrap: False" in c.out
+    assert "bootstrap: True" in c.out
+    c.run("create foo")
+    c.assert_listed_binary({"cc/1.0": ("96ae1d965ce6f2e2256ac0cfc3a35dcd4860c389", "Cache")},
+                           build=True)
diff --git a/test/integration/configuration/conf/test_conf.py b/test/integration/configuration/conf/test_conf.py
index d8329e09e..52e2bf858 100644
--- a/test/integration/configuration/conf/test_conf.py
+++ b/test/integration/configuration/conf/test_conf.py
@@ -414,3 +414,30 @@ def test_especial_strings_fail():
     assert "user.mycompany:myother: fnmatch" in c.out
     assert "user.mycompany:myfunct: re.search" in c.out
     assert "user.mycompany:mydict: {1: 're', 2: 'fnmatch'}" in c.out
+
+
+def test_conf_test_package():
+    c = TestClient()
+    dep = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            name = "dep"
+            version = "0.1"
+            def generate(self):
+                self.output.info(f'user.myteam:myconf: {self.conf.get("user.myteam:myconf")}')
+        """)
+    pkg = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            def requirements(self):
+                self.requires(self.tested_reference_str)
+            def generate(self):
+                self.output.info(f'user.myteam:myconf: {self.conf.get("user.myteam:myconf")}')
+            def test(self):
+                pass
+        """)
+    c.save({"conanfile.py": dep,
+            "test_package/conanfile.py": pkg})
+    c.run("create . -c &:user.myteam:myconf=myvalue")
+    assert "dep/0.1: user.myteam:myconf: myvalue" in c.out
+    assert "dep/0.1 (test package): user.myteam:myconf: myvalue" in c.out
diff --git a/test/integration/graph/core/test_build_requires.py b/test/integration/graph/core/test_build_requires.py
index 7e396044b..a3945956a 100644
--- a/test/integration/graph/core/test_build_requires.py
+++ b/test/integration/graph/core/test_build_requires.py
@@ -883,12 +883,15 @@ class TestLoops(GraphManagerTest):
         assert type(deps_graph.error) == GraphLoopError
 
         # Build requires always apply to the consumer
-        self.assertEqual(2, len(deps_graph.nodes))
+        self.assertEqual(4, len(deps_graph.nodes))
         app = deps_graph.root
         tool = app.dependencies[0].dst
+        tool2 = tool.dependencies[0].dst
+        tool3 = tool2.dependencies[0].dst
 
         self._check_node(app, "app/0.1", deps=[tool], dependents=[])
-        self._check_node(tool, "cmake/0.1#123", deps=[], dependents=[app])
+        self._check_node(tool, "cmake/0.1#123", deps=[tool2], dependents=[app])
+        self._check_node(tool2, "cmake/0.1#123", deps=[tool3], dependents=[tool])
 
     def test_indirect_loop_error(self):
         # app -(br)-> gtest/0.1 -(br)-> cmake/0.1 -(br)->gtest/0.1 ....
@@ -902,14 +905,16 @@ class TestLoops(GraphManagerTest):
         assert type(deps_graph.error) == GraphLoopError
 
         # Build requires always apply to the consumer
-        self.assertEqual(4, len(deps_graph.nodes))
+        self.assertEqual(6, len(deps_graph.nodes))
         app = deps_graph.root
         cmake = app.dependencies[0].dst
         gtest = cmake.dependencies[0].dst
         cmake2 = gtest.dependencies[0].dst
+        gtest2 = cmake2.dependencies[0].dst
+        cmake3 = gtest2.dependencies[0].dst
 
         assert deps_graph.error.ancestor == cmake
-        assert deps_graph.error.node == cmake2
+        assert deps_graph.error.node == cmake3
 
     def test_infinite_recursion_test(self):
         """Ensure that direct conflicts in a node are properly reported and Conan does not loop"""
diff --git a/test/integration/toolchains/google/test_bazeldeps.py b/test/integration/toolchains/google/test_bazeldeps.py
index d4b99059a..62c4a45a2 100644
--- a/test/integration/toolchains/google/test_bazeldeps.py
+++ b/test/integration/toolchains/google/test_bazeldeps.py
@@ -4,6 +4,7 @@ import textwrap
 
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
+from conan.tools.files import load
 
 
 def test_bazel():
@@ -983,3 +984,132 @@ class TestBazelGenerationBuildContext:
         # Now make sure we can actually build with build!=host context
         c.run("install app -s:h build_type=Debug --build=missing")
         assert "Install finished successfully" in c.out  # the asserts in build() didn't fail
+
+
+
+def test_shared_windows_find_libraries():
+    """
+    Testing the ``_get_libs`` mechanism in Windows, the shared libraries and their
+    import ones are correctly found.
+
+    Note: simulating dependencies with openssl, libcurl, and zlib packages:
+
+    zlib:
+        - (zlib package) bin/zlib1.dll AND lib/zdll.lib
+    libcurl:
+        - (curl component) bin/libcurl.dll AND lib/libcurl_imp.lib
+    openssl:
+        - (crypto component) bin/libcrypto-3-x64.dll AND lib/libcrypto.lib
+        - (ssl component) bin/libssl-3-x64.dll AND lib/libssl.lib
+
+
+    Issue: https://github.com/conan-io/conan/issues/16691
+    """
+    c = TestClient()
+    zlib = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.files import save
+        class Example(ConanFile):
+            name = "zlib"
+            version = "1.0"
+            options = {"shared": [True, False]}
+            default_options = {"shared": False}
+            def package(self):
+                bindirs = os.path.join(self.package_folder, "bin")
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(bindirs, "zlib1.dll"), "")
+                save(self, os.path.join(libdirs, "zdll.lib"), "")
+            def package_info(self):
+                self.cpp_info.libs = ["zdll"]
+            """)
+    openssl = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.files import save
+        class Pkg(ConanFile):
+            name = "openssl"
+            version = "1.0"
+            options = {"shared": [True, False]}
+            default_options = {"shared": False}
+            def package(self):
+                bindirs = os.path.join(self.package_folder, "bin")
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(bindirs, "libcrypto-3-x64.dll"), "")
+                save(self, os.path.join(bindirs, "libssl-3-x64.dll"), "")
+                save(self, os.path.join(libdirs, "libcrypto.lib"), "")
+                save(self, os.path.join(libdirs, "libssl.lib"), "")
+            def package_info(self):
+                self.cpp_info.components["crypto"].libs = ["libcrypto"]
+                self.cpp_info.components["ssl"].libs = ["libssl"]
+        """)
+    libcurl = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.files import save
+        class Example(ConanFile):
+            name = "libcurl"
+            version = "1.0"
+            options = {"shared": [True, False]}
+            default_options = {"shared": False}
+            def package(self):
+                bindirs = os.path.join(self.package_folder, "bin")
+                libdirs = os.path.join(self.package_folder, "lib")
+                save(self, os.path.join(bindirs, "libcurl.dll"), "")
+                save(self, os.path.join(libdirs, "libcurl_imp.lib"), "")
+            def package_info(self):
+                self.cpp_info.components["curl"].libs = ["libcurl_imp"]
+            """)
+    consumer = textwrap.dedent("""
+        [requires]
+        zlib/1.0
+        openssl/1.0
+        libcurl/1.0
+        [options]
+        *:shared=True
+    """)
+    c.save({"conanfile.txt": consumer,
+            "zlib/conanfile.py": zlib,
+            "openssl/conanfile.py": openssl,
+            "libcurl/conanfile.py": libcurl})
+    c.run("export-pkg zlib -o:a shared=True")
+    c.run("export-pkg openssl -o:a shared=True")
+    c.run("export-pkg libcurl -o:a shared=True")
+    c.run("install . -g BazelDeps")
+    libcurl_bazel_build = load(None, os.path.join(c.current_folder, "libcurl", "BUILD.bazel"))
+    zlib_bazel_build = load(None, os.path.join(c.current_folder, "zlib", "BUILD.bazel"))
+    openssl_bazel_build = load(None, os.path.join(c.current_folder, "openssl", "BUILD.bazel"))
+    libcurl_expected = textwrap.dedent("""\
+    # Components precompiled libs
+    cc_import(
+        name = "libcurl_imp_precompiled",
+        shared_library = "bin/libcurl.dll",
+        interface_library = "lib/libcurl_imp.lib",
+    )
+    """)
+    openssl_expected = textwrap.dedent("""\
+    # Components precompiled libs
+    cc_import(
+        name = "libcrypto_precompiled",
+        shared_library = "bin/libcrypto-3-x64.dll",
+        interface_library = "lib/libcrypto.lib",
+    )
+
+    cc_import(
+        name = "libssl_precompiled",
+        shared_library = "bin/libcrypto-3-x64.dll",
+        interface_library = "lib/libssl.lib",
+    )
+    """)
+    zlib_expected = textwrap.dedent("""\
+    # Components precompiled libs
+    # Root package precompiled libs
+    cc_import(
+        name = "zdll_precompiled",
+        shared_library = "bin/zlib1.dll",
+        interface_library = "lib/zdll.lib",
+    )
+    """)
+    assert libcurl_expected in libcurl_bazel_build
+    assert zlib_expected in zlib_bazel_build
+    assert openssl_expected in openssl_bazel_build
diff --git a/test/unittests/tools/google/test_bazel.py b/test/unittests/tools/google/test_bazel.py
index dae3611bf..5c5aa5836 100644
--- a/test/unittests/tools/google/test_bazel.py
+++ b/test/unittests/tools/google/test_bazel.py
@@ -4,12 +4,11 @@ from unittest.mock import MagicMock
 
 import pytest
 
+from conan.test.utils.mocks import ConanFileMock
+from conan.test.utils.test_files import temp_folder
 from conan.tools.files import save
 from conan.tools.google import Bazel
 from conan.tools.google.bazeldeps import _relativize_path, _get_libs
-from conans.model.options import Options
-from conan.test.utils.mocks import ConanFileMock
-from conan.test.utils.test_files import temp_folder
 
 
 @pytest.fixture(scope="module")
@@ -28,10 +27,12 @@ def cpp_info():
     save(ConanFileMock(), os.path.join(libdirs, "mylibwin2.if.lib"), "")
     save(ConanFileMock(), os.path.join(libdirs, "libmylib.so"), "")
     save(ConanFileMock(), os.path.join(libdirs, "subfolder", "libmylib.a"), "")  # recursive
-    cpp_info_mock = MagicMock(_base_folder=None, libdirs=None, bindirs=None, libs=None)
+    cpp_info_mock = MagicMock(_base_folder=None, libdirs=[], bindirs=[], libs=[],
+                              aggregated_components=MagicMock())
     cpp_info_mock._base_folder = folder.replace("\\", "/")
     cpp_info_mock.libdirs = [libdirs]
     cpp_info_mock.bindirs = [bindirs]
+    cpp_info_mock.aggregated_components.return_value = cpp_info_mock
     return cpp_info_mock
 
 
@@ -82,15 +83,11 @@ def test_bazeldeps_relativize_path(path, pattern, expected):
     # Win + shared
     (["mylibwin"], True, [('mylibwin', True, '{base_folder}/bin/mylibwin.dll', '{base_folder}/lib/mylibwin.lib')]),
     # Win + shared (interface with another ext)
-    (["mylibwin2"], True,
-     [('mylibwin2', True, '{base_folder}/bin/mylibwin2.dll', '{base_folder}/lib/mylibwin2.if.lib')]),
-    # Win + Mac + shared
-    (["mylibwin", "mylibmac"], True, [('mylibmac', True, '{base_folder}/bin/mylibmac.dylib', None),
-                                      ('mylibwin', True, '{base_folder}/bin/mylibwin.dll',
-                                       '{base_folder}/lib/mylibwin.lib')]),
-    # Linux + Mac + static
-    (["myliblin", "mylibmac"], False, [('mylibmac', False, '{base_folder}/lib/mylibmac.a', None),
-                                       ('myliblin', False, '{base_folder}/lib/myliblin.a', None)]),
+    (["mylibwin2"], True, [('mylibwin2', True, '{base_folder}/bin/mylibwin2.dll', '{base_folder}/lib/mylibwin2.if.lib')]),
+    # Mac + shared
+    (["mylibmac"], True, [('mylibmac', True, '{base_folder}/bin/mylibmac.dylib', None)]),
+    # Mac + static
+    (["mylibmac"], False, [('mylibmac', False, '{base_folder}/lib/mylibmac.a', None)]),
     # mylib + shared (saved as libmylib.so) -> removing the leading "lib" if it matches
     (["mylib"], True, [('mylib', True, '{base_folder}/lib/libmylib.so', None)]),
     # mylib + static (saved in a subfolder subfolder/libmylib.a) -> non-recursive at this moment
@@ -113,8 +110,10 @@ def test_bazeldeps_get_libs(cpp_info, libs, is_shared, expected):
         if interface_lib_path:
             interface_lib_path = interface_lib_path.format(base_folder=cpp_info._base_folder)
         ret.append((lib, is_shared, lib_path, interface_lib_path))
-    found_libs = _get_libs(ConanFileMock(options=Options(options_values={"shared": is_shared})),
-                           cpp_info)
+    dep = MagicMock()
+    dep.options.get_safe.return_value = is_shared
+    dep.ref.name = "my_pkg"
+    found_libs = _get_libs(dep, cpp_info)
     found_libs.sort()
     ret.sort()
     assert found_libs == ret