Skip to content

Commit f224028

Browse files
giordanovchuravy
andauthored
Add field to dependencies to filter them by platform (#176)
* Add field to dependencies to filter them by platform This let us declare that certain dependencies are needed at build/runtime only on some platforms. * Propagate `filter_platforms` through `registry_resolve!` * [Dependencies] Make the new field a vector of platforms instead of a function * Add test about `AnyPlatform` matching all platforms * [Dependencies] Add serialisation of `platforms` field * [Dependencies] Bring back `filter_platforms` function Co-authored-by: Valentin Churavy <vchuravy@users.noreply.github.com>
1 parent 0ecd8d7 commit f224028

File tree

5 files changed

+78
-25
lines changed

5 files changed

+78
-25
lines changed

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name = "BinaryBuilderBase"
22
uuid = "7f725544-6523-48cd-82d1-3fa08ff4056e"
33
authors = ["Elliot Saba <staticfloat@gmail.com>"]
4-
version = "1.1.2"
4+
version = "1.2.0"
5+
56

67
[deps]
78
CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193"

src/Dependencies.jl

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
using UUIDs
22

33
export Dependency, BuildDependency, HostBuildDependency,
4-
is_host_dependency, is_target_dependency, is_build_dependency, is_runtime_dependency
4+
is_host_dependency, is_target_dependency, is_build_dependency, is_runtime_dependency,
5+
filter_platforms
56

67

78
# Pkg.PackageSpec return different types in different Julia versions so...
@@ -54,7 +55,7 @@ Return whether `dep` is a runtime dependency or not.
5455
is_runtime_dependency
5556

5657
"""
57-
Dependency(dep::Union{PackageSpec,String}, build_version; compat)
58+
Dependency(dep::Union{PackageSpec,String}, build_version; compat, platforms)
5859
5960
Define a binary dependency that is necessary to build the package and load the
6061
generated JLL package. The argument can be either a string with the name of the
@@ -67,12 +68,19 @@ The optional keyword argument `compat` can be used to specify a string for use
6768
in the `Project.toml` of the generated Julia package. If `compat` is non-empty
6869
and `build_version` is not passed, the latter defaults to the minimum version
6970
compatible with the `compat` specifier.
71+
72+
The optional keyword argument `platforms` is a vector of `AbstractPlatform`s
73+
which indicates for which platforms the dependency should be used. By default
74+
`platforms=[AnyPlatform()]`, to mean that the dependency is compatible with all
75+
platforms.
7076
"""
7177
struct Dependency <: AbstractDependency
7278
pkg::PkgSpec
7379
build_version::Union{VersionNumber,Nothing}
7480
compat::String # semver string for use in Project.toml of the JLL
75-
function Dependency(pkg::PkgSpec, build_version = nothing; compat::String = "")
81+
platforms::Vector{<:AbstractPlatform}
82+
function Dependency(pkg::PkgSpec, build_version = nothing; compat::String = "",
83+
platforms::Vector{<:AbstractPlatform}=[AnyPlatform()])
7684
if length(compat) > 0
7785
spec = PKG_VERSIONS.semver_spec(compat) # verify compat is valid
7886
if build_version === nothing
@@ -88,45 +96,64 @@ struct Dependency <: AbstractDependency
8896
throw(ArgumentError("PackageSpec version and compat for $(pkg) are incompatible"))
8997
end
9098
end
91-
new(pkg, build_version, compat)
99+
new(pkg, build_version, compat, platforms)
92100
end
93101
end
94-
function Dependency(dep::AbstractString, build_version = nothing; compat::String = "")
95-
return Dependency(PackageSpec(; name = dep), build_version, compat = compat)
102+
function Dependency(dep::AbstractString, build_version = nothing;
103+
compat::String = "", platforms::Vector{<:AbstractPlatform}=[AnyPlatform()])
104+
return Dependency(PackageSpec(; name = dep), build_version; compat, platforms)
96105
end
97106
is_host_dependency(::Dependency) = false
98107
is_build_dependency(::Dependency) = true
99108
is_runtime_dependency(::Dependency) = true
100109

101110
"""
102-
BuildDependency(dep::Union{PackageSpec,String})
111+
BuildDependency(dep::Union{PackageSpec,String}; platforms)
103112
104113
Define a binary dependency that is necessary only to build the package. The
105-
argument can be either a string with the name of the JLL package or a
114+
`dep` argument can be either a string with the name of the JLL package or a
106115
`Pkg.PackageSpec`.
116+
117+
The optional keyword argument `platforms` is a vector of `AbstractPlatform`s
118+
which indicates for which platforms the dependency should be used. By default
119+
`platforms=[AnyPlatform()]`, to mean that the dependency is compatible with all
120+
platforms.
107121
"""
108122
struct BuildDependency <: AbstractDependency
109123
pkg::PkgSpec
124+
platforms::Vector{<:AbstractPlatform}
125+
BuildDependency(pkg::PkgSpec; platforms::Vector{<:AbstractPlatform}=[AnyPlatform()]) =
126+
new(pkg, platforms)
110127
end
111-
BuildDependency(dep::AbstractString) = BuildDependency(PackageSpec(; name = dep))
128+
BuildDependency(dep::AbstractString; platforms::Vector{<:AbstractPlatform}=[AnyPlatform()]) =
129+
BuildDependency(PackageSpec(; name = dep); platforms)
112130
is_host_dependency(::BuildDependency) = false
113131
is_build_dependency(::BuildDependency) = true
114132
is_runtime_dependency(::BuildDependency) = false
115133

116134
"""
117-
HostBuildDependency(dep::Union{PackageSpec,String})
135+
HostBuildDependency(dep::Union{PackageSpec,String}; platforms)
118136
119137
Define a binary dependency that is necessary only to build the package.
120138
Different from the [`BuildDependency`](@ref), the artifact for the host
121139
platform will be installed, instead of that for the target platform.
122140
123-
The argument can be either a string with the name of the JLL package or a
141+
The `dep` argument can be either a string with the name of the JLL package or a
124142
`Pkg.PackageSpec`.
143+
144+
The optional keyword argument `platforms` is a vector of `AbstractPlatform`s
145+
which indicates for which platforms the dependency should be used. By default
146+
`platforms=[AnyPlatform()]`, to mean that the dependency is compatible with all
147+
platforms.
125148
"""
126149
struct HostBuildDependency <: AbstractDependency
127150
pkg::PkgSpec
151+
platforms::Vector{<:AbstractPlatform}
152+
HostBuildDependency(pkg::PkgSpec; platforms::Vector{<:AbstractPlatform}=[AnyPlatform()]) =
153+
new(pkg, platforms)
128154
end
129-
HostBuildDependency(dep::AbstractString) = HostBuildDependency(PackageSpec(; name = dep))
155+
HostBuildDependency(dep::AbstractString; platforms::Vector{<:AbstractPlatform}=[AnyPlatform()]) =
156+
HostBuildDependency(PackageSpec(; name = dep); platforms)
130157
is_host_dependency(::HostBuildDependency) = true
131158
is_build_dependency(::HostBuildDependency) = true
132159
is_runtime_dependency(::HostBuildDependency) = false
@@ -145,12 +172,20 @@ end
145172
getname(x::PkgSpec) = x.name
146173
getname(x::AbstractDependency) = getname(getpkg(x))
147174

175+
"""
176+
filter_platforms(deps::AbstractVector{<:AbstractDependency}, p::AbstractPlatform)
177+
178+
Filter the dependencies `deps` which are compatible with platform `p`.
179+
"""
180+
filter_platforms(deps::AbstractVector{<:AbstractDependency}, p::AbstractPlatform) =
181+
[dep for dep in deps if any(x -> platforms_match(x, p), dep.platforms)]
182+
148183
# Wrapper around `Pkg.Types.registry_resolve!` which keeps the type of the
149184
# dependencies. TODO: improve this
150185
function registry_resolve!(ctx, dependencies::Vector{<:AbstractDependency})
151186
resolved_dependencies = Pkg.Types.registry_resolve!(ctx, getpkg.(dependencies))
152187
for idx in eachindex(dependencies)
153-
dependencies[idx] = typeof(dependencies[idx])(resolved_dependencies[idx])
188+
dependencies[idx] = typeof(dependencies[idx])(resolved_dependencies[idx]; platforms=dependencies[idx].platforms)
154189
end
155190
return dependencies
156191
end
@@ -222,7 +257,9 @@ for (type, type_descr) in ((Dependency, "dependency"), (BuildDependency, "buildd
222257
"compat" => getcompat(d),
223258
"version-major" => major(version(d)),
224259
"version-minor" => minor(version(d)),
225-
"version-patch" => patch(version(d)))
260+
"version-patch" => patch(version(d)),
261+
"platforms" => triplet.(d.platforms),
262+
)
226263
end
227264

228265
# When deserialiasing the JSON file, the dependencies are in the form of
@@ -235,12 +272,13 @@ function dependencify(d::Dict)
235272
version = VersionNumber(d["version-major"], d["version-minor"], d["version-patch"])
236273
version = version == v"0" ? nothing : version
237274
spec = PackageSpec(; name = d["name"], uuid = uuid, version = version)
275+
platforms = parse_platform.(d["platforms"])
238276
if d["type"] == "dependency"
239-
return Dependency(spec; compat = compat)
277+
return Dependency(spec; compat, platforms)
240278
elseif d["type"] == "builddependency"
241-
return BuildDependency(spec)
279+
return BuildDependency(spec; platforms)
242280
elseif d["type"] == "hostdependency"
243-
return HostBuildDependency(spec)
281+
return HostBuildDependency(spec; platforms)
244282
end
245283
end
246284
error("Cannot convert to dependency")

src/Platforms.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ Get the executable extension for the given Platform. Includes the leading `.`.
3939
platform_exeext(p::AbstractPlatform) = Sys.iswindows(p) ? ".exe" : ""
4040

4141

42+
# Helper parsing function: it extends `parse(Platform, p)` by supporting
43+
# `AnyPlatform` as well.
44+
parse_platform(p::String) = p == "any" ? AnyPlatform() : parse(Platform, p; validate_strict=true)
45+
46+
4247
# Recursively test for key presence in nested dicts
4348
function haskeys(d, keys...)
4449
for key in keys

test/dependencies.jl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ end
1717

1818
@testset "Dependencies" begin
1919
name = "Foo_jll"
20-
dep = Dependency(PackageSpec(; name = name))
20+
dep = Dependency(PackageSpec(; name = name); platforms=supported_platforms(; experimental=true, exclude=!Sys.isapple))
2121
@test Dependency(name) == dep
2222
@test !is_host_dependency(dep)
2323
@test is_target_dependency(dep)
@@ -36,7 +36,7 @@ end
3636
@test getcompat(dep_buildver) == ""
3737

3838
# the same but with compat info
39-
dep_buildver = Dependency(PackageSpec(; name = name), build_version, compat = "~1.2")
39+
dep_buildver = Dependency(PackageSpec(; name = name), build_version; compat = "~1.2", platforms=[Platform("x86_64", "linux"; cxxstring_abi="cxx11")])
4040
@test Dependency(name, build_version) == dep_buildver
4141
@test getname(dep_buildver) == name
4242
@test getpkg(dep_buildver) == PackageSpec(; name = name, version = build_version)
@@ -74,27 +74,32 @@ end
7474
@test getname(PackageSpec(; name = host_name)) == host_name
7575
@test getpkg(host_dep) == PackageSpec(; name = host_name)
7676

77+
@testset "Filter dependencies by platform" begin
78+
@test filter_platforms([dep, dep_buildver, dep_compat], Platform("x86_64", "linux"; cxxstring_abi="cxx03")) == [dep_compat]
79+
@test filter_platforms([dep, dep_buildver, dep_compat], Platform("x86_64", "macos")) == [dep, dep_compat]
80+
end
81+
7782
@testset "JSON (de)serialization" begin
7883
jdep = JSON.lower(dep)
79-
@test jdep == Dict("type" => "dependency", "name" => name, "uuid" => nothing, "compat" => "", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0)
84+
@test jdep == Dict("type" => "dependency", "name" => name, "uuid" => nothing, "compat" => "", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0, "platforms" => ["x86_64-apple-darwin", "aarch64-apple-darwin"])
8085
@test dependencify(jdep) == dep
8186

8287
jdep_buildver = JSON.lower(dep_buildver)
83-
@test jdep_buildver == Dict("type" => "dependency", "name" => name, "uuid" => nothing, "compat" => "~1.2", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0)
88+
@test jdep_buildver == Dict("type" => "dependency", "name" => name, "uuid" => nothing, "compat" => "~1.2", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0, "platforms" => ["x86_64-linux-gnu-cxx11"])
8489
# the build_version is currently not serialized, so the following test fails
8590
@test_broken dependencify(jdep_buildver) == dep_buildver
8691

8792
jbuild_dep = JSON.lower(build_dep)
88-
@test jbuild_dep == Dict("type" => "builddependency", "name" => build_name, "uuid" => nothing, "compat" => "", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0)
93+
@test jbuild_dep == Dict("type" => "builddependency", "name" => build_name, "uuid" => nothing, "compat" => "", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0, "platforms" => ["any"])
8994
@test dependencify(jbuild_dep) == build_dep
9095

9196
jhost_dep = JSON.lower(host_dep)
92-
@test jhost_dep == Dict("type" => "hostdependency", "name" => host_name, "uuid" => nothing, "compat" => "", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0)
97+
@test jhost_dep == Dict("type" => "hostdependency", "name" => host_name, "uuid" => nothing, "compat" => "", "version-major" => 0x0, "version-minor" => 0x0, "version-patch" => 0x0, "platforms" => ["any"])
9398
@test dependencify(jhost_dep) == host_dep
9499

95100
full_dep = Dependency(PackageSpec(; name = "Baz_jll", uuid = "00000000-1111-2222-3333-444444444444", version = "3.1.4"))
96101
jfull_dep = JSON.lower(full_dep)
97-
@test jfull_dep == Dict("type" => "dependency", "name" => "Baz_jll", "uuid" => "00000000-1111-2222-3333-444444444444", "compat" => "", "version-major" => 0x3, "version-minor" => 0x1, "version-patch" => 0x4)
102+
@test jfull_dep == Dict("type" => "dependency", "name" => "Baz_jll", "uuid" => "00000000-1111-2222-3333-444444444444", "compat" => "", "version-major" => 0x3, "version-minor" => 0x1, "version-patch" => 0x4, "platforms" => ["any"])
98103
@test dependencify(jfull_dep) == full_dep
99104
@test_throws ErrorException dependencify(Dict("type" => "git"))
100105
end

test/platforms.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ end
5353
)
5454
@test BinaryBuilderBase.choose_shards(AnyPlatform()) == BinaryBuilderBase.choose_shards(default_host_platform)
5555
@test BinaryBuilderBase.aatriplet(AnyPlatform()) == BinaryBuilderBase.aatriplet(default_host_platform)
56+
57+
# Make sure `AnyPlatform` matches all platforms we can possibly support.
58+
@test all(p -> platforms_match(AnyPlatform(), p),
59+
expand_microarchitectures(expand_gfortran_versions(expand_cxxstring_abis(supported_platforms(; experimental=true)))))
5660
end
5761

5862
@testset "Target properties" begin

0 commit comments

Comments
 (0)