413abbfe75
Signed-off-by: Connor Baker <ConnorBaker01@gmail.com>
494 lines
17 KiB
Nix
494 lines
17 KiB
Nix
# NOTE: buildRedist should never take manifests or fixups as callPackage-provided arguments,
|
|
# since we want to provide the flexibility to call it directly with a different fixup or manifest.
|
|
{
|
|
_cuda,
|
|
autoAddCudaCompatRunpath,
|
|
autoAddDriverRunpath,
|
|
autoPatchelfHook,
|
|
backendStdenv,
|
|
cudaMajorMinorVersion,
|
|
cudaMajorVersion,
|
|
cudaNamePrefix,
|
|
fetchurl,
|
|
lib,
|
|
manifests,
|
|
markForCudatoolkitRootHook,
|
|
removeStubsFromRunpathHook,
|
|
srcOnly,
|
|
stdenv,
|
|
stdenvNoCC,
|
|
}:
|
|
let
|
|
inherit (backendStdenv) hostRedistSystem;
|
|
inherit (_cuda.lib) getNixSystems _mkCudaVariant mkRedistUrl;
|
|
inherit (lib.attrsets)
|
|
foldlAttrs
|
|
getDev
|
|
hasAttr
|
|
isAttrs
|
|
attrNames
|
|
optionalAttrs
|
|
;
|
|
inherit (lib.customisation) extendMkDerivation;
|
|
inherit (lib.lists)
|
|
naturalSort
|
|
concatMap
|
|
unique
|
|
;
|
|
inherit (lib.trivial) mapNullable pipe;
|
|
inherit (_cuda.lib) _mkMetaBadPlatforms _mkMetaBroken _redistSystemIsSupported;
|
|
inherit (lib)
|
|
licenses
|
|
sourceTypes
|
|
teams
|
|
;
|
|
inherit (lib.asserts) assertMsg;
|
|
inherit (lib.lists)
|
|
elem
|
|
findFirst
|
|
findFirstIndex
|
|
foldl'
|
|
intersectLists
|
|
map
|
|
subtractLists
|
|
tail
|
|
;
|
|
inherit (lib.strings)
|
|
concatMapStringsSep
|
|
optionalString
|
|
toUpper
|
|
stringLength
|
|
substring
|
|
;
|
|
inherit (lib.trivial) flip;
|
|
|
|
mkOutputNameVar =
|
|
output:
|
|
assert assertMsg (output != "") "mkOutputNameVar: output name variable must not be empty";
|
|
"output" + toUpper (substring 0 1 output) + substring 1 (stringLength output - 1) output;
|
|
|
|
getSupportedReleases =
|
|
let
|
|
desiredCudaVariant = _mkCudaVariant cudaMajorVersion;
|
|
in
|
|
release:
|
|
# Always show preference to the "source", then "linux-all" redistSystem if they are available, as they are
|
|
# the most general.
|
|
if release ? source then
|
|
{
|
|
inherit (release) source;
|
|
}
|
|
else if release ? linux-all then
|
|
{
|
|
inherit (release) linux-all;
|
|
}
|
|
else
|
|
let
|
|
hasCudaVariants = release ? cuda_variant;
|
|
in
|
|
foldlAttrs (
|
|
acc: name: value:
|
|
acc
|
|
# If the value is an attribute, and when hasCudaVariants is true it has the relevant CUDA variant,
|
|
# then add it to the set.
|
|
// optionalAttrs (isAttrs value && (hasCudaVariants -> hasAttr desiredCudaVariant value)) {
|
|
${name} = value.${desiredCudaVariant} or value;
|
|
}
|
|
) { } release;
|
|
|
|
getPreferredRelease =
|
|
supportedReleases:
|
|
supportedReleases.source or supportedReleases.linux-all or supportedReleases.${hostRedistSystem}
|
|
or null;
|
|
in
|
|
extendMkDerivation {
|
|
constructDrv = backendStdenv.mkDerivation;
|
|
# These attributes are moved to passthru to avoid changing derivation hashes.
|
|
excludeDrvArgNames = [
|
|
# Core
|
|
"redistName"
|
|
"release"
|
|
|
|
# Misc
|
|
"brokenAssertions"
|
|
"platformAssertions"
|
|
"expectedOutputs"
|
|
"outputToPatterns"
|
|
"outputNameVarFallbacks"
|
|
];
|
|
extendDrvArgs =
|
|
finalAttrs:
|
|
{
|
|
# Core
|
|
redistName,
|
|
pname,
|
|
release ? manifests.${finalAttrs.passthru.redistName}.${finalAttrs.pname} or null,
|
|
|
|
# Outputs
|
|
outputs ? [ "out" ],
|
|
propagatedBuildOutputs ? [ ],
|
|
|
|
# Inputs
|
|
nativeBuildInputs ? [ ],
|
|
propagatedBuildInputs ? [ ],
|
|
buildInputs ? [ ],
|
|
|
|
# Checking
|
|
doInstallCheck ? true,
|
|
allowFHSReferences ? false,
|
|
|
|
# Fixups
|
|
appendRunpaths ? [ ],
|
|
includeRemoveStubsFromRunpathHook ? elem "stubs" finalAttrs.outputs,
|
|
postFixup ? "",
|
|
|
|
# Extra
|
|
passthru ? { },
|
|
meta ? { },
|
|
|
|
# Misc
|
|
brokenAssertions ? [ ],
|
|
platformAssertions ? [ ],
|
|
|
|
# Order is important here so we use a list.
|
|
expectedOutputs ? [
|
|
"out"
|
|
"doc"
|
|
"samples"
|
|
"python"
|
|
"bin"
|
|
"dev"
|
|
"include"
|
|
"lib"
|
|
"static"
|
|
"stubs"
|
|
],
|
|
|
|
# Traversed in the order of the outputs speficied in outputs;
|
|
# entries are skipped if they don't exist in outputs.
|
|
# NOTE: The nil LSP gets angry if we do not parenthesize the default attrset.
|
|
outputToPatterns ? {
|
|
bin = [ "bin" ];
|
|
dev = [
|
|
"**/*.pc"
|
|
"**/*.cmake"
|
|
];
|
|
include = [ "include" ];
|
|
lib = [
|
|
"lib"
|
|
"lib64"
|
|
];
|
|
static = [ "**/*.a" ];
|
|
samples = [ "samples" ];
|
|
python = [ "**/*.whl" ];
|
|
stubs = [
|
|
"stubs"
|
|
"lib/stubs"
|
|
];
|
|
},
|
|
|
|
# Defines a list of fallbacks for each potential output.
|
|
# The last fallback is the out output.
|
|
# Taken and modified from:
|
|
# https://github.com/NixOS/nixpkgs/blob/fe5e11faed6241aacf7220436088789287507494/pkgs/build-support/setup-hooks/multiple-outputs.sh#L45-L62
|
|
outputNameVarFallbacks ? {
|
|
outputBin = [ "bin" ];
|
|
outputDev = [ "dev" ];
|
|
outputDoc = [ "doc" ];
|
|
outputInclude = [
|
|
"include"
|
|
"dev"
|
|
];
|
|
outputLib = [ "lib" ];
|
|
outputOut = [ "out" ];
|
|
outputPython = [ "python" ];
|
|
outputSamples = [ "samples" ];
|
|
outputStatic = [ "static" ];
|
|
outputStubs = [
|
|
"stubs"
|
|
"lib"
|
|
];
|
|
},
|
|
...
|
|
}:
|
|
{
|
|
__structuredAttrs = true;
|
|
strictDeps = true;
|
|
|
|
# NOTE: `release` may be null if a redistributable isn't available.
|
|
version = finalAttrs.passthru.release.version or "0-unsupported";
|
|
|
|
# Name should be prefixed by cudaNamePrefix to create more descriptive path names.
|
|
name = "${cudaNamePrefix}-${finalAttrs.pname}-${finalAttrs.version}";
|
|
|
|
# We should only have the output `out` when `src` is null.
|
|
# lists.intersectLists iterates over the second list, checking if the elements are in the first list.
|
|
# As such, the order of the output is dictated by the order of the second list.
|
|
outputs =
|
|
if finalAttrs.src == null then
|
|
[ "out" ]
|
|
else
|
|
intersectLists outputs finalAttrs.passthru.expectedOutputs;
|
|
|
|
# NOTE: Because the `dev` output is special in Nixpkgs -- make-derivation.nix uses it as the default if
|
|
# it is present -- we must ensure that it brings in the expected dependencies. For us, this means that `dev`
|
|
# should include `bin`, `include`, and `lib` -- `static` is notably absent because it is quite large.
|
|
# We do not include `stubs`, as a number of packages contain stubs for libraries they already ship with!
|
|
# Only a few, like cuda_cudart, actually provide stubs for libraries we're missing.
|
|
# As such, these packages should override propagatedBuildOutputs to add `stubs`.
|
|
propagatedBuildOutputs =
|
|
intersectLists [
|
|
"bin"
|
|
"include"
|
|
"lib"
|
|
] finalAttrs.outputs
|
|
++ propagatedBuildOutputs;
|
|
|
|
# src :: null | Derivation
|
|
src = mapNullable (
|
|
{ relative_path, sha256, ... }:
|
|
srcOnly {
|
|
__structuredAttrs = true;
|
|
strictDeps = true;
|
|
stdenv = stdenvNoCC;
|
|
inherit (finalAttrs) pname version;
|
|
src = fetchurl {
|
|
url = mkRedistUrl finalAttrs.passthru.redistName relative_path;
|
|
inherit sha256;
|
|
};
|
|
}
|
|
) (getPreferredRelease finalAttrs.passthru.supportedReleases);
|
|
|
|
# Required for the hook.
|
|
inherit cudaMajorMinorVersion cudaMajorVersion;
|
|
|
|
# We do need some other phases, like configurePhase, so the multiple-output setup hook works.
|
|
dontBuild = true;
|
|
|
|
nativeBuildInputs = [
|
|
./buildRedistHook.bash
|
|
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
|
|
# TODO(@connorbaker): Given we'll have stubs available, we can switch from autoPatchelfIgnoreMissingDeps to
|
|
# allowing autoPatchelf to find and link against the stub files and rely on removeStubsFromRunpathHook to
|
|
# automatically find and replace those references with ones to the driver link lib directory.
|
|
autoAddDriverRunpath
|
|
markForCudatoolkitRootHook
|
|
]
|
|
# autoAddCudaCompatRunpath depends on cuda_compat and would cause
|
|
# infinite recursion if applied to `cuda_compat` itself (beside the fact
|
|
# that it doesn't make sense in the first place)
|
|
++ lib.optionals (finalAttrs.pname != "cuda_compat" && autoAddCudaCompatRunpath.enableHook) [
|
|
# autoAddCudaCompatRunpath must appear AFTER autoAddDriverRunpath.
|
|
# See its documentation in ./setup-hooks/extension.nix.
|
|
autoAddCudaCompatRunpath
|
|
]
|
|
++ nativeBuildInputs;
|
|
|
|
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
|
|
# NOTE: Not all packages actually need this, but it's easier to just add it than create overrides for nearly all
|
|
# of them.
|
|
(lib.getLib stdenv.cc.cc)
|
|
]
|
|
++ buildInputs;
|
|
|
|
# Picked up by autoPatchelf
|
|
# Needed e.g. for libnvrtc to locate (dlopen) libnvrtc-builtins
|
|
appendRunpaths = [ "$ORIGIN" ] ++ appendRunpaths;
|
|
|
|
# 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
|
|
# NOTE: installPhase is not moved into the builder hook because we do a lot of Nix templating.
|
|
installPhase =
|
|
let
|
|
mkMoveToOutputCommand =
|
|
output:
|
|
let
|
|
template = pattern: ''
|
|
moveToOutput "${pattern}" "${"$" + output}"
|
|
'';
|
|
patterns = finalAttrs.passthru.outputToPatterns.${output} or [ ];
|
|
in
|
|
concatMapStringsSep "\n" template patterns;
|
|
in
|
|
# Pre-install hook
|
|
''
|
|
runHook preInstall
|
|
''
|
|
# Create the primary output, out, and move the other outputs into it.
|
|
+ ''
|
|
mkdir -p "$out"
|
|
nixLog "moving tree to output out"
|
|
mv * "$out"
|
|
''
|
|
# Move the outputs into their respective outputs.
|
|
+ ''
|
|
${concatMapStringsSep "\n" mkMoveToOutputCommand (tail finalAttrs.outputs)}
|
|
''
|
|
# Post-install hook
|
|
+ ''
|
|
runHook postInstall
|
|
'';
|
|
|
|
inherit doInstallCheck;
|
|
inherit allowFHSReferences;
|
|
inherit includeRemoveStubsFromRunpathHook;
|
|
|
|
postFixup =
|
|
postFixup
|
|
+ optionalString finalAttrs.includeRemoveStubsFromRunpathHook ''
|
|
nixLog "installing stub removal runpath hook"
|
|
mkdir -p "''${!outputStubs:?}/nix-support"
|
|
printWords >>"''${!outputStubs:?}/nix-support/propagated-build-inputs" \
|
|
"${getDev removeStubsFromRunpathHook}"
|
|
'';
|
|
|
|
passthru = passthru // {
|
|
inherit redistName release;
|
|
|
|
supportedReleases =
|
|
passthru.supportedReleases
|
|
# NOTE: `release` may be null, so we must use `lib.defaultTo`
|
|
or (getSupportedReleases (lib.defaultTo { } finalAttrs.passthru.release));
|
|
|
|
supportedNixSystems =
|
|
passthru.supportedNixSystems or (pipe finalAttrs.passthru.supportedReleases [
|
|
attrNames
|
|
(concatMap getNixSystems)
|
|
naturalSort
|
|
unique
|
|
]);
|
|
|
|
supportedRedistSystems =
|
|
passthru.supportedRedistSystems or (naturalSort (attrNames finalAttrs.passthru.supportedReleases));
|
|
|
|
# NOTE: Downstream may expand this to include other outputs, but they must remember to set the appropriate
|
|
# outputNameVarFallbacks!
|
|
inherit expectedOutputs;
|
|
|
|
# Traversed in the order of the outputs speficied in outputs;
|
|
# entries are skipped if they don't exist in outputs.
|
|
inherit outputToPatterns;
|
|
|
|
# Defines a list of fallbacks for each potential output.
|
|
# The last fallback is the out output.
|
|
# Taken and modified from:
|
|
# https://github.com/NixOS/nixpkgs/blob/fe5e11faed6241aacf7220436088789287507494/pkgs/build-support/setup-hooks/multiple-outputs.sh#L45-L62
|
|
inherit outputNameVarFallbacks;
|
|
|
|
# brokenAssertions :: [Attrs]
|
|
# Used by mkMetaBroken to set `meta.broken`.
|
|
# Example: Broken on a specific version of CUDA or when a dependency has a specific version.
|
|
# NOTE: Do not use this when a broken assertion means evaluation will fail! For example, if
|
|
# a package is missing and is required for the build -- that should go in platformAssertions,
|
|
# because attempts to access attributes on the package will cause evaluation errors.
|
|
brokenAssertions = [
|
|
{
|
|
message = "lib output precedes static output";
|
|
assertion =
|
|
let
|
|
libIndex = findFirstIndex (x: x == "lib") null finalAttrs.outputs;
|
|
staticIndex = findFirstIndex (x: x == "static") null finalAttrs.outputs;
|
|
in
|
|
libIndex == null || staticIndex == null || libIndex < staticIndex;
|
|
}
|
|
{
|
|
# NOTE: We cannot (easily) check that all expected outputs have a corresponding outputNameVar attribute in
|
|
# finalAttrs because of the presence of attributes which use the "output" prefix but are not outputNameVars
|
|
# (e.g., outputChecks and outputName).
|
|
message = "outputNameVarFallbacks is a super set of expectedOutputs";
|
|
assertion =
|
|
subtractLists (map mkOutputNameVar finalAttrs.passthru.expectedOutputs) (
|
|
attrNames finalAttrs.passthru.outputNameVarFallbacks
|
|
) == [ ];
|
|
}
|
|
{
|
|
message = "outputToPatterns is a super set of expectedOutputs";
|
|
assertion =
|
|
subtractLists finalAttrs.passthru.expectedOutputs (attrNames finalAttrs.passthru.outputToPatterns)
|
|
== [ ];
|
|
}
|
|
{
|
|
message = "propagatedBuildOutputs is a subset of outputs";
|
|
assertion = subtractLists finalAttrs.outputs finalAttrs.propagatedBuildOutputs == [ ];
|
|
}
|
|
]
|
|
++ brokenAssertions;
|
|
|
|
# platformAssertions :: [Attrs]
|
|
# Used by mkMetaBadPlatforms to set `meta.badPlatforms`.
|
|
# Example: Broken on a specific system when some condition is met, like targeting Jetson or
|
|
# a required package missing.
|
|
# NOTE: Use this when a failed assertion means evaluation can fail!
|
|
platformAssertions =
|
|
let
|
|
isSupportedRedistSystem = _redistSystemIsSupported hostRedistSystem finalAttrs.passthru.supportedRedistSystems;
|
|
in
|
|
[
|
|
{
|
|
message = "src is null if and only if hostRedistSystem is unsupported";
|
|
assertion = (finalAttrs.src == null) == !isSupportedRedistSystem;
|
|
}
|
|
{
|
|
message = "hostRedistSystem (${hostRedistSystem}) is supported (${builtins.toJSON finalAttrs.passthru.supportedRedistSystems})";
|
|
assertion = isSupportedRedistSystem;
|
|
}
|
|
]
|
|
++ platformAssertions;
|
|
};
|
|
|
|
meta = meta // {
|
|
longDescription = meta.longDescription or "" + ''
|
|
By downloading and using this package you accept the terms and conditions of the associated license(s).
|
|
'';
|
|
sourceProvenance = meta.sourceProvenance or [ sourceTypes.binaryNativeCode ];
|
|
platforms = finalAttrs.passthru.supportedNixSystems;
|
|
broken = _mkMetaBroken finalAttrs;
|
|
badPlatforms = _mkMetaBadPlatforms finalAttrs;
|
|
downloadPage =
|
|
meta.downloadPage
|
|
or "https://developer.download.nvidia.com/compute/${finalAttrs.passthru.redistName}/redist/${finalAttrs.pname}";
|
|
# NOTE:
|
|
# Every redistributable should set its own license; since that's a lot of manual work, we default to
|
|
# nvidiaCudaRedist if the redistributable is from the CUDA redistributables and nvidiaCuda otherwise.
|
|
# Despite calling them "redistributable" and the download URL containing "redist", a number of these
|
|
# packages are not licensed such that redistribution is allowed.
|
|
license =
|
|
if meta ? license then
|
|
lib.toList meta.license
|
|
else if finalAttrs.passthru.redistName == "cuda" then
|
|
[ licenses.nvidiaCudaRedist ]
|
|
else
|
|
[ licenses.nvidiaCuda ];
|
|
teams = meta.teams or [ ] ++ [ teams.cuda ];
|
|
};
|
|
}
|
|
# Setup the outputNameVar variables to gracefully handle missing outputs.
|
|
# NOTE: We cannot use expectedOutputs from finalAttrs.passthru because we will infinitely recurse: presence of
|
|
# attributes in finalAttrs cannot depend on finalAttrs.
|
|
// foldl' (
|
|
acc: output:
|
|
let
|
|
outputNameVar = mkOutputNameVar output;
|
|
in
|
|
acc
|
|
// {
|
|
${outputNameVar} =
|
|
findFirst (flip elem finalAttrs.outputs) "out"
|
|
finalAttrs.passthru.outputNameVarFallbacks.${outputNameVar};
|
|
}
|
|
) { } expectedOutputs;
|
|
|
|
# Don't inherit any of stdenv.mkDerivation's arguments.
|
|
inheritFunctionArgs = false;
|
|
}
|