Merge pull request #307098 from r-ryantm/auto-update/cilium-cli
[NixPkgs.git] / pkgs / development / cuda-modules / generic-builders / manifest.nix
blob006abb456cdc538dcebf93921b884ecc6126b9ad
2   # General callPackage-supplied arguments
3   autoAddDriverRunpath,
4   autoAddCudaCompatRunpath,
5   autoPatchelfHook,
6   backendStdenv,
7   fetchurl,
8   lib,
9   lndir,
10   markForCudatoolkitRootHook,
11   flags,
12   stdenv,
13   # Builder-specific arguments
14   # Short package name (e.g., "cuda_cccl")
15   # pname : String
16   pname,
17   # Common name (e.g., "cutensor" or "cudnn") -- used in the URL.
18   # Also known as the Redistributable Name.
19   # redistName : String,
20   redistName,
21   # If libPath is non-null, it must be a subdirectory of `lib`.
22   # The contents of `libPath` will be moved to the root of `lib`.
23   libPath ? null,
24   # See ./modules/generic/manifests/redistrib/release.nix
25   redistribRelease,
26   # See ./modules/generic/manifests/feature/release.nix
27   featureRelease,
28   cudaMajorMinorVersion,
30 let
31   inherit (lib)
32     attrsets
33     lists
34     meta
35     strings
36     trivial
37     licenses
38     teams
39     sourceTypes
40     ;
42   inherit (stdenv) hostPlatform;
44   # Get the redist architectures for which package provides distributables.
45   # These are used by meta.platforms.
46   supportedRedistArchs = builtins.attrNames featureRelease;
47   # redistArch :: String
48   # The redistArch is the name of the architecture for which the redistributable is built.
49   # It is `"unsupported"` if the redistributable is not supported on the target platform.
50   redistArch = flags.getRedistArch hostPlatform.system;
52   sourceMatchesHost = flags.getNixSystem redistArch == hostPlatform.system;
54 backendStdenv.mkDerivation (finalAttrs: {
55   # NOTE: Even though there's no actual buildPhase going on here, the derivations of the
56   # redistributables are sensitive to the compiler flags provided to stdenv. The patchelf package
57   # is sensitive to the compiler flags provided to stdenv, and we depend on it. As such, we are
58   # also sensitive to the compiler flags provided to stdenv.
59   inherit pname;
60   inherit (redistribRelease) version;
62   # Don't force serialization to string for structured attributes, like outputToPatterns
63   # and brokenConditions.
64   # Avoids "set cannot be coerced to string" errors.
65   __structuredAttrs = true;
67   # Keep better track of dependencies.
68   strictDeps = true;
70   # NOTE: Outputs are evaluated jointly with meta, so in the case that this is an unsupported platform,
71   # we still need to provide a list of outputs.
72   outputs =
73     let
74       # Checks whether the redistributable provides an output.
75       hasOutput =
76         output:
77         attrsets.attrByPath [
78           redistArch
79           "outputs"
80           output
81         ] false featureRelease;
82       # Order is important here so we use a list.
83       possibleOutputs = [
84         "bin"
85         "lib"
86         "static"
87         "dev"
88         "doc"
89         "sample"
90         "python"
91       ];
92       # Filter out outputs that don't exist in the redistributable.
93       # NOTE: In the case the redistributable isn't supported on the target platform,
94       # we will have `outputs = [ "out" ] ++ possibleOutputs`. This is of note because platforms which
95       # aren't supported would otherwise have evaluation errors when trying to access outputs other than `out`.
96       # The alternative would be to have `outputs = [ "out" ]` when`redistArch = "unsupported"`, but that would
97       # require adding guards throughout the entirety of the CUDA package set to ensure `cudaSupport` is true --
98       # recall that OfBorg will evaluate packages marked as broken and that `cudaPackages` will be evaluated with
99       # `cudaSupport = false`!
100       additionalOutputs =
101         if redistArch == "unsupported" then possibleOutputs else builtins.filter hasOutput possibleOutputs;
102       # The out output is special -- it's the default output and we always include it.
103       outputs = [ "out" ] ++ additionalOutputs;
104     in
105     outputs;
107   # Traversed in the order of the outputs speficied in outputs;
108   # entries are skipped if they don't exist in outputs.
109   outputToPatterns = {
110     bin = [ "bin" ];
111     dev = [
112       "share/pkgconfig"
113       "**/*.pc"
114       "**/*.cmake"
115     ];
116     lib = [
117       "lib"
118       "lib64"
119     ];
120     static = [ "**/*.a" ];
121     sample = [ "samples" ];
122     python = [ "**/*.whl" ];
123   };
125   # Useful for introspecting why something went wrong. Maps descriptions of why the derivation would be marked as
126   # broken on have badPlatforms include the current platform.
128   # brokenConditions :: AttrSet Bool
129   # Sets `meta.broken = true` if any of the conditions are true.
130   # Example: Broken on a specific version of CUDA or when a dependency has a specific version.
131   brokenConditions = {
132     # Unclear how this is handled by Nix internals.
133     "Duplicate entries in outputs" = finalAttrs.outputs != lists.unique finalAttrs.outputs;
134     # Typically this results in the static output being empty, as all libraries are moved
135     # back to the lib output.
136     "lib output follows static output" =
137       let
138         libIndex = lists.findFirstIndex (x: x == "lib") null finalAttrs.outputs;
139         staticIndex = lists.findFirstIndex (x: x == "static") null finalAttrs.outputs;
140       in
141       libIndex != null && staticIndex != null && libIndex > staticIndex;
142   };
144   # badPlatformsConditions :: AttrSet Bool
145   # Sets `meta.badPlatforms = meta.platforms` if any of the conditions are true.
146   # Example: Broken on a specific architecture when some condition is met (like targeting Jetson).
147   badPlatformsConditions = {
148     "No source" = !sourceMatchesHost;
149   };
151   # src :: Optional Derivation
152   # If redistArch doesn't exist in redistribRelease, return null.
153   src = trivial.mapNullable (
154     { relative_path, sha256, ... }:
155     fetchurl {
156       url = "https://developer.download.nvidia.com/compute/${redistName}/redist/${relative_path}";
157       inherit sha256;
158     }
159   ) (redistribRelease.${redistArch} or null);
161   postPatch =
162     # Pkg-config's setup hook expects configuration files in $out/share/pkgconfig
163     ''
164       for path in pkg-config pkgconfig; do
165         [[ -d "$path" ]] || continue
166         mkdir -p share/pkgconfig
167         mv "$path"/* share/pkgconfig/
168         rmdir "$path"
169       done
170     ''
171     # Rewrite FHS paths with store paths
172     # NOTE: output* fall back to out if the corresponding output isn't defined.
173     + ''
174       for pc in share/pkgconfig/*.pc; do
175         sed -i \
176           -e "s|^cudaroot\s*=.*\$|cudaroot=''${!outputDev}|" \
177           -e "s|^libdir\s*=.*/lib\$|libdir=''${!outputLib}/lib|" \
178           -e "s|^includedir\s*=.*/include\$|includedir=''${!outputDev}/include|" \
179           "$pc"
180       done
181     ''
182     # Generate unversioned names.
183     # E.g. cuda-11.8.pc -> cuda.pc
184     + ''
185       for pc in share/pkgconfig/*-"$majorMinorVersion.pc"; do
186         ln -s "$(basename "$pc")" "''${pc%-$majorMinorVersion.pc}".pc
187       done
188     '';
190   env.majorMinorVersion = cudaMajorMinorVersion;
192   # We do need some other phases, like configurePhase, so the multiple-output setup hook works.
193   dontBuild = true;
195   nativeBuildInputs =
196     [
197       autoPatchelfHook
198       # This hook will make sure libcuda can be found
199       # in typically /lib/opengl-driver by adding that
200       # directory to the rpath of all ELF binaries.
201       # Check e.g. with `patchelf --print-rpath path/to/my/binary
202       autoAddDriverRunpath
203       markForCudatoolkitRootHook
204     ]
205     # autoAddCudaCompatRunpath depends on cuda_compat and would cause
206     # infinite recursion if applied to `cuda_compat` itself (beside the fact
207     # that it doesn't make sense in the first place)
208     ++ lib.optionals (pname != "cuda_compat" && flags.isJetsonBuild) [
209       # autoAddCudaCompatRunpath must appear AFTER autoAddDriverRunpath.
210       # See its documentation in ./setup-hooks/extension.nix.
211       autoAddCudaCompatRunpath
212     ];
214   buildInputs = [
215     # autoPatchelfHook will search for a libstdc++ and we're giving it
216     # one that is compatible with the rest of nixpkgs, even when
217     # nvcc forces us to use an older gcc
218     # NB: We don't actually know if this is the right thing to do
219     stdenv.cc.cc.lib
220   ];
222   # Picked up by autoPatchelf
223   # Needed e.g. for libnvrtc to locate (dlopen) libnvrtc-builtins
224   appendRunpaths = [ "$ORIGIN" ];
226   # NOTE: We don't need to check for dev or doc, because those outputs are handled by
227   # the multiple-outputs setup hook.
228   # NOTE: moveToOutput operates on all outputs:
229   # https://github.com/NixOS/nixpkgs/blob/2920b6fc16a9ed5d51429e94238b28306ceda79e/pkgs/build-support/setup-hooks/multiple-outputs.sh#L105-L107
230   installPhase =
231     let
232       mkMoveToOutputCommand =
233         output:
234         let
235           template = pattern: ''moveToOutput "${pattern}" "${"$" + output}"'';
236           patterns = finalAttrs.outputToPatterns.${output} or [ ];
237         in
238         strings.concatMapStringsSep "\n" template patterns;
239     in
240     # Pre-install hook
241     ''
242       runHook preInstall
243     ''
244     # Handle the existence of libPath, which requires us to re-arrange the lib directory
245     + strings.optionalString (libPath != null) ''
246       full_lib_path="lib/${libPath}"
247       if [[ ! -d "$full_lib_path" ]]; then
248         echo "${finalAttrs.pname}: '$full_lib_path' does not exist, only found:" >&2
249         find lib/ -mindepth 1 -maxdepth 1 >&2
250         echo "This release might not support your CUDA version" >&2
251         exit 1
252       fi
253       echo "Making libPath '$full_lib_path' the root of lib" >&2
254       mv "$full_lib_path" lib_new
255       rm -r lib
256       mv lib_new lib
257     ''
258     # Create the primary output, out, and move the other outputs into it.
259     + ''
260       mkdir -p "$out"
261       mv * "$out"
262     ''
263     # Move the outputs into their respective outputs.
264     + strings.concatMapStringsSep "\n" mkMoveToOutputCommand (builtins.tail finalAttrs.outputs)
265     # Add a newline to the end of the installPhase, so that the post-install hook doesn't
266     # get concatenated with the last moveToOutput command.
267     + "\n"
268     # Post-install hook
269     + ''
270       runHook postInstall
271     '';
273   doInstallCheck = true;
274   allowFHSReferences = true; # TODO: Default to `false`
275   postInstallCheck = ''
276     echo "Executing postInstallCheck"
278     if [[ -z "''${allowFHSReferences-}" ]]; then
279       mapfile -t outputPaths < <(for o in $(getAllOutputNames); do echo "''${!o}"; done)
280       if grep --max-count=5 --recursive --exclude=LICENSE /usr/ "''${outputPaths[@]}"; then
281         echo "Detected references to /usr" >&2
282         exit 1
283       fi
284     fi
285   '';
287   # libcuda needs to be resolved during runtime
288   autoPatchelfIgnoreMissingDeps = [
289     "libcuda.so"
290     "libcuda.so.*"
291   ];
293   # The out output leverages the same functionality which backs the `symlinkJoin` function in
294   # Nixpkgs:
295   # https://github.com/NixOS/nixpkgs/blob/d8b2a92df48f9b08d68b0132ce7adfbdbc1fbfac/pkgs/build-support/trivial-builders/default.nix#L510
296   #
297   # That should allow us to emulate "fat" default outputs without having to actually create them.
298   #
299   # It is important that this run after the autoPatchelfHook, otherwise the symlinks in out will reference libraries in lib, creating a circular dependency.
300   postPhases = [ "postPatchelf" ];
302   # For each output, create a symlink to it in the out output.
303   # NOTE: We must recreate the out output here, because the setup hook will have deleted it if it was empty.
304   postPatchelf = ''
305     mkdir -p "$out"
306     for output in $(getAllOutputNames); do
307       if [[ "$output" != "out" ]]; then
308         ${meta.getExe lndir} "''${!output}" "$out"
309       fi
310     done
311   '';
313   # Make the CUDA-patched stdenv available
314   passthru.stdenv = backendStdenv;
316   # Setting propagatedBuildInputs to false will prevent outputs known to the multiple-outputs
317   # from depending on `out` by default.
318   # https://github.com/NixOS/nixpkgs/blob/2920b6fc16a9ed5d51429e94238b28306ceda79e/pkgs/build-support/setup-hooks/multiple-outputs.sh#L196
319   # Indeed, we want to do the opposite -- fat "out" outputs that contain all the other outputs.
320   propagatedBuildOutputs = false;
322   # By default, if the dev output exists it just uses that.
323   # However, because we disabled propagatedBuildOutputs, dev doesn't contain libraries or
324   # anything of the sort. To remedy this, we set outputSpecified to true, and use
325   # outputsToInstall, which tells Nix which outputs to use when the package name is used
326   # unqualified (that is, without an explicit output).
327   outputSpecified = true;
329   meta = {
330     description = "${redistribRelease.name}. By downloading and using the packages you accept the terms and conditions of the ${finalAttrs.meta.license.shortName}";
331     sourceProvenance = [ sourceTypes.binaryNativeCode ];
332     broken = lists.any trivial.id (attrsets.attrValues finalAttrs.brokenConditions);
333     platforms = trivial.pipe supportedRedistArchs [
334       # Map each redist arch to the equivalent nix system or null if there is no equivalent.
335       (builtins.map flags.getNixSystem)
336       # Filter out unsupported systems
337       (builtins.filter (nixSystem: !(strings.hasPrefix "unsupported-" nixSystem)))
338     ];
339     badPlatforms =
340       let
341         isBadPlatform = lists.any trivial.id (attrsets.attrValues finalAttrs.badPlatformsConditions);
342       in
343       lists.optionals isBadPlatform finalAttrs.meta.platforms;
344     license = licenses.unfree;
345     maintainers = teams.cuda.members;
346     # Force the use of the default, fat output by default (even though `dev` exists, which
347     # causes Nix to prefer that output over the others if outputSpecified isn't set).
348     outputsToInstall = [ "out" ];
349   };