lcmk/lcmk.lua

191 lines
3.5 KiB
Lua

local lfs = require(jit and 'lfs_ffi' or 'lfs')
local inspect
function inspect(t,s)
s = s or ""
r = ""
for k,v in pairs(t) do
r = r..(s..k..": "..(type(v) == "table" and "\n"..inspect(v,s.." ") or tostring(v).."\n"))
end
return r
end
local path = lfs.currentdir() .. "/build.lua"
local buildf = loadfile(path)
if not buildf then
error "no build file"
end
local function initvar(name, default)
_G[name] = os.getenv(name) or default or ""
end
initvar("CC", "gcc")
initvar("CFLAGS")
initvar("LINK")
initvar("SRC", "*.c")
opt = {}
for _,arg in ipairs(arg) do
opt[arg] = true
end
function buildobj(o)
local cmd = ("%s %s %s -c -o %s %s"):format(CC, CFLAGS, LINK, o.out, o.src)
print("BUILDING OBJECT:\n"..cmd)
return os.execute(cmd)
end
function buildprog()
local objs = ""
for _,o in ipairs(OBJ) do
objs = objs..o.path.." "
end
local cmd = ("%s %s %s -o %s %s"):format(CC, CFLAGS, LINK, PROG, objs)
print("BUILDING PROGRAM:\n"..cmd)
return os.execute(cmd)
end
buildf()
if not PROG then
error "must provide 'PROG'"
end
function issourcefile(f)
return f:sub(#f-1,#f) == ".c" or f:sub(#f-3,#f) == ".cpp"
end
local function concat(dir,file) --concats directory name + filename
if not dir or dir == lfs.currentdir() then
return file
else
return ({(dir .. "/" .. file)
:gsub("[^/]*/../", "") --remove redundant expressions as in "foo/../bar" -> "bar"
})[1]
end
end
if SRC =="*.c" then
SRC = {}
local find
function find(dir)
--print("searching in "..dir)
for f,_ in lfs.dir(dir) do
local attr = lfs.attributes(concat(dir,f))
if attr and (attr.mode == "file" or attr.mode == "link") and issourcefile(f) then
table.insert(SRC, concat(dir,f))
elseif attr and attr.mode == "directory" and f ~= "." and f ~= ".." and f ~= ".git" then
find(concat(dir,f)) --recursive
end
end
end
find(lfs.currentdir())
end
if _SHOWSRC then
print(inspect(SRC))
end
function getdeps(p, exclude)
--print(p)
local f = io.open(p)
if not f then
return nil
end
exclude = exclude or {}
for k,v in ipairs(exclude) do exclude[v] = true end
local deps = {}
local _,_,dir = p:find("(.*)/.*$")
for l in f:lines() do
local _,_,s = l:find('%s*#include%s*\"(.*)\"')
if s then
s = concat(dir,s)
--print(s)
local dep2 = getdeps(s, deps)
if not exclude[s] then
for k,v in ipairs(dep2) do
table.insert(deps, v)
end
table.insert(deps,s)
end
end
end
for k,v in pairs(deps) do
if type(k) == "string" then
deps[k] = nil
end
end
return deps
end
OBJ = {}
for k,v in ipairs(SRC) do
OBJ[k] = {
path = v:gsub("%.cp?p?$", ".o"),
deps = getdeps(v),
src = v
}
table.insert(OBJ[k].deps, v)
end
if _SHOWOBJ then
print(inspect(OBJ))
end
if opt.clean then
local objs = ""
for _,o in ipairs(OBJ) do
objs = objs..o.path.." "
end
local cmd = ("rm %s %s"):format(objs, PROG)
print("CLEANING: "..cmd)
return os.execute(cmd)
end
local tobuild = {}
for _,o in ipairs(OBJ) do
local attr = lfs.attributes(o.path)
if not attr then
table.insert(tobuild, {src = o.src, out = o.path})
else
local o_mod = attr.modification
for _,dep in ipairs(o.deps) do
local d_mod = lfs.attributes(dep).modification
--print(o_mod-d_mod)
if d_mod > o_mod then
--dependency is newer: rebuild
table.insert(tobuild, {src = o.src, out = o.path})
break
end
end
end
end
if #tobuild < 1 then
print "nothing to do"
return 0
end
if _SHOWBUILD then
print(inspect(tobuild))
end
for _,o in ipairs(tobuild) do
if not buildobj(o) then
print("ERROR")
os.exit(1)
end
end
return buildprog()