Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,257 changes: 1,257 additions & 0 deletions data/version_map_compressed.jl

Large diffs are not rendered by default.

92 changes: 82 additions & 10 deletions src/HistoricalStdlibs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,89 @@ struct StdlibInfo
weakdeps::Vector{UUID}
end

# Base info struct for stdlib segments (excludes version)
Base.@kwdef struct StdlibBaseInfo
name::String
uuid::UUID
deps::Vector{UUID} = UUID[]
weakdeps::Vector{UUID} = UUID[]
end

# Segment struct that combines base info with version ranges
Base.@kwdef struct StdlibSegment
base_info::StdlibBaseInfo
version_ranges::Vector{Pair{Tuple{VersionNumber, VersionNumber}, Union{Nothing, VersionNumber}}}
end

const DictStdLibs = Dict{UUID, StdlibInfo}

# Julia standard libraries with duplicate entries removed so as to store only the
# first release in a set of releases that all contain the same set of stdlibs.
#
# This needs to be populated via HistoricalStdlibVersions.jl by consumers
# (e.g. BinaryBuilder) that want to use the "resolve things as if it were a
# different Julia version than what is currently running" feature.
# Load the compressed version map data structure
include(joinpath(@__DIR__, "..", "data", "version_map_compressed.jl"))

# Populated by HistoricalStdlibVersions.jl, used if we have a version later than we know about
const STDLIBS_BY_VERSION = Pair{VersionNumber, DictStdLibs}[]

# This is a list of stdlibs that must _always_ be treated as stdlibs,
# because they cannot be resolved in the registry; they have only ever existed within
# the Julia stdlib source tree, and because of that, trying to resolve them will fail.
const UNREGISTERED_STDLIBS = DictStdLibs()
"""
query_stdlib_segments(julia_version::VersionNumber) -> DictStdLibs

Query the compressed stdlib segments to build a dictionary of stdlib info
for the given Julia version. Returns a Dict{UUID, StdlibInfo} containing
all stdlibs available for that Julia version.
"""
function query_stdlib_segments(julia_version::VersionNumber)
result = DictStdLibs()

# Normalize the julia_version to just major.minor.patch
jv = VersionNumber(julia_version.major, julia_version.minor, julia_version.patch)

# Iterate through all stdlib UUIDs in the segments
for (uuid, segments) in STDLIB_SEGMENTS
# For each stdlib, find the segment and version range that matches
for segment in segments
for (range, pkg_version) in segment.version_ranges
min_v, max_v = range
# Check if julia_version falls within this range
if jv >= min_v && jv <= max_v
# Found the matching range, create StdlibInfo
result[uuid] = StdlibInfo(
segment.base_info.name,
segment.base_info.uuid,
pkg_version,
segment.base_info.deps,
segment.base_info.weakdeps
)
@goto next_stdlib
end
end
end
@label next_stdlib
end

return result
end

"""
version_covered_by_segments(julia_version::VersionNumber) -> Bool

Check if the given Julia version is covered by at least one stdlib in STDLIB_SEGMENTS.
Returns false if the version is outside all recorded ranges, indicating we should
fall back to STDLIBS_BY_VERSION.
"""
function version_covered_by_segments(julia_version::VersionNumber)
# Normalize the julia_version to just major.minor.patch
jv = VersionNumber(julia_version.major, julia_version.minor, julia_version.patch)

# Check if any stdlib has a range that covers this version
for (uuid, segments) in STDLIB_SEGMENTS
for segment in segments
for (range, pkg_version) in segment.version_ranges
min_v, max_v = range
if jv >= min_v && jv <= max_v
return true
end
end
end
end

return false
end
6 changes: 3 additions & 3 deletions src/Operations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ function fixups_from_projectfile!(ctx::Context)
for pkg in values(env.manifest)
if ctx.julia_version !== VERSION && is_stdlib(pkg.uuid, ctx.julia_version)
# Special handling for non-current julia_version resolving given the source for historical stdlibs
# isn't available at this stage as Pkg thinks it should not be needed, so rely on STDLIBS_BY_VERSION
# isn't available at this stage as Pkg thinks it should not be needed, so query stdlib info on-demand
stdlibs = Types.get_last_stdlibs(ctx.julia_version)
p = stdlibs[pkg.uuid]
pkg.weakdeps = Dict{String, Base.UUID}(stdlibs[uuid].name => uuid for uuid in p.weakdeps)
# pkg.exts = p.exts # TODO: STDLIBS_BY_VERSION doesn't record this
# pkg.entryfile = p.entryfile # TODO: STDLIBS_BY_VERSION doesn't record this
# pkg.exts = p.exts # TODO: stdlib data doesn't record this
# pkg.entryfile = p.entryfile # TODO: stdlib data doesn't record this
for (name, _) in pkg.weakdeps
if !(name in p.deps)
delete!(pkg.deps, name)
Expand Down
28 changes: 16 additions & 12 deletions src/Types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -566,20 +566,26 @@ function is_or_was_stdlib(uuid::UUID, julia_version::Union{VersionNumber, Nothin
end


function historical_stdlibs_check()
return if isempty(STDLIBS_BY_VERSION)
pkgerror("If you want to set `julia_version`, you must first populate the `STDLIBS_BY_VERSION` global constant. Try `using HistoricalStdlibVersions`")
end
end

# Find the entry in `STDLIBS_BY_VERSION`
# that corresponds to the requested version, and use that.
# If we can't find one, defaults to `UNREGISTERED_STDLIBS`
# Query stdlib info for a specific Julia version.
# Uses the compressed STDLIB_SEGMENTS data structure if available,
# otherwise falls back to STDLIBS_BY_VERSION for backwards compatibility.
function get_last_stdlibs(julia_version::VersionNumber; use_historical_for_current_version = false)
if !use_historical_for_current_version && julia_version == VERSION
return stdlib_infos()
end
historical_stdlibs_check()

# Use the new compressed data structure if this version is covered
if version_covered_by_segments(julia_version)
stdlibs = query_stdlib_segments(julia_version)
# Merge with unregistered stdlibs
return merge(UNREGISTERED_STDLIBS, stdlibs)
end

# Fall back to old STDLIBS_BY_VERSION logic for versions outside the covered range
if isempty(STDLIBS_BY_VERSION)
pkgerror("If you want to set `julia_version`, you must first populate the `STDLIBS_BY_VERSION` global constant. Try `using HistoricalStdlibVersions`")
end

last_stdlibs = UNREGISTERED_STDLIBS
last_version = nothing

Expand All @@ -604,7 +610,6 @@ end
# stdlibs as normal packages so that we get the latest versions of everything, ignoring
# julia compat. So we set the list of stdlibs to that of only the unregistered stdlibs.
function get_last_stdlibs(::Nothing)
historical_stdlibs_check()
return UNREGISTERED_STDLIBS
end

Expand Down Expand Up @@ -634,7 +639,6 @@ function stdlib_version(uuid::UUID, julia_version::Union{VersionNumber, Nothing}
end

function is_unregistered_stdlib(uuid::UUID)
historical_stdlibs_check()
return haskey(UNREGISTERED_STDLIBS, uuid)
end

Expand Down
2 changes: 0 additions & 2 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[deps]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
HistoricalStdlibVersions = "6df8b67a-e8a0-4029-b4b7-ac196fe72102"
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Expand All @@ -17,4 +16,3 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[compat]
Aqua = "0.8.10"
HistoricalStdlibVersions = "2"
17 changes: 0 additions & 17 deletions test/historical_stdlib_version.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,10 @@ using Base.BinaryPlatforms: HostPlatform, Platform, platforms_match
using Test
using TOML

ENV["HISTORICAL_STDLIB_VERSIONS_AUTO_REGISTER"] = "false"
using HistoricalStdlibVersions

include("utils.jl")
using .Utils

@testset "is_stdlib() across versions" begin
HistoricalStdlibVersions.register!()

networkoptions_uuid = Base.UUID("ca575930-c2e3-43a9-ace4-1e988b2c1908")
pkg_uuid = Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f")
mbedtls_jll_uuid = Base.UUID("c8ffd9c3-330d-5841-b78e-0817d7145fa1")
Expand All @@ -24,14 +19,12 @@ using .Utils
@test is_stdlib(networkoptions_uuid, v"1.6")
@test !is_stdlib(networkoptions_uuid, v"1.5")
@test !is_stdlib(networkoptions_uuid, v"1.0.0")
@test !is_stdlib(networkoptions_uuid, v"0.7")
@test !is_stdlib(networkoptions_uuid, nothing)

# Pkg is an unregistered stdlib and has always been an stdlib
@test is_stdlib(pkg_uuid)
@test is_stdlib(pkg_uuid, v"1.0")
@test is_stdlib(pkg_uuid, v"1.6")
@test is_stdlib(pkg_uuid, v"0.7")
@test is_stdlib(pkg_uuid, nothing)

# We can't serve information for unknown major.minor versions (patches can not match)
Expand All @@ -44,17 +37,13 @@ using .Utils
@test is_stdlib(mbedtls_jll_uuid, v"1.11")
@test is_stdlib(mbedtls_jll_uuid, v"1.10")

HistoricalStdlibVersions.unregister!()
# Test that we can probe for stdlibs for the current version with no STDLIBS_BY_VERSION,
# but that we throw a PkgError if we ask for a particular julia version.
@test is_stdlib(networkoptions_uuid)
@test_throws Pkg.Types.PkgError is_stdlib(networkoptions_uuid, v"1.6")
end


@testset "Pkg.add() with julia_version" begin
HistoricalStdlibVersions.register!()

# A package with artifacts that went from normal package -> stdlib
gmp_jll_uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d"
# A package that has always only ever been an stdlib
Expand Down Expand Up @@ -153,12 +142,9 @@ end
p7zip_jll_uuid = Base.UUID("3f19e933-33d8-53b3-aaab-bd5110c3b7a0")
@test !("Pkg" in keys(Pkg.dependencies()[p7zip_jll_uuid].dependencies))
end

HistoricalStdlibVersions.unregister!()
end

@testset "Resolving for another version of Julia" begin
HistoricalStdlibVersions.register!()
temp_pkg_dir() do dir
function find_by_name(versions, name)
idx = findfirst(p -> p.name == name, versions)
Expand Down Expand Up @@ -207,7 +193,6 @@ end
@test mpfr !== nothing
@test mpfr.version.major == 4 && mpfr.version.minor == 0
end
HistoricalStdlibVersions.unregister!()
end

HelloWorldC_jll_UUID = Base.UUID("dca1746e-5efc-54fc-8249-22745bc95a49")
Expand All @@ -218,7 +203,6 @@ libblastrampoline_jll_UUID = Base.UUID("8e850b90-86db-534c-a0d3-1478176c7d93")

isolate(loaded_depot = true) do
@testset "Elliot and Mosè's mini Pkg test suite" begin # https://github.com/JuliaPackaging/JLLPrefixes.jl/issues/6
HistoricalStdlibVersions.register!()
@testset "Standard add" begin
Pkg.activate(temp = true)
# Standard add (non-stdlib, flexible version)
Expand Down Expand Up @@ -350,7 +334,6 @@ isolate(loaded_depot = true) do
end
end
end
HistoricalStdlibVersions.unregister!()
end
end

Expand Down
6 changes: 1 addition & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,7 @@ module PkgTestsInner
"stdlib_compat.jl",
]

# Only test these if the test deps are available (they aren't typically via `Base.runtests`)
HSV_pkgid = Base.PkgId(Base.UUID("6df8b67a-e8a0-4029-b4b7-ac196fe72102"), "HistoricalStdlibVersions")
if Base.locate_package(HSV_pkgid) !== nothing
push!(test_files, "historical_stdlib_version.jl")
end
push!(test_files, "historical_stdlib_version.jl")
Aqua_pkgid = Base.PkgId(Base.UUID("4c88cf16-eb10-579e-8560-4a9242c79595"), "Aqua")
if Base.locate_package(Aqua_pkgid) !== nothing
push!(test_files, "aqua.jl")
Expand Down
Loading