Commit c2eedc6a authored by Karel Koci's avatar Karel Koci 🤘

Add support for virtual packages

Virtual packages can participate in dependency computation.
parent 3a03f539
......@@ -414,8 +414,7 @@ This command allows amending a package from a repository. It allows
for adding dependencies (even negative or alternative dependencies).
It allows for specifying hooks ‒ functions that are run at specific
times. It also allows creation of virtual packages ‒ a package that
doesn't really exist, but can have hooks and dependencies and
participates in the dependency computation.
doesn't really exist, but can participates in the dependency computation.
A package may be amended multiple times. Each time the options are
merged into the package options.
......
......@@ -261,10 +261,7 @@ function pkg_aggregate()
available_packages[pkg.name] = {candidates = {}, modifiers = {}}
end
local pkg_group = available_packages[pkg.name]
if pkg.virtual then
table.insert(pkg_group.candidates, pkg)
pkg_group.virtual = true
elseif pkg.content then
if pkg.content then
-- If it has content, then it is both modifier AND candidate
table.insert(pkg_group.modifiers, pkg)
table.insert(pkg_group.candidates, pkg)
......@@ -273,10 +270,6 @@ function pkg_aggregate()
end
end
for name, pkg_group in pairs(available_packages) do
-- Check if theres at most one of each virtual package.
if pkg_group.virtual and #pkg_group.candidates > 1 then
error(utils.exception("inconsistent", "More than one candidate with a virtual package " .. name))
end
-- Merge the modifiers together to form single one.
local modifier = {
tp = 'package',
......@@ -302,6 +295,10 @@ function pkg_aggregate()
candidates and the deps could differ.
]]
table.insert(modifier.deps, m.deps or {})
-- Check if theres no candidate for virtual package
if m.virtual and #pkg_group.candidates > 0 then
error(utils.exception("inconsistent", "Candidate exists for virtual package " .. name))
end
-- Take a single value or a list from the source and merge it into a set in the destination
local function set_merge(name)
local src = m[name]
......@@ -335,6 +332,7 @@ function pkg_aggregate()
modifier.reboot = m.reboot
end
modifier.replan = modifier.replan or m.replan
modifier.virtual = modifier.virtual or m.virtual
end
-- Canonize dependencies
modifier.deps = deps_canon(modifier.deps)
......
......@@ -142,8 +142,10 @@ local function build_deps(sat, satmap, pkgs, requests)
if next(sat_candidates) then
sat:clause(-pkg_var, unpack(sat_candidates)) -- package group implies that at least one candidate is chosen
else
if not utils.multi_index(pkg, "modifier", "virtual") then -- For virtual package, no candidates is correct state
satmap.missing[name] = pkg_var -- store that this package group has no candidates
end
end
-- Add dependencies of package group
local deps = utils.multi_index(pkg, 'modifier', 'deps')
if deps and (type(deps) ~= 'table' or deps.tp) then
......@@ -161,6 +163,11 @@ local function build_deps(sat, satmap, pkgs, requests)
assert(type(pkg) == 'table') -- If version specified than we should have package not just package group name
local var = sat:var()
DBG("SAT add candidate selection " .. name .. " var:" .. tostring(var))
if pkgs[name].modifier.virtual then
WARN('Package ' .. name .. ' requested with version or repository, but it is virtual. Resolved as missing.')
satmap.missing[pkg] = var
return var
end
local chosen_candidates = candidates_choose(pkgs[name].candidates, version, repository)
if next(chosen_candidates) then
-- We add here basically or, but without penalizations. Penalization is ensured from dep_pkg_group.
......@@ -449,7 +456,6 @@ function required_pkgs(pkgs, requests)
-- Deny any packages missing, without candidates or dependency on missing version if possible
DBG("Denying packages without any candidate")
-- TODO add support for virtual packages (we don't want to deny virtual packages)
for _, var in pairs(satmap.missing) do
sat:assume(-var)
end
......@@ -502,11 +508,11 @@ function filter_required(status, requests)
-- Go through the requests and look which ones are needed and which ones are satisfied
for _, request in ipairs(requests) do
local installed_version = installed[request.name]
-- TODO: Handle virtual and stand-alone packages
local requested_version = request.package.Version or ""
-- TODO: Handle stand-alone packages
local requested_version = utils.multi_index(request, "package", "Version") or ""
if request.action == "require" then
if not installed_version or installed_version ~= requested_version then
DBG("Want to install/upgrade " .. request.name)
DBG("Want to install " .. request.name)
table.insert(result, request)
else
DBG("Package " .. request.name .. " already installed")
......
......@@ -285,6 +285,67 @@ function test_missing_dep()
assert_exception(function () planner.required_pkgs(pkgs, requests) end, 'inconsistent')
end
function test_virtual()
local pkgs = {
virt1 = {
modifier = {
virtual = true,
deps = "pkg"
}
},
virt2 = {
modifier = {
virtual = true
}
},
pkg = {
candidates = {{Package = 'pkg', Version = "1", deps = 'virt2', repo = def_repo}},
modifier = {}
}
}
local requests = {
{
tp = 'install',
package = {
tp = 'package',
name = 'virt1',
}
}
}
local result = planner.required_pkgs(pkgs, requests)
local expected = {
pkg = {
action = 'require',
package = {Package = 'pkg', Version = "1", deps = "virt2", repo = def_repo},
modifier = {},
name = 'pkg'
}
}
assert_plan_dep_order(expected, result)
end
function test_virtual_version()
local pkgs = {
virt = {
modifier = {
virtual = true,
deps = "pkg"
}
}
}
local requests = {
{
tp = 'install',
package = {
tp = 'package',
name = 'virt',
version = '1'
}
}
}
assert_exception(function () planner.required_pkgs(pkgs, requests) end, 'inconsistent')
end
-- It is able to solve a circular dependency and doesn't stack overflow
function test_circular_deps()
local pkgs = {
......
......@@ -262,10 +262,13 @@ function test_pkg_merge()
modifier = {name = "another"}
},
virt = {
candidates = {{tp = "package", name = "virt", deps = {tp = "dep-and", sub = {"xyz", "abc"}}, virtual = true}},
modifier = {name = "virt"},
candidates = {},
modifier = {
name = "virt",
deps = {tp = "dep-and", sub = {"xyz", "abc"}},
virtual = true
},
},
xyz = {
candidates = {{Package = "xyz", Version = "1", repo = requests.known_repositories_all[1]}},
modifier = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment