From c7d30722bb9dd2af6a12adcd1b98ce38daea710c Mon Sep 17 00:00:00 2001 From: lemon-sherbet Date: Tue, 13 Nov 2018 20:12:28 +0100 Subject: [PATCH] owwwo --- LICENSE.txt | 25 +++++++ README.md | 50 ++++++++++++++ install.sh | 20 ++++++ lcmk.lua | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100755 install.sh create mode 100644 lcmk.lua diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..5ee3a5b --- /dev/null +++ b/LICENSE.txt @@ -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 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6c5577d --- /dev/null +++ b/README.md @@ -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) diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..ffccd1c --- /dev/null +++ b/install.sh @@ -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" diff --git a/lcmk.lua b/lcmk.lua new file mode 100644 index 0000000..2ac5a9e --- /dev/null +++ b/lcmk.lua @@ -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()