commit 5b28d35e545b60453103e3acda32ac158af9e121
parent b2dc5a6e890d7d66a8b686a30250f2f0dcd24dea
Author: phoebos <ben@bvnf.space>
Date:   Fri, 16 Jun 2023 14:04:41 +0100
add pkg.order using a tsort
Diffstat:
| M | bliss/pkg.lua | | | 33 | +++++++++++++++++++++++++++++++++ | 
| A | bliss/tsort.lua | | | 77 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
2 files changed, 110 insertions(+), 0 deletions(-)
diff --git a/bliss/pkg.lua b/bliss/pkg.lua
@@ -1,4 +1,5 @@
 local utils = require 'bliss.utils'
+local tsort = require 'bliss.tsort'
 local sys_stat = require 'posix.sys.stat'
 
 local function read_lines(file)
@@ -33,6 +34,11 @@ local function find_version(pkg, path)
     return ver[1]
 end
 
+local function find_depends(pkg, path)
+    local p = find(pkg, path) .. "/depends"
+    return read_lines(p)
+end
+
 local function find_sources(pkg, repo_dir)
     local p = repo_dir .. "/sources"
 
@@ -77,10 +83,37 @@ local function resolve(pkg, sources, env, repo_dir)
     return caches
 end
 
+local function order(env, pkgs)
+    local t = tsort.new()
+
+    for _,p in ipairs(pkgs) do
+        local deps = find_depends(p, env.PATH)
+        local deps_nomake = {}
+        for _,v in ipairs(deps) do
+            table.insert(deps_nomake, v[1])
+        end
+        t:add(p, deps_nomake)
+    end
+
+    local s, x,y = t:sort()
+    if not s then
+        utils.die("Circular dependency detected: " .. x .. " <> " .. y)
+    end
+
+    -- return s reversed (in order to be built)
+    local r = {}
+    for i = #s, 1, -1 do
+        r[i] = s[i]
+    end
+    return r
+end
+
 local M = {
     find = find,
     find_version = find_version,
+    find_depends = find_depends,
     find_sources = find_sources,
     resolve = resolve,
+    order = order,
 }
 return M
diff --git a/bliss/tsort.lua b/bliss/tsort.lua
@@ -0,0 +1,77 @@
+-- This module is not included in init.lua, but used locally by pkg.lua.
+local tsort = {}
+
+-- Returns a table of reverse deps
+local function reverse(input)
+    local reversed = {}
+    for k,v in pairs(input) do
+        for _,w in ipairs(v) do
+            reversed[w] = reversed[w] or {}
+            table.insert(reversed[w], k)
+        end
+    end
+    return reversed
+end
+
+local function find_cycle(revlinks, start)
+    local ret = {[start] = 1}
+    local old
+
+    local t = revlinks[start][1]
+    while t and not ret[t] do
+        ret[t] = 1
+        old = t
+        t = revlinks[t][1]
+    end
+    return t, old
+end
+
+function tsort.new()
+    return setmetatable({nodes={}}, {__index = tsort})
+end
+
+function tsort:add(node, deps)
+    self.nodes[node] = deps
+end
+
+function tsort:sort()
+    if not self.nodes then return nil end
+    local L = {}
+    local reversed = reverse(self.nodes)
+
+    -- find nodes with no incoming edges
+    local S = {}
+    for k in pairs(self.nodes) do
+        if not reversed[k] or #reversed[k] == 0 then
+            table.insert(S, k)
+        end
+    end
+
+    while #S ~= 0 do
+        local n = table.remove(S, 1)
+        table.insert(L, n)
+        for _,v in ipairs(self.nodes[n] or {}) do
+            -- remove edge n -> v
+            for i,j in ipairs(reversed[v]) do
+                if j == n then
+                    table.remove(reversed[v], i)
+                end
+            end
+            if not reversed[v] or #reversed[v] == 0 then
+                table.insert(S, v)
+                reversed[v] = nil
+            end
+        end
+    end
+
+    for k,v in pairs(reversed) do
+        if #v ~= 0 then
+            -- cycle detected
+            local x,y = find_cycle(reversed, k)
+            return nil, x, y
+        end
+    end
+    return L
+end
+
+return tsort