Dependent rules work!
This commit is contained in:
parent
14ccf7151e
commit
b7d2b9c3cf
|
@ -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
|
||||||
local s = {}
|
if o.outs then
|
||||||
for _, v in pairs(o) do
|
return asstring(o.outs)
|
||||||
s[#s+1] = asstring(v)
|
else
|
||||||
|
local s = {}
|
||||||
|
for _, v in pairs(o) do
|
||||||
|
s[#s+1] = asstring(v)
|
||||||
|
end
|
||||||
|
return table.concat(s, " ")
|
||||||
end
|
end
|
||||||
return table.concat(s, " ")
|
|
||||||
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 loadbuildfile(filename)
|
local function templateexpand(list, vars)
|
||||||
local data, chunk, e
|
setmetatable(vars, { __index = globals })
|
||||||
data = io.open(filename):read("*a")
|
|
||||||
if not e then
|
|
||||||
local thisglobals = {_G = thisglobals}
|
|
||||||
setmetatable(thisglobals, {__index = globals})
|
|
||||||
chunk, e = loadstring(data, filename, "text", thisglobals)
|
|
||||||
end
|
|
||||||
if e then
|
|
||||||
error(string.format("couldn't load '%s': %s", filename, e))
|
|
||||||
end
|
|
||||||
|
|
||||||
chunk()
|
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)
|
||||||
|
if not buildfiles[filename] then
|
||||||
|
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
|
||||||
|
local thisglobals = {_G = thisglobals}
|
||||||
|
setmetatable(thisglobals, {__index = globals})
|
||||||
|
chunk, e = loadstring(data, filename, "text", thisglobals)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if e then
|
||||||
|
error(string.format("couldn't load '%s': %s", filename, e))
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldcwd = cwd
|
||||||
|
cwd = dirname(filename)
|
||||||
|
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
300
first/build.lua
Normal 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
|
||||||
|
--]]
|
Loading…
Reference in a new issue