This commit is contained in:
lemon-sherbet 2018-11-13 20:12:28 +01:00
commit c7d30722bb
4 changed files with 282 additions and 0 deletions

25
LICENSE.txt Normal file
View file

@ -0,0 +1,25 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

50
README.md Normal file
View file

@ -0,0 +1,50 @@
# what??
its a c build system. it is not the fastest nor the most extensible but damn me if it aint easy to use pardner.
please do not use this for any serious projects
# installation
```
chmod +x install.sh
./install.sh
```
or, if you want to use luajit:
```
./install.sh -luajit
```
depends on [luafilesystem](https://keplerproject.github.io/luafilesystem/) or [lua filesystem ffi](https://github.com/spacewander/luafilesystem) if using luajit
# usage
`lcmk` will look for a file `build.lua` in your current directory and execute. this file may define the following global variables:
* `PROG` program name (for compilation output). REQUIRED
* `SRC` table containing source file paths (.c and .cpp); do not define or set to "\*.c" to let `lcmk` find them by itself
* `CC` the C compiler.
* `CFLAGS` string containing flags to be passed to the compiler
* `LINK` string containing libraries to link against
any of these variables might be provided through environment variables instead
then, you can invoke the program with `lcmk [options]`
options is an arbitrary list of strings stored in global table `opt` that the build file might use to change something in the build.
the clean option is builtin and will remove all \*.o files along with $PROG
an example build file could be:
```lua
SRC = {"main.c", "foo.c"}
PROG = "./foo"
CFLAGS = "-Wall"
LINK = "-lm"
if opt.debug then
CFLAGS = CFLAGS .. " -ggdb"
end
```
`lcmk` will handle everything else (object files, dependencies, etc)

20
install.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/sh
DEST=/usr/bin/
LUA="/bin/env lua"
if [ "$1" == "-h" ]; then
echo "usage:"
echo " $0 [options]"
echo "options: "
echo " -luajit: install using luajit (needs lfs_ffi: https://github.com/spacewander/luafilesystem)"
exit
elif [ "$1" == "-luajit" ]; then
LUA="/bin/env luajit"
fi
echo "#!/bin/sh" > lcmk
echo "$LUA `pwd`/lcmk.lua $@" >> lcmk
chmod +x lcmk
mv lcmk "$DEST"

187
lcmk.lua Normal file
View file

@ -0,0 +1,187 @@
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()