JuliaPackaging/BinaryBuilderBase.jl

Meta JSON serialization does not preserve VersionSpec

Closed this issue · 3 comments

The JSON serialization via --meta-json that is used during the Yggdrasil build will convert any VersionSpec to just a fixed full VersionNumber x.y.z, this is done here

for (type, type_descr) in ((Dependency, "dependency"), (BuildDependency, "builddependency"))
JSON.lower(d::type) = Dict("type" => type_descr,
"name" => d.pkg.name,
"uuid" => string_or_nothing(d.pkg.uuid),
"version-major" => major(version(d)),
"version-minor" => minor(version(d)),
"version-patch" => patch(version(d)))
end
# When deserialiasing the JSON file, the dependencies are in the form of
# dictionaries. This function converts the dictionary back to the appropriate
# AbstractDependency.
function dependencify(d::Dict)
if d["type"] == "dependency"
uuid = isnothing(d["uuid"]) ? d["uuid"] : UUID(d["uuid"])
version = VersionNumber(d["version-major"], d["version-minor"], d["version-patch"])
version = version == v"0" ? nothing : version
return Dependency(PackageSpec(; name = d["name"], uuid = uuid, version = version))
elseif d["type"] == "builddependency"
uuid = isnothing(d["uuid"]) ? d["uuid"] : UUID(d["uuid"])
version = VersionNumber(d["version-major"], d["version-minor"], d["version-patch"])
version = version == v"0" ? nothing : version
return BuildDependency(PackageSpec(; name = d["name"], uuid = uuid, version = version))

This happened for example in JuliaPackaging/Yggdrasil#2235 where

Dependency(PackageSpec(name="FLINT_jll", version=VersionSpec("200.690"))),

resulted in

cc @fingolfin

So I guess to fix it, we need proper JSON serialization for VersionSpec... the relevant types for that are:

struct VersionBound
    t::NTuple{3,UInt32}
    n::Int
...
end

struct VersionRange
    lower::VersionBound
    upper::VersionBound
...
end

struct VersionSpec
    ranges::Vector{VersionRange}
...
end

We could use the serialization support in JSON? I don't know whether JSON.jl support deserialization (I couldn't find anything about that), so I hacked something up as a proof of concept

julia> using Pkg, JSON

julia> vs = Pkg.Types.VersionSpec("1.4")
VersionSpec("1.4")

julia> str = JSON.json(vs)
"{\"ranges\":[{\"lower\":{\"t\":[1,4,0],\"n\":2},\"upper\":{\"t\":[1,4,0],\"n\":2}}]}"

julia> d = JSON.parse(str)
Dict{String,Any} with 1 entry:
  "ranges" => Any[Dict{String,Any}("lower"=>Dict{String,Any}("t"=>Any[1, 4, 0],"n"=>2),"upper"=>Dict

julia> Pkg.Types.VersionBound(d::Dict{String,Any}) = Pkg.Types.VersionBound(NTuple{d["n"],Int}(d["t"]))

julia> Pkg.Types.VersionRange(d::Dict{String,Any}) = Pkg.Types.VersionRange(Pkg.Types.VersionBound(d["lower"]), Pkg.Types.VersionBound(d["upper"]))

julia> Pkg.Types.VersionSpec(d::Dict{String,Any}) = Pkg.Types.VersionSpec([Pkg.Types.VersionRange(v) for v in d["ranges"]])

julia> Pkg.Types.VersionSpec(d)
VersionSpec("1.4")
visr commented

Does this need to be reopened now that #93 was reverted in #98?

Yes