Skip to content

Instantly share code, notes, and snippets.

@davidm
Created December 2, 2011 07:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidm/1422205 to your computer and use it in GitHub Desktop.
Save davidm/1422205 to your computer and use it in GitHub Desktop.
Lua module 'sourcepack' - Extract Lua files from other Lua files.
#!/usr/bin/env lua
--[[ FILE README.txt
LUA MODULE
sourceunpack v$(_VERSION) - Extract Lua files containing other Lua files.
SYNOPSIS
git clone git://gist.github.com/1325400.git file_slurp
cd file_slurp
../sourceunpack.lua file_slurp.lua
cd out
luarocks make
DESCRIPTION
This provides a mechanism where a single Lua file can contain
other files embedded in its comments. These files can be extracted
via this tool.
The sourceunpack.lua module can itself be extracted:
./sourceunpack.lua sourceunpack.lua
DOWNLOAD/INSTALL
Download <https://raw.github.com/gist/1422205/sourceunpack.lua>.
Alternately, if using git:
git clone git://gist.github.com/1422205.git lua-sourceunpack
Optionally unpack and install in LuaRocks:
cd lua-sourceunpack && ./sourceunpack.lua sourceunpack.lua
cd out && luarocks make
COPYRIGHT
(c) 2011-2012 David Manura. Licensed under the same terms as Lua 5.1 (MIT license).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--]]---------------------------------------------------------------------
-- sourceunpack.lua
-- (c) 2011-2012 David Manura. Licensed under the same terms as Lua 5.1 (MIT license).
-- https://gist.github.com/1325400
package.preload['file_slurp'] = function()
-- file_slurp.lua
-- (c) 2011-2012 David Manura. Licensed under the same terms as Lua 5.1 (MIT license).
local FS = {_TYPE='module', _NAME='file_slurp', _VERSION='0.4.20111129'}
local function check_options(options)
if not options then return {} end
local bad = options:match'[^tTsap]'
if bad then error('ASSERT: invalid option '..bad, 3) end
local t = {}; for v in options:gmatch'.' do t[v] = true end
if t.T and t.t then error('ASSERT: options t and T mutually exclusive', 3) end
return t
end
local function fail(tops, err, code, filename)
err = err..' [code '..code..']'
err = err..' [filename '..filename..']' -- maybe make option
if tops.s then return nil, err else error(err, 3) end
end
function FS.readfile(filename, options)
local tops = check_options(options)
local open = tops.p and io.popen or io.open
local data, ok
local fh, err, code = open(filename, 'r'..((tops.t or tops.p) and '' or 'b'))
if fh then
data, err, code = fh:read'*a'
if data then ok, err, code = fh:close() else fh:close() end
end
if not ok then return fail(tops, err, code, filename) end
if tops.T then data = data:gsub('\r', '') end
return data
end
function FS.writefile(filename, data, options)
local tops = check_options(options)
local open = tops.p and io.popen or io.open
local ok
local fh, err, code = open(filename,
(tops.a and 'a' or 'w') .. ((tops.t or tops.p) and '' or 'b'))
if fh then
ok, err, code = fh:write(data)
if ok then ok, err, code = fh:close() else fh:close() end
end
if not ok then return fail(tops, err, code, filename) end
return data
end
function FS.testfile(filename, options)
local fh, err, code = io.open(filename, options or 'r')
if fh then fh:close(); return true
else return false, err .. ' [code '..code..']' end
end
return FS
end
-------------------------------------------------
local M = {_TYPE='module', _NAME='sourceunpack', _VERSION='0.2.20120121'}
local FS = require 'file_slurp'
local function expand_rockspec_in(filename, outdir, codefilename, code)
local function getvar(V, name) -- lazy eval and cache
local val
if name == '_VERSION' then
for ver in code:gmatch[=[_VERSION *= *['"](.-)['"]]=] do -- last if multiple
val = ver
end
elseif name == 'GITID' and FS.readfile('.git/HEAD', 's') then
val = (FS.readfile('git rev-parse HEAD:'..codefilename, 'ps') or ''):match('^([0-9a-f]+)%s*$') or '$(GITID)'
elseif name == 'MD5' then
val = (FS.readfile('md5sum '..codefilename, 'ps') or ''):match('^([0-9a-f]+)')
else
return -- ignore
end
V[name] = val
io.write(name, '=', val, '\n')
return val
end
local V = setmetatable({}, {__index = getvar})
local function subst(s) return s:gsub('%$%((.-)%)', V) end
local text = FS.readfile(filename, 'T')
local packagename = assert(text:match"package *= *'([^']+)'", 'package attribute not found in rockspec.in')
local versionname = subst(assert(text:match"version *= *'([^']+)'", 'version attribute not found in rockspec.in'))
local outfilename = outdir..'/'..packagename..'-'..versionname..'.rockspec'
text = subst(text)
FS.writefile(outfilename, text)
return outfilename
end
function M.unpack(codefilename, outdir)
outdir = outdir or 'out'
local code = FS.readfile(codefilename, 'T')
local filenames = {}
code = code:gsub('%-*\n*%-%-%[(=*)%[%s*FILE%s+(%S+).-\n\n?(.-)%-%-%]%1%]%-*%s*',
function(_, filename, text)
filenames[#filenames+1] = filename
if not FS.writefile(outdir..'/.test', '', 's') then os.execute('mkdir '..outdir) end
os.remove(outdir..'/.test')
print('writing '..outdir..'/' .. filename)
FS.writefile(outdir..'/' .. filename, text)
if filename == 'rockspec.in' then
local outfilename = expand_rockspec_in(outdir..'/rockspec.in', outdir, codefilename, code)
print('writing '..outfilename)
end
return ''
end)
filenames[#filenames+1] = codefilename
filenames[#filenames+1] = 'MANIFEST'
print('writing '..outdir..'/MANIFEST')
FS.writefile(outdir..'/MANIFEST', table.concat(filenames, '\n')..'\n')
print('writing '..outdir..'/' .. codefilename)
FS.writefile(outdir..'/' .. codefilename, code)
print('testing...')
local arg0 = arg[0]; arg[0] = nil -- clear
dofile(outdir..'/test.lua')
arg[0] = arg0 -- restore
end
if arg[0] and arg[0]:match'sourceunpack%.lua$' then -- called from command line
package.loaded['sourceunpack'] = M
local SU = require 'sourceunpack'
local name = ...
if not name then io.stderr:write('usage: sourceunpack <filename>\n'); os.exit(1) end
SU.unpack(name)
end
return M
--[[ FILE rockspec.in
package = 'lua-sourceunpack'
version = '$(_VERSION)-1'
source = {
url = 'https://raw.github.com/gist/1422205/$(GITID)/sourceunpack.lua',
--url = 'https://raw.github.com/gist/1422205/sourceunpack.lua', -- latest raw
--url = 'https://gist.github.com/gists/1422205/download', -- latest archive
md5 = '$(MD5)'
}
description = {
summary = 'Extract Lua files containing other Lua files.',
detailed =
'Extract Lua files containing other Lua files.',
license = 'MIT/X11',
homepage = 'https://gist.github.com/1422205',
maintainer = 'David Manura'
}
dependencies = {}
build = {
type = 'builtin',
modules = {
['sourceunpack'] = 'sourceunpack.lua'
}
}
--]]---------------------------------------------------------------------
--[[ FILE test.lua
-- test.lua - test suite for sourceunpack.lua
local SU = require 'sourceunpack'
print 'OK'
--]]
--[[ FILE CHANGES.txt
0.2.20120121
New rockspec.in file indirectly generating .rockspec.
0.1.20111203
Initial public release
--]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment