Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
1ac4281
Update
shoumikhin Jul 31, 2026
9415cc5
Update
shoumikhin Aug 1, 2026
448aec8
Update
shoumikhin Aug 1, 2026
c8cb1fc
Update
shoumikhin Aug 1, 2026
d843d61
Update
shoumikhin Aug 1, 2026
ee03285
Update
shoumikhin Aug 1, 2026
773d4ac
Update
shoumikhin Aug 1, 2026
3dc5dc5
Update
shoumikhin Aug 1, 2026
ca8f90d
Update
shoumikhin Aug 1, 2026
3c9845d
Update
shoumikhin Aug 1, 2026
52b926d
Update
shoumikhin Aug 1, 2026
d46bb95
Update
shoumikhin Aug 1, 2026
5c03937
Update
shoumikhin Aug 1, 2026
7b89a94
Update
shoumikhin Aug 1, 2026
4444cdf
Update
shoumikhin Aug 1, 2026
5279954
Update
shoumikhin Aug 1, 2026
1705c79
Update
shoumikhin Aug 1, 2026
c222b38
Update
shoumikhin Aug 1, 2026
52ed6a4
Update
shoumikhin Aug 1, 2026
f7d5065
Update
shoumikhin Aug 1, 2026
92c5102
Update
shoumikhin Aug 1, 2026
9a32371
Update
shoumikhin Aug 1, 2026
2fc3699
Update
shoumikhin Aug 1, 2026
f03af24
Update
shoumikhin Aug 1, 2026
d2ae3ff
Update
shoumikhin Aug 1, 2026
c3e5a4e
Update
shoumikhin Aug 2, 2026
f8f4e0f
Update
shoumikhin Aug 2, 2026
30117eb
Update
shoumikhin Aug 2, 2026
f0cc8d1
Update
shoumikhin Aug 2, 2026
e294878
Update
shoumikhin Aug 2, 2026
6e800b2
Update
shoumikhin Aug 2, 2026
f4599ab
Update
shoumikhin Aug 2, 2026
262166a
Update
shoumikhin Aug 2, 2026
ff43604
Update
shoumikhin Aug 2, 2026
db98248
Update
shoumikhin Aug 2, 2026
6ffb463
Update
shoumikhin Aug 2, 2026
207b1f6
Update
shoumikhin Aug 2, 2026
9fa91a3
Update
shoumikhin Aug 2, 2026
0ba529a
Update
shoumikhin Aug 2, 2026
a403f10
Update
shoumikhin Aug 2, 2026
41245b9
Update
shoumikhin Aug 2, 2026
aa96aa7
Update
shoumikhin Aug 2, 2026
5164d18
Update
shoumikhin Aug 2, 2026
d036cf8
Update
shoumikhin Aug 2, 2026
b498dad
Update
shoumikhin Aug 2, 2026
14dfdee
Update
shoumikhin Aug 2, 2026
658edef
Update
shoumikhin Aug 2, 2026
07fc10c
Update
shoumikhin Aug 2, 2026
c67cf7d
Update
shoumikhin Aug 2, 2026
9940166
Update
shoumikhin Aug 2, 2026
744c9e2
Update
shoumikhin Aug 2, 2026
404cc6b
Update
shoumikhin Aug 2, 2026
9e11594
Update
shoumikhin Aug 2, 2026
daec777
Update
shoumikhin Aug 2, 2026
7631bab
Update
shoumikhin Aug 3, 2026
e7b36dc
Update
shoumikhin Aug 3, 2026
235ec6a
Update
shoumikhin Aug 3, 2026
aee93c2
Update
shoumikhin Aug 3, 2026
1dbfad8
Update
shoumikhin Aug 3, 2026
9f753e4
Update
shoumikhin Aug 3, 2026
51746c0
Update
shoumikhin Aug 3, 2026
324714e
Update
shoumikhin Aug 3, 2026
aae705c
Update
shoumikhin Aug 3, 2026
cd2ba2d
Update
shoumikhin Aug 3, 2026
7bca440
Update
shoumikhin Aug 3, 2026
7b0093f
Update
shoumikhin Aug 3, 2026
3d963e4
Update
shoumikhin Aug 3, 2026
649c1f0
Update
shoumikhin Aug 3, 2026
0798788
Update
shoumikhin Aug 3, 2026
8d4d5ad
Update
shoumikhin Aug 3, 2026
55d9451
Update
shoumikhin Aug 3, 2026
c67a766
Update
shoumikhin Aug 3, 2026
7196f9e
Update
shoumikhin Aug 3, 2026
db6565f
Update
shoumikhin Aug 3, 2026
59e538d
Update
shoumikhin Aug 3, 2026
57dc7e0
Update
shoumikhin Aug 3, 2026
66ca854
Update
shoumikhin Aug 3, 2026
ca35116
Update
shoumikhin Aug 3, 2026
bbe319d
Update
shoumikhin Aug 3, 2026
d19116a
Update
shoumikhin Aug 3, 2026
a19256c
Update
shoumikhin Aug 3, 2026
9829655
Update
shoumikhin Aug 3, 2026
5f4725c
Update
shoumikhin Aug 3, 2026
787bcf7
Update
shoumikhin Aug 3, 2026
bea599c
Update
shoumikhin Aug 3, 2026
f1d7443
Update
shoumikhin Aug 3, 2026
41b1113
Update
shoumikhin Aug 3, 2026
58f5e85
Update
shoumikhin Aug 4, 2026
400a3d0
Update
shoumikhin Aug 4, 2026
d8c29dd
Update
shoumikhin Aug 4, 2026
a3ea1a7
Update
shoumikhin Aug 4, 2026
fb2b031
Update
shoumikhin Aug 4, 2026
4312835
Update
shoumikhin Aug 4, 2026
91e0d8f
Update
shoumikhin Aug 4, 2026
e01fdd6
Update
shoumikhin Aug 4, 2026
5e2b7b6
Update
shoumikhin Aug 4, 2026
81834ab
Update
shoumikhin Aug 4, 2026
c8bf06b
Update
shoumikhin Aug 4, 2026
0819145
Update
shoumikhin Aug 4, 2026
53d7e46
Update
shoumikhin Aug 4, 2026
d5b0f03
Update
shoumikhin Aug 4, 2026
e3934d7
Update
shoumikhin Aug 4, 2026
40917d2
Update
shoumikhin Aug 5, 2026
dc8dc1e
Update
shoumikhin Aug 5, 2026
fbd2419
Update
shoumikhin Aug 5, 2026
3aaa065
Update
shoumikhin Aug 5, 2026
005b8b2
Update
shoumikhin Aug 5, 2026
82481e4
Update
shoumikhin Aug 5, 2026
0f3d269
Update
shoumikhin Aug 5, 2026
60d049a
Update
shoumikhin Aug 5, 2026
d816e56
Update
shoumikhin Aug 5, 2026
ddb1fb7
Update
shoumikhin Aug 5, 2026
a73813c
Update
shoumikhin Aug 5, 2026
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
128 changes: 128 additions & 0 deletions .ci/scripts/test-cuda-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,134 @@ except Exception as e:
exit(1)
"

# The CUDA delegate ships as its own shared library. Nothing else here would
# notice if it were built into more than one place, and a process with two
# copies of the delegate has two copies of its state, so check that the
# installed tree defines it exactly once.
python -c "
import shutil
import subprocess
import sys
from pathlib import Path

if shutil.which('nm') is None:
print('INFO: nm unavailable, skipping the delegate duplication check')
sys.exit(0)

import executorch

# A namespace package has no __file__, so derive the directory from the loader's
# search path instead.
locations = list(getattr(executorch, '__path__', []) or [])
if not locations:
print('INFO: cannot locate the installed package, skipping the check')
sys.exit(0)
package = Path(locations[0])
# A symbol the delegate itself defines. The shim layer's symbols stay resolvable
# even if the delegate stops being packaged, so probing one of those would prove
# the shim is present rather than that delegate code exists exactly once.
symbol = 'executorch::backends::cuda::CudaBackend::execute'
libraries = [p for p in package.rglob('*.so*') if p.is_file() and not p.is_symlink()]
definers = []
for library in libraries:
result = subprocess.run(
['nm', '-DC', str(library)], capture_output=True, text=True, check=False
)
if result.returncode != 0:
continue
for line in result.stdout.splitlines():
parts = line.split(maxsplit=2)
if len(parts) == 3 and parts[1] in 'TtWVu' and parts[2].startswith(symbol):
definers.append(str(library.relative_to(package)))
break

# The file is checked as well as the symbol. A missing delegate would already fail the
# symbol count above, but naming the file gives a clearer message about what is wrong.
delegates = [
p for p in package.rglob('libexecutorch_cuda_backend.so*')
if p.is_file() and not p.is_symlink()
]
if len(delegates) != 1:
print(f'ERROR: expected one shipped CUDA delegate library, found {delegates}')
sys.exit(1)
print(f'SUCCESS: one CUDA delegate library at {delegates[0].relative_to(package)}')

if not definers:
# This runs inside a job that just built with CUDA enabled, so a missing
# delegate means the build or the packaging stopped producing it. Treating
# that as nothing to check would let the regression through.
print('ERROR: CUDA was enabled but no shipped library defines the delegate')
sys.exit(1)
if len(definers) != 1:
print(f'ERROR: expected one library to define the CUDA delegate, found {definers}')
sys.exit(1)
print(f'SUCCESS: exactly one CUDA delegate across {len(libraries)} shipped libraries')
" || exit $?

# Loading it is what the symbol scan above cannot prove. A broken runtime
# path, an undefined symbol, or a mismatched CUDA dependency all pass a name
# check and fail here.
#
# Narrowed before the interpreter starts, not inside it: the loader reads this
# variable once at process start, so changing it later would not affect what the
# library is allowed to find. Only the entries holding a CUDA runtime are kept,
# because the wheel does not bundle it. Everything else is dropped so the check
# cannot pass on a machine whose environment happens to cover a dependency the
# wheel should have carried itself.
cuda_search_path=""
IFS=':' read -ra _search_entries <<< "${LD_LIBRARY_PATH:-}"
for _entry in "${_search_entries[@]}"; do
# Any CUDA library, not just the runtime: the nvidia pip packages put each one in
# its own directory, so matching only libcudart would drop the directory holding
# libcurand and the load would still fail. Patterns are looped over rather than
# brace-expanded, which compgen does not apply.
if [ -z "${_entry}" ]; then
continue
fi
for _pattern in libcudart libcurand libcublas libcudnn; do
if compgen -G "${_entry}/${_pattern}*.so*" > /dev/null; then
cuda_search_path="${cuda_search_path:+${cuda_search_path}:}${_entry}"
break
fi
done
done

LD_LIBRARY_PATH="${cuda_search_path}" python -c "
import ctypes, os, sys
from pathlib import Path

# Imported before anything is loaded by hand. When CUDA comes from torch's bundled
# nvidia wheels rather than a system install, importing torch is what puts those
# libraries in reach; without it the loads below fail for a reason that is not a
# packaging defect.
try:
import torch # noqa: F401
except ImportError:
pass

import executorch

package = Path(getattr(executorch, '__path__', [None])[0])
delegates = [
p for p in package.rglob('libexecutorch_cuda_backend.so*')
if p.is_file() and not p.is_symlink()
]
if len(delegates) != 1:
print(f'ERROR: expected one shipped CUDA delegate, found {delegates}')
sys.exit(1)

for library in [delegates[0]] + sorted(package.rglob('libaoti_cuda_shims.so*')):
if not library.is_file() or library.is_symlink():
continue
try:
ctypes.CDLL(str(library), mode=ctypes.RTLD_GLOBAL)
except OSError as error:
print(f'ERROR: {library.relative_to(package)} does not load: {error}')
sys.exit(1)
print(f'loaded {library.relative_to(package)}')
print('SUCCESS: the CUDA delegate and its shim load from the installed package')
" || exit $?

echo "SUCCESS: ExecuTorch CUDA ${cuda_version} build and verification completed successfully"
}

Expand Down
8 changes: 8 additions & 0 deletions .ci/scripts/wheel/pre_build_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,11 @@ else
export CMAKE_ARGS="${CMAKE_ARGS:-} -DEXECUTORCH_BUILD_VULKAN=OFF"
echo "CMAKE_ARGS=${CMAKE_ARGS}" >> "${GITHUB_ENV}"
fi

# The wheel smoke test compares the wheel's contents against its declared platform tag,
# which needs auditwheel. Installed here rather than by the test, so a release check does
# not depend on the network or change the environment it is verifying. Linux only, since
# auditwheel inspects ELF files.
if [[ "$(uname -s)" == "Linux" ]]; then
pip install auditwheel
fi
Loading
Loading