Skip to content

Instantly share code, notes, and snippets.

@profburke
Created November 19, 2017 18:12
Show Gist options
  • Save profburke/535638390532fab567706d099fa03778 to your computer and use it in GitHub Desktop.
Save profburke/535638390532fab567706d099fa03778 to your computer and use it in GitHub Desktop.
Wrapper around swift command that makes it easier to import your own modules
#!/usr/bin/env lua
--
-- @author Matthew M. Burke <[email protected]>
-- @date 2017-11-17
-- @version 1.0
-- @project Wrapper for Swift command
-- @file swiftr
--
-- Want to use your own Swift modules in the Swift REPL?
--
-- To do so, there are three steps:
-- 1) the module must provide a dynamic library (see below),
-- 2) add the appropriate -I, -L, and -l flags when you invoke Swift
-- 3) `import` the module in your Swift code.
--
-- Swiftr is a wrapper that makes this easier by automatically constructing
-- the command line flags for `swfit`. If you only want a subset of your
-- modules available, you can specify them on the `swiftr` command line. Additionally,
-- you can use `swiftr` in "interpreter" mode by specifying a .swift file on the command line.
--
-- In order to use `swiftr` you need to first create a directory, /usr/local/share/spm,
-- and copy your modules (both the .swiftmodule and .dylib/.so) to this directory.
--
-- EXAMPLES:
--
-- Suppose you have created modules Foo, Bar, Snafu, and Possum, and you have copied
-- these four modules to /usr/local/share/spm then
--
-- > swiftr
--
-- in the REPL you can now import any or all four of these modules
--
-- > swiftr -mFoo
--
-- in the REPL you can `import Foo`. You cannot import any of the other three.
--
-- > swiftr -mSnafu -mPossum mycode.swift
--
-- "interprets" mycode.swift. In mycode.swift you can import Snafu and Possum
-- (you cannot import Foo and Bar)
--
-- BUILDING A DYNAMIC LIBRARY:
--
-- As mentioned above, in order to import a module into the REPL, you must build a dynamic library
-- when you build your module. Suppose your module is named Foo, then insert the following into
-- your manifest's lists of products:
--
-- .product(
-- name: "Foo",
-- type: .dynamic,
-- targets: ["Foo"]),
--
--
-- TODO: implement --help
-- TODO: override module directory with a command line flag
-- TODO: specify multiple module directories (1 possibility: specify a "top of tree" directory
-- and find all Swift modules in that tree)
-- TODO: pass-through args; like swift build, swift package, etc
debug = false
swiftExecutable = "/usr/bin/swift"
REPLMODS = os.getenv 'REPLMODS' or "/usr/local/share/spm"
pathFlags = string.format("-I %s -L %s", REPLMODS, REPLMODS)
function programName(fullPath)
local f = io.popen(string.format('basename %s', fullPath))
local result = f:read()
f:close()
return result
end
progname = programName(arg[0])
function errorExit(msg)
print(string.format('%s: %s', progname, msg))
os.exit(1)
end
function exists(arg, dir, checkDir)
local fullPath
if checkDir then
fullPath = dir
else
local filename = string.format('lib%s.dylib', arg)
fullPath = string.format("%s/%s", dir, filename)
end
local fh = io.open(fullPath)
if fh then
fh:close()
end
return fh
end
function beginsWith(option, prefix)
local first, last = string.find(option, prefix)
return first == 1
end
function endsWith(option, suffix)
local first, last = string.find(option, suffix)
return last == string.len(option)
end
function isModule(option)
return beginsWith(option, '-m')
end
function allModules(moduleDirectory)
local modules = {}
local f = io.popen(string.format('ls -1 %s', moduleDirectory))
if f then
for file in f:lines() do
if endsWith(file, '.swiftmodule') then
local name = string.gsub(file, '.swiftmodule', '')
table.insert(modules, name)
end
end
f:close()
end
return modules
end
function process(args, moduleDirectory)
local loadAll = true
local modules = {}
local script = nil
for i = 1, #args do
local option = args[i]
if option == '--debug' then
debug = true
elseif isModule(option) then
option = string.sub(option, 3)
if exists(option, moduleDirectory) then
table.insert(modules, option)
loadAll = false
else
errorExit(string.format("Could not find module '%s'", option))
end
else
if script then
errorExit('Cannot feed more than one Swift file to the interpreter')
else
if not endsWith(option, '.swift') then
errorExit(string.format("Script argument '%s' must end in '.swift'", option))
end
script = option
end
end
end
local remainingFlags = ""
if loadAll then
modules = allModules(moduleDirectory)
end
for _, module in pairs(modules) do
remainingFlags = remainingFlags .. string.format(" -l%s ", module)
end
remainingFlags = remainingFlags .. (script or '')
return remainingFlags
end
-- -----------------------------------------------------------------------------
--
-- Now, do the work...
--
-- -----------------------------------------------------------------------------
if not exists('', REPLMODS, true) then
errorExit(string.format("Module directory '%s' does not exist", REPLMODS))
end
command = string.format("%s %s %s", swiftExecutable, pathFlags, process(arg, REPLMODS))
if debug then
print(command)
else
os.execute(command)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment