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) 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) 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 buildobj(o) end buildprog()