You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
6.2 KiB
Lua

local fs = (function()
local ok, ocfs = pcall(require, 'filesystem')
if ok then
--in [openos, plan9k, standard lua], io.open() resolves paths relative to the current directory.
--in standard lua, lfs.mkdir() also resolves paths relative to the current directory.
--in [openos, plan9k], filesystem.makeDirectory() resolves paths relative to... the filesystem root.
--this script builds openos's filesystem apis accurately on top of luafilesystem because it's easier than
--the other way around, but in this case luafilesystem is right and we fix opencomputers to match.
local shell = require('shell')
return setmetatable({
makeDirectory = function(path)
return ocfs.makeDirectory(ocfs.concat(shell.getWorkingDirectory(), path))
end,
}, {__index = ocfs})
end
--if we're not in minecraft, implement the functions we need on top of LuaFileSystem
local lfs = require('lfs')
local fs = {}
function fs.copy(src, dst)
--lfs doesn't seem to have this? for small files we can just `cat <src >dst`
src, err = io.open(src, 'rb')
if not src then
return nil, err
end
dst, err = io.open(dst, 'wb')
if not dst then
src:close()
return nil, err
end
--outside of opencomputers, we more than likely have more than enough ram, so read everything in one shot
local ok; ok, err = src:read('a')
if ok then ok, err = dst:write(ok) end
if ok then ok, err = dst:flush() end
src:close()
dst:close()
return ok, err
end
function fs.list(path)
local entries = {}
for name in lfs.dir(path) do
if name ~= '.' and name ~= '..' then
if lfs.attributes(path .. '/' .. name, 'mode') == 'directory' then
name = name .. '/'
end
table.insert(entries, name)
end
end
return entries
end
function fs.makeDirectory(path)
--drop the third return on error
local ok, err, code = lfs.mkdir(path)
if ok then
return true
else
return nil, string.format('%s (error %d)', err, code)
end
end
return fs
end)()
local stderr = io.stderr or io.error() --openos does this weirdly
--configuration
local outdir = 'dist/'
--argument parsing
do
local args = {...}
for i, arg in ipairs(args) do
if arg == '-h' or arg == '--help' then
print('usage: lua build.lua [-h|--help]')
return
end
end
--for now, error on any arguments (after parsing -h/--help)
if args[1] then
stderr:write(string.format('error: unexpected argument %q\n', args[1]))
os.exit(64)
end
end
local function preprocess(srcf, dstf)
--for now, run a very minimal minifier
local longEnd --nil normally, some "]==]" string if we're in a long comment
for line in srcf:lines() do
::stripComments:: --strip comments, including weird ones like "--[[ ]]-- --[=[ ]=]-- --text" all in one line
--first, if we're in a long comment, see if that's over
if longEnd then
local i1, i2 = line:find(longEnd, 1, true)
if i1 then
--strip the long comment and mark that it ended
line = line:sub(i2+1)
longEnd = nil
else
--still in a long comment, so don't output anything
goto nextLine
end
end
--look for the start of the next comment
local comment = line:find('--', 1, true)
if not comment then --nothing left to do, break out of the loop
goto emitLine
end
if line:byte(comment+2) == string.byte('[') then --start of a long comment
local depth = #line:sub(comment+2):match('^%[(%=*)%[') --validating syntax earlier ensures this will match
longEnd = string.format(']%s]', string.rep('=', depth))
line = line:sub(comment + 4 + depth) --len('--[') + depth + len('[')
goto stripComments --start from the beginning in case the comment ends on the same line it starts on
else
--found a short comment, remove to end of line
line = line:sub(1, comment - 1)
end
::emitLine::
assert(not longEnd, 'shouldn\'t emit anything in a long comment')
line = assert(line:match('^%s*(.-)%s*$')) --strip leading and trailing whitespace
if #line > 0 then
assert(dstf:write(line, '\n'))
end
::nextLine::
end
assert(not longEnd, 'unclosed long comment')
assert(dstf:flush())
end
local function exportFile(srcpath, dstpath)
dstpath = outdir .. (dstpath or srcpath)
if dstpath:sub(-1) == '/' then
dstpath = dstpath .. srcpath
end
srcpath = 'src/' .. srcpath
--load the file to validate syntax, as a very minimal linter
do
local ok, err = loadfile(srcpath)
if not ok then
stderr:write(err, '\n')
os.exit(1)
end
end
local srcf = assert(io.open(srcpath))
local dstf = assert(io.open(dstpath, 'w'))
preprocess(srcf, dstf)
dstf:close()
srcf:close()
end
local function buildKernel(dst)
local srcdir = 'src/kernel/'
local sources = fs.list(srcdir)
table.sort(sources)
for i, path in ipairs(sources) do
path = srcdir .. path
sources[i] = path
assert(loadfile(path)) --validate syntax
end
function sources:lines()
return coroutine.wrap(function()
for i, path in ipairs(sources) do
local f = assert(io.open(path))
for l in f:lines() do
coroutine.yield(l)
end
f:close()
end
end)
end
local dstf = assert(io.open(outdir .. dst, 'w'))
preprocess(sources, dstf)
dstf:close()
end
fs.makeDirectory(outdir)
--boot eeprom
exportFile('boot.lua')
--[=====[ core: a minimal system to build off of ]=====]--
fs.makeDirectory(outdir .. 'core/')
buildKernel('core/init.lua')
-- core bin/
fs.makeDirectory(outdir .. 'core/bin')
exportFile('bin/lua.lua', 'core/')
exportFile('bin/multitty.lua', 'core/')
-- core lib/
fs.makeDirectory(outdir .. 'core/lib')
exportFile('lib/term.lua', 'core/')
--[=====[ livedisk: a self-contained bootable system for an introduction to kitn ]=====]--
fs.makeDirectory(outdir .. 'livedisk/')
assert(fs.copy(outdir .. 'core/init.lua', outdir .. 'livedisk/init.lua'))
-- livedisk bin/
fs.makeDirectory(outdir .. 'livedisk/bin/')
assert(fs.copy(outdir .. 'core/bin/lua.lua', outdir .. 'livedisk/bin/lua.lua'))
assert(fs.copy(outdir .. 'core/bin/multitty.lua', outdir .. 'livedisk/bin/multitty.lua'))
exportFile('bin/install.lua', 'livedisk/')
-- livedisk lib/
fs.makeDirectory(outdir .. 'livedisk/lib/')
assert(fs.copy(outdir .. 'core/lib/term.lua', outdir .. 'livedisk/lib/term.lua'))
-- livedisk etc/
fs.makeDirectory(outdir .. 'livedisk/etc/')
exportFile('livedisk/init.lua', 'livedisk/etc/init.lua')
-- vi: set ts=2: