diff --git a/conan/tools/google/bazeldeps.py b/conan/tools/google/bazeldeps.py index 4df876e3b5f..8a7b1af2094 100644 --- a/conan/tools/google/bazeldeps.py +++ b/conan/tools/google/bazeldeps.py @@ -66,6 +66,14 @@ def _get_dependency_buildfile_content(self, dependency): ) {% endfor %} + {% for libname, (lib_path, dll_path) in shared_with_interface_libs.items() %} + cc_import( + name = "{{ libname }}_precompiled", + interface_library = "{{ lib_path }}", + shared_library = "{{ dll_path }}", + ) + {% endfor %} + cc_library( name = "{{ name }}", {% if headers %} @@ -81,11 +89,14 @@ def _get_dependency_buildfile_content(self, dependency): linkopts = [{{ linkopts }}], {% endif %} visibility = ["//visibility:public"], - {% if libs %} + {% if libs or shared_with_interface_libs %} deps = [ {% for lib in libs %} ":{{ lib }}_precompiled", {% endfor %} + {% for lib in shared_with_interface_libs %} + ":{{ lib }}_precompiled", + {% endfor %} {% for dep in dependencies %} "@{{ dep }}", {% endfor %} @@ -137,16 +148,26 @@ def _relativize_path(p, base_path): for req, dep in dependency.dependencies.items(): dependencies.append(dep.ref.name) + shared_library = dependency.options.get_safe("shared") if dependency.options else False + libs = {} + shared_with_interface_libs = {} for lib in cpp_info.libs: - real_path = self._get_lib_file_paths(cpp_info.libdirs, lib) - if real_path: - libs[lib] = _relativize_path(real_path, package_folder) + lib_path, dll_path = self._get_lib_file_paths(shared_library, + cpp_info.libdirs, + cpp_info.bindirs, + lib) + if lib_path and dll_path: + shared_with_interface_libs[lib] = ( + _relativize_path(lib_path, package_folder), + _relativize_path(dll_path, package_folder)) + elif lib_path: + libs[lib] = _relativize_path(lib_path, package_folder) - shared_library = dependency.options.get_safe("shared") if dependency.options else False context = { "name": dependency.ref.name, "libs": libs, + "shared_with_interface_libs": shared_with_interface_libs, "libdir": lib_dir, "headers": headers, "includes": includes, @@ -158,12 +179,31 @@ def _relativize_path(p, base_path): content = Template(template).render(**context) return content - def _get_lib_file_paths(self, libdirs, lib): + def _get_dll_file_paths(self, bindirs, expected_file): + """Find a given dll file in bin directories. If found return the full + path, otherwise return None. + """ + for each_bin in bindirs: + if not os.path.exists(each_bin): + self._conanfile.output.warning("The bin folder doesn't exist: {}".format(each_bin)) + continue + files = os.listdir(each_bin) + for f in files: + full_path = os.path.join(each_bin, f) + if not os.path.isfile(full_path): + continue + if f == expected_file: + return full_path + return None + + def _get_lib_file_paths(self, shared, libdirs, bindirs, lib): for libdir in libdirs: if not os.path.exists(libdir): self._conanfile.output.warning("The library folder doesn't exist: {}".format(libdir)) continue files = os.listdir(libdir) + lib_basename = None + lib_path = None for f in files: full_path = os.path.join(libdir, f) if not os.path.isfile(full_path): # Make sure that directories are excluded @@ -171,15 +211,26 @@ def _get_lib_file_paths(self, libdirs, lib): # Users may not name their libraries in a conventional way. For example, directly # use the basename of the lib file as lib name. if f == lib: - return full_path + lib_basename = f + lib_path = full_path + break name, ext = os.path.splitext(f) if ext in (".so", ".lib", ".a", ".dylib", ".bc"): if ext != ".lib" and name.startswith("lib"): name = name[3:] if lib == name: - return full_path + lib_basename = f + lib_path = full_path + break + if lib_path is not None: + dll_path = None + name, ext = os.path.splitext(lib_basename) + if shared and ext == ".lib": + dll_path = self._get_dll_file_paths(bindirs, name+".dll") + return lib_path, dll_path self._conanfile.output.warning("The library {} cannot be found in the " "dependency".format(lib)) + return None, None def _create_new_local_repository(self, dependency, dependency_buildfile_name): if dependency.package_folder is None: diff --git a/conans/test/unittests/tools/google/test_bazeldeps.py b/conans/test/unittests/tools/google/test_bazeldeps.py index 789591bd50f..acef3622ea2 100644 --- a/conans/test/unittests/tools/google/test_bazeldeps.py +++ b/conans/test/unittests/tools/google/test_bazeldeps.py @@ -11,10 +11,21 @@ from conans.model.conanfile_interface import ConanFileInterface from conans.model.dependencies import Requirement, ConanFileDependencies from conans.model.ref import ConanFileReference +from conans.test.utils.mocks import MockOptions from conans.test.utils.test_files import temp_folder from conans.util.files import save +class MockConanFileDeps(ConanFile): + def __init__(self, deps, *args, **kwargs): + super(MockConanFileDeps, self).__init__(*args, **kwargs) + self._deps = deps + + @property + def dependencies(self): + return self._deps + + def test_bazeldeps_dependency_buildfiles(): conanfile = ConanFile(Mock(), None) @@ -165,6 +176,57 @@ def test_bazeldeps_interface_buildfiles(): assert(dependency_content == 'load("@rules_cc//cc:defs.bzl","cc_import","cc_library")cc_library(name="OriginalDepName",hdrs=glob(["include/**"]),includes=["include"],visibility=["//visibility:public"],)') +def test_bazeldeps_shared_library_interface_buildfiles(): + + cpp_info = CppInfo("mypkg", "dummy_root_folder2") + cpp_info.libs = ["lib1"] + + options = MockOptions({"shared": True}) + conanfile_dep = MockConanFileDeps(ConanFileDependencies({}), Mock(), None) + conanfile_dep.options = options + conanfile_dep.cpp_info = cpp_info + conanfile_dep._conan_node = Mock() + conanfile_dep.folders.set_base_package(temp_folder()) + conanfile_dep._conan_node.ref = ConanFileReference.loads("OriginalDepName/1.0") + + package_folder = temp_folder() + save(os.path.join(package_folder, "lib", "lib1.lib"), "") + save(os.path.join(package_folder, "bin", "lib1.dll"), "") + conanfile_dep.folders.set_base_package(package_folder) + + req = Requirement(ConanFileReference.loads("OriginalDepName/1.0")) + mock_deps = ConanFileDependencies( + {req: ConanFileInterface(conanfile_dep)}) + conanfile = MockConanFileDeps(mock_deps, Mock(), None) + + bazeldeps = BazelDeps(conanfile) + + dependency = next(iter(bazeldeps._conanfile.dependencies.host.values())) + dependency_content = re.sub(r"\s", + "", + bazeldeps._get_dependency_buildfile_content(dependency)) + expected_content = """ +load("@rules_cc//cc:defs.bzl","cc_import","cc_library") + +cc_import( + name = "lib1_precompiled", + interface_library = "lib/lib1.lib", + shared_library = "bin/lib1.dll", +) + +cc_library( + name = "OriginalDepName", + hdrs=glob(["include/**"]), + includes=["include"], + visibility=["//visibility:public"], + deps = [ + ":lib1_precompiled", + ], +) +""" + assert(dependency_content == re.sub(r"\s", "", expected_content)) + + def test_bazeldeps_main_buildfile(): expected_content = [ 'def load_conan_dependencies():',