Created
November 19, 2017 18:12
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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