Created
April 6, 2018 09:36
-
-
Save christineponyl/4e93446ff4dc4283e98b0a08db1a78ae to your computer and use it in GitHub Desktop.
Shared via Pony Playground
This file contains hidden or 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
""" | |
This is ponyrun - a pony program to compile pony source code files. | |
It will not properly run in the playground as it expects command line arguments | |
and will try to use ponyc and stuff. Checkout the repo to clone and run it on your machine: | |
https://github.com/mfelsche/ponyrun | |
It can be used to execute pony source files directly on linux using binfmt_misc. | |
Checkout: https://github.com/mfelsche/ponyrun#usage-with-binfmt_misc | |
Or use a shebang pointing to your ponyrun binary (beware that a pony file with a shebang will not compile | |
using plain ponyc). | |
Besides the aforementioned awesomeness, this projects shows the usage of some useful | |
stdlib packages like cli, process and signals. | |
""" | |
use "files" | |
use "cli" | |
use "process" | |
use "collections" | |
use "signals" | |
use "debug" | |
primitive FileCopy | |
fun copy(from: FilePath, to_file: FilePath): Bool => | |
match OpenFile(from) | |
| let source: File => | |
match CreateFile(to_file) | |
| let target: File => | |
var gimme_more = true | |
while gimme_more and (source.errno() is FileOK) and (target.errno() is FileOK) do | |
let source_line = source.read(1024) | |
try | |
if (source_line(0)? == '#') and (source_line(1)? == '!') then | |
// skip until newline | |
let shebang_newline = source_line.find('\n')? | |
source_line.trim_in_place(shebang_newline + 1, source_line.size()) | |
end | |
end | |
gimme_more = target.write(consume source_line) | |
end | |
true | |
else | |
Debug("could not open " + to_file.path + " for writing") | |
false | |
end | |
else | |
Debug("could not open " + from.path + " for reading") | |
false | |
end | |
primitive Environ | |
fun apply(env: Env): Map[String val, String val] val => | |
let map = recover trn Map[String val, String val] end | |
for kv in env.vars().values() do | |
let splitted = kv.split_by("=") | |
try | |
map(splitted(0)?) = splitted(1)? | |
end | |
end | |
consume map | |
primitive Ponyc | |
fun find(env: Env): FilePath ? => | |
let envvars = Environ(env) | |
if envvars.contains("PONYC") then | |
let path = FilePath(env.root as AmbientAuth, envvars("PONYC")?)? | |
if path.exists() then | |
return path | |
end | |
elseif envvars.contains("PATH") then | |
for path_entry in Path.split_list(envvars("PATH")?).values() do | |
let ponyc_candidate = FilePath(env.root as AmbientAuth, Path.join(path_entry, "ponyc"))? | |
Debug("trying " + ponyc_candidate.path) | |
if ponyc_candidate.exists() then | |
return ponyc_candidate | |
end | |
end | |
end | |
Debug("could not find ponyc") | |
error | |
actor Main | |
new create(env: Env) => | |
try | |
let auth = env.root as AmbientAuth | |
let cs = | |
try | |
CommandSpec.leaf( | |
"ponyrun", | |
"Shebang your pony", | |
[], | |
[ | |
ArgSpec.string("file", "pony source file") | |
])? .> add_help()? | |
else | |
Debug("could not create command spec") | |
env.exitcode(1) | |
return | |
end | |
let cmd = | |
match CommandParser(cs).parse(env.args, env.vars()) | |
| let c: Command => c | |
| let ch: CommandHelp => | |
ch.print_help(env.out) | |
env.exitcode(0) | |
return | |
| let se: SyntaxError => | |
env.err.print(se.string()) | |
env.exitcode(1) | |
return | |
end | |
let file_arg = cmd.arg("file").string() | |
let source_file = | |
try | |
FilePath(auth, file_arg)? | |
else | |
env.err.print("unable to access " + file_arg) | |
env.exitcode(1) | |
return | |
end | |
let ponyc = | |
try | |
Ponyc.find(env)? | |
else | |
env.err.print("could not find ponyc on the PATH or with PONYC") | |
env.exitcode(1) | |
return | |
end | |
let envvars = Environ(env) | |
let tmp_dir_base = | |
FilePath( | |
auth, | |
try | |
envvars("TMPDIR")? | |
else | |
"/tmp" | |
end)? | |
Debug("tmp dir is " + tmp_dir_base.path) | |
// create temp directory | |
let tmp_dir = | |
try | |
FilePath.mkdtemp( | |
tmp_dir_base, | |
"ponyrun_" + Path.base(file_arg, false))? | |
else | |
env.err.print("could not create tmp dir") | |
env.exitcode(1) | |
return | |
end | |
Debug("created " + tmp_dir.path) | |
let ctrlc_handler = SignalHandler( | |
object iso is SignalNotify | |
fun ref apply(count: U32): Bool => | |
if count > 0 then | |
tmp_dir.remove() | |
false | |
end | |
true | |
end, | |
Sig.int()) | |
let tmp_pony_dir = tmp_dir.join("src")? | |
if not tmp_pony_dir.mkdir() then | |
env.err.print("could not create tmp pony dir: " + tmp_pony_dir.path) | |
tmp_dir.remove() | |
env.exitcode(1) | |
return | |
end | |
// copy source_file there | |
let compile_source_path = tmp_pony_dir.join("source.pony")? | |
if not FileCopy.copy(source_file, compile_source_path) then | |
env.err.print("could not copy source file to " + compile_source_path.path) | |
tmp_dir.remove() | |
env.exitcode(1) | |
return | |
end | |
let resulting_binary_path = tmp_dir.join(Path.base(file_arg, false))? | |
let notifier = | |
object iso is ProcessNotify | |
// TODO: - add possibility to hand through args to the pony program | |
// - support writing to stdin | |
fun ref stdout(process: ProcessMonitor ref, data: Array[U8] iso) => | |
Debug(consume data) | |
fun ref stderr(process: ProcessMonitor ref, data: Array[U8] iso) => | |
Debug(consume data) | |
fun ref failed(process: ProcessMonitor ref, err: ProcessError) => | |
env.err.print("ponyc failed") | |
fun ref dispose(process: ProcessMonitor ref, child_exit_code: I32) => | |
Debug("ponyc finished with " + child_exit_code.string()) | |
if not resulting_binary_path.exists() then | |
env.err.print("ponyc failed compiling the binary") | |
tmp_dir.remove() | |
env.exitcode(1) | |
return | |
end | |
Debug("starting " + resulting_binary_path.path) | |
// run the resulting binary | |
let ppm = ProcessMonitor( | |
auth, | |
auth, | |
object iso is ProcessNotify | |
fun ref stdout(process: ProcessMonitor ref, data: Array[U8] iso) => | |
Debug("received something") | |
env.out.write(consume data) | |
fun ref stderr(process: ProcessMonitor ref, data: Array[U8] iso) => | |
env.err.write(consume data) | |
fun ref failed(process: ProcessMonitor ref, err: ProcessError) => | |
env.err.print(resulting_binary_path.path + " failed") | |
fun ref dispose(process: ProcessMonitor ref, child_exit_code: I32) => | |
env.exitcode(child_exit_code) | |
tmp_dir.remove() | |
Debug("DONE") | |
end, | |
resulting_binary_path, | |
recover | |
Array[String](1).>push(Path.base(resulting_binary_path.path)) end, | |
env.vars()) | |
ppm.done_writing() | |
end | |
let args: Array[String] iso = recover Array[String](4) end | |
args | |
.>push("ponyc") | |
.>push("--output=" + tmp_dir.path) | |
.>push("--bin-name=" + Path.base(file_arg, false)) | |
.>push(tmp_pony_dir.path) | |
let pm = ProcessMonitor( | |
auth, | |
auth, | |
consume notifier, | |
ponyc, | |
consume args, | |
env.vars()) | |
pm.done_writing() | |
else | |
env.err.print("some unspecific error happened!") | |
env.exitcode(1) | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment