303 lines
5.9 KiB
Lua
303 lines
5.9 KiB
Lua
lp = {
|
|
name = 'LuaPack',
|
|
version = '0.1',
|
|
copyright = '(c) Er2 2022, Zlib License',
|
|
|
|
path = {},
|
|
}
|
|
|
|
-- String interpolation
|
|
function lp.strf(str, env)
|
|
return str:gsub('%$([%w_]+)%]?', (n) ==>
|
|
if env[n]
|
|
then return tostring(env[n])
|
|
end
|
|
<==) .. ''
|
|
end
|
|
|
|
-- Helper function
|
|
function lp.insPath(t)
|
|
for _, v in pairs(t)
|
|
do table.insert(lp.path, v)
|
|
end
|
|
end
|
|
|
|
function lp.foreach(e, fn)
|
|
for _, v in pairs(e.plug)
|
|
do fn(v) end
|
|
end
|
|
|
|
function lp.reqf(file, opts)
|
|
if opts.ext[file]
|
|
then return opts.ext[file]
|
|
end
|
|
end
|
|
|
|
function lp.req(e, opts)
|
|
return lp.reqf(e.file, opts)
|
|
end
|
|
|
|
-- Polyfill
|
|
function table.contains(t, V)
|
|
for k, v in pairs(t) do
|
|
if v == V
|
|
then return k
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Try to find file path
|
|
function lp.resolve(name, dir)
|
|
if path.absolute(name)
|
|
then return name
|
|
end
|
|
|
|
name = name:gsub('%.[%w_]+$', '')
|
|
|
|
for _, v in pairs(lp.path) do
|
|
v = v:gsub('%?', name)
|
|
v = path.join(dir, v)
|
|
|
|
local f = io.open(v)
|
|
if f then
|
|
f:close()
|
|
return v
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Use this in plugins to get module
|
|
function lp.get(d, e, opts)
|
|
local f
|
|
for _, v in pairs(e.plug) do
|
|
f = lp.resolve(d, e.dir)
|
|
if f then break end
|
|
end
|
|
|
|
if not f then
|
|
print(lp.strf(' [WRN] Cannot find "$file"', {file = d}))
|
|
return nil
|
|
end
|
|
|
|
local c = lp.reqf(f, opts)
|
|
if not c
|
|
then c = lp.make(f, opts, f .. '.out.lua')
|
|
end
|
|
|
|
if not e.req[c.i]
|
|
then e.reqs = e.reqs + 1
|
|
end
|
|
e.req[c.i] = true
|
|
|
|
print(lp.strf(' [GOT] $file', c))
|
|
|
|
return c
|
|
end
|
|
|
|
-- Alias to makeEnt without entry flag
|
|
function lp.make(file, opts, out)
|
|
return lp.makeEnt(file, opts, out, false)
|
|
end
|
|
|
|
-- Make function
|
|
function lp.makeEnt(file, opts, out, entry)
|
|
local dir = path.abs(path.dir(file))
|
|
local ext = file:match '%.([%w_]+)$'
|
|
local oext = out:match '%.([%w_]+)$'
|
|
|
|
file = lp.resolve(file, dir, ext)
|
|
|
|
local e = lp.reqf(file, opts)
|
|
if e then return e end
|
|
|
|
local f, err = io.open(file)
|
|
assert(f, err)
|
|
local src = f:read '*a'
|
|
f:close()
|
|
|
|
e = {
|
|
i = #opts.ext + 1,
|
|
file = file,
|
|
dir = dir,
|
|
ext = ext,
|
|
src = src,
|
|
reqs = 0,
|
|
req = {},
|
|
entry = entry,
|
|
plug = opts.plug,
|
|
}
|
|
table.insert(opts.ext, file)
|
|
opts.ext[file] = e
|
|
|
|
if #e.plug == 0
|
|
then error(lp.strf('File "$file" cannot be loaded. Add plugins.', e))
|
|
end
|
|
|
|
lp.foreach(e, (v) ==>
|
|
v.load(e, ext, oext)
|
|
e.src = v.deps(e, opts) or e.src
|
|
e.src = v.transform(e, opts) or e.src
|
|
<==)
|
|
|
|
return e
|
|
end
|
|
|
|
local alp = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
function lp.randgen(n)
|
|
local g = ''
|
|
for i = 1, n do
|
|
local v = math.random(#alp)
|
|
g = g.. alp:sub(v, v)
|
|
end
|
|
return g
|
|
end
|
|
|
|
function lp.save(e, out)
|
|
out = lp.strf(out, {
|
|
name = path.base(e.file):gsub('%.[%w_]+$', ''),
|
|
hash = lp.randgen(6),
|
|
})
|
|
local f, err = io.open(out, 'w')
|
|
assert(f, err)
|
|
f:write(e.src)
|
|
f:close()
|
|
return out
|
|
end
|
|
|
|
function lp.config(conf, dir)
|
|
local cfg = {
|
|
-- defaults
|
|
entry = {},
|
|
output = {},
|
|
ext = {},
|
|
plug = {
|
|
require 'plug.lua' {}
|
|
},
|
|
}
|
|
for k, v in pairs(conf) do
|
|
if k == 'entry'
|
|
or k == 'output'
|
|
then
|
|
if type(v) == 'string'
|
|
then table.insert(cfg[k], path.abs(v, dir))
|
|
elseif type(v) == 'table' then
|
|
for _, v in pairs(v)
|
|
do table.insert(cfg[k], path.abs(tostring(v), dir))
|
|
end
|
|
else error(lp.strf('Unknown type $type in "$opt"', {type = type(v), opt = k}))
|
|
end
|
|
elseif k == 'plug'
|
|
then for _, v in pairs(v) do
|
|
if type(v) == 'table'
|
|
then table.insert(cfg[k], v)
|
|
v.load = v.load or function(e, ext, oext) end
|
|
v.deps = v.deps or function(e, opts) end
|
|
v.transform = v.transform or function(e, opts) end
|
|
v.postbuild = v.postbuild or function(e, opts) end
|
|
else error(lp.strf('Unknown type $type in "$opt"', {type = type(v), opt = k}))
|
|
end
|
|
end
|
|
else error(lp.strf('Unknown option "$opt"', {opt = k}))
|
|
end
|
|
end
|
|
|
|
return cfg
|
|
end
|
|
|
|
function lp.build(conf, dir)
|
|
local opts = lp.config(conf, dir)
|
|
for k, v in pairs(opts.entry) do
|
|
local out = opts.output[math.min(k, #opts.output)]
|
|
print(lp.strf('[BUILD] $file => $out', {file = v, out = out}))
|
|
local e = lp.makeEnt(v, opts, out, true)
|
|
lp.foreach(e, function(v)
|
|
v.postbuild(e, opts)
|
|
end)
|
|
local save = lp.save(e, out)
|
|
print(lp.strf('[BUILT] $o => $r', {o = e.file, r = save}))
|
|
end
|
|
end
|
|
|
|
function main(argv)
|
|
print(lp.name ..' '.. lp.version)
|
|
print(lp.copyright)
|
|
print ''
|
|
|
|
local int = (argv[0] or ''):match '^lua' -- started using interpreter
|
|
if #argv == 0
|
|
or (#argv == 1 and int) then
|
|
print(('Usage: %s config.lua'):format(int and (argv[0] ..' '.. argv[1]) or argv[0]))
|
|
return -1
|
|
end
|
|
|
|
local t = os.time()
|
|
|
|
local ent = path.abs(int and argv[2] or argv[1])
|
|
local dir = path.dir(ent)
|
|
|
|
local f, err = io.open(ent)
|
|
assert(f, err)
|
|
local conf = f:read '*a'
|
|
f:close()
|
|
|
|
f, err = load(conf, 'config')
|
|
if not f
|
|
then error(err)
|
|
else conf = f()
|
|
end
|
|
|
|
lp.build(conf, dir)
|
|
|
|
print(lp.strf('Built under $sec seconds.', {sec = os.time() - t}))
|
|
|
|
return 0
|
|
end
|
|
|
|
path = {
|
|
absolute = (f) ==>
|
|
f = f:sub(1, 1)
|
|
return f == '/' or f == '\\'
|
|
<==
|
|
|
|
, relative = (f) => not path.absolute(f)
|
|
|
|
, abs = (f, pwd) ==>
|
|
if path.absolute(f)
|
|
then return f end
|
|
|
|
pwd or= os.getenv 'PWD'
|
|
|
|
f = pwd .. '/'.. f
|
|
f = f:gsub('(.*)[\\/]+%.%.', path.dir)
|
|
f = f:gsub('%.[\\/]+', '/')
|
|
f = f:gsub('[\\/]+', '/')
|
|
|
|
return f
|
|
<==
|
|
|
|
, rel = (f) => f
|
|
|
|
, base = (f) => f:match '([^/\\]*)[/\\]*$'
|
|
|
|
, dir = (f) => f:match '^(.*)[\\/]+[^\\/]*[\\/]*$'
|
|
|
|
, file = (f) => f:match '([^\\/]+)[\\/]*$'
|
|
|
|
, join = function(base, add)
|
|
if path.absolute(add)
|
|
then return add end
|
|
|
|
if path.relative(base)
|
|
then base = path.abs(base)
|
|
end
|
|
|
|
local p = path.abs(base ..'/'.. add)
|
|
if path.relative(p)
|
|
then return add end
|
|
|
|
return p
|
|
end,
|
|
}
|
|
|
|
math.randomseed(os.time())
|
|
os.exit(main(arg))
|