2024-01-02 11:29:13 +00:00
|
|
|
{ lib
|
|
|
|
, julia
|
|
|
|
, python3
|
|
|
|
, runCommand
|
|
|
|
|
|
|
|
, augmentedRegistry
|
|
|
|
, packageNames
|
|
|
|
, packageOverrides
|
|
|
|
, packageImplications
|
|
|
|
}:
|
|
|
|
|
|
|
|
let
|
|
|
|
# The specific package resolution code depends on the Julia version
|
|
|
|
# These are pretty similar and could be combined to reduce duplication
|
|
|
|
resolveCode = if lib.versionOlder julia.version "1.7" then resolveCode1_6 else resolveCode1_8;
|
|
|
|
|
|
|
|
resolveCode1_6 = ''
|
|
|
|
import Pkg.API: check_package_name
|
|
|
|
import Pkg.Types: Context!, PRESERVE_NONE, manifest_info, project_deps_resolve!, registry_resolve!, stdlib_resolve!, ensure_resolved
|
|
|
|
import Pkg.Operations: _resolve, assert_can_add, is_dep, update_package_add
|
|
|
|
|
|
|
|
foreach(pkg -> check_package_name(pkg.name, :add), pkgs)
|
|
|
|
pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members
|
|
|
|
Context!(ctx)
|
|
|
|
|
|
|
|
project_deps_resolve!(ctx, pkgs)
|
|
|
|
registry_resolve!(ctx, pkgs)
|
|
|
|
stdlib_resolve!(pkgs)
|
|
|
|
ensure_resolved(ctx, pkgs, registry=true)
|
|
|
|
|
|
|
|
assert_can_add(ctx, pkgs)
|
|
|
|
|
|
|
|
for (i, pkg) in pairs(pkgs)
|
|
|
|
entry = manifest_info(ctx, pkg.uuid)
|
|
|
|
pkgs[i] = update_package_add(ctx, pkg, entry, is_dep(ctx, pkg))
|
|
|
|
end
|
|
|
|
|
|
|
|
foreach(pkg -> ctx.env.project.deps[pkg.name] = pkg.uuid, pkgs)
|
|
|
|
|
|
|
|
pkgs, deps_map = _resolve(ctx, pkgs, PRESERVE_NONE)
|
|
|
|
'';
|
|
|
|
|
|
|
|
resolveCode1_8 = ''
|
|
|
|
import Pkg.API: handle_package_input!
|
2024-07-27 06:49:29 +00:00
|
|
|
import Pkg.Types: PRESERVE_NONE, UUID, VersionSpec, project_deps_resolve!, registry_resolve!, stdlib_resolve!, ensure_resolved
|
2024-01-02 11:29:13 +00:00
|
|
|
import Pkg.Operations: _resolve, assert_can_add, update_package_add
|
2024-07-27 06:49:29 +00:00
|
|
|
import TOML
|
2024-01-02 11:29:13 +00:00
|
|
|
|
|
|
|
foreach(handle_package_input!, pkgs)
|
|
|
|
|
|
|
|
# The handle_package_input! call above clears pkg.path, so we have to apply package overrides after
|
|
|
|
overrides = Dict{String, String}(${builtins.concatStringsSep ", " (lib.mapAttrsToList (name: path: ''"${name}" => "${path}"'') packageOverrides)})
|
|
|
|
println("Package overrides: ")
|
|
|
|
println(overrides)
|
|
|
|
for pkg in pkgs
|
|
|
|
if pkg.name in keys(overrides)
|
|
|
|
pkg.path = overrides[pkg.name]
|
2024-07-27 06:49:29 +00:00
|
|
|
|
|
|
|
# Try to read the UUID from $(pkg.path)/Project.toml. If successful, put the package into ctx.env.project.deps.
|
|
|
|
# This is necessary for the ensure_resolved call below to succeed, and will allow us to use an override even
|
|
|
|
# if it does not appear in the registry.
|
|
|
|
# See https://github.com/NixOS/nixpkgs/issues/279853
|
|
|
|
project_toml = joinpath(pkg.path, "Project.toml")
|
|
|
|
if isfile(project_toml)
|
|
|
|
toml_data = TOML.parsefile(project_toml)
|
|
|
|
if haskey(toml_data, "uuid")
|
|
|
|
ctx.env.project.deps[pkg.name] = UUID(toml_data["uuid"])
|
|
|
|
end
|
|
|
|
end
|
2024-01-02 11:29:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
project_deps_resolve!(ctx.env, pkgs)
|
|
|
|
registry_resolve!(ctx.registries, pkgs)
|
|
|
|
stdlib_resolve!(pkgs)
|
|
|
|
ensure_resolved(ctx, ctx.env.manifest, pkgs, registry=true)
|
|
|
|
|
|
|
|
assert_can_add(ctx, pkgs)
|
|
|
|
|
|
|
|
for (i, pkg) in pairs(pkgs)
|
|
|
|
entry = Pkg.Types.manifest_info(ctx.env.manifest, pkg.uuid)
|
|
|
|
is_dep = any(uuid -> uuid == pkg.uuid, [uuid for (name, uuid) in ctx.env.project.deps])
|
|
|
|
pkgs[i] = update_package_add(ctx, pkg, entry, is_dep)
|
|
|
|
end
|
|
|
|
|
|
|
|
foreach(pkg -> ctx.env.project.deps[pkg.name] = pkg.uuid, pkgs)
|
|
|
|
|
|
|
|
# Save the original pkgs for later. We might need to augment it with the weak dependencies
|
|
|
|
orig_pkgs = pkgs
|
|
|
|
|
|
|
|
pkgs, deps_map = _resolve(ctx.io, ctx.env, ctx.registries, pkgs, PRESERVE_NONE, ctx.julia_version)
|
|
|
|
|
|
|
|
if VERSION >= VersionNumber("1.9")
|
2024-01-13 08:15:51 +00:00
|
|
|
while true
|
|
|
|
# Check for weak dependencies, which appear on the RHS of the deps_map but not in pkgs.
|
|
|
|
# Build up weak_name_to_uuid
|
|
|
|
uuid_to_name = Dict()
|
|
|
|
for pkg in pkgs
|
|
|
|
uuid_to_name[pkg.uuid] = pkg.name
|
|
|
|
end
|
|
|
|
weak_name_to_uuid = Dict()
|
|
|
|
for (uuid, deps) in pairs(deps_map)
|
|
|
|
for (dep_name, dep_uuid) in pairs(deps)
|
|
|
|
if !haskey(uuid_to_name, dep_uuid)
|
|
|
|
weak_name_to_uuid[dep_name] = dep_uuid
|
|
|
|
end
|
2024-01-02 11:29:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-01-13 08:15:51 +00:00
|
|
|
if isempty(weak_name_to_uuid)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
# We have nontrivial weak dependencies, so add each one to the initial pkgs and then re-run _resolve
|
2024-01-02 11:29:13 +00:00
|
|
|
println("Found weak dependencies: $(keys(weak_name_to_uuid))")
|
|
|
|
|
|
|
|
orig_uuids = Set([pkg.uuid for pkg in orig_pkgs])
|
|
|
|
|
|
|
|
for (name, uuid) in pairs(weak_name_to_uuid)
|
|
|
|
if uuid in orig_uuids
|
|
|
|
continue
|
|
|
|
end
|
|
|
|
|
|
|
|
pkg = PackageSpec(name, uuid)
|
|
|
|
|
|
|
|
push!(orig_uuids, uuid)
|
|
|
|
push!(orig_pkgs, pkg)
|
|
|
|
ctx.env.project.deps[name] = uuid
|
|
|
|
entry = Pkg.Types.manifest_info(ctx.env.manifest, uuid)
|
|
|
|
orig_pkgs[length(orig_pkgs)] = update_package_add(ctx, pkg, entry, false)
|
|
|
|
end
|
|
|
|
|
2024-01-13 08:15:51 +00:00
|
|
|
global pkgs, deps_map = _resolve(ctx.io, ctx.env, ctx.registries, orig_pkgs, PRESERVE_NONE, ctx.julia_version)
|
2024-01-02 11:29:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
'';
|
|
|
|
|
|
|
|
juliaExpression = packageNames: ''
|
|
|
|
import Pkg
|
|
|
|
Pkg.Registry.add(Pkg.RegistrySpec(path="${augmentedRegistry}"))
|
|
|
|
|
|
|
|
import Pkg.Types: Context, PackageSpec
|
|
|
|
|
|
|
|
input = ${lib.generators.toJSON {} packageNames}
|
|
|
|
|
|
|
|
if isfile("extra_package_names.txt")
|
|
|
|
append!(input, readlines("extra_package_names.txt"))
|
|
|
|
end
|
|
|
|
|
|
|
|
input = unique(input)
|
|
|
|
|
|
|
|
println("Resolving packages: " * join(input, " "))
|
|
|
|
|
|
|
|
pkgs = [PackageSpec(pkg) for pkg in input]
|
|
|
|
|
|
|
|
ctx = Context()
|
|
|
|
|
|
|
|
${resolveCode}
|
|
|
|
|
|
|
|
open(ENV["out"], "w") do io
|
|
|
|
for spec in pkgs
|
|
|
|
println(io, "- name: " * spec.name)
|
|
|
|
println(io, " uuid: " * string(spec.uuid))
|
|
|
|
println(io, " version: " * string(spec.version))
|
|
|
|
if endswith(spec.name, "_jll") && haskey(deps_map, spec.uuid)
|
|
|
|
println(io, " depends_on: ")
|
|
|
|
for (dep_name, dep_uuid) in pairs(deps_map[spec.uuid])
|
|
|
|
println(io, " \"$(dep_name)\": \"$(dep_uuid)\"")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
'';
|
|
|
|
in
|
|
|
|
|
|
|
|
runCommand "julia-package-closure.yml" { buildInputs = [julia (python3.withPackages (ps: with ps; [pyyaml]))]; } ''
|
|
|
|
mkdir home
|
|
|
|
export HOME=$(pwd)/home
|
|
|
|
|
|
|
|
echo "Resolving Julia packages with the following inputs"
|
|
|
|
echo "Julia: ${julia}"
|
|
|
|
echo "Registry: ${augmentedRegistry}"
|
|
|
|
|
|
|
|
# Prevent a warning where Julia tries to download package server info
|
|
|
|
export JULIA_PKG_SERVER=""
|
|
|
|
|
|
|
|
julia -e '${juliaExpression packageNames}';
|
|
|
|
|
|
|
|
# See if we need to add any extra package names based on the closure
|
|
|
|
# and the packageImplications
|
|
|
|
python ${./python}/find_package_implications.py "$out" '${lib.generators.toJSON {} packageImplications}' extra_package_names.txt
|
|
|
|
|
|
|
|
if [ -f extra_package_names.txt ]; then
|
|
|
|
echo "Re-resolving with additional package names"
|
|
|
|
julia -e '${juliaExpression packageNames}';
|
|
|
|
fi
|
|
|
|
''
|