Dependent rules work!

This commit is contained in:
David Given 2016-06-07 00:10:22 +02:00
parent 14ccf7151e
commit b7d2b9c3cf
2 changed files with 383 additions and 33 deletions

View file

@ -30,11 +30,15 @@ local function asstring(o)
elseif (t == "number") then elseif (t == "number") then
return o return o
elseif (t == "table") then elseif (t == "table") then
if o.outs then
return asstring(o.outs)
else
local s = {} local s = {}
for _, v in pairs(o) do for _, v in pairs(o) do
s[#s+1] = asstring(v) s[#s+1] = asstring(v)
end end
return table.concat(s, " ") return table.concat(s, " ")
end
else else
error(string.format("can't turn values of type '%s' into strings", t)) error(string.format("can't turn values of type '%s' into strings", t))
end end
@ -99,23 +103,54 @@ local function emit(...)
end end
end end
local function templateexpand(list, vars)
setmetatable(vars, { __index = globals })
local o = {}
for _, s in ipairs(list) do
o[#o+1] = s:gsub("%%%b{}",
function(expr)
expr = expr:sub(3, -2)
local chunk, e = loadstring("return "..expr, expr, "text", vars)
if e then
error(string.format("error evaluating expression: %s", e))
end
return asstring(chunk())
end
)
end
return o
end
local function loadbuildfile(filename) local function loadbuildfile(filename)
local data, chunk, e if not buildfiles[filename] then
data = io.open(filename):read("*a") buildfiles[filename] = true
local fp, data, chunk, e
io.stderr:write("loading ", filename, "\n")
fp, e = io.open(filename)
if not e then
data, e = fp:read("*a")
fp:close()
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 = loadstring(data, filename, "text", thisglobals)
end end
end
if e then if e then
error(string.format("couldn't load '%s': %s", filename, e)) error(string.format("couldn't load '%s': %s", filename, e))
end end
local oldcwd = cwd
cwd = dirname(filename)
chunk() chunk()
cwd = oldcwd
end
end end
local function loadtarget(targetname) local function loadtarget(targetname)
if targets[target] then if targets[targetname] then
return targets[targetname] return targets[targetname]
end end
@ -129,23 +164,20 @@ local function loadtarget(targetname)
} }
targets[targetname] = target targets[targetname] = target
else else
local _, _, filepart, targetpart = targetname:find("^([^:]+):(%w+)$") local _, _, filepart, targetpart = targetname:find("^([^:]*):(%w+)$")
if not filepart or not targetpart then if not filepart or not targetpart then
error(string.format("malformed target name '%s'", targetname)) error(string.format("malformed target name '%s'", targetname))
end end
if not buildfiles[filepart] then if (filepart == "") then
buildfiles[filepart] = true filepart = cwd
local oldcwd = cwd
cwd = filepart
loadbuildfile(filepart.."/build.lua")
cwd = oldcwd
end end
local filename = concatpath(filepart, "/build.lua")
loadbuildfile(concatpath(filename))
target = targets[targetname] target = targets[targetname]
if not target then if not target then
error(string.format("build file '%s' contains no rule '%s'", error(string.format("build file '%s' contains no rule '%s'",
filepart, targetpart)) filename, targetpart))
end end
end end
@ -168,6 +200,8 @@ local typeconverters = {
if s:find("^//") then if s:find("^//") then
s = s:gsub("^//", "") s = s:gsub("^//", "")
elseif s:find("^:") then
s = cwd..s
elseif s:find("^[^/]") then elseif s:find("^[^/]") then
s = concatpath(cwd, s) s = concatpath(cwd, s)
end end
@ -228,7 +262,11 @@ local function definerule(rulename, types, cb)
end end
args.environment = environment args.environment = environment
cb(args)
local result = cb(args) or {}
result.is = result.is or {}
result.is[rulename] = true
targets[cwd..":"..args.name] = result
end end
end end
@ -260,11 +298,11 @@ function environment:mkdirs(dirs)
end end
function environment:exec(commands) function environment:exec(commands)
local o = {} emit("\t$(hide)", table.concat(commands, " && "), "\n")
for _, s in ipairs(commands) do end
o[#o+1] = "("..s..")"
end function environment:endrule()
emit("\t$(hide)", table.concat(o, " && "), "\n") emit("\n")
end end
definerule("simplerule", definerule("simplerule",
@ -278,7 +316,19 @@ definerule("simplerule",
e.environment:rule(filenamesof(e.ins), e.outs) e.environment:rule(filenamesof(e.ins), e.outs)
e.environment:label(e.name, " ", e.label or "") e.environment:label(e.name, " ", e.label or "")
e.environment:mkdirs(dirnames(e.outs)) e.environment:mkdirs(dirnames(e.outs))
e.environment:exec(e.commands) e.environment:exec(
templateexpand(e.commands,
{
ins = e.ins,
outs = e.outs
}
)
)
e.environment:endrule()
return {
outs = e.outs
}
end end
) )

300
first/build.lua Normal file
View file

@ -0,0 +1,300 @@
simplerule {
name = "random",
ins = {},
outs = {"out"},
commands = {
"dd if=/dev/random of=%{outs} bs=1024 count=1"
}
}
simplerule {
name = "sorted",
ins = { ":random" },
outs = { "sorted" },
commands = {
"sort %{ins} > %{outs}"
}
}
--[[
function environment:cfileflags()
emit("$CFLAGS")
end
function environment:cfile(srcs, obj, includes)
emit("$CC -o", obj, srcs)
emit(ab.expand(includes, "-I%"))
self:cflags()
emit("\n")
end
function environment:clinkflags()
emit("$LDFLAGS")
end
function environment:clink(objs, exe, libraryFlags)
emit("$CC -o", exe, objs, libraryFlags)
self:clinkflags()
emit("\n")
end
--
-- Targets:
--
-- {
-- dir = target's build directory
-- outs = target's object files
-- is = { set of rule types which made the target }
-- }
function M.subenv(p)
local e = p[1]
setmetable(p, {__index = e})
return p
end
local function check_filename(fn)
if type(fn) == "table" then
for _, f in ipairs(fn) do
check_filename(f)
end
else
if fn:find("%s") then
error("Filename '"+fn+"' contains spaces. This will make the build system sad.")
end
end
end
function M.basename(fn)
if type(fn) == "table" then
local nfn = {}
for _, f in ipairs(fn) do
nfn[#nfn+1] = M.basename(f)
end
return nfn
else
local _, _, base = fn:find("([^/]*)$")
return base
end
end
function M.flatten(t)
if t == nil then
return {}
end
local tt = {}
for _, subt in ipairs(t) do
if type(subt) == "table" then
for _, subt in ipairs(M.flatten(subt)) do
tt[#tt+1] = subt
end
else
tt[#tt+1] = subt
end
end
return tt
end
local function append(...)
local ts = {}
for _, t in ipairs({...}) do
for _, v in ipairs(t) do
ts[#ts+1] = v
end
end
return ts
end
local function settotable(s)
local t = {}
for k in pairs(s) do
t[#t+1] = k
end
return t
end
local function asstring(t)
return table.concat(M.flatten(t), " ")
end
local function emit(...)
for _, s in ipairs({...}) do
io.stdout:write(s)
end
end
function M.rawtarget(p)
local description = p.description or error("no description supplied")
local ins = M.flatten(p.ins)
local outs = M.flatten(p.outs)
local cmd = p.command
if type(cmd) ~= "table" then
cmd = {cmd}
end
for _, s in ipairs(ins) do
check_filename(s)
end
for _, s in ipairs(outs) do
check_filename(s)
end
emit(outs[1], ":")
for _, s in ipairs(ins) do
emit(" ", s)
end
emit("\n")
emit("\t@echo ", p.description, "\n")
emit("\t$(hide) ", table.concat(cmd, " && "), "\n")
for i = 2, #outs do
emit(outs[i], ": ", outs[1], "\n")
end
emit("\n")
end
function M.export(p)
local e = p[1]
local dest = p.dest or error("no export destination provided")
local deps = p.deps or error("nothing to export")
local fdeps = M.flatten(deps)
if #fdeps ~= 1 then
error("you can only export one thing at a time")
end
return M.rawtarget {e,
ins=deps,
outs={dest},
command="cp "..fdeps[1].." "..dest,
description="EXPORT "..dest
}
end
function M.hermetic(p)
local e = p[1]
local ins = M.flatten(p.ins)
local deps = M.flatten(p.deps)
local baseouts = p.baseouts or error("you must specify some baseouts")
local description = p.description
local absouts = {}
local path = e.PATH .. "/" .. p.baseouts[1] .. ".env"
for _, s in ipairs(M.flatten(p.baseouts)) do
absouts[#absouts+1] = path .. "/" .. s
end
local dirset = {}
for _, s in ipairs(absouts) do
local d = s:gsub("^(.*/).*$", "%1")
if d then
dirset[d] = true
end
end
local newcmd = {
"rm -rf "..path,
"mkdir -p "..asstring(settotable(dirset)),
"ln -srf "..asstring(append(ins, deps)).." "..path,
"cd "..path
}
for _, s in ipairs(p.command) do
newcmd[#newcmd+1] = "(" .. s .. ")"
end
M.rawtarget {e,
ins={ins, unpack(deps)},
outs=absouts,
command=newcmd,
description=description
}
return absouts
end
function M.cfile(p)
local e = p[1]
local src = p.src
local deps = p.deps or {}
local outfile = p.src:gsub("%.c$", ".o")
local basesrc = M.basename(p.src)
return M.hermetic {e,
ins={src},
deps=deps,
baseouts={outfile},
command={e.CC.." "..e.CFLAGS.." -c -o "..outfile.." "..basesrc},
description="CC "..src
}
end
function M.cprogram(p)
local e = p[1]
local name = p.name or error("cprogram must have a name specified")
local deps = p.deps or {}
local libs = p.libraries or {}
local headers = p.headers or {}
if p.srcs then
local mainlib = M.clibrary {e,
name=name..".a",
deps=deps,
srcs=p.srcs,
headers=headers
}
deps = append(deps, {mainlib})
end
local libflags = {}
for _, s in ipairs(libs) do
libflags[#libflags+1] = "-l"..s
end
return M.hermetic {e,
ins=deps,
baseouts={name},
command={e.CC.." "..e.CFLAGS.." -o "..name.." "..asstring(M.basename(deps)).." "..asstring(libflags)},
description="CLINK "..name
}
end
function M.clibrary(p)
local e = p[1]
local name = p.name or error("clibrary must have a name specified")
local deps = M.flatten(p.deps)
local srcs = M.flatten(p.srcs)
local headers = M.flatten(p.headers)
local baseouts = {name}
local objs = deps
for _, f in ipairs(srcs) do
objs[#objs+1] = M.cfile {e,
src=f,
deps=headers
}
end
return M.hermetic {e,
ins=append(objs, headers),
baseouts=baseouts,
command={
e.AR.." q "..name.." "..asstring(M.basename(objs)),
e.RANLIB.." "..name,
},
description="CLIBRARY "..name
}
end
emit("hide = @\n")
return M
--]]