From a170847f1595d9f90b0b4f86046d65ea1c6e2af4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 29 Jun 2023 09:46:57 -0400 Subject: [PATCH 1/2] Resolve otool and install_name_tool with xcrun --- conan/tools/apple/apple.py | 32 +++++++++++++------ .../unittests/tools/apple/test_apple_tools.py | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/conan/tools/apple/apple.py b/conan/tools/apple/apple.py index c42aec928f7..1450786ac12 100644 --- a/conan/tools/apple/apple.py +++ b/conan/tools/apple/apple.py @@ -175,9 +175,19 @@ def libtool(self): """path to libtool""" return self.find('libtool') + @property + def otool(self): + """path to otool""" + return self.find('otool') + + @property + def install_name_tool(self): + """path to install_name_tool""" + return self.find('install_name_tool') -def _get_dylib_install_name(path_to_dylib): - command = "otool -D {}".format(path_to_dylib) + +def _get_dylib_install_name(otool, path_to_dylib): + command = f"{otool} -D {path_to_dylib}" output = iter(check_output_runner(command).splitlines()) # Note: if otool return multiple entries for different architectures # assume they are the same and pick the first one. @@ -194,26 +204,30 @@ def fix_apple_shared_install_name(conanfile): *install_name_tool* utility available in macOS to set ``@rpath``. """ + xcrun = XCRun(conanfile) + otool = xcrun.otool + install_name_tool = xcrun.install_name_tool + def _darwin_is_binary(file, binary_type): if binary_type not in ("DYLIB", "EXECUTE") or os.path.islink(file) or os.path.isdir(file): return False - check_file = f"otool -hv {file}" + check_file = f"{otool} -hv {file}" return binary_type in check_output_runner(check_file) def _darwin_collect_binaries(folder, binary_type): return [os.path.join(folder, f) for f in os.listdir(folder) if _darwin_is_binary(os.path.join(folder, f), binary_type)] def _fix_install_name(dylib_path, new_name): - command = f"install_name_tool {dylib_path} -id {new_name}" + command = f"{install_name_tool} {dylib_path} -id {new_name}" conanfile.run(command) def _fix_dep_name(dylib_path, old_name, new_name): - command = f"install_name_tool {dylib_path} -change {old_name} {new_name}" + command = f"{install_name_tool} {dylib_path} -change {old_name} {new_name}" conanfile.run(command) def _get_rpath_entries(binary_file): entries = [] - command = "otool -l {}".format(binary_file) + command = f"{otool} -l {binary_file}" otool_output = check_output_runner(command).splitlines() for count, text in enumerate(otool_output): pass @@ -223,7 +237,7 @@ def _get_rpath_entries(binary_file): return entries def _get_shared_dependencies(binary_file): - command = "otool -L {}".format(binary_file) + command = f"{otool} -L {binary_file}" all_shared = check_output_runner(command).strip().split(":")[1].strip() ret = [s.split("(")[0].strip() for s in all_shared.splitlines()] return ret @@ -239,7 +253,7 @@ def _fix_dylib_files(conanfile): shared_libs = _darwin_collect_binaries(full_folder, "DYLIB") # fix LC_ID_DYLIB in first pass for shared_lib in shared_libs: - install_name = _get_dylib_install_name(shared_lib) + install_name = _get_dylib_install_name(otool, shared_lib) #TODO: we probably only want to fix the install the name if # it starts with `/`. rpath_name = f"@rpath/{os.path.basename(install_name)}" @@ -282,7 +296,7 @@ def _fix_executables(conanfile, substitutions): existing_rpaths = _get_rpath_entries(executable) rpaths_to_add = list(set(rel_paths) - set(existing_rpaths)) for entry in rpaths_to_add: - command = f"install_name_tool {executable} -add_rpath {entry}" + command = f"{install_name_tool} {executable} -add_rpath {entry}" conanfile.run(command) if is_apple_os(conanfile): diff --git a/conans/test/unittests/tools/apple/test_apple_tools.py b/conans/test/unittests/tools/apple/test_apple_tools.py index 0fa56f48e72..50e410e3756 100644 --- a/conans/test/unittests/tools/apple/test_apple_tools.py +++ b/conans/test/unittests/tools/apple/test_apple_tools.py @@ -68,5 +68,5 @@ def test_get_dylib_install_name(): for mock_output in (single_arch, universal_binary): with mock.patch("conan.tools.apple.apple.check_output_runner") as mock_output_runner: mock_output_runner.return_value = mock_output - install_name = _get_dylib_install_name("/path/to/libwebp.7.dylib") + install_name = _get_dylib_install_name("otool", "/path/to/libwebp.7.dylib") assert "/absolute/path/lib/libwebp.7.dylib" == install_name From 070d98aa74aea6671c2196094d446ff06294467a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 5 Jul 2023 14:43:05 -0400 Subject: [PATCH 2/2] Fix non-apple calls --- conan/tools/apple/apple.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/conan/tools/apple/apple.py b/conan/tools/apple/apple.py index 1450786ac12..8f51b8ef273 100644 --- a/conan/tools/apple/apple.py +++ b/conan/tools/apple/apple.py @@ -204,6 +204,9 @@ def fix_apple_shared_install_name(conanfile): *install_name_tool* utility available in macOS to set ``@rpath``. """ + if not is_apple_os(conanfile): + return + xcrun = XCRun(conanfile) otool = xcrun.otool install_name_tool = xcrun.install_name_tool @@ -299,10 +302,9 @@ def _fix_executables(conanfile, substitutions): command = f"{install_name_tool} {executable} -add_rpath {entry}" conanfile.run(command) - if is_apple_os(conanfile): - substitutions = _fix_dylib_files(conanfile) + substitutions = _fix_dylib_files(conanfile) - # Only "fix" executables if dylib files were patched, otherwise - # there is nothing to do. - if substitutions: - _fix_executables(conanfile, substitutions) + # Only "fix" executables if dylib files were patched, otherwise + # there is nothing to do. + if substitutions: + _fix_executables(conanfile, substitutions)