2024-01-02 11:29:13 +00:00
{
# General callPackage-supplied arguments
2024-04-21 15:54:59 +00:00
autoAddDriverRunpath ,
autoAddCudaCompatRunpath ,
2024-01-02 11:29:13 +00:00
autoPatchelfHook ,
backendStdenv ,
fetchurl ,
lib ,
lndir ,
markForCudatoolkitRootHook ,
flags ,
stdenv ,
# Builder-specific arguments
# Short package name (e.g., "cuda_cccl")
# pname : String
pname ,
# Common name (e.g., "cutensor" or "cudnn") -- used in the URL.
# Also known as the Redistributable Name.
# redistName : String,
redistName ,
# If libPath is non-null, it must be a subdirectory of `lib`.
# The contents of `libPath` will be moved to the root of `lib`.
libPath ? null ,
# See ./modules/generic/manifests/redistrib/release.nix
redistribRelease ,
# See ./modules/generic/manifests/feature/release.nix
featureRelease ,
2024-01-13 08:15:51 +00:00
cudaMajorMinorVersion ,
2024-01-02 11:29:13 +00:00
} :
let
inherit ( lib )
attrsets
lists
meta
strings
trivial
licenses
teams
sourceTypes
;
2024-04-21 15:54:59 +00:00
inherit ( stdenv ) hostPlatform ;
2024-01-02 11:29:13 +00:00
# Get the redist architectures for which package provides distributables.
# These are used by meta.platforms.
supportedRedistArchs = builtins . attrNames featureRelease ;
2024-01-13 08:15:51 +00:00
# redistArch :: String
# The redistArch is the name of the architecture for which the redistributable is built.
# It is `"unsupported"` if the redistributable is not supported on the target platform.
2024-01-02 11:29:13 +00:00
redistArch = flags . getRedistArch hostPlatform . system ;
2024-01-25 14:12:00 +00:00
2024-04-21 15:54:59 +00:00
sourceMatchesHost = flags . getNixSystem redistArch == hostPlatform . system ;
2024-01-02 11:29:13 +00:00
in
2024-04-21 15:54:59 +00:00
backendStdenv . mkDerivation ( finalAttrs : {
# NOTE: Even though there's no actual buildPhase going on here, the derivations of the
# redistributables are sensitive to the compiler flags provided to stdenv. The patchelf package
# is sensitive to the compiler flags provided to stdenv, and we depend on it. As such, we are
# also sensitive to the compiler flags provided to stdenv.
inherit pname ;
inherit ( redistribRelease ) version ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Don't force serialization to string for structured attributes, like outputToPatterns
# and brokenConditions.
# Avoids "set cannot be coerced to string" errors.
__structuredAttrs = true ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Keep better track of dependencies.
strictDeps = true ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# NOTE: Outputs are evaluated jointly with meta, so in the case that this is an unsupported platform,
# we still need to provide a list of outputs.
outputs =
let
# Checks whether the redistributable provides an output.
hasOutput =
output :
attrsets . attrByPath [
redistArch
" o u t p u t s "
output
] false featureRelease ;
# Order is important here so we use a list.
possibleOutputs = [
" b i n "
2024-01-02 11:29:13 +00:00
" l i b "
2024-04-21 15:54:59 +00:00
" s t a t i c "
" d e v "
" d o c "
" s a m p l e "
" p y t h o n "
2024-01-02 11:29:13 +00:00
] ;
2024-04-21 15:54:59 +00:00
# Filter out outputs that don't exist in the redistributable.
# NOTE: In the case the redistributable isn't supported on the target platform,
# we will have `outputs = [ "out" ] ++ possibleOutputs`. This is of note because platforms which
# aren't supported would otherwise have evaluation errors when trying to access outputs other than `out`.
# The alternative would be to have `outputs = [ "out" ]` when`redistArch = "unsupported"`, but that would
# require adding guards throughout the entirety of the CUDA package set to ensure `cudaSupport` is true --
# recall that OfBorg will evaluate packages marked as broken and that `cudaPackages` will be evaluated with
# `cudaSupport = false`!
additionalOutputs =
if redistArch == " u n s u p p o r t e d " then possibleOutputs else builtins . filter hasOutput possibleOutputs ;
# The out output is special -- it's the default output and we always include it.
outputs = [ " o u t " ] ++ additionalOutputs ;
in
outputs ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Traversed in the order of the outputs speficied in outputs;
# entries are skipped if they don't exist in outputs.
outputToPatterns = {
bin = [ " b i n " ] ;
dev = [
" s h a r e / p k g c o n f i g "
" * * / * . p c "
" * * / * . c m a k e "
] ;
lib = [
" l i b "
" l i b 6 4 "
] ;
static = [ " * * / * . a " ] ;
sample = [ " s a m p l e s " ] ;
python = [ " * * / * . w h l " ] ;
} ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Useful for introspecting why something went wrong. Maps descriptions of why the derivation would be marked as
# broken on have badPlatforms include the current platform.
2024-01-13 08:15:51 +00:00
2024-04-21 15:54:59 +00:00
# brokenConditions :: AttrSet Bool
# Sets `meta.broken = true` if any of the conditions are true.
# Example: Broken on a specific version of CUDA or when a dependency has a specific version.
brokenConditions = {
# Unclear how this is handled by Nix internals.
" D u p l i c a t e e n t r i e s i n o u t p u t s " = finalAttrs . outputs != lists . unique finalAttrs . outputs ;
# Typically this results in the static output being empty, as all libraries are moved
# back to the lib output.
" l i b o u t p u t f o l l o w s s t a t i c o u t p u t " =
let
libIndex = lists . findFirstIndex ( x : x == " l i b " ) null finalAttrs . outputs ;
staticIndex = lists . findFirstIndex ( x : x == " s t a t i c " ) null finalAttrs . outputs ;
in
libIndex != null && staticIndex != null && libIndex > staticIndex ;
} ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# badPlatformsConditions :: AttrSet Bool
# Sets `meta.badPlatforms = meta.platforms` if any of the conditions are true.
# Example: Broken on a specific architecture when some condition is met (like targeting Jetson).
badPlatformsConditions = {
" N o s o u r c e " = ! sourceMatchesHost ;
} ;
# src :: Optional Derivation
# If redistArch doesn't exist in redistribRelease, return null.
src = trivial . mapNullable (
{ relative_path , sha256 , . . . }:
fetchurl {
url = " h t t p s : / / d e v e l o p e r . d o w n l o a d . n v i d i a . c o m / c o m p u t e / ${ redistName } / r e d i s t / ${ relative_path } " ;
inherit sha256 ;
}
) ( redistribRelease . ${ redistArch } or null ) ;
2024-01-13 08:15:51 +00:00
2024-04-21 15:54:59 +00:00
postPatch =
# Pkg-config's setup hook expects configuration files in $out/share/pkgconfig
''
for path in pkg-config pkgconfig ; do
2024-01-13 08:15:51 +00:00
[ [ - d " $ p a t h " ] ] || continue
mkdir - p share/pkgconfig
mv " $ p a t h " /* s h a r e / p k g c o n f i g /
rmdir " $ p a t h "
done
2024-04-21 15:54:59 +00:00
''
# Rewrite FHS paths with store paths
# NOTE: output* fall back to out if the corresponding output isn't defined.
+ ''
for pc in share/pkgconfig /* . p c ; d o
2024-01-02 11:29:13 +00:00
sed - i \
- e " s | ^ c u d a r o o t \s * = . * \$ | c u d a r o o t = ' ' ${ ! outputDev } | " \
- e " s | ^ l i b d i r \s * = . * / l i b \$ | l i b d i r = ' ' ${ ! outputLib } / l i b | " \
- e " s | ^ i n c l u d e d i r \s * = . * / i n c l u d e \$ | i n c l u d e d i r = ' ' ${ ! outputDev } / i n c l u d e | " \
" $ p c "
done
2024-04-21 15:54:59 +00:00
''
# Generate unversioned names.
# E.g. cuda-11.8.pc -> cuda.pc
+ ''
for pc in share/pkgconfig /* - " $ m a j o r M i n o r V e r s i o n . p c " ; d o
2024-01-13 08:15:51 +00:00
ln - s " $ ( b a s e n a m e " $ pc " ) " " ' ' ${ pc % - $ majorMinorVersion . pc } " . pc
done
2024-01-02 11:29:13 +00:00
'' ;
2024-04-21 15:54:59 +00:00
env . majorMinorVersion = cudaMajorMinorVersion ;
2024-01-13 08:15:51 +00:00
2024-04-21 15:54:59 +00:00
# We do need some other phases, like configurePhase, so the multiple-output setup hook works.
dontBuild = true ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
nativeBuildInputs =
[
2024-01-02 11:29:13 +00:00
autoPatchelfHook
# This hook will make sure libcuda can be found
# in typically /lib/opengl-driver by adding that
# directory to the rpath of all ELF binaries.
# Check e.g. with `patchelf --print-rpath path/to/my/binary
2024-04-21 15:54:59 +00:00
autoAddDriverRunpath
2024-01-02 11:29:13 +00:00
markForCudatoolkitRootHook
]
2024-04-21 15:54:59 +00:00
# autoAddCudaCompatRunpath depends on cuda_compat and would cause
2024-01-02 11:29:13 +00:00
# infinite recursion if applied to `cuda_compat` itself (beside the fact
# that it doesn't make sense in the first place)
++ lib . optionals ( pname != " c u d a _ c o m p a t " && flags . isJetsonBuild ) [
2024-04-21 15:54:59 +00:00
# autoAddCudaCompatRunpath must appear AFTER autoAddDriverRunpath.
2024-01-02 11:29:13 +00:00
# See its documentation in ./setup-hooks/extension.nix.
2024-04-21 15:54:59 +00:00
autoAddCudaCompatRunpath
2024-01-02 11:29:13 +00:00
] ;
2024-04-21 15:54:59 +00:00
buildInputs = [
# autoPatchelfHook will search for a libstdc++ and we're giving it
# one that is compatible with the rest of nixpkgs, even when
# nvcc forces us to use an older gcc
# NB: We don't actually know if this is the right thing to do
stdenv . cc . cc . lib
] ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Picked up by autoPatchelf
# Needed e.g. for libnvrtc to locate (dlopen) libnvrtc-builtins
appendRunpaths = [ " $ O R I G I N " ] ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# NOTE: We don't need to check for dev or doc, because those outputs are handled by
# the multiple-outputs setup hook.
# NOTE: moveToOutput operates on all outputs:
# https://github.com/NixOS/nixpkgs/blob/2920b6fc16a9ed5d51429e94238b28306ceda79e/pkgs/build-support/setup-hooks/multiple-outputs.sh#L105-L107
installPhase =
let
mkMoveToOutputCommand =
output :
let
template = pattern : '' m o v e T o O u t p u t " ${ pattern } " " ${ " $ " + output } " '' ;
patterns = finalAttrs . outputToPatterns . ${ output } or [ ] ;
in
strings . concatMapStringsSep " \n " template patterns ;
in
# Pre-install hook
''
runHook preInstall
''
# Handle the existence of libPath, which requires us to re-arrange the lib directory
+ strings . optionalString ( libPath != null ) ''
full_lib_path = " l i b / ${ libPath } "
if [ [ ! - d " $ f u l l _ l i b _ p a t h " ] ] ; then
echo " ${ finalAttrs . pname } : ' $ f u l l _ l i b _ p a t h ' d o e s n o t e x i s t , o n l y f o u n d : " > & 2
find lib / - mindepth 1 - maxdepth 1 > & 2
echo " T h i s r e l e a s e m i g h t n o t s u p p o r t y o u r C U D A v e r s i o n " > & 2
exit 1
fi
echo " M a k i n g l i b P a t h ' $ f u l l _ l i b _ p a t h ' t h e r o o t o f l i b " > & 2
mv " $ f u l l _ l i b _ p a t h " lib_new
rm - r lib
mv lib_new lib
''
# Create the primary output, out, and move the other outputs into it.
+ ''
mkdir - p " $ o u t "
mv * " $ o u t "
''
# Move the outputs into their respective outputs.
+ strings . concatMapStringsSep " \n " mkMoveToOutputCommand ( builtins . tail finalAttrs . outputs )
# Add a newline to the end of the installPhase, so that the post-install hook doesn't
# get concatenated with the last moveToOutput command.
+ " \n "
# Post-install hook
+ ''
runHook postInstall
'' ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
doInstallCheck = true ;
allowFHSReferences = true ; # TODO: Default to `false`
postInstallCheck = ''
echo " E x e c u t i n g p o s t I n s t a l l C h e c k "
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
if [ [ - z " ' ' ${ allowFHSReferences- } " ] ] ; then
mapfile - t outputPaths < < ( for o in $ ( getAllOutputNames ) ; do echo " ' ' ${ ! o } " ; done )
if grep - - max-count = 5 - - recursive - - exclude = LICENSE /usr / " ' ' ${ outputPaths [ @ ] } " ; then
echo " D e t e c t e d r e f e r e n c e s t o / u s r " > & 2
exit 1
2024-01-02 11:29:13 +00:00
fi
2024-04-21 15:54:59 +00:00
fi
'' ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# libcuda needs to be resolved during runtime
autoPatchelfIgnoreMissingDeps = [
" l i b c u d a . s o "
" l i b c u d a . s o . * "
] ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# The out output leverages the same functionality which backs the `symlinkJoin` function in
# Nixpkgs:
# https://github.com/NixOS/nixpkgs/blob/d8b2a92df48f9b08d68b0132ce7adfbdbc1fbfac/pkgs/build-support/trivial-builders/default.nix#L510
#
# That should allow us to emulate "fat" default outputs without having to actually create them.
#
# It is important that this run after the autoPatchelfHook, otherwise the symlinks in out will reference libraries in lib, creating a circular dependency.
postPhases = [ " p o s t P a t c h e l f " ] ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# For each output, create a symlink to it in the out output.
# NOTE: We must recreate the out output here, because the setup hook will have deleted it if it was empty.
postPatchelf = ''
mkdir - p " $ o u t "
for output in $ ( getAllOutputNames ) ; do
if [ [ " $ o u t p u t " != " o u t " ] ] ; then
$ { meta . getExe lndir } " ' ' ${ ! output } " " $ o u t "
fi
done
'' ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Make the CUDA-patched stdenv available
passthru . stdenv = backendStdenv ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# Setting propagatedBuildInputs to false will prevent outputs known to the multiple-outputs
# from depending on `out` by default.
# https://github.com/NixOS/nixpkgs/blob/2920b6fc16a9ed5d51429e94238b28306ceda79e/pkgs/build-support/setup-hooks/multiple-outputs.sh#L196
# Indeed, we want to do the opposite -- fat "out" outputs that contain all the other outputs.
propagatedBuildOutputs = false ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
# By default, if the dev output exists it just uses that.
# However, because we disabled propagatedBuildOutputs, dev doesn't contain libraries or
# anything of the sort. To remedy this, we set outputSpecified to true, and use
# outputsToInstall, which tells Nix which outputs to use when the package name is used
# unqualified (that is, without an explicit output).
outputSpecified = true ;
2024-01-02 11:29:13 +00:00
2024-04-21 15:54:59 +00:00
meta = {
description = " ${ redistribRelease . name } . B y d o w n l o a d i n g a n d u s i n g t h e p a c k a g e s y o u a c c e p t t h e t e r m s a n d c o n d i t i o n s o f t h e ${ finalAttrs . meta . license . shortName } " ;
sourceProvenance = [ sourceTypes . binaryNativeCode ] ;
broken = lists . any trivial . id ( attrsets . attrValues finalAttrs . brokenConditions ) ;
platforms = trivial . pipe supportedRedistArchs [
# Map each redist arch to the equivalent nix system or null if there is no equivalent.
( builtins . map flags . getNixSystem )
# Filter out unsupported systems
( builtins . filter ( nixSystem : ! ( strings . hasPrefix " u n s u p p o r t e d - " nixSystem ) ) )
] ;
badPlatforms =
let
isBadPlatform = lists . any trivial . id ( attrsets . attrValues finalAttrs . badPlatformsConditions ) ;
in
lists . optionals isBadPlatform finalAttrs . meta . platforms ;
license = licenses . unfree ;
maintainers = teams . cuda . members ;
# Force the use of the default, fat output by default (even though `dev` exists, which
# causes Nix to prefer that output over the others if outputSpecified isn't set).
outputsToInstall = [ " o u t " ] ;
} ;
} )