Zorya-NEO/luabuild.lua

297 lines
6.6 KiB
Lua

--#!/usr/bin/env luajit
EXPORT = {}
local version = "0.1.0"
local lanes = require("lanes").configure({
demote_full_userdata = true
})
local argparse = require("argparse")
local nproc = 0
do
local h = io.popen("nproc", "r")
local n = h:read("*a"):match("%d+")
h:close()
if n and tonumber(n) then
nproc = tonumber(n)
end
end
_NPROC = nproc
local tags = {
["info"] = "\27[36mINFO\27[0m",
["warning"] = "\27[93mWARNING\27[0m",
["error"] = "\27[91mERROR\27[0m",
["build"] = "\27[35mBUILD\27[0m",
["ok"] = "\27[92mOK\27[0m",
["link"] = "\27[34mLINK\27[0m",
["pack"] = "\27[95mPACK\27[0m",
}
function lastmod(path)
end
local function wait_start(t)
while t.status == "pending" do lanes.sleep() end
end
local function getwh()
local f = io.popen("stty size", "r")
local w, h = f:read("*n"), f:read("*n")
f:close()
return tonumber(w), tonumber(h)
end
local function draw_bar(tag, object, max, current)
if (#object > 15) then
object = object:sub(1, 12).."..."
end
os.execute("stty raw -echo 2> /dev/null") -- i cannot comprehend what retardation lead me to have to do this
io.stdout:write("\27[6n")
io.stdout:flush()
local lc = ""
while lc ~= "\27" do
-- print(string.byte(lc) or "<none>")
lc = io.stdin:read(1)
-- print(string.byte(lc))
end
io.stdin:read(1)
local buf = ""
while lc ~= "R" do
lc = io.stdin:read(1)
buf = buf .. lc
end
os.execute("stty sane 2> /dev/null")
--print(buf)
local y, x = buf:match("(%d+);(%d+)")
x = tonumber(x)
y = tonumber(y)
--print(os.getenv("LINES"), os.getenv("COLUMNS"))
--local l = tonumber(os.getenv("LINES"))
--local c = tonumber(os.getenv("COLUMNS"))
--print(l, c)
local l, c = getwh() -- WHY
if (y == l) then
print("")
y = y - 1
end
local mx = string.format("%x", max)
local cur = string.format("%."..#mx.."x", current)
local bar = (current/max)*(c-(26+#mx*2))
if bar ~= bar then bar = 0 end
io.stdout:write("\27["..l.."H")
local pad = 6-#tag
local opad = 16-#object
--print(math.floor(bar))
local hashes = string.rep("#", math.floor(bar))
local dashes = string.rep("-", c-(26+#mx*2+#hashes))
io.stdout:write(tags[tag], string.rep(" ", pad), object, string.rep(" ", opad), cur, "/", mx, " [", hashes, dashes, "]")
io.stdout:write("\27[", x, ";", y, "H")
end
function status(tag, msg)
print(tags[tag], msg)
end
local threads = {}
local stat = status
local function run(cmd)
if not status then status = stat end
local h = io.popen(cmd.." 2>&1", "r")
local out = h:read("*a")
local rtn = h:close()
return rtn, out
end
local tasks = {}
local reflect = {}
function task(name, stuff)
tasks[#tasks+1] = {name, stuff, false}
end
reflect.task = task
reflect._NPROC = nproc
reflect.status = status
reflect.draw_bar = draw_bar
function dep(name)
if not status then setmetatable(_G, {__index=reflect}) end
for i=1, #tasks do
if (tasks[i][1] == name) then
if not tasks[i][3] then
tasks[i][3] = true
tasks[i][2]()
end
return
end
end
status("error", "Task `"..name.."' not found!")
os.exit(1)
end
reflect.dep = dep
local function sync()
local errors = {}
while #threads > 0 do
for i=1, #threads do
if (threads[j].status ~= "running") then
if not threads[j][1] then
errors[#errors+1] = threads[j][2]
end
table.remove(threads, j)
break
end
end
end
return errors
end
reflect.sync = sync
local CC_STATE = {
compiler = "clang",
flags = {},
args = {},
libs = {},
}
local run_t = lanes.gen("*", run)
local function compile(file)
local compiler_command = compiler .. " -fdiagnostics-color=always "
for i=1, #CC_STATE.flags do
compiler_command = compiler_command .. "-f"..CC_STATE.flags[i].." "
end
for i=1, #CC_STATE.args do
compiler_command = compiler_command .. CC_STATE.args[i].." "
end
compiler_command = compiler_command .. file
return run_t(compiler_command)
end
local function link(target, files)
local nfiles = {}
for i=1, #files do
nfiles[i] = files[i]:gsub("%.c$", ".o")
end
local compiler_command = compiler .. " -fdiagnostics-color=always "
for i=1, #CC_STATE.libs do
compiler_command = compiler_command .. "-l"..CC_STATE.args[i].." "
end
compiler_command = compiler_command "-o "..target.. " " .. table.concat(files, " ")
run(compiler_command)
end
function build(target, files)
status("build", target.." ("..#files.." source files)")
draw_bar("build", target, 0, #files)
for i=1, #files do
if (#threads == nproc) then
while true do
for j=1, #threads do
if (threads[j].status ~= "running") then
if not threads[j][1] then
status("error", "Error in compile, waiting for all threads to finish...")
local errors = sync()
print(threads[j][2])
for k=1, #errors do
print(errors[k])
end
os.exit(1)
else
threads[j] = compile(files[i])
wait_start(threads[j])
break
end
end
end
lanes.sleep()
end
else
threads[#threads+1] = compile(files[i])
end
draw_bar("build", target, #files, i)
end
local errors = sync()
if #errors > 0 then
for i=1, #errors do
print(errors[i])
end
os.exit(1)
end
status("link", target)
draw_bar("link", target, 1, 1)
link(target, files)
end
reflect.build = build
reflect.EXPORT = EXPORT
function find(path)
local entries = {}
local h = io.popen("find "..path, "r")
for l in h:lines() do
entries[#entries+1] = l
end
return entries
end
reflect.find = find
local files = find(".build")
for i=1, #files do
if (files[i]:match("%.lua$")) then
dofile(files[i])
end
end
task("list", function()
for i=1, #tasks do
print(tasks[i][1])
end
end)
local dep_t = lanes.gen("*", dep)
local function run_task(task)
local t = dep_t(task)
wait_start(t)
while true do
if (t.status ~= "running") then
if (t.status ~= "done") then
status("error", "Task '"..task.."' has run into an error!")
print(t[1])
end
break
end
end
lanes.sleep()
end
local parser = argparse("luabuild", "High-speed lua build system.")
parser:option("-j --threads", "Number of threads", nproc)
parser:argument("tasks", "Tasks to run"):args("*")
local args = parser:parse()
status("info", "luabuild version is "..version)
status("info", "lua verison is ".._VERSION)
if not tonumber(args.threads) then
status("error", "Number of threads must be a number!")
os.exit(1)
end
nproc = tonumber(args.threads)
status("info", "core count: "..nproc)
for i=1, #args.tasks do
status("info", "Current task: "..args.tasks[i])
local st = os.clock()
run_task(args.tasks[i])
local dur = os.clock()-st
status("ok", "Task `"..args.tasks[i].."' completed in "..string.format("%.2fs", dur))
end
status("ok", "Build completed in "..string.format("%.2fs", os.clock()))