1use std::{io::Write, path::Path};
2use wasmtime::{Config, Engine};
3
4fn main() {
5 let base = Path::new("../../plugins");
6
7 // Find all files and folders that don't change when rebuilt
8 let crates = std::fs::read_dir(base).expect("Could not find plugin directory");
9 for dir in crates {
10 let path = dir.unwrap().path();
11 let name = path.file_name().and_then(|x| x.to_str());
12 let is_dir = path.is_dir();
13 if is_dir && name != Some("target") && name != Some("bin") {
14 println!("cargo:rerun-if-changed={}", path.display());
15 }
16 }
17
18 // Clear out and recreate the plugin bin directory
19 let _ = std::fs::remove_dir_all(base.join("bin"));
20 let _ =
21 std::fs::create_dir_all(base.join("bin")).expect("Could not make plugins bin directory");
22
23 // Compile the plugins using the same profile as the current Zed build
24 let (profile_flags, profile_target) = match std::env::var("PROFILE").unwrap().as_str() {
25 "debug" => (&[][..], "debug"),
26 "release" => (&["--release"][..], "release"),
27 unknown => panic!("unknown profile `{}`", unknown),
28 };
29
30 // Get the target architecture for pre-cross-compilation of plugins
31 // and write it to disk to be used when embedding plugins
32 let target_triple = std::env::var("TARGET").unwrap().to_string();
33 println!("cargo:rerun-if-env-changed=TARGET");
34
35 // Invoke cargo to build the plugins
36 let build_successful = std::process::Command::new("cargo")
37 .args([
38 "build",
39 "--target",
40 "wasm32-wasi",
41 "--manifest-path",
42 base.join("Cargo.toml").to_str().unwrap(),
43 ])
44 .args(profile_flags)
45 .status()
46 .expect("Could not build plugins")
47 .success();
48 assert!(build_successful);
49
50 // Find all compiled binaries
51 let engine = create_default_engine(&target_triple);
52 let binaries = std::fs::read_dir(base.join("target/wasm32-wasi").join(profile_target))
53 .expect("Could not find compiled plugins in target");
54
55 // Copy and precompile all compiled plugins we can find
56 for file in binaries {
57 let is_wasm = || {
58 let path = file.ok()?.path();
59 if path.extension()? == "wasm" {
60 Some(path)
61 } else {
62 None
63 }
64 };
65
66 if let Some(path) = is_wasm() {
67 let out_path = base.join("bin").join(path.file_name().unwrap());
68 std::fs::copy(&path, &out_path).expect("Could not copy compiled plugin to bin");
69 precompile(&out_path, &engine, &target_triple);
70 }
71 }
72}
73
74/// Creates an engine with the default configuration.
75/// N.B. This must create an engine with the same config as the one
76/// in `plugin_runtime/src/plugin.rs`.
77fn create_default_engine(target_triple: &str) -> Engine {
78 let mut config = Config::default();
79 config
80 .target(target_triple)
81 .expect(&format!("Could not set target to `{}`", target_triple));
82 config.async_support(true);
83 config.consume_fuel(true);
84 Engine::new(&config).expect("Could not create precompilation engine")
85}
86
87fn precompile(path: &Path, engine: &Engine, target_triple: &str) {
88 let bytes = std::fs::read(path).expect("Could not read wasm module");
89 let compiled = engine
90 .precompile_module(&bytes)
91 .expect("Could not precompile module");
92 let out_path = path.parent().unwrap().join(&format!(
93 "{}.{}",
94 path.file_name().unwrap().to_string_lossy(),
95 target_triple,
96 ));
97 let mut out_file = std::fs::File::create(out_path)
98 .expect("Could not create output file for precompiled module");
99 out_file.write_all(&compiled).unwrap();
100}