Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .ci/scripts/wheel/test_cuda_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,27 @@ def test_cuda_libraries_resolve_relatively() -> None:
print(f"✓ {name} resolves the CUDA runtime relatively ({relative[0]})")


def test_no_library_needs_cuda_torch() -> None:
"""No shipped library may record a dependency on PyTorch's own CUDA libraries.

The CUDA delegate needs only the CUDA runtime. A library that names libtorch_cuda or
libc10_cuda cannot load next to a CPU-only torch, so the whole wheel then fails to import
there even though nothing in it calls into those libraries.
"""
package_dir = _package_dir()
cuda_torch = {"libtorch_cuda.so", "libc10_cuda.so"}
offenders = {}
for library in test_shared_libraries._shipped_shared_objects(package_dir):
needed = test_shared_libraries._recorded_dependencies(library) & cuda_torch
if needed:
offenders[str(library.relative_to(package_dir))] = sorted(needed)
assert not offenders, (
f"these libraries depend on PyTorch's CUDA libraries, so the wheel cannot be imported "
f"with a CPU-only torch: {offenders}"
)
print("✓ no shipped library depends on PyTorch's CUDA libraries")


def _row_architectures() -> list[str]:
"""The architectures this row claims, from the same script the build uses.

Expand Down Expand Up @@ -586,6 +607,7 @@ def test_a_model_runs_through_the_delegate() -> None:
test_cuda_libraries_are_shipped()
test_cuda_runtime_is_declared()
test_cuda_libraries_resolve_relatively()
test_no_library_needs_cuda_torch()
test_device_code_covers_the_row()
test_portable_device_code_is_present()
test_the_delegate_registers()
Expand Down
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1450,11 +1450,16 @@ if(EXECUTORCH_BUILD_PYBIND)
aten_bridge PUBLIC C10_USING_CUSTOM_GENERATED_MACROS
)
target_compile_options(aten_bridge PUBLIC ${_pybind_compile_options})
# torch_cpu_library is the CPU half of torch. Linking torch would also force
# libtorch_cuda into DT_NEEDED in a CUDA build, and a CPU-only torch could
# then not load this library.
if(EXECUTORCH_BUILD_SHARED)
target_link_libraries(aten_bridge PRIVATE torch c10 executorch_shared)
target_link_libraries(
aten_bridge PRIVATE torch_cpu_library c10 executorch_shared
)
else()
target_link_libraries(
aten_bridge PRIVATE torch c10 executorch extension_tensor
aten_bridge PRIVATE torch_cpu_library c10 executorch extension_tensor
)
endif()
endif()
Expand Down
5 changes: 4 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,11 @@ if(EXECUTORCH_BUILD_KERNELS_LLM_AOT)
endif()
executorch_target_link_shared_runtime(custom_ops_aot_lib)

# torch_cpu_library is the CPU half of torch. Linking torch would also force
# libtorch_cuda into DT_NEEDED in a CUDA build, and a CPU-only torch could
# then not load this library.
target_link_libraries(
custom_ops_aot_lib PUBLIC cpublas torch extension_tensor
custom_ops_aot_lib PUBLIC cpublas torch_cpu_library extension_tensor
extension_threadpool
)
if(EXECUTORCH_BUILD_KERNELS_LLM_QUANTIZED_MOE_OPTIMIZED)
Expand Down
8 changes: 6 additions & 2 deletions extension/llm/runner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,14 @@ if(EXECUTORCH_BUILD_PYBIND)
find_library(
TORCH_PYTHON_LIBRARY torch_python PATHS "${TORCH_INSTALL_PREFIX}/lib"
)
# Link with the extension_llm_runner library and its dependencies
# Link with the extension_llm_runner library and its dependencies.
#
# torch_cpu_library is the CPU half of torch. Linking torch would also force
# libtorch_cuda into DT_NEEDED in a CUDA build, and a CPU-only torch could
# then not load this library.
target_link_libraries(
_llm_runner PRIVATE extension_llm_runner tokenizers::tokenizers
portable_lib ${TORCH_PYTHON_LIBRARY} ${TORCH_LIBRARIES}
portable_lib ${TORCH_PYTHON_LIBRARY} torch_cpu_library
)
executorch_target_link_shared_runtime(_llm_runner)

Expand Down
11 changes: 8 additions & 3 deletions extension/training/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,18 @@ if(EXECUTORCH_BUILD_PYBIND)
# Pybind library. When the consolidated shared runtime is built, the runtime
# is resolved from it rather than from the whole-archive-forcing static
# `executorch` target, so this module shares the one backend registry.
#
# torch_cpu_library is the CPU half of torch. Linking torch would also force
# libtorch_cuda into DT_NEEDED in a CUDA build, and a CPU-only torch could
# then not load this library.
if(EXECUTORCH_BUILD_SHARED)
set(_pybind_training_dep_libs ${TORCH_PYTHON_LIBRARY} etdump aten_bridge
torch extension_training
torch_cpu_library extension_training
)
else()
set(_pybind_training_dep_libs ${TORCH_PYTHON_LIBRARY} etdump executorch
aten_bridge torch extension_training
set(_pybind_training_dep_libs
${TORCH_PYTHON_LIBRARY} etdump executorch aten_bridge torch_cpu_library
extension_training
)
endif()

Expand Down
5 changes: 4 additions & 1 deletion tools/cmake/Codegen.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,10 @@ function(gen_custom_ops_aot_lib)
set_target_properties(${GEN_LIB_NAME} PROPERTIES CXX_STANDARD 20)
target_compile_definitions(${GEN_LIB_NAME} PRIVATE USE_ATEN_LIB=1)
include_directories(${TORCH_INCLUDE_DIRS})
target_link_libraries(${GEN_LIB_NAME} PRIVATE torch)
# torch_cpu_library is the CPU half of torch. Linking torch would also force
# libtorch_cuda into DT_NEEDED in a CUDA build, and a CPU-only torch could
# then not load this library.
target_link_libraries(${GEN_LIB_NAME} PRIVATE torch_cpu_library)

executorch_target_link_options_shared_lib(${GEN_LIB_NAME})
if(TARGET portable_lib)
Expand Down
Loading