Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at flake-libs 295 lines 8.7 kB view raw
1{ 2 callPackage, 3 fetchgit, 4 fontconfig, 5 git, 6 lib, 7 makeWrapper, 8 python3, 9 runCommand, 10 system, 11 writeText, 12 writeTextFile, 13 14 # Artifacts dependencies 15 fetchurl, 16 glibc, 17 pkgs, 18 stdenv, 19 20 julia, 21 22 # Special registry which is equal to JuliaRegistries/General, but every Versions.toml 23 # entry is augmented with a Nix sha256 hash 24 augmentedRegistry ? callPackage ./registry.nix { }, 25 26 # Other overridable arguments 27 extraLibs ? [ ], 28 juliaCpuTarget ? null, 29 makeTransitiveDependenciesImportable ? false, # Used to support symbol indexing 30 makeWrapperArgs ? "", 31 packageOverrides ? { }, 32 precompile ? true, 33 setDefaultDepot ? true, 34}: 35 36packageNames: 37 38let 39 util = callPackage ./util.nix { }; 40 41 # Some Julia packages require access to Python. Provide a Nixpkgs version so it 42 # doesn't try to install its own. 43 pythonToUse = 44 let 45 extraPythonPackages = ( 46 (callPackage ./extra-python-packages.nix { inherit python3; }).getExtraPythonPackages packageNames 47 ); 48 in 49 ( 50 if extraPythonPackages == [ ] then 51 python3 52 else 53 util.addPackagesToPython python3 (map (pkg: lib.getAttr pkg python3.pkgs) extraPythonPackages) 54 ); 55 56 # Start by wrapping Julia so it has access to Python and any other extra libs. 57 # Also, prevent various packages (CondaPkg.jl, PythonCall.jl) from trying to do network calls. 58 juliaWrapped = 59 runCommand "julia-${julia.version}-wrapped" 60 { 61 nativeBuildInputs = [ makeWrapper ]; 62 inherit makeWrapperArgs; 63 } 64 '' 65 mkdir -p $out/bin 66 makeWrapper ${julia}/bin/julia $out/bin/julia \ 67 --suffix LD_LIBRARY_PATH : "${lib.makeLibraryPath extraLibs}" \ 68 --set FONTCONFIG_FILE ${fontconfig.out}/etc/fonts/fonts.conf \ 69 --set PYTHONHOME "${pythonToUse}" \ 70 --prefix PYTHONPATH : "${pythonToUse}/${pythonToUse.sitePackages}" \ 71 --set PYTHON ${pythonToUse}/bin/python $makeWrapperArgs \ 72 --set JULIA_CONDAPKG_OFFLINE yes \ 73 --set JULIA_CONDAPKG_BACKEND Null \ 74 --set JULIA_PYTHONCALL_EXE "@PyCall" 75 ''; 76 77 # If our closure ends up with certain packages, add others. 78 packageImplications = { 79 # Because we want to put PythonCall in PyCall mode so it doesn't try to download 80 # Python packages 81 PythonCall = [ "PyCall" ]; 82 }; 83 84 # Invoke Julia resolution logic to determine the full dependency closure 85 packageOverridesRepoified = lib.mapAttrs util.repoifySimple packageOverrides; 86 closureYaml = callPackage ./package-closure.nix { 87 inherit 88 augmentedRegistry 89 julia 90 packageNames 91 packageImplications 92 ; 93 packageOverrides = packageOverridesRepoified; 94 }; 95 96 # Generate a Nix file consisting of a map from dependency UUID --> package info with fetchgit call: 97 # { 98 # "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" = { 99 # src = fetchgit {...}; 100 # name = "..."; 101 # version = "..."; 102 # treehash = "..."; 103 # }; 104 # ... 105 # } 106 dependencies = 107 runCommand "julia-sources.nix" 108 { 109 buildInputs = [ 110 (python3.withPackages ( 111 ps: with ps; [ 112 toml 113 pyyaml 114 ] 115 )) 116 git 117 ]; 118 } 119 '' 120 python ${./python}/sources_nix.py \ 121 "${augmentedRegistry}" \ 122 '${lib.generators.toJSON { } packageOverridesRepoified}' \ 123 "${closureYaml}" \ 124 "$out" 125 ''; 126 127 # Import the Nix file from the previous step (IFD) and turn each dependency repo into 128 # a dummy Git repository, as Julia expects. Format the results as a YAML map from 129 # dependency UUID -> Nix store location: 130 # { 131 # "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3":"/nix/store/...-NaNMath.jl-0877504", 132 # ... 133 # } 134 # This is also the point where we apply the packageOverrides. 135 dependencyUuidToInfo = import dependencies { inherit fetchgit; }; 136 fillInOverrideSrc = 137 uuid: info: 138 if lib.hasAttr info.name packageOverrides then 139 (info // { src = lib.getAttr info.name packageOverrides; }) 140 else 141 info; 142 dependencyUuidToRepo = lib.mapAttrs util.repoifyInfo ( 143 lib.mapAttrs fillInOverrideSrc dependencyUuidToInfo 144 ); 145 dependencyUuidToRepoYaml = writeTextFile { 146 name = "dependency-uuid-to-repo.yml"; 147 text = lib.generators.toYAML { } dependencyUuidToRepo; 148 }; 149 150 # Given the augmented registry, closure info yaml, and dependency path yaml, construct a complete 151 # Julia registry containing all the necessary packages 152 dependencyUuidToInfoYaml = writeTextFile { 153 name = "dependency-uuid-to-info.yml"; 154 text = lib.generators.toYAML { } dependencyUuidToInfo; 155 }; 156 fillInOverrideSrc' = 157 uuid: info: 158 if lib.hasAttr info.name packageOverridesRepoified then 159 (info // { src = lib.getAttr info.name packageOverridesRepoified; }) 160 else 161 info; 162 overridesOnly = lib.mapAttrs fillInOverrideSrc' ( 163 lib.filterAttrs (uuid: info: info.src == null) dependencyUuidToInfo 164 ); 165 minimalRegistry = 166 runCommand "minimal-julia-registry" 167 { 168 buildInputs = [ 169 (python3.withPackages ( 170 ps: with ps; [ 171 toml 172 pyyaml 173 ] 174 )) 175 git 176 ]; 177 } 178 '' 179 python ${./python}/minimal_registry.py \ 180 "${augmentedRegistry}" \ 181 "${closureYaml}" \ 182 '${lib.generators.toJSON { } overridesOnly}' \ 183 "${dependencyUuidToRepoYaml}" \ 184 "$out" 185 ''; 186 187 # Next, deal with artifacts. Scan each artifacts file individually and generate a Nix file that 188 # produces the desired Overrides.toml. 189 artifactsNix = 190 runCommand "julia-artifacts.nix" 191 { 192 buildInputs = [ 193 (python3.withPackages ( 194 ps: with ps; [ 195 toml 196 pyyaml 197 ] 198 )) 199 ]; 200 } 201 '' 202 python ${./python}/extract_artifacts.py \ 203 "${dependencyUuidToRepoYaml}" \ 204 "${closureYaml}" \ 205 "${juliaWrapped}/bin/julia" \ 206 "${ 207 if lib.versionAtLeast julia.version "1.7" then ./extract_artifacts.jl else ./extract_artifacts_16.jl 208 }" \ 209 '${lib.generators.toJSON { } (import ./extra-libs.nix)}' \ 210 '${lib.generators.toJSON { } (stdenv.hostPlatform.isDarwin)}' \ 211 "$out" 212 ''; 213 214 # Import the artifacts Nix to build Overrides.toml (IFD) 215 artifacts = import artifactsNix ( 216 { 217 inherit 218 lib 219 fetchurl 220 pkgs 221 stdenv 222 ; 223 } 224 // lib.optionalAttrs (!stdenv.targetPlatform.isDarwin) { 225 inherit glibc; 226 } 227 ); 228 overridesJson = writeTextFile { 229 name = "Overrides.json"; 230 text = lib.generators.toJSON { } artifacts; 231 }; 232 overridesToml = 233 runCommand "Overrides.toml" { buildInputs = [ (python3.withPackages (ps: with ps; [ toml ])) ]; } 234 '' 235 python ${./python}/format_overrides.py \ 236 "${overridesJson}" \ 237 "$out" 238 ''; 239 240 # Build a Julia project and depot. The project contains Project.toml/Manifest.toml, while the 241 # depot contains package build products (including the precompiled libraries, if precompile=true) 242 projectAndDepot = callPackage ./depot.nix { 243 inherit 244 closureYaml 245 extraLibs 246 juliaCpuTarget 247 overridesToml 248 packageImplications 249 precompile 250 ; 251 julia = juliaWrapped; 252 registry = minimalRegistry; 253 packageNames = 254 if makeTransitiveDependenciesImportable then 255 lib.mapAttrsToList (uuid: info: info.name) dependencyUuidToInfo 256 else 257 packageNames; 258 }; 259 260in 261 262runCommand "julia-${julia.version}-env" 263 { 264 nativeBuildInputs = [ makeWrapper ]; 265 266 passthru = { 267 inherit julia; 268 inherit juliaWrapped; 269 inherit (julia) pname version meta; 270 271 # Expose the steps we used along the way in case the user wants to use them, for example to build 272 # expressions and build them separately to avoid IFD. 273 inherit dependencies; 274 inherit closureYaml; 275 inherit dependencyUuidToInfoYaml; 276 inherit dependencyUuidToRepoYaml; 277 inherit minimalRegistry; 278 inherit artifactsNix; 279 inherit overridesJson; 280 inherit overridesToml; 281 inherit projectAndDepot; 282 }; 283 } 284 ( 285 '' 286 mkdir -p $out/bin 287 makeWrapper ${juliaWrapped}/bin/julia $out/bin/julia \ 288 --suffix JULIA_DEPOT_PATH : "${projectAndDepot}/depot" \ 289 --set-default JULIA_PROJECT "${projectAndDepot}/project" \ 290 --set-default JULIA_LOAD_PATH '@:${projectAndDepot}/project/Project.toml:@v#.#:@stdlib' 291 '' 292 + lib.optionalString setDefaultDepot '' 293 sed -i '2 i\JULIA_DEPOT_PATH=''${JULIA_DEPOT_PATH-"$HOME/.julia"}' $out/bin/julia 294 '' 295 )