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 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 sources = fs.list('src/kernel/') table.sort(sources) function sources:lines() return coroutine.wrap(function() for i, path in ipairs(sources) do local f = assert(io.open('src/kernel/' .. 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) exportFile('boot.lua') fs.makeDirectory(outdir .. 'core/') buildKernel('core/init.lua') fs.makeDirectory(outdir .. 'core/bin') exportFile('bin/tty.lua', 'core/') fs.makeDirectory(outdir .. 'livedisk/') assert(fs.copy(outdir .. 'core/init.lua', outdir .. 'livedisk/init.lua')) fs.makeDirectory(outdir .. 'livedisk/bin/') assert(fs.copy(outdir .. 'core/bin/tty.lua', outdir .. 'livedisk/bin/tty.lua')) fs.makeDirectory(outdir .. 'livedisk/etc/') exportFile('livedisk/init.lua', 'livedisk/etc/init.lua') -- vi: set ts=2: