Skip to content

Commit a617fb6

Browse files
committed
macOS linking: switch shared library linking back to a two-level namespace
On macOS, extension library builds usually rely on the flag ``-undefined dynamic_lookup`` to get the linker to temporarily accept missing CPython API symbols. As the name implies, those symbols are then dynamically resolved when Python imports such an extension library. Binaries produced in this way are more portable since they don't contain a hardcoded path to the CPython library. This is critical when distributing binary wheels on PyPI, etc.. When targeting macOS>=12, XCode recently started to generate a somewhat ominous warning: ``-undefined dynamic_lookup may not work with chained fixups`` This suggested that the dynamic resolution functionality became broken. I reported this to Apple via feedback request FB11767124 and switched nanobind to an alternative set of flags (``-undefined suppress -flat_namespace``) in the meantime. The flat namespace setup has various limitations though, so this was not a satisfactory long-term solution. Apple investigated the request and updated the behavior of ``ld`` starting with XCode 14.3b1+: it now disables the fixup chain linker optimization whenever ``-undefined dynamic_lookup`` is specified. The feedback from Apple also stated that the parameter ``-Wl,-no_fixup_chains`` should be specified on pre-14.3 XCode versions to ensure correct behavior. This commit realizes those changes by reverting to a two-level namespace for the overall linking process, checking the XCode version, and potentially manually disabling fixup chains when needed. Related discussion is available in the pybind11 (pybind/pybind11#4301) and CPython repositories (python/cpython#97524).
1 parent 214260f commit a617fb6

File tree

1 file changed

+30
-10
lines changed

1 file changed

+30
-10
lines changed

cmake/nanobind-config.cmake

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ set(NB_DIR ${NB_DIR} CACHE INTERNAL "")
2626
# ---------------------------------------------------------------------------
2727
# Helper function to strip unnecessary sections from binaries on Linux/macOS
2828
# ---------------------------------------------------------------------------
29+
2930
function(nanobind_strip name)
3031
if (CMAKE_STRIP AND NOT MSVC AND NOT CMAKE_BUILD_TYPE MATCHES Debug|RelWithDebInfo)
3132
if(APPLE)
@@ -39,6 +40,34 @@ function(nanobind_strip name)
3940
endif()
4041
endfunction()
4142

43+
# ---------------------------------------------------------------------------
44+
# Helper function to handle undefined CPython API symbols on macOS
45+
# ---------------------------------------------------------------------------
46+
47+
# Try to detect the XCode version
48+
if (NOT NB_XCODE_VERSION AND CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")
49+
execute_process(
50+
COMMAND xcodebuild -version
51+
OUTPUT_VARIABLE NB_XCODE_VERSION_STR
52+
OUTPUT_STRIP_TRAILING_WHITESPACE
53+
ERROR_FILE /dev/null)
54+
string(REGEX MATCH "Xcode ([0-9][0-9]?([.][0-9])+)" NB_XCODE_VERSION_MATCH ${NB_XCODE_VERSION_STR})
55+
if (NB_XCODE_VERSION_MATCH)
56+
set(NB_XCODE_VERSION ${CMAKE_MATCH_1} CACHE INTERNAL "")
57+
message(STATUS "nanobind: detected Xcode version ${NB_XCODE_VERSION}.")
58+
else()
59+
message(WARNING "nanobind: could not detect Xcode version!")
60+
endif()
61+
endif()
62+
63+
function (nanobind_link_options name)
64+
if (APPLE)
65+
target_link_options(${name} PRIVATE -undefined dynamic_lookup)
66+
if (NB_XCODE_VERSION AND NB_XCODE_VERSION VERSION_LESS 14.3)
67+
target_link_options(${name} PRIVATE -Wl,-no_fixup_chains)
68+
endif()
69+
endif()
70+
endfunction()
4271

4372
# ---------------------------------------------------------------------------
4473
# Create shared/static library targets for nanobind's non-templated core
@@ -103,10 +132,7 @@ function (nanobind_build_library TARGET_NAME)
103132
)
104133

105134
if (TARGET_TYPE STREQUAL "SHARED")
106-
if (APPLE)
107-
target_link_options(${TARGET_NAME} PRIVATE -undefined suppress -flat_namespace)
108-
endif()
109-
135+
nanobind_link_options(${TARGET_NAME})
110136
target_compile_definitions(${TARGET_NAME} PRIVATE -DNB_BUILD)
111137
target_compile_definitions(${TARGET_NAME} PUBLIC -DNB_SHARED)
112138
nanobind_strip(${TARGET_NAME})
@@ -196,12 +222,6 @@ function (nanobind_compile_options)
196222
endif()
197223
endfunction()
198224

199-
function (nanobind_link_options name)
200-
if (APPLE)
201-
target_link_options(${name} PRIVATE -undefined suppress -flat_namespace)
202-
endif()
203-
endfunction()
204-
205225
function(nanobind_add_module name)
206226
cmake_parse_arguments(PARSE_ARGV 1 ARG "NOMINSIZE;STABLE_ABI;NOSTRIP;NB_STATIC;NB_SHARED;PROTECT_STACK;LTO" "" "")
207227

0 commit comments

Comments
 (0)