diff --git a/docs/src/compatibility.md b/docs/src/compatibility.md index 271d503f1f..48ee7666a8 100644 --- a/docs/src/compatibility.md +++ b/docs/src/compatibility.md @@ -98,14 +98,67 @@ PkgG = "~0" # [0.0.0, 1.0.0) For all versions with a major version of 0 the tilde and caret specifiers are equivalent. +### Equality specifier + +Equality can be used to specify an exact version: + +```toml +[compat] +PkgA = "= 1.2.3" # [1.2.3, 1.2.3] +``` + ### Inequality specifiers Inequalities can also be used to specify version ranges: ```toml [compat] -PkgA = ">= 1.2.3" # [1.2.3, ∞) -PkgB = "≥ 1.2.3" # [1.2.3, ∞) -PkgC = "= 1.2.3" # [1.2.3, 1.2.3] -PkgD = "< 1.2.3" # [0.0.0, 1.2.2] +PkgB = ">= 1.2.3" # [1.2.3, ∞) +PkgC = "≥ 1.2.3" # [1.2.3, ∞) +PkgD = "< 1.2.3" # [0.0.0, 1.2.3) = [0.0.0, 1.2.2] +``` + +### Hyphen specifiers + +!!! compat "Julia 1.4" + Hyphen specifiers requires at least Julia 1.4, so it is recomended to also add + ```toml + [compat] + julia = "1.4" + ``` + to the project file when using them. +Hyphen syntax can also be used to specify version ranges. Make sure that you have a space on both sides of the hyphen. + +```toml +[compat] +PkgA = "1.2.3 - 4.5.6" # [1.2.3, 4.5.6] +PkgA = "0.2.3 - 4.5.6" # [0.2.3, 4.5.6] +``` + +Any unspecified trailing numbers in the first end-point are considered to be zero: + +```toml +[compat] +PkgA = "1.2 - 4.5.6" # [1.2.0, 4.5.6] +PkgA = "1 - 4.5.6" # [1.0.0, 4.5.6] +PkgA = "0.2 - 4.5.6" # [0.2.0, 4.5.6] +PkgA = "0.2 - 0.5.6" # [0.2.0, 0.5.6] +``` + +Any unspecified trailing numbers in the second end-point will be considered to be wildcards: + +```toml +[compat] +PkgA = "1.2.3 - 4.5" # 1.2.3 - 4.5.* = [1.2.3, 4.6.0) +PkgA = "1.2.3 - 4" # 1.2.3 - 4.*.* = [1.2.3, 5.0.0) +PkgA = "1.2 - 4.5" # 1.2.0 - 4.5.* = [1.2.0, 4.6.0) +PkgA = "1.2 - 4" # 1.2.0 - 4.*.* = [1.2.0, 5.0.0) +PkgA = "1 - 4.5" # 1.0.0 - 4.5.* = [1.0.0, 4.6.0) +PkgA = "1 - 4" # 1.0.0 - 4.*.* = [1.0.0, 5.0.0) +PkgA = "0.2.3 - 4.5" # 0.2.3 - 4.5.* = [0.2.3, 4.6.0) +PkgA = "0.2.3 - 4" # 0.2.3 - 4.*.* = [0.2.3, 5.0.0) +PkgA = "0.2 - 4.5" # 0.2.0 - 4.5.* = [0.2.0, 4.6.0) +PkgA = "0.2 - 4" # 0.2.0 - 4.*.* = [0.2.0, 5.0.0) +PkgA = "0.2 - 0.5" # 0.2.0 - 0.5.* = [0.2.0, 0.6.0) +PkgA = "0.2 - 0" # 0.2.0 - 0.*.* = [0.2.0, 1.0.0) ``` diff --git a/src/versions.jl b/src/versions.jl index 425e873433..8b1568d6dd 100644 --- a/src/versions.jl +++ b/src/versions.jl @@ -256,7 +256,9 @@ Base.show(io::IO, s::VersionSpec) = print(io, "VersionSpec(\"", s, "\")") ################### function semver_spec(s::String) - s = replace(s, " " => "") + s = replace(s, r"\s-\s" => "→") # replace " - " with "→" + s = replace(s, " " => "") # remove all spaces + s = replace(s, "→" => " - ") # replace "→" with " - " ranges = VersionRange[] for ver in split(s, ',') range = nothing @@ -343,9 +345,36 @@ function inequality_interval(m::RegexMatch) end end +function hyphen_interval(m::RegexMatch) + @assert length(m.captures) == 6 + _lower_major, _lower_minor, _lower_patch, _upper_major, _upper_minor, _upper_patch = m.captures + if isnothing(_lower_minor) + lower_bound = VersionBound(parse(Int, _lower_major)) + elseif isnothing(_lower_patch) + lower_bound = VersionBound(parse(Int, _lower_major), + parse(Int, _lower_minor)) + else + lower_bound = VersionBound(parse(Int, _lower_major), + parse(Int, _lower_minor), + parse(Int, _lower_patch)) + end + if isnothing(_upper_minor) + upper_bound = VersionBound(parse(Int, _upper_major)) + elseif isnothing(_upper_patch) + upper_bound = VersionBound(parse(Int, _upper_major), + parse(Int, _upper_minor)) + else + upper_bound = VersionBound(parse(Int, _upper_major), + parse(Int, _upper_minor), + parse(Int, _upper_patch)) + end + return VersionRange(lower_bound, upper_bound) +end + const version = "v?([0-9]+?)(?:\\.([0-9]+?))?(?:\\.([0-9]+?))?" const ver_regs = [ Regex("^([~^]?)?$version\$") => semver_interval, # 0.5 ^0.4 ~0.3.2 Regex("^((?:≥)|(?:>=)|(?:=)|(?:<)|(?:=))v?$version\$") => inequality_interval,# < 0.2 >= 0.5,2 + Regex("^[\\s]*$version[\\s]*?\\s-\\s[\\s]*?$version[\\s]*\$") => hyphen_interval, # 0.7 - 1.3 ] diff --git a/test/pkg.jl b/test/pkg.jl index 93789987a2..9007e09839 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -88,6 +88,58 @@ import Pkg.Types: semver_spec, VersionSpec @test v"1.2.3" in semver_spec(">=1.2.3") @test !(v"1.2.2" in semver_spec(">=1.2.3")) + @test_throws ErrorException semver_spec("0.1.0-0.2.2") + @test semver_spec("0.1.0 - 0.2.2") == VersionSpec("0.1.0 - 0.2.2") + @test semver_spec("1.2.3 - 4.5.6") == semver_spec("1.2.3 - 4.5.6") == semver_spec("1.2.3 - 4.5.6") == semver_spec("1.2.3 - 4.5.6") + @test semver_spec("0.0.1 - 0.0.2") == VersionSpec("0.0.1 - 0.0.2") + @test semver_spec("0.0.1 - 0.1.0") == VersionSpec("0.0.1 - 0.1.0") + @test semver_spec("0.0.1 - 0.1") == VersionSpec("0.0.1 - 0.1") + @test semver_spec("0.0.1 - 1") == VersionSpec("0.0.1 - 1") + @test semver_spec("0.1 - 0.2") == VersionSpec("0.1 - 0.2") + @test semver_spec("0.1.0 - 0.2") == VersionSpec("0.1.0 - 0.2") + @test semver_spec("0.1 - 0.2.0") == VersionSpec("0.1 - 0.2.0") + @test semver_spec("0.1.0 - 0.2.0") == VersionSpec("0.1.0 - 0.2.0") + @test semver_spec("0.1.1 - 0.2") == VersionSpec("0.1.1 - 0.2") + @test semver_spec("0.1 - 0.2.1") == VersionSpec("0.1 - 0.2.1") + @test semver_spec("0.1.1 - 0.2.1") == VersionSpec("0.1.1 - 0.2.1") + @test semver_spec("1 - 2") == VersionSpec("1 - 2") + @test semver_spec("1.0 - 2") == VersionSpec("1.0 - 2") + @test semver_spec("1 - 2.0") == VersionSpec("1 - 2.0") + @test semver_spec("1.0 - 2.0") == VersionSpec("1.0 - 2.0") + @test semver_spec("1.0.0 - 2.0") == VersionSpec("1.0.0 - 2.0") + @test semver_spec("1.0 - 2.0.0") == VersionSpec("1.0 - 2.0.0") + @test semver_spec("1.0.0 - 2.0.0") == VersionSpec("1.0.0 - 2.0.0") + @test semver_spec("1.0.1 - 2") == VersionSpec("1.0.1 - 2") + @test semver_spec("1.0.1 - 2.0") == VersionSpec("1.0.1 - 2.0") + @test semver_spec("1.0.1 - 2.0.0") == VersionSpec("1.0.1 - 2.0.0") + @test semver_spec("1.0.1 - 2.0.1") == VersionSpec("1.0.1 - 2.0.1") + @test semver_spec("1.0.1 - 2.1.0") == VersionSpec("1.0.1 - 2.1.0") + @test semver_spec("1.0.1 - 2.1.1") == VersionSpec("1.0.1 - 2.1.1") + @test semver_spec("1.1 - 2") == VersionSpec("1.1 - 2") + @test semver_spec("1.1 - 2.0") == VersionSpec("1.1 - 2.0") + @test semver_spec("1.1 - 2.0.0") == VersionSpec("1.1 - 2.0.0") + @test semver_spec("1.1 - 2.0.1") == VersionSpec("1.1 - 2.0.1") + @test semver_spec("1.1 - 2.1.0") == VersionSpec("1.1 - 2.1.0") + @test semver_spec("1.1 - 2.1.1") == VersionSpec("1.1 - 2.1.1") + @test semver_spec("1.1.0 - 2") == VersionSpec("1.1.0 - 2") + @test semver_spec("1.1.0 - 2.0") == VersionSpec("1.1.0 - 2.0") + @test semver_spec("1.1.0 - 2.0.0") == VersionSpec("1.1.0 - 2.0.0") + @test semver_spec("1.1.0 - 2.0.1") == VersionSpec("1.1.0 - 2.0.1") + @test semver_spec("1.1.0 - 2.1.0") == VersionSpec("1.1.0 - 2.1.0") + @test semver_spec("1.1.0 - 2.1.1") == VersionSpec("1.1.0 - 2.1.1") + @test semver_spec("1.1.1 - 2") == VersionSpec("1.1.1 - 2") + @test semver_spec("1.1.1 - 2.0") == VersionSpec("1.1.1 - 2.0") + @test semver_spec("1.1.1 - 2.0.0") == VersionSpec("1.1.1 - 2.0.0") + @test semver_spec("1.1.1 - 2.0.1") == VersionSpec("1.1.1 - 2.0.1") + @test semver_spec("1.1.1 - 2.1.0") == VersionSpec("1.1.1 - 2.1.0") + @test semver_spec("1.1.1 - 2.1.1") == VersionSpec("1.1.1 - 2.1.1") + + @test semver_spec("0.1.0 - 0.2.2, 1.2") == VersionSpec(["0.1.0 - 0.2.2", "1.2.0-1"]) + @test semver_spec("0.1.0 - 0.2.2, >=1.2") == VersionSpec(["0.1.0 - 0.2.2", "1.2.0-*"]) + @test !(v"0.3" in semver_spec("0.1 - 0.2")) + @test v"0.2.99" in semver_spec("0.1 - 0.2") + @test v"0.3" in semver_spec("0.1 - 0") + @test_throws ErrorException semver_spec("^^0.2.3") @test_throws ErrorException semver_spec("^^0.2.3.4") @test_throws ErrorException semver_spec("0.0.0")