summaryrefslogtreecommitdiffstats
path: root/pkgs/development/cuda-modules/default.nix
blob: 2acd56edef5f75e4ee809762b1793e8c55361992 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
{
  _cuda,
  config,
  lib,
  pkgs,
  # Manually provided arguments
  manifests,
}:
let
  inherit (lib.customisation) callPackagesWith;
  inherit (lib.filesystem) packagesFromDirectoryRecursive;
  inherit (lib.fixedPoints) composeManyExtensions extends;
  inherit (lib.lists) optionals;
  inherit (lib.strings) versionAtLeast versionOlder;
  inherit (lib.versions) major majorMinor;
  inherit (_cuda.lib)
    dropDots
    formatCapabilities
    mkVersionedName
    ;

  # NOTE: This value is considered an implementation detail and should not be exposed in the attribute set.
  cudaMajorMinorPatchVersion = manifests.cuda.release_label;
  cudaMajorMinorVersion = majorMinor cudaMajorMinorPatchVersion;
  cudaMajorVersion = major cudaMajorMinorPatchVersion;

  # We must use an instance of Nixpkgs where the CUDA package set we're building is the default; if we do not, members
  # of the versioned, non-default package sets may rely on (transitively) members of the default, unversioned CUDA
  # package set.
  # See `Using cudaPackages.pkgs` in doc/languages-frameworks/cuda.section.md for more information.
  pkgs' =
    let
      cudaPackagesUnversionedName = "cudaPackages";
      cudaPackagesMajorVersionedName = mkVersionedName cudaPackagesUnversionedName cudaMajorVersion;
      cudaPackagesMajorMinorVersionedName = mkVersionedName cudaPackagesUnversionedName cudaMajorMinorVersion;
    in
    if
      # If the default CUDA package set is the same as the one we're constructing, pass through pkgs unchanged.
      # This is a handy speedup for cases where we're using the default CUDA package set and don't need to pay for
      # a re-instantiation of Nixpkgs.
      pkgs.${cudaPackagesMajorMinorVersionedName}.manifests
      == pkgs.${cudaPackagesMajorVersionedName}.manifests
      &&
        pkgs.${cudaPackagesMajorMinorVersionedName}.manifests
        == pkgs.${cudaPackagesUnversionedName}.manifests
    then
      pkgs
    else
      pkgs.extend (
        final: _: {
          recurseForDerivations = false;
          # The CUDA package set will be available as cudaPackages_x_y, so we need only update the aliases for the
          # minor-versioned and unversioned package sets.
          # However, if we do not replace the major-minor-versioned CUDA package set with our own, our use of splicing
          # causes the package set to be re-evaluated for each build/host/target!

          # cudaPackages_x_y = <package set created in this file>
          ${cudaPackagesMajorMinorVersionedName} = cudaPackages;
          # cudaPackages_x = cudaPackages_x_y
          ${cudaPackagesMajorVersionedName} = final.${cudaPackagesMajorMinorVersionedName};
          # cudaPackages = cudaPackages_x
          ${cudaPackagesUnversionedName} = final.${cudaPackagesMajorVersionedName};
        }
      );

  cudaPackagesFixedPoint =
    finalCudaPackages:
    {
      # NOTE:
      # It is important that _cuda is not part of the package set fixed-point. As described by
      # @SomeoneSerge:
      # > The layering should be: configuration -> (identifies/is part of) cudaPackages -> (is built using) cudaLib.
      # > No arrows should point in the reverse directions.
      # That is to say that cudaLib should only know about package sets and configurations, because it implements
      # functionality for interpreting configurations, resolving them against data, and constructing package sets.
      # This decision is driven both by a separation of concerns and by "NAMESET STRICTNESS" (see above).
      # Also see the comment in `pkgs/top-level/all-packages.nix` about the `_cuda` attribute.

      inherit
        cudaMajorMinorPatchVersion
        cudaMajorMinorVersion
        cudaMajorVersion
        ;

      pkgs = pkgs';

      # Core
      callPackages = callPackagesWith (pkgs' // finalCudaPackages);

      cudaNamePrefix = "cuda${cudaMajorMinorVersion}";

      cudaOlder = versionOlder cudaMajorMinorVersion;
      cudaAtLeast = versionAtLeast cudaMajorMinorVersion;

      # These must be modified through callPackage, not by overriding the scope, since we cannot
      # depend on them recursively as they are used to add top-level attributes.
      inherit manifests;

      # Construct without relying on the fixed-point to allow the use of backendStdenv in creating CUDA package sets.
      # For example, this allows selecting manifests by predicating on values like
      # `backendStdenv.hasJetsonCudaCapability`, which would otherwise result in infinite recursion due to the reliance
      # on `pkgs'`, which in turn depends on the manifests provided to this file (which can depend on `backendStdenv`).
      backendStdenv = import ./backendStdenv {
        inherit
          _cuda
          config
          cudaMajorMinorVersion
          lib
          pkgs
          ;
        inherit (pkgs)
          stdenv
          stdenvAdapters
          ;
      };

      # Create backendStdenv variants for different host compilers, since users may want to build a CUDA project with
      # Clang or GCC specifically.
      # TODO(@connorbaker): Because of the way our setup hooks and patching of NVCC works, the user's choice of
      # backendStdenv is largely disregarded or will cause build failures; fixing this would require the setup hooks
      # and patching to be made aware of the current environment (perhaps by reading certain environment variables set
      # by our backendStdenv).
      # backendClangStdenv = finalCudaPackages.callPackage ./packages/backendStdenv.nix {
      #   stdenv = pkgs'.clangStdenv;
      # };
      # backendGccStdenv = finalCudaPackages.callPackage ./packages/backendStdenv.nix {
      #   stdenv = pkgs'.gccStdenv;
      # };

      # Must be constructed without `callPackage` to avoid replacing the `override` attribute with that of
      # `callPackage`'s.
      buildRedist = import ./buildRedist {
        inherit
          _cuda
          lib
          ;
        inherit (pkgs)
          autoAddDriverRunpath
          autoPatchelfHook
          fetchurl
          srcOnly
          stdenv
          stdenvNoCC
          zstd
          ;
        inherit (finalCudaPackages)
          autoAddCudaCompatRunpath
          backendStdenv
          cudaMajorMinorVersion
          cudaMajorVersion
          cudaNamePrefix
          manifests
          markForCudatoolkitRootHook
          removeStubsFromRunpathHook
          ;
      };

      flags =
        formatCapabilities {
          inherit (finalCudaPackages.backendStdenv) cudaCapabilities cudaForwardCompat;
          inherit (_cuda.db) cudaCapabilityToInfo;
        }
        # TODO(@connorbaker): Enable the corresponding warnings in `./aliases.nix` after some
        # time to allow users to migrate to cudaLib and backendStdenv.
        // {
          inherit dropDots;
          cudaComputeCapabilityToName =
            cudaCapability: _cuda.db.cudaCapabilityToInfo.${cudaCapability}.archName;
          dropDot = dropDots;
          isJetsonBuild = finalCudaPackages.backendStdenv.hasJetsonCudaCapability;
        };
    }
    // packagesFromDirectoryRecursive {
      inherit (finalCudaPackages) callPackage;
      directory = ./packages;
    };

  composedExtensions = composeManyExtensions (
    optionals config.allowAliases [
      (import ./aliases.nix { inherit lib; })
    ]
    ++ _cuda.extensions
  );

  # Using lib.makeScopeWithSplicing' instead of the templated one from pkgs' allows us to defer calling pkgs.extend.
  cudaPackages =
    lib.makeScopeWithSplicing'
      {
        splicePackages = pkgs'.splicePackages;
        newScope = pkgs'.newScope;
      }
      {
        # In pkgs', the default CUDA package set is always the one we've constructed here.
        otherSplices = pkgs'.generateSplicesForMkScope [ "cudaPackages" ];
        f = extends composedExtensions cudaPackagesFixedPoint;
      };
in
cudaPackages