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