Skip to content

base: deps_version_parser.lua #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Changes from 1 commit
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
99 changes: 60 additions & 39 deletions core/installer/base/deps_version_parser.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- 解析单个版本字符串到版本对象
function parse_single_version(version_str)
function parse_single_version_range(version_str)
local version = {}
if version_str == "any" or version_str == "*" then
Copy link
Collaborator

@lvyuemeng lvyuemeng Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local Version = {}
Version.__index = Version

function Version:new(str)
      local self = setmetatable({},Version}
      self:parse(str)
      return self
end

function Version:parse(str)
   local pattern = (...)
   local major,minor,patch,prerelease,build = str:match(pattern)
   self.major = tonumber(major) or 0
   self.minor = tonumber(minor) or 0
   self.patch = tonumber(patch) or 0
   self.prerelease = prerelease or ""
   self.build = build or "" 
end

function Version:compare(other)
    if self.major ~= other.major then
        return self.major - other.major
    end
    if self.minor ~= other.minor then
        return self.minor - other.minor
    end
    if self.patch ~= other.patch then
        return self.patch - other.patch
    end

    if self.prerelease ~= other.prerelease then
        if self.prerelease == "" then
            return 1
        elseif other.prerelease == "" then
            return -1
        else
            return self.prerelease < other.prerelease and 1 or -1
        end
    end

    return 0
end

function Version:__lt(other)
    return self:compare(other) < 0
end

function Version:__le(other)
    return self:compare(other) <= 0
end

function Version:__eq(other)
    return self:compare(other) == 0
end

function Version:__gt(other)
    return self:compare(other) > 0
end

function Version:__ge(other)
    return self:compare(other) >= 0
end

return version end
Expand Down Expand Up @@ -51,10 +51,10 @@ function parse_single_version(version_str)
end

-- 解析复合版本字符串到版本对象
function parse_version(version_str)
function parse_version_ranges(version_str)
Copy link
Collaborator

@lvyuemeng lvyuemeng Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local Constraint = {}
Constraint.__index = Constraint

function Constraint:new(str)
   local self = setmetatable({},Constraint)
   self:parse(str)
   return self
end

function Constraint:parse(str)
   local pattern = (...)
   local op,version_str = str:match(pattern)
   self.op = op
   self.version = Version:new(version_str)
end

function Constraint:include(version)
     if self.operator == "*" then
        return true  -- "*" means any version
    elseif self.operator == "<=" then
        return version <= self.version
    elseif self.operator == "<" then
        return version < self.version
    elseif self.operator == ">=" then
        return version >= self.version
    elseif self.operator == ">" then
        return version > self.version
    elseif self.operator == "=" then
        return version == self.version
    else
        return false
    end
end

Copy link
Collaborator

@lvyuemeng lvyuemeng Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local Constraints = {}
Constraints.__index = Constraints

function Constraints:new(str)
   local self = setmetatable({},Constraints)
   self:parse(str)
   return self
end

function Constraints:parse(str)
   self.constraints = {}
   for part in str:gmatch(...) do
        table.insert(self.constraints,Constraint:new(part))
    end
end

function Constraints:include(version)
    for _,constraint in ipairs(self.constraints) do
          if not constraint:include(verison) then
                 return false
          end
     end
     return true
end

local conditions = {}
for cond in version_str:gmatch("[^ ,]+") do
table.insert(conditions, parse_single_version(cond))
table.insert(conditions, parse_single_version_range(cond))
end
local merged_version
for _, cond in ipairs(conditions) do
Expand Down Expand Up @@ -87,36 +87,16 @@ function merge_range(a, b)
return merged
end

-- 比较两个版本字符串

function compare_version(a, b)
-- TODO 这正则没研究明白
local av, ar = a:match("^([%d%.]+)([-.+]?.*)$")
local bv, br = b:match("^([%d%.]+)([-.+]?.*)$")
av = av or a; bv = bv or b
local cv = compare_version_number(av, bv)
if cv ~= 0 then return cv end
ar = string.sub(ar, -#ar+1)
br = string.sub(br, -#br+1)
return compare_version_suffix(ar, br)
end

function compare_version_number(a, b)
--print("comparing number", a or "nil", b or "nil")
if not a then return -1 end
if not b then return 1 end
if a == b then return 0 end
local a_parts = {}
local a_parts = {} local b_parts = {}
for num in a:gmatch("%d+") do table.insert(a_parts, tonumber(num)) end
local b_parts = {}
for num in b:gmatch("%d+") do table.insert(b_parts, tonumber(num)) end
local length = math.max(#a_parts, #b_parts)
while #a_parts < length do table.insert(a_parts, 0) end
while #b_parts < length do table.insert(b_parts, 0) end
for i = 1, length do if a_parts[i] ~= b_parts[i] then
return a_parts[i] > b_parts[i] and 1 or -1
end end
return 0
end end return 0
end

pre_flags = {
Expand All @@ -125,7 +105,6 @@ pre_flags = {
alpha = -3, a = -3,
}
function compare_version_suffix(a, b)
--print("comparing suffix", a or "nil", b or "nil")
a = a or ""; b = b or ""
if a == b then return 0 end
if a == "" and b ~= "" then return 1 end
Expand All @@ -141,43 +120,85 @@ function compare_version_suffix(a, b)
return 0
end

function compare_version(a, b)
-- TODO 这正则没研究明白
local av, ar = a:match("^([%d%.]+)([-.+]?.*)$")
local bv, br = b:match("^([%d%.]+)([-.+]?.*)$")
av = av or a; bv = bv or b
local cv = compare_version_number(av, bv)
if cv ~= 0 then return cv end
ar = string.sub(ar, -#ar+1)
br = string.sub(br, -#br+1)
return compare_version_suffix(ar, br)
end

-- 生成所需依赖
function gen_deps(deps)
local needed_deps = {}
for _, dep_set in ipairs(deps) do for pkg_name, version_str in pairs(dep_set) do
needed_deps[pkg_name] = not needed_deps[pkg_name] and
parse_version(version_str) or
merge_range(needed_deps[pkg_name], parse_version(version_str))
parse_version_ranges(version_str) or
merge_range(needed_deps[pkg_name], parse_version_ranges(version_str))
end end return needed_deps
end

function match_version(range, version)
range = range or {} local cv
cv = range.min and compare_version(range.min, version) or -1
if cv > 0 then return false end
if cv == 0 and not range.min_inclusive then return false end
cv = range.max and compare_version(range.max, version) or 1
cv = range.max and compare_version(range.max, version) or 1
if cv < 0 then return false end
if cv == 0 and not range.max_inclusive then return false end
return true
end


-- 测试代码
function main()
-- test gen_deps
local deps = {
{ packageA = "*", packageB = "*", packageC = ">=2" },
{ packageB = "~5.7.1", packageC = "~3.2.7" },
{ packageC = "3.0.x" },
{ packageC = "3.0.5", packageD = ">=3 <4" },
{ packageE = ">3.15" },
{ packageA = "*", packageB = "*", packageC = ">=2" },
{ packageB = "~5.7.1", packageC = "~3.2.7" },
{ packageC = "3.0.x" },
{ packageD = ">=3 <4", packageC = "3.0.5", },
{ packageE = ">3.15" },
}
local needed_deps = gen_deps(deps)
for pkg, range in pairs(needed_deps) do
print(pkg, range)
end

-- test compare_version
local versions = {
{ a = "1.2.3", b = "1.3.2" },
{ a = "1.0.0-beta1", b = "1.0.0-beta2" },
{ a = "1.0.0", b = "1.0-beta3" },
{ a = "1.0.0-alpha1", b = "1.0.0-beta2" }
{ a = "1.2.3", b = "1.3.2", result = -1 }, -- <
{ a = "1.0.0-beta1", b = "1.0.0-beta2", result = -1 }, -- <
{ a = "1.0.0-alpha2", b = "1.0.0-beta1", result = -1 }, -- <
{ a = "1.0.0", b = "1.0-beta3", result = 1 }, -- >
}
local function c(a, b)
local r = compare_version(a, b)
if r == 0 then return "[ (==) equals (==) ]" end
return r > 0 and "[ greater (>) than ]" or "[ less (<) than ]"
return r, r > 0 and "[ greater (>) than ]" or "[ less (<) than ]"
end
for _, v in ipairs(versions) do
print(v.a, c(v.a, v.b), v.b)
local r, a = c(v.a, v.b)
print(v.a, a, v.b, " \t\t--",
r == v.result and "test pass" or "TEST ERROR!")
end

-- test match_version
local matches = {
{ version = "1.2.3", range = "*", result = true },
{ version = "1.2.3", range = "~1.2.4", result = false },
{ version = "1.2.3", range = ">=1.2.3-beta", result = true },
{ version = "1.2.3", range = "1.2.3-beta", result = false },
}
for _, m in ipairs(matches) do
local range = parse_version_ranges(m.range)
local r = match_version(range, m.version)
print(m.version, r and "in" or "not in", m.range, " \t\t--",
r == m.result and "test pass" or "TEST ERROR!")
end
end