mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Merge pull request #3627 from rtfeldman/glue
Replace separate bindgen CLI with `roc glue`
This commit is contained in:
commit
089fae2d56
59 changed files with 215 additions and 541 deletions
62
Cargo.lock
generated
62
Cargo.lock
generated
|
@ -3314,37 +3314,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc-bindgen"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"clap 3.2.11",
|
||||
"cli_utils",
|
||||
"ctor",
|
||||
"dircpy",
|
||||
"fnv",
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_reporting",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_test_utils",
|
||||
"roc_types",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_alias_analysis"
|
||||
version = "0.1.0"
|
||||
|
@ -3475,6 +3444,7 @@ dependencies = [
|
|||
"roc_error_macros",
|
||||
"roc_fmt",
|
||||
"roc_gen_llvm",
|
||||
"roc_glue",
|
||||
"roc_linker",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
|
@ -3737,6 +3707,36 @@ dependencies = [
|
|||
"roc_target",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_glue"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"clap 3.2.11",
|
||||
"cli_utils",
|
||||
"dircpy",
|
||||
"fnv",
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_reporting",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_test_utils",
|
||||
"roc_types",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_highlight"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -33,7 +33,7 @@ members = [
|
|||
"crates/vendor/inkwell",
|
||||
"crates/vendor/pathfinding",
|
||||
"crates/vendor/pretty",
|
||||
"crates/bindgen",
|
||||
"crates/glue",
|
||||
"crates/editor",
|
||||
"crates/ast",
|
||||
"crates/cli",
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
static TEMPLATE: &[u8] = include_bytes!("../templates/template.c");
|
||||
|
||||
pub fn write_template(writer: &mut impl io::Write) -> io::Result<()> {
|
||||
writer.write_all(TEMPLATE)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// pub fn write_bindings(_writer: &mut impl io::Write) -> io::Result<()> {
|
||||
// extern struct RocStr roc__mainForHost_1_exposed();
|
||||
|
||||
// int main() {
|
||||
// struct RocStr str = roc__mainForHost_1_exposed();
|
||||
|
||||
// // Determine str_len and the str_bytes pointer,
|
||||
// // taking into account the small string optimization.
|
||||
// size_t str_len = roc_str_len(str);
|
||||
// char* str_bytes;
|
||||
|
||||
// if (is_small_str(str)) {
|
||||
// str_bytes = (char*)&str;
|
||||
// } else {
|
||||
// str_bytes = str.bytes;
|
||||
// }
|
||||
|
||||
// // Write to stdout
|
||||
// if (write(1, str_bytes, str_len) >= 0) {
|
||||
// // Writing succeeded!
|
||||
// return 0;
|
||||
// } else {
|
||||
// printf("Error writing to stdout: %s\n", strerror(errno));
|
||||
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
|
@ -1,18 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
static TEMPLATE: &[u8] = include_bytes!("../templates/template.zig");
|
||||
|
||||
pub fn write_template(writer: &mut impl io::Write) -> io::Result<()> {
|
||||
writer.write_all(TEMPLATE)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_bindings(_writer: &mut impl io::Write) -> io::Result<()> {
|
||||
// extern "C" {
|
||||
// #[link_name = "roc__mainForHost_1_exposed"]
|
||||
// fn roc_main() -> RocStr;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
use clap::Parser;
|
||||
use roc_bindgen::bindgen_rs;
|
||||
use roc_bindgen::load::load_types;
|
||||
use roc_load::Threading;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
/// Printed in error messages if you try to use an unsupported extension.
|
||||
const SUPPORTED_EXTENSIONS: &str = ".c, .rs, .zig, and .json";
|
||||
|
||||
// TODO add an option for --targets so that you can specify
|
||||
// e.g. 64-bit, 32-bit, *and* 16-bit (which can matter for alignment because of pointers)
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(about)]
|
||||
struct Opts {
|
||||
/// The path to the `platform` module .roc file
|
||||
platform_module: PathBuf,
|
||||
|
||||
/// The output file, e.g. `test.rs`
|
||||
dest: PathBuf,
|
||||
}
|
||||
|
||||
enum OutputType {
|
||||
Rust,
|
||||
C,
|
||||
Zig,
|
||||
Json,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let opts = Opts::parse();
|
||||
let input_path = opts.platform_module;
|
||||
let output_path = opts.dest;
|
||||
let output_type = match output_path.extension().and_then(OsStr::to_str) {
|
||||
Some("rs") => OutputType::Rust,
|
||||
Some("c") => OutputType::C,
|
||||
Some("zig") => OutputType::Zig,
|
||||
Some("json") => OutputType::Json,
|
||||
Some(other) => {
|
||||
eprintln!(
|
||||
"Unsupported output file extension: \".{}\" - currently supported extensions are {}",
|
||||
other,
|
||||
SUPPORTED_EXTENSIONS
|
||||
);
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
None => {
|
||||
eprintln!("The output file path needs to have a file extension in order to tell what output format to use. Currently supported extensions are {}", SUPPORTED_EXTENSIONS);
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
match load_types(input_path.clone(), Threading::AllAvailable) {
|
||||
Ok(types_and_targets) => {
|
||||
let mut file = File::create(output_path.clone()).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Unable to create output file {} - {:?}",
|
||||
output_path.display(),
|
||||
err
|
||||
);
|
||||
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
let mut buf;
|
||||
match output_type {
|
||||
OutputType::Rust => {
|
||||
buf = std::str::from_utf8(bindgen_rs::HEADER).unwrap().to_string();
|
||||
let body = bindgen_rs::emit(&types_and_targets);
|
||||
|
||||
buf.push_str(&body);
|
||||
}
|
||||
OutputType::C => todo!("TODO: Generate bindings for C"),
|
||||
OutputType::Zig => todo!("TODO: Generate bindings for Zig"),
|
||||
OutputType::Json => todo!("TODO: Generate bindings for JSON"),
|
||||
};
|
||||
|
||||
file.write_all(buf.as_bytes()).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Unable to write bindings to output file {} - {:?}",
|
||||
output_path.display(),
|
||||
err
|
||||
);
|
||||
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
println!(
|
||||
"🎉 Generated type declarations in:\n\n\t{}",
|
||||
output_path.display()
|
||||
);
|
||||
}
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
eprintln!("Platform module file not found: {}", input_path.display());
|
||||
process::exit(1);
|
||||
}
|
||||
error => {
|
||||
eprintln!(
|
||||
"Error loading platform module file {} - {:?}",
|
||||
input_path.display(),
|
||||
error
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void* roc_alloc(size_t size, unsigned int alignment) { return malloc(size); }
|
||||
|
||||
void* roc_realloc(void* ptr, size_t new_size, size_t old_size, unsigned int alignment) {
|
||||
return realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void roc_dealloc(void* ptr, unsigned int alignment) { free(ptr); }
|
||||
|
||||
void roc_panic(void* ptr, unsigned int alignment) {
|
||||
char* msg = (char*)ptr;
|
||||
fprintf(stderr,
|
||||
"Application crashed with message\n\n %s\n\nShutting down\n", msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void* roc_memcpy(void* dest, const void* src, size_t n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// roc_std
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct RocStr {
|
||||
char* bytes;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
bool is_small_str(struct RocStr str) { return ((ssize_t)str.len) < 0; }
|
||||
|
||||
// Determine the length of the string, taking into
|
||||
// account the small string optimization
|
||||
size_t roc_str_len(struct RocStr str) {
|
||||
char* bytes = (char*)&str;
|
||||
char last_byte = bytes[sizeof(str) - 1];
|
||||
char last_byte_xored = last_byte ^ 0b10000000;
|
||||
size_t small_len = (size_t)(last_byte_xored);
|
||||
size_t big_len = str.len;
|
||||
|
||||
// Avoid branch misprediction costs by always
|
||||
// determining both small_len and big_len,
|
||||
// so this compiles to a cmov instruction.
|
||||
if (is_small_str(str)) {
|
||||
return small_len;
|
||||
} else {
|
||||
return big_len;
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
// TODO don't have these depend on the libc crate; instead, use default
|
||||
// allocator, built-in memset, etc.
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TODO: rust_main should be removed once we use surgical linking everywhere.
|
||||
// It's just a workaround to get cargo to build an object file the way
|
||||
// the non-surgical linker needs it to. The surgical linker works on
|
||||
// executables, not object files, so this workaround is not needed there.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
use roc_std::RocStr;
|
||||
|
||||
unsafe {
|
||||
let roc_str = roc_main();
|
||||
|
||||
let len = roc_str.len();
|
||||
let str_bytes = roc_str.as_bytes().as_ptr() as *const libc::c_void;
|
||||
|
||||
if libc::write(1, str_bytes, len) < 0 {
|
||||
panic!("Writing to stdout failed!");
|
||||
}
|
||||
}
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
const std = @import("std");
|
||||
const str = @import("str");
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (std.builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const Align = 2 * @alignOf(usize);
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
|
||||
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||
extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void;
|
||||
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
|
||||
|
||||
const DEBUG: bool = false;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
if (DEBUG) {
|
||||
var ptr = malloc(size);
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
|
||||
return ptr;
|
||||
} else {
|
||||
return malloc(size);
|
||||
}
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
if (DEBUG) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
|
||||
}
|
||||
|
||||
return realloc(@alignCast(Align, @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||
if (DEBUG) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable;
|
||||
}
|
||||
|
||||
free(@alignCast(Align, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
|
||||
_ = tag_id;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
|
||||
return memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
|
||||
return memset(dst, value, size);
|
||||
}
|
|
@ -44,6 +44,7 @@ target-all = [
|
|||
roc_collections = { path = "../compiler/collections" }
|
||||
roc_can = { path = "../compiler/can" }
|
||||
roc_docs = { path = "../docs" }
|
||||
roc_glue = { path = "../glue" }
|
||||
roc_parse = { path = "../compiler/parse" }
|
||||
roc_region = { path = "../compiler/region" }
|
||||
roc_module = { path = "../compiler/module" }
|
||||
|
|
|
@ -43,6 +43,7 @@ pub const CMD_CHECK: &str = "check";
|
|||
pub const CMD_VERSION: &str = "version";
|
||||
pub const CMD_FORMAT: &str = "format";
|
||||
pub const CMD_TEST: &str = "test";
|
||||
pub const CMD_GLUE: &str = "glue";
|
||||
|
||||
pub const FLAG_DEBUG: &str = "debug";
|
||||
pub const FLAG_DEV: &str = "dev";
|
||||
|
@ -59,6 +60,7 @@ pub const FLAG_VALGRIND: &str = "valgrind";
|
|||
pub const FLAG_CHECK: &str = "check";
|
||||
pub const ROC_FILE: &str = "ROC_FILE";
|
||||
pub const ROC_DIR: &str = "ROC_DIR";
|
||||
pub const GLUE_FILE: &str = "GLUE_FILE";
|
||||
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
||||
|
||||
|
@ -171,7 +173,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
)
|
||||
)
|
||||
.subcommand(Command::new(CMD_TEST)
|
||||
.about("Run all top-level `expect`s in a root module and any modules it imports.")
|
||||
.about("Run all top-level `expect`s in a main module and any modules it imports.")
|
||||
.arg(flag_optimize.clone())
|
||||
.arg(flag_max_threads.clone())
|
||||
.arg(flag_opt_size.clone())
|
||||
|
@ -183,7 +185,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.arg(flag_valgrind.clone())
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file for the root module")
|
||||
.help("The .roc file for the main module")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(false)
|
||||
.default_value(DEFAULT_ROC_FILENAME)
|
||||
|
@ -246,6 +248,21 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.allow_invalid_utf8(true)
|
||||
)
|
||||
)
|
||||
.subcommand(Command::new(CMD_GLUE)
|
||||
.about("Generate glue code between a platform's Roc API and its host language.")
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file for the platform module")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(GLUE_FILE)
|
||||
.help("The filename for the generated glue code. Currently, this must be a .rs file because only Rust glue generation is supported so far.")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true)
|
||||
)
|
||||
)
|
||||
.trailing_var_arg(true)
|
||||
.arg(flag_optimize)
|
||||
.arg(flag_max_threads.clone())
|
||||
|
|
|
@ -2,11 +2,12 @@ use roc_build::link::LinkType;
|
|||
use roc_cli::build::check_file;
|
||||
use roc_cli::{
|
||||
build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DOCS,
|
||||
CMD_EDIT, CMD_FORMAT, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK,
|
||||
FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, ROC_FILE,
|
||||
CMD_EDIT, CMD_FORMAT, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES,
|
||||
FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_FILE, ROC_FILE,
|
||||
};
|
||||
use roc_docs::generate_docs_html;
|
||||
use roc_error_macros::user_error;
|
||||
use roc_glue;
|
||||
use roc_load::{LoadingProblem, Threading};
|
||||
use std::fs::{self, FileType};
|
||||
use std::io;
|
||||
|
@ -64,6 +65,18 @@ fn main() -> io::Result<()> {
|
|||
Ok(1)
|
||||
}
|
||||
}
|
||||
Some((CMD_GLUE, matches)) => {
|
||||
let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap());
|
||||
let output_path = Path::new(matches.value_of_os(GLUE_FILE).unwrap());
|
||||
|
||||
if Some("rs") == output_path.extension().and_then(OsStr::to_str) {
|
||||
roc_glue::generate(input_path, output_path)
|
||||
} else {
|
||||
eprintln!("Currently, `roc glue` only supports generating Rust glue files (with the .rs extension). In the future, the plan is to decouple `roc glue` from any particular output format, by having it accept a second .roc file which gets executed as a plugin to generate glue code for any desired language. However, this has not yet been implemented, and for now only .rs is supported.");
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
Some((CMD_BUILD, matches)) => {
|
||||
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default();
|
||||
|
||||
|
|
|
@ -65,22 +65,18 @@ where
|
|||
run_with_stdin(&roc_binary_path, args, stdin_vals)
|
||||
}
|
||||
|
||||
pub fn run_bindgen<I, S>(args: I) -> Out
|
||||
pub fn run_glue<I, S>(args: I) -> Out
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
run_with_stdin(&path_to_bindgen_binary(), args, &[])
|
||||
run_with_stdin(&path_to_roc_binary(), args, &[])
|
||||
}
|
||||
|
||||
pub fn path_to_roc_binary() -> PathBuf {
|
||||
path_to_binary("roc")
|
||||
}
|
||||
|
||||
pub fn path_to_bindgen_binary() -> PathBuf {
|
||||
path_to_binary("roc-bindgen")
|
||||
}
|
||||
|
||||
pub fn path_to_binary(binary_name: &str) -> PathBuf {
|
||||
// Adapted from https://github.com/volta-cli/volta/blob/cefdf7436a15af3ce3a38b8fe53bb0cfdb37d3dd/tests/acceptance/support/sandbox.rs#L680
|
||||
// by the Volta Contributors - license information can be found in
|
||||
|
|
|
@ -3766,7 +3766,7 @@ fn send_header_two<'a>(
|
|||
|
||||
{
|
||||
// If we don't have an app module id (e.g. because we're doing
|
||||
// `roc check myplatform.roc` or because we're doing bindgen),
|
||||
// `roc check myplatform.roc` or because we're generating glue code),
|
||||
// insert the `requires` symbols into the platform module's IdentIds.
|
||||
//
|
||||
// Otherwise, get them from the app module's IdentIds, because it
|
||||
|
|
|
@ -3304,7 +3304,7 @@ impl<'a> LayoutIds<'a> {
|
|||
}
|
||||
|
||||
/// Compare two fields when sorting them for code gen.
|
||||
/// This is called by both code gen and bindgen, so that
|
||||
/// This is called by both code gen and glue, so that
|
||||
/// their field orderings agree.
|
||||
#[inline(always)]
|
||||
pub fn cmp_fields<L: Ord>(
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
[package]
|
||||
name = "roc-bindgen"
|
||||
name = "roc_glue"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
repository = "https://github.com/rtfeldman/roc"
|
||||
edition = "2021"
|
||||
description = "A CLI for roc-bindgen"
|
||||
|
||||
[[bin]]
|
||||
name = "roc-bindgen"
|
||||
path = "src/main.rs"
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../roc_std" }
|
||||
|
@ -40,4 +32,3 @@ indoc = "1.0.3"
|
|||
cli_utils = { path = "../cli_utils" }
|
||||
roc_test_utils = { path = "../test_utils" }
|
||||
dircpy = "0.3.9"
|
||||
ctor = "0.1.22"
|
|
@ -1,7 +1,7 @@
|
|||
pub mod bindgen_c;
|
||||
pub mod bindgen_rs;
|
||||
pub mod bindgen_zig;
|
||||
pub mod enums;
|
||||
pub mod load;
|
||||
pub mod rust_glue;
|
||||
pub mod structs;
|
||||
pub mod types;
|
||||
|
||||
pub use load::generate;
|
|
@ -1,13 +1,68 @@
|
|||
use crate::rust_glue;
|
||||
use crate::types::{Env, Types};
|
||||
use bumpalo::Bump;
|
||||
use roc_load::{LoadedModule, Threading};
|
||||
use roc_load::{LoadedModule, LoadingProblem, Threading};
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_target::{Architecture, TargetInfo};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::io::{self, ErrorKind, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use strum::IntoEnumIterator;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub fn generate(input_path: &Path, output_path: &Path) -> io::Result<i32> {
|
||||
match load_types(input_path.to_path_buf(), Threading::AllAvailable) {
|
||||
Ok(types_and_targets) => {
|
||||
let mut file = File::create(output_path).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Unable to create output file {} - {:?}",
|
||||
output_path.display(),
|
||||
err
|
||||
);
|
||||
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
let mut buf = std::str::from_utf8(rust_glue::HEADER).unwrap().to_string();
|
||||
let body = rust_glue::emit(&types_and_targets);
|
||||
|
||||
buf.push_str(&body);
|
||||
|
||||
file.write_all(buf.as_bytes()).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Unable to write bindings to output file {} - {:?}",
|
||||
output_path.display(),
|
||||
err
|
||||
);
|
||||
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
println!(
|
||||
"🎉 Generated type declarations in:\n\n\t{}",
|
||||
output_path.display()
|
||||
);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
eprintln!("Platform module file not found: {}", input_path.display());
|
||||
process::exit(1);
|
||||
}
|
||||
error => {
|
||||
eprintln!(
|
||||
"Error loading platform module file {} - {:?}",
|
||||
input_path.display(),
|
||||
error
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_types(
|
||||
full_file_path: PathBuf,
|
||||
threading: Threading,
|
||||
|
@ -32,7 +87,16 @@ pub fn load_types(
|
|||
RenderTarget::Generic,
|
||||
threading,
|
||||
)
|
||||
.expect("Problem loading platform module");
|
||||
.unwrap_or_else(|problem| match problem {
|
||||
LoadingProblem::FormattedReport(report) => {
|
||||
eprintln!("{}", report);
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
problem => {
|
||||
todo!("{:?}", problem);
|
||||
}
|
||||
});
|
||||
|
||||
let decls = declarations_by_id.remove(&home).unwrap();
|
||||
let subs = solved.inner_mut();
|
||||
|
@ -42,7 +106,7 @@ pub fn load_types(
|
|||
|
||||
if !can_problems.is_empty() || !type_problems.is_empty() {
|
||||
todo!(
|
||||
"Gracefully report compilation problems during bindgen: {:?}, {:?}",
|
||||
"Gracefully report compilation problems during glue generation: {:?}, {:?}",
|
||||
can_problems,
|
||||
type_problems
|
||||
);
|
|
@ -3,7 +3,6 @@ use indexmap::IndexMap;
|
|||
use roc_target::{Architecture, TargetInfo};
|
||||
use std::fmt::{Display, Write};
|
||||
|
||||
pub static TEMPLATE: &[u8] = include_bytes!("../templates/template.rs");
|
||||
pub static HEADER: &[u8] = include_bytes!("../templates/header.rs");
|
||||
const INDENT: &str = " ";
|
||||
const DISCRIMINANT_DOC_COMMENT: &str =
|
||||
|
@ -255,7 +254,7 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
|
|||
// so no extra work needs to happen.
|
||||
}
|
||||
RocType::Function { .. } => {
|
||||
// TODO actually bindgen functions!
|
||||
// TODO actually generate glue functions!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,8 +312,8 @@ fn add_tag_union(
|
|||
types: &Types,
|
||||
impls: &mut Impls,
|
||||
) {
|
||||
// We should never be attempting to bindgen empty tag unions; RocType should not
|
||||
// have let this happen.
|
||||
// We should never be attempting to generate glue for empty tag unions;
|
||||
// RocType should not have let this happen.
|
||||
debug_assert_ne!(tags.len(), 0);
|
||||
|
||||
let tag_names = tags.iter().map(|(name, _)| name).cloned().collect();
|
||||
|
@ -472,7 +471,7 @@ pub struct {name} {{
|
|||
);
|
||||
} else {
|
||||
todo!(
|
||||
"Support {} tags in a recursive tag union on target_info {:?}. (This is too many tags for pointer tagging to work, so we need to bindgen something different.)",
|
||||
"Support {} tags in a recursive tag union on target_info {:?}. (This is too many tags for pointer tagging to work, so we need to generate different glue.)",
|
||||
tags.len(),
|
||||
target_info
|
||||
);
|
|
@ -1111,11 +1111,11 @@ fn add_tag_union<'a>(
|
|||
| Layout::Boxed(_)
|
||||
| Layout::LambdaSet(_)
|
||||
| Layout::RecursivePointer => {
|
||||
// These must be single-tag unions. Bindgen ordinary nonrecursive
|
||||
// These must be single-tag unions. Generate ordinary nonrecursive
|
||||
// tag unions for them, and let Rust do the unwrapping.
|
||||
//
|
||||
// This should be a very rare use case, and it's not worth overcomplicating
|
||||
// the rest of bindgen to make it do something different.
|
||||
// the rest of glue to make it do something different.
|
||||
RocType::TagUnion(RocTagUnion::NonRecursive {
|
||||
name: name.clone(),
|
||||
tags,
|
|
@ -1,4 +1,4 @@
|
|||
// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc-bindgen` CLI
|
||||
// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc glue` CLI command
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_mut)]
|
|
@ -2,7 +2,7 @@ Cargo.lock
|
|||
Cargo.toml
|
||||
build.rs
|
||||
host.c
|
||||
bindings.rs
|
||||
test_glue.rs
|
||||
roc_externs.rs
|
||||
main.rs
|
||||
app
|
|
@ -1,7 +1,7 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
use bindings::Rbt;
|
||||
use indoc::indoc;
|
||||
use test_glue::Rbt;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
|
@ -1,8 +1,8 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut bindings::MyRcd);
|
||||
fn roc_main(_: *mut test_glue::MyRcd);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -11,7 +11,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let record = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<bindings::MyRcd> = core::mem::MaybeUninit::uninit();
|
||||
let mut ret: core::mem::MaybeUninit<test_glue::MyRcd> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
use bindings::Expr;
|
||||
use indoc::indoc;
|
||||
use test_glue::Expr;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
|
@ -1,8 +1,8 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut bindings::MyEnum);
|
||||
fn roc_main(_: *mut test_glue::MyEnum);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -11,7 +11,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<bindings::MyEnum> = core::mem::MaybeUninit::uninit();
|
||||
let mut ret: core::mem::MaybeUninit<test_glue::MyEnum> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
|
@ -39,8 +39,8 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
println!(
|
||||
"tag_union was: {:?}, Bar is: {:?}, Baz is: {:?}",
|
||||
tag_union,
|
||||
bindings::MyEnum::Bar,
|
||||
bindings::MyEnum::Baz,
|
||||
test_glue::MyEnum::Bar,
|
||||
test_glue::MyEnum::Baz,
|
||||
); // Debug
|
||||
|
||||
// Exit code
|
|
@ -1,7 +1,7 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
use bindings::Rbt;
|
||||
use indoc::indoc;
|
||||
use test_glue::Rbt;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
|
@ -1,8 +1,8 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut bindings::Outer);
|
||||
fn roc_main(_: *mut test_glue::Outer);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -10,7 +10,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::cmp::Ordering;
|
||||
|
||||
let outer = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<bindings::Outer> = core::mem::MaybeUninit::uninit();
|
||||
let mut ret: core::mem::MaybeUninit<test_glue::Outer> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
use bindings::StrConsList;
|
||||
use indoc::indoc;
|
||||
use test_glue::StrConsList;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
|
@ -1,6 +1,6 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
use bindings::NonRecursive;
|
||||
use test_glue::NonRecursive;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
|
@ -8,7 +8,7 @@ platform "test-platform"
|
|||
# This case is important to test because there's no padding
|
||||
# after the largest variant, so the compiler adds an extra u8
|
||||
# (rounded up to alignment, so an an extra 8 bytes) in which
|
||||
# to store the discriminant. We have to bindgen accordingly!
|
||||
# to store the discriminant. We have to generate glue code accordingly!
|
||||
NonRecursive : [Foo Str, Bar I64, Blah I32, Baz]
|
||||
|
||||
mainForHost : NonRecursive
|
|
@ -1,8 +1,8 @@
|
|||
mod bindings;
|
||||
mod test_glue;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut bindings::NonRecursive);
|
||||
fn roc_main(_: *mut test_glue::NonRecursive);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -11,7 +11,7 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<bindings::NonRecursive> =
|
||||
let mut ret: core::mem::MaybeUninit<test_glue::NonRecursive> =
|
||||
core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
@ -30,10 +30,10 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
println!(
|
||||
"tag_union was: {:?}\n`Foo \"small str\"` is: {:?}\n`Bar 123` is: {:?}\n`Baz` is: {:?}\n`Blah 456` is: {:?}",
|
||||
tag_union,
|
||||
bindings::NonRecursive::Foo("small str".into()),
|
||||
bindings::NonRecursive::Bar(123),
|
||||
bindings::NonRecursive::Baz,
|
||||
bindings::NonRecursive::Blah(456),
|
||||
test_glue::NonRecursive::Foo("small str".into()),
|
||||
test_glue::NonRecursive::Bar(123),
|
||||
test_glue::NonRecursive::Baz,
|
||||
test_glue::NonRecursive::Blah(456),
|
||||
); // Debug
|
||||
|
||||
let mut set = HashSet::new();
|
|
@ -1,5 +1,5 @@
|
|||
use roc_bindgen::bindgen_rs;
|
||||
use roc_bindgen::load::load_types;
|
||||
use roc_glue::load::load_types;
|
||||
use roc_glue::rust_glue;
|
||||
use roc_load::Threading;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
|
@ -40,7 +40,7 @@ pub fn generate_bindings(decl_src: &str) -> String {
|
|||
result.expect("had problems loading")
|
||||
};
|
||||
|
||||
bindgen_rs::emit(&pairs)
|
||||
rust_glue::emit(&pairs)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -49,7 +49,7 @@ pub fn fixtures_dir(dir_name: &str) -> PathBuf {
|
|||
|
||||
// Descend into cli/tests/fixtures/{dir_name}
|
||||
path.push("crates");
|
||||
path.push("bindgen");
|
||||
path.push("glue");
|
||||
path.push("tests");
|
||||
path.push("fixtures");
|
||||
path.push(dir_name);
|
|
@ -10,41 +10,11 @@ extern crate roc_collections;
|
|||
mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod bindgen_cli_run {
|
||||
use crate::helpers::{fixtures_dir, root_dir};
|
||||
use cli_utils::helpers::{run_bindgen, run_roc, Out};
|
||||
mod glue_cli_run {
|
||||
use crate::helpers::fixtures_dir;
|
||||
use cli_utils::helpers::{run_glue, run_roc, Out};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
// All of these tests rely on `target/` for the `cli` crate being up-to-date,
|
||||
// so do a `cargo build` on it first!
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
let args = if cfg!(debug_assertions) {
|
||||
vec!["build"]
|
||||
} else {
|
||||
vec!["build", "--release"]
|
||||
};
|
||||
|
||||
println!(
|
||||
"Running `cargo {}` on the `cli` crate before running the tests. This may take a bit!",
|
||||
args.join(" ")
|
||||
);
|
||||
|
||||
let output = Command::new("cargo")
|
||||
.args(args)
|
||||
.current_dir(root_dir().join("crates").join("cli"))
|
||||
.output()
|
||||
.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Failed to `cargo build` roc CLI for bindgen CLI tests - error was: {:?}",
|
||||
err
|
||||
)
|
||||
});
|
||||
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
/// This macro does two things.
|
||||
///
|
||||
|
@ -68,7 +38,7 @@ mod bindgen_cli_run {
|
|||
fn $test_name() {
|
||||
let dir = fixtures_dir($fixture_dir);
|
||||
|
||||
generate_bindings_for(&dir, std::iter::empty());
|
||||
generate_glue_for(&dir, std::iter::empty());
|
||||
let out = run_app(&dir.join("app.roc"), std::iter::empty());
|
||||
|
||||
assert!(out.status.success());
|
||||
|
@ -137,8 +107,6 @@ mod bindgen_cli_run {
|
|||
fn check_for_tests(all_fixtures: &mut roc_collections::VecSet<String>) {
|
||||
use roc_collections::VecSet;
|
||||
|
||||
// todo!("Remove a bunch of duplication - don't have a ton of files in there.");
|
||||
|
||||
let fixtures = fixtures_dir("");
|
||||
let entries = std::fs::read_dir(fixtures.as_path()).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
|
@ -156,7 +124,7 @@ mod bindgen_cli_run {
|
|||
|
||||
if !all_fixtures.remove(&fixture_dir_name) {
|
||||
panic!(
|
||||
"The bindgen fixture directory {} does not have any corresponding tests in cli_run. Please add one, so if it ever stops working, we'll know about it right away!",
|
||||
"The glue fixture directory {} does not have any corresponding tests in test_glue_cli. Please add one, so if it ever stops working, we'll know about it right away!",
|
||||
entry.path().to_string_lossy()
|
||||
);
|
||||
}
|
||||
|
@ -166,12 +134,12 @@ mod bindgen_cli_run {
|
|||
assert_eq!(all_fixtures, &mut VecSet::default());
|
||||
}
|
||||
|
||||
fn generate_bindings_for<'a, I: IntoIterator<Item = &'a str>>(
|
||||
fn generate_glue_for<'a, I: IntoIterator<Item = &'a str>>(
|
||||
platform_dir: &'a Path,
|
||||
args: I,
|
||||
) -> Out {
|
||||
let platform_module_path = platform_dir.join("platform.roc");
|
||||
let bindings_file = platform_dir.join("src").join("bindings.rs");
|
||||
let glue_file = platform_dir.join("src").join("test_glue.rs");
|
||||
let fixture_templates_dir = platform_dir
|
||||
.parent()
|
||||
.unwrap()
|
||||
|
@ -179,44 +147,49 @@ mod bindgen_cli_run {
|
|||
.unwrap()
|
||||
.join("fixture-templates");
|
||||
|
||||
dbg!(&platform_module_path);
|
||||
dbg!(&glue_file);
|
||||
|
||||
// Copy the rust template from the templates directory into the fixture dir.
|
||||
dircpy::CopyBuilder::new(fixture_templates_dir.join("rust"), platform_dir)
|
||||
.overwrite(true) // overwrite any files that were already present
|
||||
.run()
|
||||
.unwrap();
|
||||
|
||||
// Delete the bindings file to make sure we're actually regenerating it!
|
||||
if bindings_file.exists() {
|
||||
fs::remove_file(&bindings_file)
|
||||
.expect("Unable to remove bindings.rs in order to regenerate it in the test");
|
||||
// Delete the glue file to make sure we're actually regenerating it!
|
||||
if glue_file.exists() {
|
||||
fs::remove_file(&glue_file)
|
||||
.expect("Unable to remove test_glue.rs in order to regenerate it in the test");
|
||||
}
|
||||
|
||||
// Generate a fresh bindings.rs for this platform
|
||||
let bindgen_out = run_bindgen(
|
||||
// Generate a fresh test_glue.rs for this platform
|
||||
let glue_out = run_glue(
|
||||
// converting these all to String avoids lifetime issues
|
||||
std::iter::once("glue".to_string()).chain(
|
||||
args.into_iter().map(|arg| arg.to_string()).chain([
|
||||
platform_module_path.to_str().unwrap().to_string(),
|
||||
bindings_file.to_str().unwrap().to_string(),
|
||||
glue_file.to_str().unwrap().to_string(),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// If there is any stderr, it should be reporting the runtime and that's it!
|
||||
if !(bindgen_out.stderr.is_empty()
|
||||
|| bindgen_out.stderr.starts_with("runtime: ") && bindgen_out.stderr.ends_with("ms\n"))
|
||||
if !(glue_out.stderr.is_empty()
|
||||
|| glue_out.stderr.starts_with("runtime: ") && glue_out.stderr.ends_with("ms\n"))
|
||||
{
|
||||
panic!(
|
||||
"`roc-bindgen` command had unexpected stderr: {}",
|
||||
bindgen_out.stderr
|
||||
"`roc glue` command had unexpected stderr: {}",
|
||||
glue_out.stderr
|
||||
);
|
||||
}
|
||||
|
||||
assert!(bindgen_out.status.success(), "bad status {:?}", bindgen_out);
|
||||
assert!(glue_out.status.success(), "bad status {:?}", glue_out);
|
||||
|
||||
bindgen_out
|
||||
glue_out
|
||||
}
|
||||
|
||||
fn run_app<'a, I: IntoIterator<Item = &'a str>>(app_file: &'a Path, args: I) -> Out {
|
||||
// Generate bindings.rs for this platform
|
||||
// Generate test_glue.rs for this platform
|
||||
let compile_out = run_roc(
|
||||
// converting these all to String avoids lifetime issues
|
||||
args.into_iter()
|
|
@ -1,4 +1,4 @@
|
|||
// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc-bindgen` CLI
|
||||
// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc glue` CLI command
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_mut)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue