diff --git a/CMakeLists.txt b/CMakeLists.txt index 40cbef38abe..e096734ce05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1421,9 +1421,10 @@ if(EXECUTORCH_BUILD_PYBIND) # --no-as-needed around its own file to every consumer, so the CUDA and # OpenVINO backends are covered by that and are deliberately absent here. That # export is emitted only for NOT APPLE, so it does nothing for an Apple-only - # delegate like Core ML; those are covered instead by Mach-O, which keeps a - # named dylib's namespace-scope initializers without an --as-needed - # equivalent. + # delegate like Core ML; that dylib is instead kept because it is named + # directly on the link line, which retains its namespace-scope initializers + # without an --as-needed equivalent. Only -dead_strip_dylibs would drop it, + # and nothing here passes that. foreach(_retained_component optimized_native_cpu_ops_lib xnnpack_backend extension_threadpool etdump ) diff --git a/backends/apple/coreml/CMakeLists.txt b/backends/apple/coreml/CMakeLists.txt index 4b7b4e70c13..d32cc0ec80a 100644 --- a/backends/apple/coreml/CMakeLists.txt +++ b/backends/apple/coreml/CMakeLists.txt @@ -199,7 +199,10 @@ if(APPLE) # link it, and so the Python extension links it dynamically instead of # absorbing its code. Keyed on EXECUTORCH_BUILD_SHARED rather than # BUILD_SHARED_LIBS, the same way the other shipped delegates decide this, so - # the wheel controls it independently of the generic CMake switch. + # the wheel controls it independently of the generic CMake switch. Shipping it + # shared means a duplicate-symbol conflict would surface at load time rather + # than at static-link time; the process must hold exactly one copy of the + # delegate, which the single shipped library and the dynamic link preserve. if(EXECUTORCH_BUILD_SHARED) set(_coreml_backend_library_type SHARED) else() @@ -270,7 +273,9 @@ if(APPLE) # the delegate references the helpers it uses, so ordinary resolution pulls # exactly those objects. Neither helper registers anything from an # unreferenced namespace-scope initializer, which is the only thing - # force_load would add here. + # force_load would add here. Private also keeps the helpers' include + # directories out of this library's export interface, which is intended: a + # wheel consumer links the bundled delegate and never names the helpers. target_link_libraries( coremldelegate PRIVATE coreml_util coreml_inmemoryfs executorch_shared )