blob: 132fb1ab9db15c04d7ea532d4ff61be1c256ce37 [file] [log] [blame]
#!/usr/bin/env lua
local short_opts = { v = "verbose", vv = "very_verbose", o = "output", q = "quiet", qq = "very_quiet", g = "debug" }
local opts = { use_http = false };
for _, opt in ipairs(arg) do
if opt:match("^%-") then
local name = opt:match("^%-%-?([^%s=]+)()")
name = (short_opts[name] or name):gsub("%-+", "_");
if name:match("^no_") then
name = name:sub(4, -1);
opts[name] = false;
else
opts[name] = opt:match("=(.*)$") or true;
end
else
base_path = opt;
end
end
if opts.very_verbose then opts.verbose = true; end
if opts.very_quiet then opts.quiet = true; end
local noprint = function () end
local print_err, print_info, print_verbose, print_debug = noprint, noprint, noprint, noprint;
if not opts.very_quiet then print_err = print; end
if not opts.quiet then print_info = print; end
if opts.verbose or opts.very_verbose then print_verbose = print; end
if opts.very_verbose then print_debug = print; end
print = print_verbose;
local modules, main_files, resources = {}, {}, {};
-- Functions to be called from squishy file --
function Module(name)
if modules[name] then
print_verbose("Ignoring duplicate module definition for "..name);
return function () end
end
local i = #modules+1;
modules[i] = { name = name, url = ___fetch_url };
modules[name] = modules[i];
return function (path)
modules[i].path = path;
end
end
function Resource(name, path)
local i = #resources+1;
resources[i] = { name = name, path = path or name };
return function (path)
resources[i].path = path;
end
end
function AutoFetchURL(url)
___fetch_url = url;
end
function Main(fn)
table.insert(main_files, fn);
end
function Output(fn)
if opts.output == nil then
out_fn = fn;
end
end
function Option(name)
name = name:gsub("%-", "_");
if opts[name] == nil then
opts[name] = true;
return function (value)
opts[name] = value;
end
else
return function () end;
end
end
function GetOption(name)
return opts[name:gsub('%-', '_')];
end
function Message(message)
if not opts.quiet then
print_info(message);
end
end
function Error(message)
if not opts.very_quiet then
print_err(message);
end
end
function Exit()
os.exit(1);
end
-- -- -- -- -- -- -- --- -- -- -- -- -- -- -- --
base_path = (base_path or "."):gsub("/$", "").."/"
squishy_file = base_path .. "squishy";
out_fn = opts.output;
local ok, err = pcall(dofile, squishy_file);
if not ok then
print_err("Couldn't read squishy file: "..err);
os.exit(1);
end
if not out_fn then
print_err("No output file specified by user or squishy file");
os.exit(1);
elseif #main_files == 0 and #modules == 0 and #resources == 0 then
print_err("No files, modules or resources. Not going to generate an empty file.");
os.exit(1);
end
local fetch = {};
function fetch.filesystem(path)
local f, err = io.open(path);
if not f then return false, err; end
local data = f:read("*a");
f:close();
return data;
end
if opts.use_http then
function fetch.http(url)
local http = require "socket.http";
local body, status = http.request(url);
if status == 200 then
return body;
end
return false, "HTTP status code: "..tostring(status);
end
else
function fetch.http(url)
return false, "Module not found. Re-squish with --use-http option to fetch it from "..url;
end
end
print_info("Writing "..out_fn.."...");
local f, err = io.open(out_fn, "w+");
if not f then
print_err("Couldn't open output file: "..tostring(err));
os.exit(1);
end
if opts.executable then
if opts.executable == true then
f:write("#!/usr/bin/env lua\n");
else
f:write(opts.executable, "\n");
end
end
if opts.debug then
f:write(require_resource("squish.debug"));
end
print_verbose("Resolving modules...");
do
local LUA_DIRSEP = package.config:sub(1,1);
local LUA_PATH_MARK = package.config:sub(5,5);
local package_path = package.path:gsub("[^;]+", function (path)
if not path:match("^%"..LUA_DIRSEP) then
return base_path..path;
end
end):gsub("/%./", "/");
local package_cpath = package.cpath:gsub("[^;]+", function (path)
if not path:match("^%"..LUA_DIRSEP) then
return base_path..path;
end
end):gsub("/%./", "/");
function resolve_module(name, path)
name = name:gsub("%.", LUA_DIRSEP);
for c in path:gmatch("[^;]+") do
c = c:gsub("%"..LUA_PATH_MARK, name);
print_debug("Looking for "..c)
local f = io.open(c);
if f then
print_debug("Found!");
f:close();
return c;
end
end
return nil; -- not found
end
for i, module in ipairs(modules) do
if not module.path then
module.path = resolve_module(module.name, package_path);
if not module.path then
print_err("Couldn't resolve module: "..module.name);
else
-- Strip base_path from resolved path
module.path = module.path:gsub("^"..base_path:gsub("%p", "%%%1"), "");
end
end
end
end
print_verbose("Packing modules...");
for _, module in ipairs(modules) do
local modulename, path = module.name, module.path;
if module.path:sub(1,1) ~= "/" then
path = base_path..module.path;
end
print_debug("Packing "..modulename.." ("..path..")...");
local data, err = fetch.filesystem(path);
if (not data) and module.url then
print_debug("Fetching: ".. module.url:gsub("%?", module.path))
data, err = fetch.http(module.url:gsub("%?", module.path));
end
if data then
f:write("package.preload['", modulename, "'] = (function (...)\n");
f:write(data);
f:write(" end)\n");
if opts.debug then
f:write(string.format("package.preload[%q] = ___adjust_chunk(package.preload[%q], %q);\n\n",
modulename, modulename, "@"..path));
end
else
print_err("Couldn't pack module '"..modulename.."': "..(err or "unknown error... path to module file correct?"));
os.exit(1);
end
end
if #resources > 0 then
print_verbose("Packing resources...")
f:write("do local resources = {};\n");
for _, resource in ipairs(resources) do
local name, path = resource.name, resource.path;
local res_file, err = io.open(base_path..path, "rb");
if not res_file then
print_err("Couldn't load resource: "..tostring(err));
os.exit(1);
end
local data = res_file:read("*a");
local maxequals = 0;
data:gsub("(=+)", function (equals_string) maxequals = math.max(maxequals, #equals_string); end);
f:write(("resources[%q] = %q"):format(name, data));
--[[ f:write(("resources[%q] = ["):format(name), string.rep("=", maxequals+1), "[");
f:write(data);
f:write("]", string.rep("=", maxequals+1), "];"); ]]
end
if opts.virtual_io then
local vio = require_resource("vio");
if not vio then
print_err("Virtual IO requested but is not enabled in this build of squish");
else
-- Insert vio library
f:write(vio, "\n")
-- Override standard functions to use vio if opening a resource
f:write[[local io_open, io_lines = io.open, io.lines; function io.open(fn, mode)
if not resources[fn] then
return io_open(fn, mode);
else
return vio.open(resources[fn]);
end end
function io.lines(fn)
if not resources[fn] then
return io_lines(fn);
else
return vio.open(resources[fn]):lines()
end end
local _dofile = dofile;
function dofile(fn)
if not resources[fn] then
return _dofile(fn);
else
return assert(loadstring(resources[fn]))();
end end
local _loadfile = loadfile;
function loadfile(fn)
if not resources[fn] then
return _loadfile(fn);
else
return loadstring(resources[fn], "@"..fn);
end end ]]
end
end
f:write[[function require_resource(name) return resources[name] or error("resource '"..tostring(name).."' not found"); end end ]]
end
print_debug("Finalising...")
for _, fn in pairs(main_files) do
local fin, err = io.open(base_path..fn);
if not fin then
print_err("Failed to open "..fn..": "..err);
os.exit(1);
else
f:write((fin:read("*a"):gsub("^#.-\n", "")));
fin:close();
end
end
f:close();
print_info("OK!");