diff --git a/hooks/conan-center.py b/hooks/conan-center.py index 78e31097..9d0d87eb 100644 --- a/hooks/conan-center.py +++ b/hooks/conan-center.py @@ -5,6 +5,7 @@ import glob import inspect import os +import platform import re import sys import subprocess @@ -12,9 +13,11 @@ from logging import WARNING, ERROR, INFO, DEBUG, NOTSET import yaml +from conan.tools.apple import is_apple_os from conans import tools from conans.client.graph.python_requires import ConanPythonRequire from conans.client.loader import parse_conanfile +from conans.util.runners import check_output_runner try: from conans import Settings except ImportError: @@ -87,6 +90,7 @@ "KB-H074": "STATIC ARTIFACTS", "KB-H075": "REQUIREMENT OVERRIDE PARAMETER", "KB-H076": "EITHER STATIC OR SHARED OF EACH LIB", + "KB-H077": "APPLE RELOCATABLE SHARED LIBS", } @@ -1240,6 +1244,14 @@ def test(out): for lib in libs: out.warn("Library '{}' links to system library '{}' but it is not in cpp_info.{}.".format(lib, missing_system_lib, attribute)) + @run_test("KB-H077", output) + def test(out): + if not is_apple_os(conanfile): + return + not_relocatable_libs = _get_non_relocatable_shared_libs(conanfile) + if not_relocatable_libs: + out.error(f"install_name dir of these shared libs is not @rpath: {', '.join(not_relocatable_libs)}") + @raise_if_error_output def post_package_info(output, conanfile, reference, **kwargs): @@ -1605,6 +1617,25 @@ def _deplibs_from_shlibs(conanfile, out): return deplibs +def _get_non_relocatable_shared_libs(conanfile): + if platform.system() != "Darwin": + return None + + bad_shared_libs = [] + + libdirs = [os.path.join(conanfile.package_folder, libdir) + for libdir in getattr(conanfile.cpp.package, "libdirs")] + for libdir in libdirs: + for dylib_path in glob.glob(os.path.join(libdir, "*.dylib")): + command = f"otool -D {dylib_path}" + install_name = check_output_runner(command).strip().split(":")[1].strip() + install_name_dir = os.path.dirname(install_name) + if install_name_dir != "@rpath": + bad_shared_libs.append(os.path.basename(dylib_path)) + + return bad_shared_libs + + _GLIBC_LIBS = { "anl", "BrokenLocale", "crypt", "dl", "g", "m", "mvec", "nsl", "nss_compat", "nss_db", "nss_dns", "nss_files", "nss_hesiod", "pthread", "resolv", "rt", "thread_db", "util", diff --git a/tests/test_hooks/conan-center/test_apple_relocatable.py b/tests/test_hooks/conan-center/test_apple_relocatable.py new file mode 100644 index 00000000..c1a56163 --- /dev/null +++ b/tests/test_hooks/conan-center/test_apple_relocatable.py @@ -0,0 +1,126 @@ +import os +import platform +import textwrap + +from parameterized import parameterized + +from conans import tools + +from tests.utils.test_cases.conan_client import ConanClientTestCase + + +class TestAppleRelocatableSharedLibs(ConanClientTestCase): + def _get_environ(self, **kwargs): + kwargs = super(TestAppleRelocatableSharedLibs, self)._get_environ(**kwargs) + kwargs.update({ + "CONAN_HOOKS": os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir, os.pardir, "hooks", "conan-center") + }) + return kwargs + + conanfile = textwrap.dedent("""\ + from conan import ConanFile + from conan.tools.cmake import CMake, cmake_layout + + class FooConan(ConanFile): + name = "foo" + url = "fake_url.com" + license = "fake_license" + description = "whatever" + homepage = "homepage.com" + topics = ("fake_topic", "another_fake_topic") + + settings = "os", "arch", "compiler", "build_type" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + exports_sources = "CMakeLists.txt", "foo.h", "foo.c" + generators = "CMakeToolchain" + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + self.settings.rm_safe("compiler.cppstd") + self.settings.rm_safe("compiler.libcxx") + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["foo"] + """) + + foo_h = textwrap.dedent("""\ + #pragma once + #include "foo_export.h" + FOO_API int foo_test(); + """) + + foo_c = textwrap.dedent("""\ + #include "foo.h" + int foo_test() {return 1;} + """) + + @staticmethod + def cmakelists(install_name_dir="@rpath"): + if install_name_dir == "@rpath": + cmake_install_name_dir = "" + else: + cmake_install_name_dir = f"set(CMAKE_INSTALL_NAME_DIR \"{install_name_dir}\")" + + return textwrap.dedent(f"""\ + cmake_minimum_required(VERSION 3.15) + project(test_relocatable LANGUAGES C) + + include(GenerateExportHeader) + include(GNUInstallDirs) + + {cmake_install_name_dir} + + add_library(foo foo.c) + generate_export_header(foo EXPORT_MACRO_NAME FOO_API) + set_target_properties(foo PROPERTIES C_VISIBILITY_PRESET hidden) + target_include_directories(foo PUBLIC + $ + $ + ) + + install(FILES ${{PROJECT_SOURCE_DIR}}/foo.h ${{PROJECT_BINARY_DIR}}/foo_export.h + DESTINATION ${{CMAKE_INSTALL_INCLUDEDIR}}) + install(TARGETS foo + RUNTIME DESTINATION ${{CMAKE_INSTALL_BINDIR}} + ARCHIVE DESTINATION ${{CMAKE_INSTALL_LIBDIR}} + LIBRARY DESTINATION ${{CMAKE_INSTALL_LIBDIR}}) + """) + + @parameterized.expand([ + (False, "@rpath"), + (False, ""), + (False, "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"), + (True, "@rpath"), + (True, ""), + (True, "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"), + ]) + def test_relocatable(self, shared, install_name_dir): + tools.save("conanfile.py", content=self.conanfile) + tools.save("CMakeLists.txt", content=self.cmakelists(install_name_dir)) + tools.save("foo.h", content=self.foo_h) + tools.save("foo.c", content=self.foo_c) + output = self.conan(["create", ".", "foo/1.0@user/test", "-o", f"foo:shared={shared}"]) + if platform.system() == "Darwin" and shared and install_name_dir != "@rpath": + self.assertIn("ERROR: [APPLE RELOCATABLE SHARED LIBS (KB-H077)] install_name dir of these shared libs is not @rpath: libfoo.dylib", output) + else: + self.assertIn("[APPLE RELOCATABLE SHARED LIBS (KB-H077)] OK", output)