diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index b374d57ce9731..c8049d6965544 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -1096,4 +1096,7 @@ Dict{Platform,String}()[HostPlatform()] = "" Platform("x86_64", "linux", Dict{String,Any}(); validate_strict=true) Platform("x86_64", "linux", Dict{String,String}(); validate_strict=false) # called this way from Artifacts.unpack_platform +# PkgCompat module for Pkg.jl +include("binaryplatforms_pkgcompat.jl") + end # module diff --git a/base/binaryplatforms_pkgcompat.jl b/base/binaryplatforms_pkgcompat.jl new file mode 100644 index 0000000000000..311d4d9a3b807 --- /dev/null +++ b/base/binaryplatforms_pkgcompat.jl @@ -0,0 +1,150 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Base.BinaryPlatforms.PkgCompat is the old BinaryPlatforms API exposed by +Pkg.jl as Pkg.BinaryPlatforms +""" +module PkgCompat + +using Base.BinaryPlatforms +export platform_key_abi, platform_dlext, valid_dl_path, arch, libc, + libgfortran_version, libstdcxx_version, cxxstring_abi, parse_dl_name_version, + detect_libgfortran_version, detect_libstdcxx_version, detect_cxxstring_abi, + call_abi, wordsize, triplet, select_platform, platforms_match, + CompilerABI, Platform, UnknownPlatform, Linux, MacOS, Windows, FreeBSD + +import Base.BinaryPlatforms: libgfortran_version, libstdcxx_version, platform_name, + wordsize, platform_dlext, tags, arch, libc, call_abi, + cxxstring_abi + +struct UnknownPlatform <: AbstractPlatform + UnknownPlatform(args...; kwargs...) = new() +end +tags(::UnknownPlatform) = Dict{String,String}("os"=>"unknown") + + +struct CompilerABI + libgfortran_version::Union{Nothing,VersionNumber} + libstdcxx_version::Union{Nothing,VersionNumber} + cxxstring_abi::Union{Nothing,Symbol} + + function CompilerABI(;libgfortran_version::Union{Nothing, VersionNumber} = nothing, + libstdcxx_version::Union{Nothing, VersionNumber} = nothing, + cxxstring_abi::Union{Nothing, Symbol} = nothing) + return new(libgfortran_version, libstdcxx_version, cxxstring_abi) + end +end + +# Easy replacement constructor +function CompilerABI(cabi::CompilerABI; libgfortran_version=nothing, + libstdcxx_version=nothing, + cxxstring_abi=nothing) + return CompilerABI(; + libgfortran_version=something(libgfortran_version, Some(cabi.libgfortran_version)), + libstdcxx_version=something(libstdcxx_version, Some(cabi.libstdcxx_version)), + cxxstring_abi=something(cxxstring_abi, Some(cabi.cxxstring_abi)), + ) +end + +libgfortran_version(cabi::CompilerABI) = cabi.libgfortran_version +libstdcxx_version(cabi::CompilerABI) = cabi.libstdcxx_version +cxxstring_abi(cabi::CompilerABI) = cabi.cxxstring_abi + +for T in (:Linux, :Windows, :MacOS, :FreeBSD) + @eval begin + struct $(T) <: AbstractPlatform + p::Platform + function $(T)(arch::Symbol; compiler_abi=nothing, kwargs...) + if compiler_abi !== nothing + kwargs = (; kwargs..., + :libgfortran_version => libgfortran_version(compiler_abi), + :libstdcxx_version => libstdcxx_version(compiler_abi), + :cxxstring_abi => cxxstring_abi(compiler_abi) + ) + end + return new(Platform(string(arch), $(string(T)); kwargs..., validate_strict=true)) + end + end + end +end + +const PlatformUnion = Union{Linux,MacOS,Windows,FreeBSD} + +# First, methods we need to coerce to Symbol for backwards-compatibility +for f in (:arch, :libc, :call_abi, :cxxstring_abi) + @eval begin + function $(f)(p::PlatformUnion) + str = $(f)(p.p) + if str === nothing + return nothing + end + return Symbol(str) + end + end +end + +# Next, things we don't need to coerce +for f in (:libgfortran_version, :libstdcxx_version, :platform_name, :wordsize, :platform_dlext, :tags, :triplet) + @eval begin + $(f)(p::PlatformUnion) = $(f)(p.p) + end +end + +# Finally, add equality testing between these wrapper types and other AbstractPlatforms +@eval begin + Base.:(==)(a::PlatformUnion, b::AbstractPlatform) = b == a.p +end + +# Add one-off functions +MacOS(; kwargs...) = MacOS(:x86_64; kwargs...) +FreeBSD(; kwargs...) = FreeBSD(:x86_64; kwargs...) + +function triplet(p::AbstractPlatform) + # We are going to sub off to `Base.BinaryPlatforms.triplet()` here, + # with the important exception that we override `os_version` to better + # mimic the old behavior of `triplet()` + if Sys.isfreebsd(p) + p = deepcopy(p) + p["os_version"] = "11.1.0" + elseif Sys.isapple(p) + p = deepcopy(p) + p["os_version"] = "14.0.0" + end + return Base.BinaryPlatforms.triplet(p) +end + +""" + platform_key_abi(machine::AbstractString) + +Returns the platform key for the current platform, or any other though the +the use of the `machine` parameter. + +This method is deprecated, import `Base.BinaryPlatforms` and use either `HostPlatform()` +to get the current host platform, or `parse(Base.BinaryPlatforms.Platform, triplet)` +to parse the triplet for some other platform instead. +""" +platform_key_abi() = HostPlatform() +platform_key_abi(triplet::AbstractString) = parse(Platform, triplet) + +""" + valid_dl_path(path::AbstractString, platform::Platform) + +Return `true` if the given `path` ends in a valid dynamic library filename. +E.g. returns `true` for a path like `"usr/lib/libfoo.so.3.5"`, but returns +`false` for a path like `"libbar.so.f.a"`. + +This method is deprecated and will be removed in Julia 2.0. +""" +function valid_dl_path(path::AbstractString, platform::AbstractPlatform) + try + parse_dl_name_version(path, string(os(platform))::String) + return true + catch e + if isa(e, ArgumentError) + return false + end + rethrow(e) + end +end + +end # module BinaryPlatforms diff --git a/test/binaryplatforms.jl b/test/binaryplatforms.jl index 8de522e9c6c8b..701191f94f546 100644 --- a/test/binaryplatforms.jl +++ b/test/binaryplatforms.jl @@ -421,3 +421,5 @@ end @test platforms_match(ac, ac) @test platforms_match(bc, bc) end + +include("binaryplatforms_pkgcompat.jl") diff --git a/test/binaryplatforms_pkgcompat.jl b/test/binaryplatforms_pkgcompat.jl new file mode 100644 index 0000000000000..f8c5935f6db12 --- /dev/null +++ b/test/binaryplatforms_pkgcompat.jl @@ -0,0 +1,156 @@ +module BinaryPlatformPkgCompatTests +# This test module was ported over from Pkg.jl/test/binaryplatforms.jl + +using Test, Base.BinaryPlatforms.PkgCompat +import Base.BinaryPlatforms.PkgCompat: platform_name + +# The platform we're running on +const platform = @inferred Platform platform_key_abi() + +# This is a compatibility test; once we've fully migrated away from Pkg.BinaryPlatforms +# to the new Base.BinaryPlatforms module, we can throw away the shim definitions in +# `BinaryPlatforms_compat.jl` and drop these tests. +@testset "Compat - PlatformNames" begin + # Ensure the platform type constructors are well behaved + @testset "Platform constructors" begin + @test_throws ArgumentError Linux(:not_a_platform) + @test_throws ArgumentError Linux(:x86_64; libc=:crazy_libc) + @test_throws ArgumentError Linux(:x86_64; libc=:glibc, call_abi=:crazy_abi) + @test_throws ArgumentError Linux(:x86_64; libc=:glibc, call_abi=:eabihf) + @test_throws ArgumentError Linux(:armv7l; libc=:glibc, call_abi=:kekeke) + @test_throws ArgumentError MacOS(:i686) + @test_throws ArgumentError MacOS(:x86_64; libc=:glibc) + @test_throws ArgumentError MacOS(:x86_64; call_abi=:eabihf) + @test_throws ArgumentError Windows(:x86_64; libc=:glibc) + @test_throws ArgumentError Windows(:x86_64; call_abi=:eabihf) + @test_throws ArgumentError FreeBSD(:not_a_platform) + @test_throws ArgumentError FreeBSD(:x86_64; libc=:crazy_libc) + @test_throws ArgumentError FreeBSD(:x86_64; call_abi=:crazy_abi) + @test_throws ArgumentError FreeBSD(:x86_64; call_abi=:eabihf) + + # Test copy constructor + cabi = CompilerABI(; + libgfortran_version=v"3", + libstdcxx_version=v"3.4.18", + cxxstring_abi=:cxx03, + ) + cabi2 = CompilerABI(cabi; cxxstring_abi=:cxx11) + @test libgfortran_version(cabi) == libgfortran_version(cabi2) + @test libstdcxx_version(cabi) == libstdcxx_version(cabi2) + @test cxxstring_abi(cabi) != cxxstring_abi(cabi2) + + # Explicitly test that we can pass arguments to UnknownPlatform, + # and it doesn't do anything. + @test UnknownPlatform(:riscv; libc=:fuchsia_libc) == UnknownPlatform() + end + + @testset "Platform properties" begin + # Test that we can get the name of various platforms + for T in (Linux, MacOS, Windows, FreeBSD) + @test endswith(lowercase(string(T)), lowercase(platform_name(T(:x86_64)))) + end + + # Test that we can get the arch of various platforms + @test arch(Linux(:aarch64; libc=:musl)) == :aarch64 + @test arch(Windows(:i686)) == :i686 + @test arch(FreeBSD(:amd64)) == :x86_64 + @test arch(FreeBSD(:i386)) == :i686 + @test arch(UnknownPlatform(:ppc64le)) == nothing + + # Test that our platform_dlext stuff works + @test platform_dlext(Linux(:x86_64)) == platform_dlext(Linux(:i686)) + @test platform_dlext(Windows(:x86_64)) == platform_dlext(Windows(:i686)) + @test platform_dlext(MacOS()) != platform_dlext(Linux(:armv7l)) + @test platform_dlext(FreeBSD(:x86_64)) == platform_dlext(Linux(:x86_64)) + @test platform_dlext() == platform_dlext(platform) + + @test wordsize(Linux(:i686)) == wordsize(Linux(:armv7l)) == 32 + @test wordsize(MacOS()) == wordsize(Linux(:aarch64)) == 64 + @test wordsize(FreeBSD(:x86_64)) == wordsize(Linux(:powerpc64le)) == 64 + + @test call_abi(Linux(:x86_64)) === nothing + @test call_abi(Linux(:armv6l)) == :eabihf + @test call_abi(Linux(:armv7l; call_abi=:eabihf)) == :eabihf + @test call_abi(UnknownPlatform(;call_abi=:eabihf)) === nothing + + @test triplet(Windows(:i686)) == "i686-w64-mingw32" + @test triplet(Linux(:x86_64; libc=:musl)) == "x86_64-linux-musl" + @test triplet(Linux(:armv7l; libc=:musl)) == "armv7l-linux-musleabihf" + @test triplet(Linux(:armv6l; libc=:musl, call_abi=:eabihf)) == "armv6l-linux-musleabihf" + @test triplet(Linux(:x86_64)) == "x86_64-linux-gnu" + @test triplet(Linux(:armv6l)) == "armv6l-linux-gnueabihf" + @test triplet(MacOS()) == "x86_64-apple-darwin14" + @test triplet(FreeBSD(:x86_64)) == "x86_64-unknown-freebsd11.1" + @test triplet(FreeBSD(:i686)) == "i686-unknown-freebsd11.1" + end + + @testset "Valid DL paths" begin + # Test some valid dynamic library paths + @test valid_dl_path("libfoo.so.1.2.3", Linux(:x86_64)) + @test valid_dl_path("libfoo.1.2.3.so", Linux(:x86_64)) + @test valid_dl_path("libfoo-1.2.3.dll", Windows(:x86_64)) + @test valid_dl_path("libfoo.1.2.3.dylib", MacOS()) + @test !valid_dl_path("libfoo.dylib", Linux(:x86_64)) + @test !valid_dl_path("libfoo.so", Windows(:x86_64)) + @test !valid_dl_path("libfoo.dll", MacOS()) + @test !valid_dl_path("libfoo.so.1.2.3.", Linux(:x86_64)) + @test !valid_dl_path("libfoo.so.1.2a.3", Linux(:x86_64)) + end + + @testset "platforms_match()" begin + # Just do a quick combinatorial sweep for completeness' sake for platform matching + for libgfortran_version in (nothing, v"3", v"5"), + libstdcxx_version in (nothing, v"3.4.18", v"3.4.26"), + cxxstring_abi in (nothing, :cxx03, :cxx11) + + cabi = CompilerABI(; + libgfortran_version=libgfortran_version, + libstdcxx_version=libstdcxx_version, + cxxstring_abi=cxxstring_abi, + ) + @test platforms_match(Linux(:x86_64), Linux(:x86_64, compiler_abi=cabi)) + @test platforms_match(Linux(:x86_64, compiler_abi=cabi), Linux(:x86_64)) + + # Also test auto-string-parsing + @test platforms_match(triplet(Linux(:x86_64)), Linux(:x86_64, compiler_abi=cabi)) + @test platforms_match(Linux(:x86_64), triplet(Linux(:x86_64, compiler_abi=cabi))) + end + + # Ensure many of these things do NOT match + @test !platforms_match(Linux(:x86_64), Linux(:i686)) + @test !platforms_match(Linux(:x86_64), Windows(:x86_64)) + @test !platforms_match(Linux(:x86_64), MacOS()) + @test !platforms_match(Linux(:x86_64), UnknownPlatform()) + + # Make some explicitly non-matching cabi's + base_cabi = CompilerABI(; + libgfortran_version=v"5", + cxxstring_abi=:cxx11, + ) + for arch in (:x86_64, :i686, :aarch64, :armv6l, :armv7l), + cabi in ( + CompilerABI(libgfortran_version=v"3"), + CompilerABI(cxxstring_abi=:cxx03), + CompilerABI(libgfortran_version=v"4", cxxstring_abi=:cxx11), + CompilerABI(libgfortran_version=v"3", cxxstring_abi=:cxx03), + ) + + @test !platforms_match(Linux(arch, compiler_abi=base_cabi), Linux(arch, compiler_abi=cabi)) + end + end + + @testset "Sys.is* overloading" begin + # Test that we can indeed ask if something is linux or windows, etc... + @test Sys.islinux(Linux(:aarch64)) + @test !Sys.islinux(Windows(:x86_64)) + @test Sys.iswindows(Windows(:i686)) + @test !Sys.iswindows(Linux(:x86_64)) + @test Sys.isapple(MacOS()) + @test !Sys.isapple(Linux(:powerpc64le)) + @test Sys.isbsd(MacOS()) + @test Sys.isbsd(FreeBSD(:x86_64)) + @test !Sys.isbsd(Linux(:powerpc64le; libc=:musl)) + end +end + +end # module