Skip to content
This repository was archived by the owner on Jan 27, 2026. It is now read-only.

Commit 76b691d

Browse files
authored
Flatten build variants to build/<variant> (#293)
Prior to this change, kernels were stored in `build/<variant>/<extname>`. However, this was fragile because the extension name had to correspond to the repository name. This change flattens kernels to be stored inside `build/<variant>`. For compatibility with older versions of kernels, we add a module `build/<variant>/<extname>` that loads `build/<variant>`, this compatibility module will removed when the `kernels` update has been around for a while.
1 parent ce202d6 commit 76b691d

File tree

8 files changed

+119
-43
lines changed

8 files changed

+119
-43
lines changed

lib/torch-extension/arch.nix

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
cuda_nvcc,
1414
get-kernel-check,
1515
kernel-abi-check,
16+
kernel-layout-check,
1617
ninja,
1718
python3,
1819
remove-bytecode-hook,
@@ -79,7 +80,12 @@ in
7980
stdenv.mkDerivation (prevAttrs: {
8081
name = "${extensionName}-torch-ext";
8182

82-
inherit doAbiCheck nvccThreads src;
83+
inherit
84+
doAbiCheck
85+
extensionName
86+
nvccThreads
87+
src
88+
;
8389

8490
# Generate build files.
8591
postPatch = ''
@@ -123,10 +129,11 @@ stdenv.mkDerivation (prevAttrs: {
123129
'';
124130

125131
nativeBuildInputs = [
126-
kernel-abi-check
127132
cmake
128133
ninja
129134
build2cmake
135+
kernel-abi-check
136+
kernel-layout-check
130137
remove-bytecode-hook
131138
]
132139
++ lib.optionals doGetKernelCheck [
@@ -223,28 +230,31 @@ stdenv.mkDerivation (prevAttrs: {
223230
postInstall = ''
224231
(
225232
cd ..
226-
cp -r torch-ext/${extensionName} $out/
233+
cp -r torch-ext/${extensionName}/* $out/
227234
)
228-
cp $out/_${extensionName}_*/* $out/${extensionName}
229-
rm -rf $out/_${extensionName}_*
235+
mv $out/_${extensionName}_*/* $out/
236+
rm -d $out/_${extensionName}_${rev}
237+
238+
# Set up a compatibility module for older kernels versions, remove when
239+
# the updated kernels has been around for a while.
240+
mkdir $out/${extensionName}
241+
cp ${./compat.py} $out/${extensionName}/__init__.py
230242
''
231243
+ (lib.optionalString (stripRPath && stdenv.hostPlatform.isLinux)) ''
232-
find $out/${extensionName} -name '*.so' \
244+
find $out/ -name '*.so' \
233245
-exec patchelf --set-rpath "" {} \;
234246
''
235247
+ (lib.optionalString (stripRPath && stdenv.hostPlatform.isDarwin)) ''
236-
find $out/${extensionName} -name '*.so' \
248+
find $out/ -name '*.so' \
237249
-exec rewrite-nix-paths-macho {} \;
238250
239251
# Stub some rpath.
240-
find $out/${extensionName} -name '*.so' \
252+
find $out/ -name '*.so' \
241253
-exec install_name_tool -add_rpath "@loader_path/lib" {} \;
242254
'';
243255

244256
doInstallCheck = true;
245257

246-
getKernelCheck = extensionName;
247-
248258
# We need access to the host system on Darwin for the Metal compiler.
249259
__noChroot = metalSupport;
250260

lib/torch-extension/compat.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import ctypes
2+
import sys
3+
4+
import importlib
5+
from pathlib import Path
6+
from types import ModuleType
7+
8+
def _import_from_path(file_path: Path) -> ModuleType:
9+
# We cannot use the module name as-is, after adding it to `sys.modules`,
10+
# it would also be used for other imports. So, we make a module name that
11+
# depends on the path for it to be unique using the hex-encoded hash of
12+
# the path.
13+
path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14+
module_name = path_hash
15+
spec = importlib.util.spec_from_file_location(module_name, file_path)
16+
if spec is None:
17+
raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18+
module = importlib.util.module_from_spec(spec)
19+
if module is None:
20+
raise ImportError(f"Cannot load module {module_name} from spec")
21+
sys.modules[module_name] = module
22+
spec.loader.exec_module(module) # type: ignore
23+
return module
24+
25+
26+
globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))

lib/torch-extension/no-arch.nix

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
build2cmake,
66
get-kernel-check,
7+
kernel-layout-check,
78
remove-bytecode-hook,
89
torch,
910
}:
@@ -23,14 +24,15 @@
2324
stdenv.mkDerivation (prevAttrs: {
2425
name = "${extensionName}-torch-ext";
2526

26-
inherit src;
27+
inherit extensionName src;
2728

2829
# Add Torch as a dependency, so that devshells for universal kernels
2930
# also get torch as a build input.
3031
buildInputs = [ torch ];
3132

3233
nativeBuildInputs = [
3334
build2cmake
35+
kernel-layout-check
3436
remove-bytecode-hook
3537
]
3638
++ lib.optionals doGetKernelCheck [
@@ -48,10 +50,10 @@ stdenv.mkDerivation (prevAttrs: {
4850

4951
installPhase = ''
5052
mkdir -p $out
51-
cp -r torch-ext/${extensionName} $out/
53+
cp -r torch-ext/${extensionName}/* $out/
54+
mkdir $out/${extensionName}
55+
cp ${./compat.py} $out/${extensionName}/__init__.py
5256
'';
5357

5458
doInstallCheck = true;
55-
56-
getKernelCheck = extensionName;
5759
})

overlay.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ final: prev: {
99

1010
kernel-abi-check = prev.callPackage ./pkgs/kernel-abi-check { };
1111

12+
kernel-layout-check = prev.callPackage ./pkgs/kernel-layout-check { };
13+
1214
rewrite-nix-paths-macho = prev.callPackage ./pkgs/rewrite-nix-paths-macho { };
1315

1416
remove-bytecode-hook = prev.callPackage ./pkgs/remove-bytecode-hook { };

pkgs/get-kernel-check/get-kernel-check-hook.sh

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,39 @@
33
echo "Sourcing get-kernel-check-hook.sh"
44

55
_getKernelCheckHook() {
6-
if [ ! -z "${getKernelCheck}" ]; then
7-
echo "Checking loading kernel with get_kernel"
8-
echo "Check whether the kernel can be loaded with get-kernel: ${getKernelCheck}"
9-
10-
# We strip the full library paths from the extension. Unfortunately,
11-
# in a Nix environment, the library dependencies cannot be found
12-
# anymore. So we have to add the Torch library directory to the
13-
# dynamic linker path to get it to pick it up.
14-
if [ $(uname -s) == "Darwin" ]; then
15-
TORCH_DIR=$(python -c "from pathlib import Path; import torch; print(Path(torch.__file__).parent)")
16-
export DYLD_LIBRARY_PATH="${TORCH_DIR}/lib:${DYLD_LIBRARY_PATH}"
17-
fi
18-
19-
TMPDIR=$(mktemp -d -t test.XXXXXX) || exit 1
20-
trap "rm -rf '$TMPDIR'" EXIT
21-
22-
# Some kernels want to write stuff (especially when they use Triton).
23-
HOME=$(mktemp -d -t test.XXXXXX) || exit 1
24-
trap "rm -rf '$HOME'" EXIT
25-
26-
# Emulate the bundle layout that kernels expects. This even works
27-
# for universal kernels, since kernels checks the non-universal
28-
# path first.
29-
BUILD_VARIANT=$(python -c "from kernels.utils import build_variant; print(build_variant())")
30-
mkdir -p "${TMPDIR}/build"
31-
ln -s "$out" "${TMPDIR}/build/${BUILD_VARIANT}"
32-
33-
python -c "from pathlib import Path; import kernels; kernels.get_local_kernel(Path('${TMPDIR}'), '${getKernelCheck}')"
6+
echo "Checking loading kernel with get_kernel"
7+
8+
if [ -z ${extensionName+x} ]; then
9+
echo "extensionName must be set in derivation"
10+
exit 1
11+
fi
12+
13+
echo "Check whether the kernel can be loaded with get-kernel: ${extensionName}"
14+
15+
# We strip the full library paths from the extension. Unfortunately,
16+
# in a Nix environment, the library dependencies cannot be found
17+
# anymore. So we have to add the Torch library directory to the
18+
# dynamic linker path to get it to pick it up.
19+
if [ $(uname -s) == "Darwin" ]; then
20+
TORCH_DIR=$(python -c "from pathlib import Path; import torch; print(Path(torch.__file__).parent)")
21+
export DYLD_LIBRARY_PATH="${TORCH_DIR}/lib:${DYLD_LIBRARY_PATH}"
3422
fi
23+
24+
TMPDIR=$(mktemp -d -t test.XXXXXX) || exit 1
25+
trap "rm -rf '$TMPDIR'" EXIT
26+
27+
# Some kernels want to write stuff (especially when they use Triton).
28+
HOME=$(mktemp -d -t test.XXXXXX) || exit 1
29+
trap "rm -rf '$HOME'" EXIT
30+
31+
# Emulate the bundle layout that kernels expects. This even works
32+
# for universal kernels, since kernels checks the non-universal
33+
# path first.
34+
BUILD_VARIANT=$(python -c "from kernels.utils import build_variant; print(build_variant())")
35+
mkdir -p "${TMPDIR}/build"
36+
ln -s "$out" "${TMPDIR}/build/${BUILD_VARIANT}"
37+
38+
python -c "from pathlib import Path; import kernels; kernels.get_local_kernel(Path('${TMPDIR}'), '${extensionName}')"
3539
}
3640

3741
postInstallCheckHooks+=(_getKernelCheckHook)

pkgs/kernel-abi-check/kernel-abi-check-hook.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ _checkAbiHook() {
55
echo "Skipping ABI check"
66
else
77
echo "Checking of ABI compatibility"
8-
find "$out/${extensionName}" -name '*.so' -print0 | \
8+
find "$out/" -name '*.so' -print0 | \
99
xargs -0 -n1 kernel-abi-check
1010
fi
1111
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{ makeSetupHook, python3 }:
2+
3+
makeSetupHook {
4+
name = "kernel-layout-check-hook";
5+
} ./kernel-layout-check-hook.sh
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
3+
echo "Sourcing kernel-layout-check-hook.sh"
4+
5+
kernelLayoutCheckHook() {
6+
echo "Checking kernel layout"
7+
8+
if [ -z ${extensionName+x} ]; then
9+
echo "extensionName must be set in derivation"
10+
exit 1
11+
fi
12+
13+
if [ ! -f source/torch-ext/${extensionName}/__init__.py ]; then
14+
echo "Python module at source/torch-ext/${extensionName} must contain __init__.py"
15+
exit 1
16+
fi
17+
18+
# TODO: remove once the old location is removed from kernels.
19+
if [ -e source/torch-ext/${extensionName}/${extensionName} ]; then
20+
echo "Python module at source/torch-ext/${extensionName} must not have ${extensionName} file or directory."
21+
exit 1
22+
fi
23+
}
24+
25+
if [ -z "${dontCheckLayout-}" ]; then
26+
postUnpackHooks+=(kernelLayoutCheckHook)
27+
fi

0 commit comments

Comments
 (0)