3 -- Initialise LuaRocks if present
4 pcall(require
, "luarocks.require");
6 local short_opts
= { v
= "verbose", vv
= "very_verbose", o
= "output", q
= "quiet", qq
= "very_quiet", g
= "debug" }
7 local opts
= { use_http
= false };
9 for _
, opt
in ipairs(arg
) do
10 if opt
:match("^%-") then
11 local name
= opt
:match("^%-%-?([^%s=]+)()")
12 name
= (short_opts
[name
] or name
):gsub("%-+", "_");
13 if name
:match("^no_") then
14 name
= name
:sub(4, -1);
17 opts
[name
] = opt
:match("=(.*)$") or true;
24 if opts
.very_verbose
then opts
.verbose
= true; end
25 if opts
.very_quiet
then opts
.quiet
= true; end
27 local noprint
= function () end
28 local print_err
, print_info
, print_verbose
, print_debug
= noprint
, noprint
, noprint
, noprint
;
30 if not opts
.very_quiet
then print_err
= print; end
31 if not opts
.quiet
then print_info
= print; end
32 if opts
.verbose
or opts
.very_verbose
then print_verbose
= print; end
33 if opts
.very_verbose
then print_debug
= print; end
35 print = print_verbose
;
37 local modules
, main_files
, resources
= {}, {}, {};
39 -- Functions to be called from squishy file --
43 print_verbose("Ignoring duplicate module definition for "..name
);
44 return function () end
47 modules
[i
] = { name
= name
, url
= ___fetch_url
};
48 modules
[name
] = modules
[i
];
49 return function (path
)
50 modules
[i
].path
= path
;
54 function Resource(name
, path
)
55 local i
= #resources
+1;
56 resources
[i
] = { name
= name
, path
= path
or name
};
57 return function (path
)
58 resources
[i
].path
= path
;
62 function AutoFetchURL(url
)
67 table.insert(main_files
, fn
);
71 if opts
.output
== nil then
77 name
= name
:gsub("%-", "_");
78 if opts
[name
] == nil then
80 return function (value
)
84 return function () end;
88 function GetOption(name
)
89 return opts
[name
:gsub('%-', '_')];
92 function Message(message
)
93 if not opts
.quiet
then
98 function Error(message
)
99 if not opts
.very_quiet
then
107 -- -- -- -- -- -- -- --- -- -- -- -- -- -- -- --
109 squishy_file
= squishy_file
or "./squishy"
110 base_path
= string.match(squishy_file
, "(.*/)") or "./"
111 out_fn
= opts
.output
;
113 local ok
, err
= pcall(dofile, squishy_file
);
116 print_err("Couldn't read squishy file: "..err
);
121 print_err("No output file specified by user or squishy file");
123 elseif #main_files
== 0 and #modules
== 0 and #resources
== 0 then
124 print_err("No files, modules or resources. Not going to generate an empty file.");
129 function fetch
.filesystem(path
)
130 local f
, err
= io
.open(path
);
131 if not f
then return false, err
; end
133 local data
= f
:read("*a");
139 if opts
.use_http
then
140 function fetch
.http(url
)
141 local http
= require
"socket.http";
143 local body
, status
= http
.request(url
);
144 if status
== 200 then
147 return false, "HTTP status code: "..tostring(status
);
150 function fetch
.http(url
)
151 return false, "Module not found. Re-squish with --use-http option to fetch it from "..url
;
155 print_verbose("Resolving modules...");
157 local LUA_DIRSEP
= package
.config
:sub(1,1);
158 local LUA_PATH_MARK
= package
.config
:sub(5,5);
160 local package_path
= package
.path
:gsub("[^;]+", function (path
)
161 if not path
:match("^%"..LUA_DIRSEP
) then
162 return base_path
..path
;
164 end):gsub("/%./", "/");
165 local package_cpath
= package
.cpath
:gsub("[^;]+", function (path
)
166 if not path
:match("^%"..LUA_DIRSEP
) then
167 return base_path
..path
;
169 end):gsub("/%./", "/");
171 function resolve_module(name
, path
)
172 name
= name
:gsub("%.", LUA_DIRSEP
);
173 for c
in path
:gmatch("[^;]+") do
174 c
= c
:gsub("%"..LUA_PATH_MARK
, name
);
175 print_debug("Looking for "..c
)
176 local f
= io
.open(c
);
178 print_debug("Found!");
183 return nil; -- not found
186 for i
, module
in ipairs(modules
) do
187 if not module
.path
then
188 module
.path
= resolve_module(module
.name
, package_path
);
189 if not module
.path
then
190 print_err("Couldn't resolve module: "..module
.name
);
192 -- Strip base_path from resolved path
193 module
.path
= module
.path
:gsub("^"..base_path
:gsub("%p", "%%%1"), "");
199 for _
, module
in ipairs(modules
) do
200 if not module
.path
then
201 print_err("Exiting due to missing modules without a path");
206 if opts
.list_files
or opts
.list_missing_files
then
207 local function write(filename
)
208 if opts
.list_missing_files
then
209 local f
= io
.open(filename
);
215 io
.write(filename
, "\n");
217 for _
, fn
in pairs(main_files
) do
220 for _
, module
in ipairs(modules
) do
223 for _
, resource
in ipairs(resources
) do
224 write(resource
.path
);
229 print_info("Writing "..out_fn
.."...");
230 local f
, err
= io
.open(out_fn
, "w+");
232 print_err("Couldn't open output file: "..tostring(err
));
236 if opts
.executable
then
237 if opts
.executable
== true then
238 f
:write("#!/usr/bin/env lua\n");
240 f
:write(opts
.executable
, "\n");
244 print_verbose("Packing modules...");
245 for _
, module
in ipairs(modules
) do
246 local modulename
, path
= module
.name
, module
.path
;
247 if module
.path
:sub(1,1) ~= "/" then
248 path
= base_path
..module
.path
;
250 print_debug("Packing "..modulename
.." ("..path
..")...");
251 local data
, err
= fetch
.filesystem(path
);
252 if (not data
) and module
.url
then
253 local url
= module
.url
:gsub("%?", module
.path
);
254 print_debug("Fetching: ".. url
)
255 if url
:match("^https?://") then
256 data
, err
= fetch
.http(url
);
257 elseif url
:match("^file://") or url
:match("^[/%.]") then
258 local dataf
, dataerr
= io
.open((url
:gsub("^file://", "")));
260 data
, err
= dataf
:read("*a");
263 data
, err
= nil, dataerr
;
268 data
= data
:gsub("^#[^\r\n]*\r?\n", ""); -- Remove shebang if any (or we can't concat)
269 if not opts
.debug
then
270 f
:write("package.preload['", modulename
, "'] = (function (...)\n");
274 f
:write("package.preload['", modulename
, "'] = assert(loadstring(\n");
275 f
:write(("%q\n"):format(data
));
276 f
:write(", ", ("%q"):format("@"..path
), "))\n");
279 print_err("Couldn't pack module '"..modulename
.."': "..(err
or "unknown error... path to module file correct?"));
284 if #resources
> 0 then
285 print_verbose("Packing resources...")
286 f
:write("do local resources = {};\n");
287 for _
, resource
in ipairs(resources
) do
288 local name
, path
= resource
.name
, resource
.path
;
289 local res_file
, err
= io
.open(base_path
..path
, "rb");
291 print_err("Couldn't load resource: "..tostring(err
));
294 local data
= res_file
:read("*a");
296 data
:gsub("(=+)", function (equals_string
) maxequals
= math
.max(maxequals
, #equals_string
); end);
298 f
:write(("resources[%q] = %q"):format(name
, data
));
299 --[[ f:write(("resources[%q] = ["):format(name), string.rep("=", maxequals+1), "[");
301 f:write("]", string.rep("=", maxequals+1), "];"); ]]
303 if opts
.virtual_io
then
304 local vio
= require_resource("vio");
306 print_err("Virtual IO requested but is not enabled in this build of squish");
308 -- Insert vio library
310 -- Override standard functions to use vio if opening a resource
311 f
:write[[local io_open, io_lines = io.open, io.lines; function io.open(fn, mode)
312 if not resources[fn] then
313 return io_open(fn, mode);
315 return vio.open(resources[fn]);
317 function io.lines(fn)
318 if not resources[fn] then
321 return vio.open(resources[fn]):lines()
323 local _dofile = dofile;
325 if not resources[fn] then
328 return assert(loadstring(resources[fn]))();
330 local _loadfile = loadfile;
331 function loadfile(fn)
332 if not resources[fn] then
333 return _loadfile(fn);
335 return loadstring(resources[fn], "@"..fn);
339 f
:write[[function require_resource(name) return resources[name] or error("resource '"..tostring(name).."' not found"); end end ]]
342 print_debug("Finalising...")
343 for _
, fn
in pairs(main_files
) do
344 local fin
, err
= io
.open(base_path
..fn
);
346 print_err("Failed to open "..fn
..": "..err
);
349 f
:write((fin
:read("*a"):gsub("^#.-\n", "")));