Skip to content

Commit

Permalink
Fix: interface_library should be used with shared_library on Windows (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
kkpattern authored May 31, 2022
1 parent eadf41f commit a98bb36
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 8 deletions.
67 changes: 59 additions & 8 deletions conan/tools/google/bazeldeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand All @@ -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 %}
Expand Down Expand Up @@ -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,
Expand All @@ -158,28 +179,58 @@ 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
continue
# 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:
Expand Down
62 changes: 62 additions & 0 deletions conans/test/unittests/tools/google/test_bazeldeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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():',
Expand Down

0 comments on commit a98bb36

Please sign in to comment.