Current section
Files
Jump to
Current section
Files
src/gleescript.gleam
// TODO: Do not include dev deps
import filepath
import gleam/dynamic.{type Dynamic}
import gleam/erlang/charlist.{type Charlist}
import gleam/io
import gleam/list
import gleam/result
import gleam/string
import simplifile
import snag
import tom
const build = "build/dev/erlang"
pub fn main() {
case run() {
Ok(_) -> Nil
Error(error) -> {
io.println_error(snag.pretty_print(error))
shutdown(1)
}
}
}
type Config {
Config(package_name: String)
}
pub fn run() -> snag.Result(Nil) {
use config <- result.try(
load_config()
|> snag.context("Failed to load configuration"),
)
use files <- result.try(
locate_beam_files()
|> snag.context("Failed to scan packages in build directory"),
)
let emu_args =
"-escript main gleescript_main_shim -env GLEESCRIPT_MAIN "
<> config.package_name
use files <- result.try(
list.try_map(files, fn(f) {
let name = charlist.from_string(filepath.base_name(f))
use content <- result.map(
simplifile.read_bits(f)
|> snag_inspect_error
|> snag.context("Failed to read " <> f),
)
#(name, content)
}),
)
let result =
erlang_escript_create(charlist.from_string(config.package_name), [
Shebang,
Comment(charlist.from_string("")),
EmuArgs(charlist.from_string(emu_args)),
Archive(files, []),
])
let assert Ok(result) = dynamic.result(Ok, Ok)(result)
use _ <- result.try(
result
|> snag_inspect_error
|> snag.context("Failed to build escript"),
)
let name = config.package_name
use _ <- result.try(
simplifile.set_permissions_octal(name, 0o777)
|> snag_inspect_error
|> snag.context("Failed to make ./" <> name <> " executable"),
)
io.println(" \u{001b}[95mGenerated\u{001b}[0m ./" <> config.package_name)
Ok(Nil)
}
fn locate_beam_files() -> snag.Result(List(String)) {
use files <- result.try(
simplifile.get_files(build)
|> snag_inspect_error
|> snag.context("Failed to read build directory"),
)
files
|> list.filter(
fn(
f,
// The @@ modules belong to the Gleam build tool
) {
!string.contains(f, "@@")
&& { string.ends_with(f, ".beam") || string.ends_with(f, ".app") }
},
)
|> Ok
}
fn load_config() -> snag.Result(Config) {
use text <- result.try(
simplifile.read("gleam.toml")
|> snag_inspect_error
|> snag.context("Failed to read gleam.toml"),
)
use config <- result.try(
tom.parse(text)
|> snag_inspect_error
|> snag.context("Failed to parse gleam.toml"),
)
use package_name <- result.try(
tom.get_string(config, ["name"])
|> snag_inspect_error
|> snag.context("Failed to get package name from gleam.toml"),
)
Ok(Config(package_name: package_name))
}
fn snag_inspect_error(result: Result(t, e)) -> snag.Result(t) {
case result {
Ok(value) -> Ok(value)
Error(error) -> snag.error(string.inspect(error))
}
}
type ErlangOption {
Shebang
Comment(Charlist)
EmuArgs(Charlist)
Archive(files: List(#(Charlist, BitArray)), zip_options: List(Never))
}
type Never
@external(erlang, "escript", "create")
fn erlang_escript_create(
file: Charlist,
sections: List(ErlangOption),
) -> Dynamic
@external(erlang, "init", "stop")
fn shutdown(status: Int) -> a