Massive ackbuilder refactor --- cleaner and more expressive. Lists

are automatically flattened (leading to better build files), and the
list and filename functions are vastly more orthogonal.
This commit is contained in:
David Given 2016-08-04 23:51:19 +02:00
parent b2bb4ce3b2
commit 5e84be70fd
11 changed files with 198 additions and 149 deletions

View file

@ -18,27 +18,51 @@ local cwd = "."
local vars = {} local vars = {}
local parente = {} local parente = {}
-- Forward references
local loadtarget
local function print(...) local function print(...)
for _, s in ipairs({...}) do local function print_no_nl(list)
if (type(s) ~= "string") then for _, s in ipairs(list) do
s = tostring(s) if (type(s) == "table") then
io.stderr:write("{")
for k, v in pairs(s) do
print_no_nl({k})
io.stderr:write("=")
print_no_nl({v})
io.stderr:write(" ")
end
io.stderr:write("}")
else
io.stderr:write(tostring(s))
end
end end
io.stderr:write(s)
end end
print_no_nl({...})
io.stderr:write("\n") io.stderr:write("\n")
end end
local function assertString(s, i)
if (type(s) ~= "string") then
error(string.format("parameter %d must be a string", i))
end
end
local function concat(...) local function concat(...)
local r = {} local r = {}
for _, t in ipairs({...}) do
if (type(t) == "table") and not t.is then local function process(list)
for _, v in ipairs(t) do for _, t in ipairs(list) do
r[#r+1] = v if (type(t) == "table") and not t.is then
process(t)
else
r[#r+1] = t
end end
else
r[#r+1] = t
end end
end end
process({...})
return r return r
end end
@ -90,84 +114,108 @@ local function concatpath(...)
return (p:gsub("/+", "/"):gsub("^%./", ""):gsub("/%./", "/")) return (p:gsub("/+", "/"):gsub("^%./", ""):gsub("/%./", "/"))
end end
local function filenamesof(targets, pattern) -- Turns a list of strings or targets into a list of targets, expanding
local f = {} -- recursive lists and wildcards.
if targets then local function targetsof(...)
if targets.is then local o = {}
targets = {targets}
end
for _, r in pairs(targets) do local function process(items)
if (type(r) == "table") and r.is then for _, item in ipairs(items) do
if r.outs then if (type(item) == "table") then
for _, o in pairs(r.outs) do if item.is then
if not pattern or o:find(pattern) then -- This is a target.
f[#f+1] = o o[#o+1] = item
end else
end -- This is a list.
process(item)
end end
elseif (type(r) == "string") then elseif (type(item) == "string") then
f[#f+1] = r -- Filename!
if item:find("^%+") then
item = cwd..item
elseif item:find("^%./") then
item = concatpath(cwd, item)
end
o[#o+1] = loadtarget(item)
else else
error(string.format("list of targets contains a %s which isn't a target", error(string.format("member of target list is not a string or a target"))
type(r)))
end end
end end
end end
return f
process({...})
return o
end end
local function targetnamesof(targets) local function filenamesof(...)
local targets = targetsof(...)
local f = {} local f = {}
if targets then for _, r in ipairs(targets) do
if targets.is then if (type(r) == "table") and r.is then
targets = {targets} if r.outs then
end for _, o in ipairs(r.outs) do
f[#f+1] = o
for _, r in pairs(targets) do end
if (type(r) == "table") and r.is then
f[#f+1] = r.fullname
elseif (type(r) == "string") then
f[#f+1] = r
else
error(string.format("list of targets contains a %s which isn't a target",
type(r)))
end end
elseif (type(r) == "string") then
f[#f+1] = r
else
error(string.format("list of targets contains a %s which isn't a target",
type(r)))
end end
end end
return f return f
end end
local function dotocollection(collection, callback) local function targetnamesof(...)
if (type(collection) == "string") then local targets = targetsof(...)
return callback(collection)
elseif collection.is then local f
local files = filenamesof(collection.outs) for _, r in pairs(targets) do
if (#files ~= 1) then if (type(r) == "table") and r.is then
error("inputs with more than one output need to be in a list") f[#f+1] = r.fullname
elseif (type(r) == "string") then
f[#f+1] = r
else
error(string.format("list of targets contains a %s which isn't a target",
type(r)))
end end
end
return f
end
local function dotocollection(files, callback)
if (#files == 1) and (type(files[1]) == "string") then
return callback(files[1]) return callback(files[1])
end end
local o = {} local o = {}
for _, s in pairs(collection) do local function process(files)
if s.is then for _, s in ipairs(files) do
for _, b in pairs(dotocollection(filenamesof(s), callback)) do if (type(s) == "table") then
o[#o+1] = b if s.is then
end error("passed target to a filename manipulation function")
else else
local b = callback(s) process(s)
if (b ~= "") then end
o[#o+1] = b else
local b = callback(s)
if (b ~= "") then
o[#o+1] = b
end
end end
end end
end end
process(files)
return o return o
end end
local function abspath(collection)
return dotocollection(collection, local function abspath(...)
return dotocollection({...},
function(filename) function(filename)
assertString(filename, 1)
if not filename:find("^[/$]") then if not filename:find("^[/$]") then
filename = concatpath(posix.getcwd(), filename) filename = concatpath(posix.getcwd(), filename)
end end
@ -176,9 +224,11 @@ local function abspath(collection)
) )
end end
local function basename(collection)
return dotocollection(collection, local function basename(...)
return dotocollection({...},
function(filename) function(filename)
assertString(filename, 1)
local _, _, b = filename:find("^.*/([^/]*)$") local _, _, b = filename:find("^.*/([^/]*)$")
if not b then if not b then
return filename return filename
@ -188,9 +238,11 @@ local function basename(collection)
) )
end end
local function dirname(collection)
return dotocollection(collection, local function dirname(...)
return dotocollection({...},
function(filename) function(filename)
assertString(filename, 1)
local _, _, b = filename:find("^(.*)/[^/]*$") local _, _, b = filename:find("^(.*)/[^/]*$")
if not b then if not b then
return "" return ""
@ -200,25 +252,34 @@ local function dirname(collection)
) )
end end
local function replace(collection, pattern, repl) local function replace(files, pattern, repl)
return dotocollection(collection, return dotocollection(files,
function(filename) function(filename)
return filename:gsub(pattern, repl) return filename:gsub(pattern, repl)
end end
) )
end end
local function fpairs(collection) local function fpairs(...)
if (type(collection) == "string") or collection.is then return ipairs(filenamesof(...))
return fpairs({collection})
end
return pairs(filenamesof(collection))
end end
local function matching(collection, pattern)
local o = {}
dotocollection(collection,
function(filename)
if filename:find(pattern) then
o[#o+1] = filename
end
end
)
return o
end
-- Selects all targets containing at least one output file that matches -- Selects all targets containing at least one output file that matches
-- the pattern (or all, if the pattern is nil). -- the pattern (or all, if the pattern is nil).
local function selectof(targets, pattern) local function selectof(targets, pattern)
local targets = targetsof(targets)
local o = {} local o = {}
for k, v in pairs(targets) do for k, v in pairs(targets) do
if v.is and v.outs then if v.is and v.outs then
@ -237,16 +298,16 @@ local function selectof(targets, pattern)
return o return o
end end
local function uniquify(collection) local function uniquify(...)
local s = {} local s = {}
local o = {} return dotocollection({...},
for _, v in pairs(collection) do function(filename)
if not s[v] then if not s[filename] then
s[v] = true s[filename] = true
o[#o+1] = v return filename
end
end end
end )
return o
end end
local function startswith(needle, haystack) local function startswith(needle, haystack)
@ -274,13 +335,13 @@ local function templateexpand(list, vars)
o[#o+1] = s:gsub("%%%b{}", o[#o+1] = s:gsub("%%%b{}",
function(expr) function(expr)
expr = expr:sub(3, -2) expr = expr:sub(3, -2)
local chunk, e = loadstring("return "..expr, expr, "text", vars) local chunk, e = load("return ("..expr..")", expr, "text", vars)
if e then if e then
error(string.format("error evaluating expression: %s", e)) error(string.format("error evaluating expression: %s", e))
end end
local value = chunk() local value = chunk()
if (value == nil) then if (value == nil) then
error(string.format("template expression expands to nil (probably an undefined variable)")) error(string.format("template expression '%s' expands to nil (probably an undefined variable)", expr))
end end
return asstring(value) return asstring(value)
end end
@ -302,7 +363,7 @@ local function loadbuildfile(filename)
if not e then if not e then
local thisglobals = {_G = thisglobals} local thisglobals = {_G = thisglobals}
setmetatable(thisglobals, {__index = globals}) setmetatable(thisglobals, {__index = globals})
chunk, e = loadstring(data, filename, "text", thisglobals) chunk, e = load(data, "@"..filename, "text", thisglobals)
end end
end end
if e then if e then
@ -316,7 +377,7 @@ local function loadbuildfile(filename)
end end
end end
local function loadtarget(targetname) loadtarget = function(targetname)
if targets[targetname] then if targets[targetname] then
return targets[targetname] return targets[targetname]
end end
@ -368,24 +429,7 @@ local typeconverters = {
elseif (type(i) ~= "table") then elseif (type(i) ~= "table") then
error(string.format("property '%s' must be a target list", propname)) error(string.format("property '%s' must be a target list", propname))
end end
return targetsof(i)
local o = {}
for k, s in pairs(i) do
if (type(s) == "table") and s.is then
o[k] = s
elseif (type(s) == "string") then
if s:find("^%+") then
s = cwd..s
elseif s:find("^%./") then
s = concatpath(cwd, s)
end
o[k] = loadtarget(s)
else
error(string.format("member of target list '%s' is not a string or a target",
propname))
end
end
return o
end, end,
strings = function(propname, i) strings = function(propname, i)
@ -394,7 +438,7 @@ local typeconverters = {
elseif (type(i) ~= "table") then elseif (type(i) ~= "table") then
error(string.format("property '%s' must be a string list", propname)) error(string.format("property '%s' must be a string list", propname))
end end
return i return concat(i)
end, end,
boolean = function(propname, i) boolean = function(propname, i)
@ -547,7 +591,7 @@ local function install_ninja_emitter()
emit("\n") emit("\n")
local function unmake(collection) local function unmake(collection)
return dotocollection(collection, return dotocollection({collection},
function(s) function(s)
return s:gsub("%$%b()", return s:gsub("%$%b()",
function(expr) function(expr)
@ -589,14 +633,12 @@ definerule("simplerule",
vars = { type="table", default={} }, vars = { type="table", default={} },
}, },
function (e) function (e)
emitter:rule(e.fullname, emitter:rule(e.fullname, filenamesof(e.ins, e.deps), e.outs)
concat(filenamesof(e.ins), filenamesof(e.deps)),
e.outs)
emitter:label(e.fullname, " ", e.label or "") emitter:label(e.fullname, " ", e.label or "")
local vars = inherit(e.vars, { local vars = inherit(e.vars, {
ins = e.ins, ins = filenamesof(e.ins),
outs = e.outs outs = filenamesof(e.outs)
}) })
emitter:exec(templateexpand(e.commands, vars)) emitter:exec(templateexpand(e.commands, vars))
@ -610,7 +652,7 @@ definerule("simplerule",
definerule("installable", definerule("installable",
{ {
map = { type="targets", default={} }, map = { type="table", default={} },
}, },
function (e) function (e)
local deps = {} local deps = {}
@ -618,6 +660,12 @@ definerule("installable",
local srcs = {} local srcs = {}
local dests = {} local dests = {}
for dest, src in pairs(e.map) do for dest, src in pairs(e.map) do
src = targetsof(src)
if (#src ~= 1) then
error("installable can only cope with one target at a time")
end
src = src[1]
if src.is.installable then if src.is.installable then
if (type(dest) ~= "number") then if (type(dest) ~= "number") then
error("can't specify a destination filename when installing an installable") error("can't specify a destination filename when installing an installable")
@ -631,7 +679,7 @@ definerule("installable",
error("installable can only cope with targets emitting single files") error("installable can only cope with targets emitting single files")
end end
deps[#deps+1] = src deps[#deps+1] = src.fullname
dests[#dests+1] = dest dests[#dests+1] = dest
commands[#commands+1] = "cp "..f[1].." "..dest commands[#commands+1] = "cp "..f[1].." "..dest
end end
@ -717,9 +765,9 @@ globals = {
inherit = inherit, inherit = inherit,
print = print, print = print,
replace = replace, replace = replace,
matching = matching,
selectof = selectof, selectof = selectof,
startswith = startswith, startswith = startswith,
targetnamesof = targetnamesof,
uniquify = uniquify, uniquify = uniquify,
vars = vars, vars = vars,
} }

View file

@ -14,8 +14,8 @@ definerule("normalrule",
function (e) function (e)
local dir = e.objdir or objdir(e) local dir = e.objdir or objdir(e)
local realouts = {} local realouts = {}
for k, v in pairs(e.outleaves) do for _, v in pairs(e.outleaves) do
realouts[k] = concatpath(dir, v) realouts[#realouts+1] = concatpath(dir, v)
end end
local vars = inherit(e.vars, { local vars = inherit(e.vars, {
@ -185,7 +185,7 @@ definerule("clibrary",
cwd = e.cwd, cwd = e.cwd,
ins = ins, ins = ins,
deps = concat(e.hdrs, e.deps), deps = concat(e.hdrs, e.deps),
outleaves = { e.name..".a", unpack(basename(hdrs)) }, outleaves = concat(e.name..".a", basename(hdrs)),
label = e.label, label = e.label,
commands = commands, commands = commands,
vars = { vars = {
@ -207,19 +207,21 @@ definerule("cprogram",
} }
}, },
function (e) function (e)
local libs = filenamesof(e.deps, "%.a$") local libs = matching(filenamesof(e.deps), "%.a$")
if (#e.srcs > 0) then if (#e.srcs > 0) then
for _, f in pairs(filenamesof( for _, f in pairs(
{ matching(
clibrary { filenamesof(
name = e.name .. "/main", clibrary {
cwd = e.cwd, name = e.name .. "/main",
srcs = e.srcs, cwd = e.cwd,
deps = e.deps, srcs = e.srcs,
} deps = e.deps,
}, }
"%.a$" ),
)) do "%.a$"
)
) do
libs[#libs+1] = f libs[#libs+1] = f
end end
end end

View file

@ -1,4 +1,3 @@
local posix = require("posix")
include("util/LLgen/build.lua") include("util/LLgen/build.lua")
normalrule { normalrule {
@ -13,7 +12,7 @@ normalrule {
} }
} }
local str_files = basename(posix.glob(cwd().."/*.str")) local str_files = basename(filenamesof("./*.str"))
local str_targets = {} local str_targets = {}
for _, f in ipairs(str_files) do for _, f in ipairs(str_files) do
@ -46,7 +45,7 @@ clibrary {
hdrs = str_targets, hdrs = str_targets,
deps = { deps = {
"+parameters", "+parameters",
unpack(str_targets) str_targets
} }
} }

View file

@ -9,9 +9,9 @@ local allocd_header = definerule(null,
name = e.name, name = e.name,
ins = { ins = {
"./make.allocd", "./make.allocd",
unpack(e.srcs) e.srcs
}, },
outleaves = replace(basename(e.srcs), "%.str$", ".h"), outleaves = basename(filenamesof(e.srcs)[1]):gsub("%.str$", ".h"),
commands = { commands = {
"%{ins[1]} < %{ins[2]} > %{outs}" "%{ins[1]} < %{ins[2]} > %{outs}"
} }
@ -82,7 +82,7 @@ cprogram {
name = "cpp", name = "cpp",
srcs = concat( srcs = concat(
"./*.c", "./*.c",
filenamesof(llgen, "%.c$"), matching(filenamesof(llgen), "%.c$"),
"+next_c", "+next_c",
"+symbol2str_c", "+symbol2str_c",
"+tabgen_c" "+tabgen_c"

View file

@ -32,7 +32,7 @@ definerule("build_as",
name = e.name, name = e.name,
srcs = concat( srcs = concat(
"mach/proto/as/*.c", "mach/proto/as/*.c",
filenamesof(yaccfiles, "%.c$") matching(filenamesof(yaccfiles), "%.c$")
), ),
deps = { deps = {
"h+emheaders", "h+emheaders",

View file

@ -35,7 +35,6 @@ definerule("build_ncg",
"modules/src/flt_arith+lib", "modules/src/flt_arith+lib",
"modules/src/object+lib", "modules/src/object+lib",
"util/data+em_data", "util/data+em_data",
archlib, -- for .h file
headers, headers,
tables, -- for .h file tables, -- for .h file
} }

View file

@ -16,7 +16,7 @@ definerule("ackfile",
"plat/"..plat.."+tools", "plat/"..plat.."+tools",
"util/ack+pkg", "util/ack+pkg",
"lang/cem/cpp.ansi+pkg", "lang/cem/cpp.ansi+pkg",
unpack(e.deps) e.deps
}, },
commands = { commands = {
"ACKDIR=$(INSDIR) $(INSDIR)/bin/ack -m%{plat} -c -o %{outs} %{ins} %{hdrpaths} %{ackcflags}" "ACKDIR=$(INSDIR) $(INSDIR)/bin/ack -m%{plat} -c -o %{outs} %{ins} %{hdrpaths} %{ackcflags}"
@ -38,10 +38,11 @@ definerule("acklibrary",
hdrs = e.hdrs, hdrs = e.hdrs,
deps = { deps = {
"util/arch+pkg", "util/arch+pkg",
unpack(e.deps) e.deps
}, },
_cfile = ackfile, _cfile = ackfile,
commands = { commands = {
"rm -f %{outs[1]}",
"ACKDIR=$(INSDIR) $(INSDIR)/bin/aal q %{outs[1]} %{ins}" "ACKDIR=$(INSDIR) $(INSDIR)/bin/aal q %{outs[1]} %{ins}"
} }
} }

View file

@ -21,7 +21,7 @@ definerule("llgen",
function(e) function(e)
-- Remember this is executed from the caller's directory; local -- Remember this is executed from the caller's directory; local
-- target names will resolve there -- target names will resolve there
local fs = replace(basename(e.srcs), "%.g$", "") local fs = replace(basename(filenamesof(e.srcs)), "%.g$", "")
return normalrule { return normalrule {
name = e.name, name = e.name,
@ -29,11 +29,11 @@ definerule("llgen",
outleaves = { outleaves = {
"Lpars.c", "Lpars.c",
"Lpars.h", "Lpars.h",
unpack(replace(fs, "$", ".c")) replace(fs, "$", ".c")
}, },
ins = { ins = {
"util/LLgen+llgen", "util/LLgen+llgen",
unpack(e.srcs), e.srcs,
}, },
commands = { commands = {
"cd %{dir} && %{abspath(ins)}" "cd %{dir} && %{abspath(ins)}"

View file

@ -8,13 +8,13 @@ definerule("tabgen",
srcs = { type="targets" }, srcs = { type="targets" },
}, },
function(e) function(e)
local symname = replace(basename(e.srcs[1]), "%.tab$", "") local symname = basename(filenamesof(e.srcs)[1]):gsub("%.tab$", "")
return normalrule { return normalrule {
name = e.name, name = e.name,
ins = { ins = {
"util/cmisc+tabgen", "util/cmisc+tabgen",
unpack(e.srcs) e.srcs
}, },
outleaves = { symname..".c" }, outleaves = { symname..".c" },
commands = { commands = {

View file

@ -24,7 +24,7 @@ clibrary {
name = "em_data", name = "em_data",
srcs = concat( srcs = concat(
"./em_ptyp.c", "./em_ptyp.c",
filenamesof(generated, "%.c$") matching(filenamesof(generated), "%.c$")
), ),
hdrs = { hdrs = {
"+generated" -- so we export the H files "+generated" -- so we export the H files

View file

@ -12,11 +12,11 @@ local cgglexer = flex {
normalrule { normalrule {
name = "keywords", name = "keywords",
ins = concat( ins = {
"./cvtkeywords", "./cvtkeywords",
"./keywords", "./keywords",
filenamesof({cggparser}, "%.h$") matching(filenamesof(cggparser), "%.h$")
), },
outleaves = { "enterkeyw.c" }, outleaves = { "enterkeyw.c" },
commands = { commands = {
"%{ins[1]} %{ins[2]} %{ins[3]} %{outs[1]}" "%{ins[1]} %{ins[2]} %{ins[3]} %{outs[1]}"
@ -27,8 +27,8 @@ cprogram {
name = "ncgg", name = "ncgg",
srcs = concat( srcs = concat(
"./*.c", "./*.c",
filenamesof({cggparser}, "%.c$"), matching(filenamesof(cggparser), "%.c$"),
filenamesof({cgglexer}, "%.c$"), matching(filenamesof(cgglexer), "%.c$"),
"+keywords" "+keywords"
), ),
deps = { deps = {