diff --git a/.circleci/config.yml b/.circleci/config.yml index f2028826..be914cf4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,10 +47,7 @@ jobs: command: | source "$HOME/.cargo/env" eval $(opam env) - OCAMLLIB=$(ocamlopt.opt -config | grep standard_library: | awk '{ print $2 }');export OCAMLLIB cargo build - (cd ocamlpool/test&& ./ocamlpool_test.sh) - (cd rust/ocamlrep/test&& ./ocamlrep_test.sh) cargo test macos-build-and-test: @@ -67,10 +64,7 @@ jobs: command: | source "$HOME/.cargo/env" eval $(opam env) - OCAMLLIB=$(ocamlopt.opt -config | grep standard_library: | awk '{ print $2 }');export OCAMLLIB cargo build - (cd ocamlpool/test&& ./ocamlpool_test.sh) - (cd rust/ocamlrep/test&& ./ocamlrep_test.sh) cargo test workflows: diff --git a/README.md b/README.md index 85cfc071..3afc7f09 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ This project is stand-alone and requires or works with: ## Building ocamlrep Assume an opam installation on a `4.14.0` like switch. From the repository root, ```bash - OCAMLLIB=$(ocamlopt.opt -config | grep standard_library: | awk '{ print $2 }'); export OCAMLLIB cargo build ``` diff --git a/ocamlpool/build.rs b/ocamlpool/build.rs index e4cc1a33..4dbda59c 100644 --- a/ocamlpool/build.rs +++ b/ocamlpool/build.rs @@ -2,10 +2,23 @@ // --set-switch)"`) then to find the prevailing standard library caml // headers, `OCAMLLIB=$(ocamlopt.opt -config | grep standard_library: // | awk '{ print $2 }')`. +fn ocamllib_dir() -> std::path::PathBuf { + let mut sh = std::process::Command::new("sh"); + sh.args([ + "-c", + "ocamlopt.opt -config | grep standard_library: | awk '{ print $2 }'", + ]); + std::path::Path::new( + std::str::from_utf8(&sh.output().unwrap().stdout) + .unwrap() + .trim(), + ) + .to_path_buf() +} fn main() { cc::Build::new() - .include(env!("OCAMLLIB")) + .include(ocamllib_dir().as_path().to_str().unwrap()) .file("ocamlpool.c") .compile("ocamlpool"); } diff --git a/ocamlpool/test/Cargo.toml b/ocamlpool/test/Cargo.toml index 7778ef72..f1e60c83 100644 --- a/ocamlpool/test/Cargo.toml +++ b/ocamlpool/test/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [lib] path = "ocamlpool_test.rs" -test = false +test = true # Don't forget to update the buck TARGETS file! doctest = false crate-type = ["lib", "staticlib"] diff --git a/ocamlpool/test/ocamlpool_test.ml b/ocamlpool/test/ocamlpool_test.ml index 4dee45e5..085f92b6 100644 --- a/ocamlpool/test/ocamlpool_test.ml +++ b/ocamlpool/test/ocamlpool_test.ml @@ -13,4 +13,8 @@ external test : unit -> unit = "test" * in order to allocate memory for ocaml. Calling rust from ocaml * is a good way of ensuring this dependecy is built. *) -let () = test () +let () = begin + print_endline "[ocamlpool_test][info]: start"; + test (); + print_endline "[ocamlpool_test][info]: finish" +end diff --git a/ocamlpool/test/ocamlpool_test.rs b/ocamlpool/test/ocamlpool_test.rs index 2f11fae0..1c851182 100644 --- a/ocamlpool/test/ocamlpool_test.rs +++ b/ocamlpool/test/ocamlpool_test.rs @@ -3,6 +3,8 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. +#![feature(exit_status_error)] + use ocamlrep_ocamlpool::ocaml_ffi; extern "C" { @@ -31,3 +33,80 @@ ocaml_ffi! { } } } + +#[cfg(test)] +mod tests { + use std::process::{Command, ExitStatusError}; + + fn cmd(prog: &str, args: &[&str]) -> Command { + let mut prog_cmd = Command::new(prog); + prog_cmd.args(args); + prog_cmd + } + + fn ocamlopt_cmd(args: &[&str]) -> Command { + cmd("ocamlopt.opt", args) + } + + fn sh_cmd(args: &[&str]) -> Command { + cmd("sh", args) + } + + fn cargo_cmd(args: &[&str]) -> Command { + cmd("cargo", args) + } + + fn workspace_dir(ds: &[&str]) -> std::path::PathBuf { + let mut cargo_cmd = cargo_cmd(&["locate-project", "--workspace", "--message-format=plain"]); + let output = cargo_cmd.output().unwrap().stdout; + let root_cargo_toml = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()); + let mut p = root_cargo_toml.parent().unwrap().to_path_buf(); + for d in ds { + p.push(d); + } + p + } + + fn run(mut cmd: Command) -> Result<(), ExitStatusError> { + cmd.spawn().unwrap().wait().ok().unwrap().exit_ok() + } + + fn fmt_exit_status_err(err: ExitStatusError) -> String { + format!("error status: {err}") + } + + fn build_flavor() -> &'static str { + if cfg!(debug_assertions) { + "debug" + } else { + "release" + } + } + + #[test] + fn ocamlpool_test() { + let compile_cmd = ocamlopt_cmd(&[ + "-verbose", + "-c", + "ocamlpool_test.ml", + "-o", + "ocamlpool_test_ml.cmx", + ]); + assert_eq!(run(compile_cmd).map_err(fmt_exit_status_err), Ok(())); + let link_cmd = ocamlopt_cmd(&[ + "-verbose", + "-o", + "ocamlpool_test", + "ocamlpool_test_ml.cmx", + "-ccopt", + &("-L".to_owned() + workspace_dir(&["target", build_flavor()]).to_str().unwrap()), + "-cclib", + "-locamlpool_test", + "-cclib", + "-locamlpool", + ]); + assert_eq!(run(link_cmd).map_err(fmt_exit_status_err), Ok(())); + let ocamlpool_test_cmd = sh_cmd(&["-c", "./ocamlpool_test"]); + assert_eq!(run(ocamlpool_test_cmd).map_err(fmt_exit_status_err), Ok(())); + } +} diff --git a/ocamlpool/test/ocamlpool_test.sh b/ocamlpool/test/ocamlpool_test.sh deleted file mode 100755 index 8c9260d9..00000000 --- a/ocamlpool/test/ocamlpool_test.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -root="$(dirname "$(cargo locate-project --workspace --message-format plain)")" -targets="$root/target/debug" - -# Assumes `cargo build` has already run. -ocamlopt.opt -verbose -c ocamlpool_test.ml -o ocamlpool_test_ml.cmx -ocamlopt.opt -verbose -o ocamlpool_test ocamlpool_test_ml.cmx -ccopt -L"$targets" -cclib -locamlpool_test -cclib -locamlpool - -./ocamlpool_test -rm ./*.o ./*.cmi ./*.cmx ocamlpool_test diff --git a/rust/ocamlrep/test/ocamlrep_test.sh b/rust/ocamlrep/test/ocamlrep_test.sh deleted file mode 100755 index 19863f0f..00000000 --- a/rust/ocamlrep/test/ocamlrep_test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -root="$(dirname "$(cargo locate-project --workspace --message-format plain)")" -targets="$root/target/debug" - -# Assumes `cargo build` has already run. -ocamlopt.opt -verbose -c test_ocamlrep.ml -o test_ocamlrep_ml.cmx -ocamlopt.opt -verbose -o ocamlrep_test test_ocamlrep_ml.cmx -ccopt -L"$targets" -cclib -ltest_bindings -cclib -locamlpool -./ocamlrep_test -rm ./*.o ./*.cmi ./*.cmx ocamlrep_test diff --git a/rust/ocamlrep/test/test_bindings.rs b/rust/ocamlrep/test/test_bindings.rs index dbd10a2f..0be01b17 100644 --- a/rust/ocamlrep/test/test_bindings.rs +++ b/rust/ocamlrep/test/test_bindings.rs @@ -2,6 +2,7 @@ // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. +#![feature(exit_status_error)] use std::cell::RefCell; use std::collections::BTreeMap; @@ -286,3 +287,82 @@ pub extern "C" fn roundtrip_int64(value: usize) -> usize { let i = unsafe { ocamlrep_caml_builtins::Int64::from_ocaml(value).unwrap() }; val(i) } + +#[cfg(test)] +mod tests { + use std::process::{Command, ExitStatusError}; + + fn cmd(prog: &str, args: &[&str]) -> Command { + let mut prog_cmd = Command::new(prog); + prog_cmd.args(args); + prog_cmd + } + + fn ocamlopt_cmd(args: &[&str]) -> Command { + cmd("ocamlopt.opt", args) + } + + fn sh_cmd(args: &[&str]) -> Command { + cmd("sh", args) + } + + fn cargo_cmd(args: &[&str]) -> Command { + cmd("cargo", args) + } + + fn workspace_dir(ds: &[&str]) -> std::path::PathBuf { + let mut cargo_cmd = cargo_cmd(&["locate-project", "--workspace", "--message-format=plain"]); + let output = cargo_cmd.output().unwrap().stdout; + let root_cargo_toml = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()); + let mut p = root_cargo_toml.parent().unwrap().to_path_buf(); + for d in ds { + p.push(d); + } + p + } + + fn run(mut cmd: Command) -> Result<(), ExitStatusError> { + cmd.spawn().unwrap().wait().ok().unwrap().exit_ok() + } + + fn fmt_exit_status_err(err: ExitStatusError) -> String { + format!("error status: {err}") + } + + fn build_flavor() -> &'static str { + if cfg!(debug_assertions) { + "debug" + } else { + "release" + } + } + #[test] + fn ocamlrep_test() { + let mut compile_cmd = ocamlopt_cmd(&[ + "-verbose", + "-c", + "test_ocamlrep.ml", + "-o", + "test_ocamlrep_ml.cmx", + ]); + compile_cmd.current_dir(".."); + assert_eq!(run(compile_cmd).map_err(fmt_exit_status_err), Ok(())); + let mut link_cmd = ocamlopt_cmd(&[ + "-verbose", + "-o", + "ocamlrep_test", + "test_ocamlrep_ml.cmx", + "-ccopt", + &("-L".to_owned() + workspace_dir(&["target", build_flavor()]).to_str().unwrap()), + "-cclib", + "-ltest_bindings", + "-cclib", + "-locamlpool", + ]); + link_cmd.current_dir(".."); + assert_eq!(run(link_cmd).map_err(fmt_exit_status_err), Ok(())); + let mut ocamlpool_test_cmd = sh_cmd(&["-c", "./ocamlrep_test"]); + ocamlpool_test_cmd.current_dir(".."); + assert_eq!(run(ocamlpool_test_cmd).map_err(fmt_exit_status_err), Ok(())); + } +} diff --git a/rust/ocamlrep/test/test_bindings/Cargo.toml b/rust/ocamlrep/test/test_bindings/Cargo.toml index 4dea0e14..e530c33d 100644 --- a/rust/ocamlrep/test/test_bindings/Cargo.toml +++ b/rust/ocamlrep/test/test_bindings/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [lib] path = "../test_bindings.rs" -test = false +test = true # Don't forget to update the buck TARGETS file! doctest = false crate-type = ["lib", "staticlib"] diff --git a/rust/ocamlrep/test/test_ocamlrep.ml b/rust/ocamlrep/test/test_ocamlrep.ml index 9a7f1cd9..29bc61f5 100644 --- a/rust/ocamlrep/test/test_ocamlrep.ml +++ b/rust/ocamlrep/test/test_ocamlrep.ml @@ -823,4 +823,8 @@ let test_cases = let main () = List.iter (fun test -> test ()) test_cases -let () = main () +let () = begin + print_endline "[ocamlrep_test][info]: start"; + main (); + print_endline "[ocamlrep_test][info]: finish" +end diff --git a/rust/ocamlrep_marshal/cargo/ocamlrep_marshal_ffi_bindings/Cargo.toml b/rust/ocamlrep_marshal/cargo/ocamlrep_marshal_ffi_bindings/Cargo.toml index cb3435f6..feb3f9e7 100644 --- a/rust/ocamlrep_marshal/cargo/ocamlrep_marshal_ffi_bindings/Cargo.toml +++ b/rust/ocamlrep_marshal/cargo/ocamlrep_marshal_ffi_bindings/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [lib] path = "../../ocamlrep_marshal_ffi_bindings.rs" -test = false +#test = true Remember to update the buck TARGETS file! doctest = false crate-type = ["lib", "staticlib"] diff --git a/rust/ocamlrep_marshal/ocamlrep_marshal_ffi_bindings.rs b/rust/ocamlrep_marshal/ocamlrep_marshal_ffi_bindings.rs index c4d47bb9..8c5620a3 100644 --- a/rust/ocamlrep_marshal/ocamlrep_marshal_ffi_bindings.rs +++ b/rust/ocamlrep_marshal/ocamlrep_marshal_ffi_bindings.rs @@ -2,6 +2,7 @@ // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. +#![feature(exit_status_error)] use ocamlrep::FromOcamlRep; @@ -34,3 +35,112 @@ unsafe extern "C" fn ocamlrep_marshal_input_value_from_string( ocamlrep_marshal::input_value(str, &pool).to_bits() }) } + +#[cfg(test)] +mod tests { + use std::process::{Command, ExitStatusError}; + fn cmd(prog: &str, args: &[&str]) -> Command { + let mut prog_cmd = Command::new(prog); + prog_cmd.args(args); + prog_cmd + } + + fn ocamlopt_cmd(args: &[&str]) -> Command { + cmd("ocamlopt.opt", args) + } + + fn sh_cmd(args: &[&str]) -> Command { + cmd("sh", args) + } + + fn cargo_cmd(args: &[&str]) -> Command { + cmd("cargo", args) + } + + fn workspace_dir(ds: &[&str]) -> std::path::PathBuf { + let mut cargo_cmd = cargo_cmd(&["locate-project", "--workspace", "--message-format=plain"]); + let output = cargo_cmd.output().unwrap().stdout; + let root_cargo_toml = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()); + let mut p = root_cargo_toml.parent().unwrap().to_path_buf(); + for d in ds { + p.push(d); + } + p + } + + fn run(mut cmd: Command) -> Result<(), ExitStatusError> { + cmd.spawn().unwrap().wait().ok().unwrap().exit_ok() + } + + fn fmt_exit_status_err(err: ExitStatusError) -> String { + format!("error status: {err}") + } + + fn build_flavor() -> &'static str { + if cfg!(debug_assertions) { + "debug" + } else { + "release" + } + } + + #[test] + fn ocamlrep_marshal_test() { + let ocamlrep_marshal_dir = "../.."; + + let mut compile_cmd = ocamlopt_cmd(&[ + "-verbose", + "-c", + "ocamlrep_marshal_ffi.ml", + "-o", + "ocamlrep_marshal_ffi.cmx", + ]); + compile_cmd.current_dir(&ocamlrep_marshal_dir); + assert_eq!(run(compile_cmd).map_err(fmt_exit_status_err), Ok(())); + let mut archive_cmd = ocamlopt_cmd(&[ + "-verbose", + "-a", + "-o", + "ocamlrep_marshal_ffi.cmxa", + "ocamlrep_marshal_ffi.cmx", + "-ccopt", + &("-L".to_owned() + workspace_dir(&["target", build_flavor()]).to_str().unwrap()), + "-cclib", + "-locamlrep_marshal_ffi_bindings", + ]); + archive_cmd.current_dir(&ocamlrep_marshal_dir); + assert_eq!(run(archive_cmd).map_err(fmt_exit_status_err), Ok(())); + let mut compile_cmd = ocamlopt_cmd(&[ + "-verbose", + "-c", + "test/test_ocamlrep_marshal.ml", + "-o", + "test_ocamlrep_marshal_ml.cmx", + ]); + compile_cmd.current_dir(&ocamlrep_marshal_dir); + assert_eq!(run(compile_cmd).map_err(fmt_exit_status_err), Ok(())); + let mut link_cmd = ocamlopt_cmd(&[ + "-verbose", + "-o", + "ocamlrep_marshal_test", + "ocamlrep_marshal_ffi.cmxa", + "test_ocamlrep_marshal_ml.cmx", + "-ccopt", + &("-L".to_owned() + workspace_dir(&["target", build_flavor()]).to_str().unwrap()), + "-cclib", + "-locamlrep_marshal", + "-cclib", + "-locamlrep_marshal_ffi_bindings", + "-cclib", + "-locamlpool", + ]); + link_cmd.current_dir(&ocamlrep_marshal_dir); + assert_eq!(run(link_cmd).map_err(fmt_exit_status_err), Ok(())); + let mut ocamlrep_marshal_test_cmd = sh_cmd(&["-c", "./ocamlrep_marshal_test"]); + ocamlrep_marshal_test_cmd.current_dir("../.."); + assert_eq!( + run(ocamlrep_marshal_test_cmd).map_err(fmt_exit_status_err), + Ok(()) + ); + } +} diff --git a/rust/ocamlrep_marshal/test/test_ocamlrep_marshal.ml b/rust/ocamlrep_marshal/test/test_ocamlrep_marshal.ml index 6a63f90b..70dd94b1 100644 --- a/rust/ocamlrep_marshal/test/test_ocamlrep_marshal.ml +++ b/rust/ocamlrep_marshal/test/test_ocamlrep_marshal.ml @@ -22,11 +22,15 @@ let test_round_trip show (x : 'a) = let _ = Printf.printf "y = %s\n" (show y) in assert (x = y) -let show_pair_int_int = [%derive.show: int * int] +let show_pair_int_int (x, y) = Printf.sprintf "(%d, %d)" x y -let show_pair_opt_int_string = [%derive.show: int option * string] +let show_pair_opt_int_string (x, y) = + let xx = match x with + | Some i -> Printf.sprintf "Some %i" i + | None -> "None" + in Printf.sprintf "(%s, %S)" xx y -let show_float_list = [%derive.show: float list] +let show_float_list xs = "[" ^ String.concat "; " ( List.map (fun x -> Printf.sprintf "%f" x) xs) ^ "]" let show_float_array x = show_float_list (Array.to_list x) @@ -94,6 +98,8 @@ let test_sharing () = () let () = + print_endline "[ocamlrep_marshal_test][info]: start"; + assert_eq 'c'; assert_eq 5; assert_eq 3.14; @@ -116,4 +122,6 @@ let () = test_sharing (); + print_endline "[ocamlrep_marshal_test][info]: finish"; + ()