1 /// Build the OCaml code and statically link it into the Rust library; see the
2 /// [ocaml-interop
3 /// example](https://github.com/tezedge/ocaml-interop/blob/master/testing/rust-caller/build.rs)
4 /// for more details. After playing with this a bit, I discovered that the best
5 /// approach to avoid missing symbols was to imitate `dune`: I observed `rm -rf
6 /// _build && dune build ./ocaml/interpret.exe.o --display=verbose` and used
7 /// that as a pattern, now encoded in `ocaml/Makefile` for easier debugging.
8 use std::{env, path::PathBuf, process::Command};
9 
10 const LIB_NAME: &'static str = "interpret";
11 const OCAML_DIR: &'static str = "ocaml";
12 const SPEC_DIR: &'static str = "ocaml/spec";
13 const SPEC_REPOSITORY: &'static str = "https://github.com/conrad-watt/spec";
14 const SPEC_REPOSITORY_BRANCH: &'static str = "wasmtime_fuzzing";
15 const SPEC_REPOSITORY_REV: &'static str = "c6bab4461e10229e557aae2e1027cadfce0161ce";
16 
main()17 fn main() {
18     println!("cargo:rustc-check-cfg=cfg(feature, values(\"has-libinterpret\"))");
19     println!("cargo:rustc-check-cfg=cfg(fuzzing)");
20     if cfg!(feature = "build-libinterpret") {
21         build();
22     }
23 }
24 
build()25 fn build() {
26     let out_dir = &env::var("OUT_DIR").unwrap();
27 
28     // Re-run if changed.
29     println!("cargo:rerun-if-changed={OCAML_DIR}/{LIB_NAME}.ml");
30     println!("cargo:rerun-if-changed={OCAML_DIR}/Makefile");
31 
32     if let Some(other_dir) = env::var_os("FFI_LIB_DIR") {
33         // Link with a library provided in the `FFI_LIB_DIR`.
34         println!("cargo:rustc-link-search={}", other_dir.to_str().unwrap());
35         println!("cargo:rustc-link-lib=static={LIB_NAME}");
36     } else {
37         // Ensure the spec repository is present.
38         if is_spec_repository_empty(SPEC_DIR) {
39             retrieve_spec_repository(SPEC_DIR)
40         }
41 
42         // Build the library to link to.
43         build_lib(out_dir, OCAML_DIR);
44         println!("cargo:rustc-link-search={out_dir}");
45         println!("cargo:rustc-link-lib=static={LIB_NAME}");
46     }
47 
48     // Enabling this feature alerts the compiler to use the `with_library`
49     // module.
50     println!("cargo:rustc-cfg=feature=\"has-libinterpret\"");
51 }
52 
53 // Build the OCaml library into Cargo's `out` directory.
build_lib(out_dir: &str, ocaml_dir: &str)54 fn build_lib(out_dir: &str, ocaml_dir: &str) {
55     let status = Command::new("make")
56         .arg(format!("BUILD_DIR={out_dir}"))
57         .current_dir(ocaml_dir)
58         .status()
59         .expect("Failed to execute 'make' command to build OCaml library");
60 
61     assert!(
62         status.success(),
63         "Failed to build the OCaml library using 'make'."
64     )
65 }
66 
67 // Check if the spec repository directory contains any files.
is_spec_repository_empty(destination: &str) -> bool68 fn is_spec_repository_empty(destination: &str) -> bool {
69     PathBuf::from(destination)
70         .read_dir()
71         .map(|mut i| i.next().is_none())
72         .unwrap_or(true)
73 }
74 
75 // Clone the spec repository into `destination`. This exists due to the large
76 // size of the dependencies (e.g. KaTeX) that are pulled if this were cloned
77 // recursively as a submodule.
retrieve_spec_repository(destination: &str)78 fn retrieve_spec_repository(destination: &str) {
79     let status = Command::new("git")
80         .arg("clone")
81         .arg(SPEC_REPOSITORY)
82         .arg("-b")
83         .arg(SPEC_REPOSITORY_BRANCH)
84         .arg(destination)
85         .status()
86         .expect("Failed to execute 'git' command to clone spec repository.");
87     assert!(status.success(), "Failed to retrieve the spec repository.");
88 
89     let status = Command::new("git")
90         .arg("reset")
91         .arg("--hard")
92         .arg(SPEC_REPOSITORY_REV)
93         .current_dir(destination)
94         .status()
95         .expect("Failed to execute 'git' command to clone spec repository.");
96     assert!(status.success(), "Failed to reset to revision.");
97 }
98