• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env lua
2
3local short_opts = { v = "verbose", vv = "very_verbose", o = "output", q = "quiet", qq = "very_quiet", g = "debug" }
4local opts = { use_http = false };
5
6for _, opt in ipairs(arg) do
7	if opt:match("^%-") then
8		local name = opt:match("^%-%-?([^%s=]+)()")
9		name = (short_opts[name] or name):gsub("%-+", "_");
10		if name:match("^no_") then
11			name = name:sub(4, -1);
12			opts[name] = false;
13		else
14			opts[name] = opt:match("=(.*)$") or true;
15		end
16	else
17		base_path = opt;
18	end
19end
20
21if opts.very_verbose then opts.verbose = true; end
22if opts.very_quiet then opts.quiet = true; end
23
24local noprint = function () end
25local print_err, print_info, print_verbose, print_debug = noprint, noprint, noprint, noprint;
26
27if not opts.very_quiet then print_err = print; end
28if not opts.quiet then print_info = print; end
29if opts.verbose or opts.very_verbose then print_verbose = print; end
30if opts.very_verbose then print_debug = print; end
31
32print = print_verbose;
33
34local modules, main_files, resources = {}, {}, {};
35
36--  Functions to be called from squishy file  --
37
38function Module(name)
39	if modules[name] then
40		print_verbose("Ignoring duplicate module definition for "..name);
41		return function () end
42	end
43	local i = #modules+1;
44	modules[i] = { name = name, url = ___fetch_url };
45	modules[name] = modules[i];
46	return function (path)
47		modules[i].path = path;
48	end
49end
50
51function Resource(name, path)
52	local i = #resources+1;
53	resources[i] = { name = name, path = path or name };
54	return function (path)
55		resources[i].path = path;
56	end
57end
58
59function AutoFetchURL(url)
60	___fetch_url = url;
61end
62
63function Main(fn)
64	table.insert(main_files, fn);
65end
66
67function Output(fn)
68	if opts.output == nil then
69		out_fn = fn;
70	end
71end
72
73function Option(name)
74	name = name:gsub("%-", "_");
75	if opts[name] == nil then
76		opts[name] = true;
77		return function (value)
78			opts[name] = value;
79		end
80	else
81		return function () end;
82	end
83end
84
85function GetOption(name)
86	return opts[name:gsub('%-', '_')];
87end
88
89function Message(message)
90	if not opts.quiet then
91		print_info(message);
92	end
93end
94
95function Error(message)
96	if not opts.very_quiet then
97		print_err(message);
98	end
99end
100
101function Exit()
102	os.exit(1);
103end
104-- -- -- -- -- -- -- --- -- -- -- -- -- -- -- --
105
106base_path = (base_path or "."):gsub("/$", "").."/"
107squishy_file = base_path .. "squishy";
108out_fn = opts.output;
109
110local ok, err = pcall(dofile, squishy_file);
111
112if not ok then
113	print_err("Couldn't read squishy file: "..err);
114	os.exit(1);
115end
116
117if not out_fn then
118	print_err("No output file specified by user or squishy file");
119	os.exit(1);
120elseif #main_files == 0 and #modules == 0 and #resources == 0 then
121	print_err("No files, modules or resources. Not going to generate an empty file.");
122	os.exit(1);
123end
124
125local fetch = {};
126function fetch.filesystem(path)
127	local f, err = io.open(path);
128	if not f then return false, err; end
129
130	local data = f:read("*a");
131	f:close();
132
133	return data;
134end
135
136if opts.use_http then
137	function fetch.http(url)
138		local http = require "socket.http";
139
140		local body, status = http.request(url);
141		if status == 200 then
142			return body;
143		end
144		return false, "HTTP status code: "..tostring(status);
145	end
146else
147	function fetch.http(url)
148		return false, "Module not found. Re-squish with --use-http option to fetch it from "..url;
149	end
150end
151
152print_info("Writing "..out_fn.."...");
153local f, err = io.open(out_fn, "w+");
154if not f then
155	print_err("Couldn't open output file: "..tostring(err));
156	os.exit(1);
157end
158
159if opts.executable then
160	if opts.executable == true then
161		f:write("#!/usr/bin/env lua\n");
162	else
163		f:write(opts.executable, "\n");
164	end
165end
166
167if opts.debug then
168	f:write(require_resource("squish.debug"));
169end
170
171print_verbose("Resolving modules...");
172do
173	local LUA_DIRSEP = package.config:sub(1,1);
174	local LUA_PATH_MARK = package.config:sub(5,5);
175
176	local package_path = package.path:gsub("[^;]+", function (path)
177			if not path:match("^%"..LUA_DIRSEP) then
178				return base_path..path;
179			end
180		end):gsub("/%./", "/");
181	local package_cpath = package.cpath:gsub("[^;]+", function (path)
182			if not path:match("^%"..LUA_DIRSEP) then
183				return base_path..path;
184			end
185		end):gsub("/%./", "/");
186
187	function resolve_module(name, path)
188	        name = name:gsub("%.", LUA_DIRSEP);
189	        for c in path:gmatch("[^;]+") do
190	                c = c:gsub("%"..LUA_PATH_MARK, name);
191	                print_debug("Looking for "..c)
192	                local f = io.open(c);
193	                if f then
194				print_debug("Found!");
195	                        f:close();
196                        return c;
197			end
198		end
199		return nil; -- not found
200	end
201
202	for i, module in ipairs(modules) do
203		if not module.path then
204			module.path = resolve_module(module.name, package_path);
205			if not module.path then
206				print_err("Couldn't resolve module: "..module.name);
207			else
208				-- Strip base_path from resolved path
209				module.path = module.path:gsub("^"..base_path:gsub("%p", "%%%1"), "");
210			end
211		end
212	end
213end
214
215
216print_verbose("Packing modules...");
217for _, module in ipairs(modules) do
218	local modulename, path = module.name, module.path;
219	if module.path:sub(1,1) ~= "/" then
220		path = base_path..module.path;
221	end
222	print_debug("Packing "..modulename.." ("..path..")...");
223	local data, err = fetch.filesystem(path);
224	if (not data) and module.url then
225		print_debug("Fetching: ".. module.url:gsub("%?", module.path))
226		data, err = fetch.http(module.url:gsub("%?", module.path));
227	end
228	if data then
229		f:write("package.preload['", modulename, "'] = (function (...)\n");
230		f:write(data);
231		f:write(" end)\n");
232		if opts.debug then
233			f:write(string.format("package.preload[%q] = ___adjust_chunk(package.preload[%q], %q);\n\n",
234				modulename, modulename, "@"..path));
235		end
236	else
237		print_err("Couldn't pack module '"..modulename.."': "..(err or "unknown error... path to module file correct?"));
238		os.exit(1);
239	end
240end
241
242if #resources > 0 then
243	print_verbose("Packing resources...")
244	f:write("do local resources = {};\n");
245	for _, resource in ipairs(resources) do
246		local name, path = resource.name, resource.path;
247		local res_file, err = io.open(base_path..path, "rb");
248		if not res_file then
249			print_err("Couldn't load resource: "..tostring(err));
250			os.exit(1);
251		end
252		local data = res_file:read("*a");
253		local maxequals = 0;
254		data:gsub("(=+)", function (equals_string) maxequals = math.max(maxequals, #equals_string); end);
255
256		f:write(("resources[%q] = %q"):format(name, data));
257--[[		f:write(("resources[%q] = ["):format(name), string.rep("=", maxequals+1), "[");
258		f:write(data);
259		f:write("]", string.rep("=", maxequals+1), "];"); ]]
260	end
261	if opts.virtual_io then
262		local vio = require_resource("vio");
263		if not vio then
264			print_err("Virtual IO requested but is not enabled in this build of squish");
265		else
266			-- Insert vio library
267			f:write(vio, "\n")
268			-- Override standard functions to use vio if opening a resource
269			f:write[[local io_open, io_lines = io.open, io.lines; function io.open(fn, mode)
270					if not resources[fn] then
271						return io_open(fn, mode);
272					else
273						return vio.open(resources[fn]);
274				end end
275				function io.lines(fn)
276					if not resources[fn] then
277						return io_lines(fn);
278					else
279						return vio.open(resources[fn]):lines()
280				end end
281				local _dofile = dofile;
282				function dofile(fn)
283					if not resources[fn] then
284						return _dofile(fn);
285					else
286						return assert(loadstring(resources[fn]))();
287				end end
288				local _loadfile = loadfile;
289				function loadfile(fn)
290					if not resources[fn] then
291						return _loadfile(fn);
292					else
293						return loadstring(resources[fn], "@"..fn);
294				end end ]]
295		end
296	end
297	f:write[[function require_resource(name) return resources[name] or error("resource '"..tostring(name).."' not found"); end end ]]
298end
299
300print_debug("Finalising...")
301for _, fn in pairs(main_files) do
302	local fin, err = io.open(base_path..fn);
303	if not fin then
304		print_err("Failed to open "..fn..": "..err);
305		os.exit(1);
306	else
307		f:write((fin:read("*a"):gsub("^#.-\n", "")));
308		fin:close();
309	end
310end
311
312f:close();
313
314print_info("OK!");
315