diff --git a/Cargo.lock b/Cargo.lock index e27fa5c4a..3ce702103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2213,6 +2213,7 @@ dependencies = [ "fern", "log", "structopt", + "tempfile", "wasmer", "wasmer-cache", "wasmer-compiler", diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index 885102827..b9eea99ae 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -28,6 +28,9 @@ # define DEPRECATED(message) __declspec(deprecated(message)) #endif +// The `jit` feature has been enabled for this build. +#define WASMER_JIT_ENABLED + // The `compiler` feature has been enabled for this build. #define WASMER_COMPILER_ENABLED diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index b73828bf8..19f353b9c 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -45,6 +45,7 @@ cfg-if = "0.1" # For debug feature fern = { version = "0.6", features = ["colored"], optional = true } log = { version = "0.4", optional = true } +tempfile = "3" [features] # Don't add the compiler features in default, please add them on the Makefile diff --git a/lib/cli/src/bin/wasmer.rs b/lib/cli/src/bin/wasmer.rs index 2b1d5792e..6d72964bf 100644 --- a/lib/cli/src/bin/wasmer.rs +++ b/lib/cli/src/bin/wasmer.rs @@ -1,4 +1,6 @@ use anyhow::Result; +#[cfg(feature = "object-file")] +use wasmer_cli::commands::CreateExe; #[cfg(feature = "wast")] use wasmer_cli::commands::Wast; use wasmer_cli::commands::{Cache, Compile, Config, Inspect, Run, SelfUpdate, Validate}; @@ -26,6 +28,11 @@ enum WasmerCLIOptions { #[structopt(name = "compile")] Compile(Compile), + /// Compile a WebAssembly binary into a native executable + #[cfg(feature = "object-file")] + #[structopt(name = "create-exe")] + CreateExe(CreateExe), + /// Get various configuration information needed /// to compile programs which use Wasmer #[structopt(name = "config")] @@ -53,6 +60,8 @@ impl WasmerCLIOptions { Self::Cache(cache) => cache.execute(), Self::Validate(validate) => validate.execute(), Self::Compile(compile) => compile.execute(), + #[cfg(feature = "object-file")] + Self::CreateExe(create_exe) => create_exe.execute(), Self::Config(config) => config.execute(), Self::Inspect(inspect) => inspect.execute(), #[cfg(feature = "wast")] diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index fe7036e36..4d07c37e8 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -2,6 +2,8 @@ mod cache; mod compile; mod config; +#[cfg(feature = "object-file")] +mod create_exe; mod inspect; mod run; mod self_update; @@ -9,6 +11,8 @@ mod validate; #[cfg(feature = "wast")] mod wast; +#[cfg(feature = "object-file")] +pub use create_exe::*; #[cfg(feature = "wast")] pub use wast::*; pub use {cache::*, compile::*, config::*, inspect::*, run::*, self_update::*, validate::*}; diff --git a/lib/cli/src/commands/compile.rs b/lib/cli/src/commands/compile.rs index 1e77a08f2..bd70d6ffc 100644 --- a/lib/cli/src/commands/compile.rs +++ b/lib/cli/src/commands/compile.rs @@ -38,8 +38,7 @@ impl Compile { .context(format!("failed to compile `{}`", self.path.display())) } - fn get_recommend_extension( - &self, + pub(crate) fn get_recommend_extension( engine_type: &EngineType, target_triple: &Triple, ) -> &'static str { @@ -82,7 +81,7 @@ impl Compile { .file_stem() .map(|osstr| osstr.to_string_lossy().to_string()) .unwrap_or_default(); - let recommended_extension = self.get_recommend_extension(&engine_type, target.triple()); + let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple()); match self.output.extension() { Some(ext) => { if ext != recommended_extension { diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs new file mode 100644 index 000000000..0238da708 --- /dev/null +++ b/lib/cli/src/commands/create_exe.rs @@ -0,0 +1,273 @@ +//! Create a standalone native executable for a given Wasm file. + +use crate::store::{CompilerOptions, EngineType}; +use anyhow::{Context, Result}; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; +use structopt::StructOpt; +use wasmer::*; + +const WASMER_MAIN_C_SOURCE: &[u8] = include_bytes!("wasmer_create_exe_main.c"); + +// TODO: to get this working locally I had to add wasmer_wasm.h and wasm.h to `.wasmer/include` manually. +// this needs to be fixed before we can ship this. + +#[derive(Debug, StructOpt)] +/// The options for the `wasmer create-exe` subcommand +pub struct CreateExe { + /// Input file + #[structopt(name = "FILE", parse(from_os_str))] + path: PathBuf, + + /// Output file + #[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))] + output: PathBuf, + + /// Compilation Target triple + #[structopt(long = "target")] + target_triple: Option, + + #[structopt(flatten)] + compiler: CompilerOptions, + + #[structopt(short = "m", multiple = true)] + cpu_features: Vec, + + /// Additional libraries to link against. + /// This is useful for fixing linker errors that may occur on some systems. + #[structopt(short = "l", multiple = true)] + libraries: Vec, +} + +impl CreateExe { + /// Runs logic for the `compile` subcommand + pub fn execute(&self) -> Result<()> { + let target = self + .target_triple + .as_ref() + .map(|target_triple| { + let mut features = self + .cpu_features + .clone() + .into_iter() + .fold(CpuFeature::set(), |a, b| a | b); + // Cranelift requires SSE2, so we have this "hack" for now to facilitate + // usage + features |= CpuFeature::SSE2; + Target::new(target_triple.clone(), features) + }) + .unwrap_or_default(); + let engine_type = EngineType::ObjectFile; + let (store, compiler_type) = self + .compiler + .get_store_for_target_and_engine(target.clone(), engine_type)?; + + println!("Engine: {}", engine_type.to_string()); + println!("Compiler: {}", compiler_type.to_string()); + println!("Target: {}", target.triple()); + + let working_dir = tempfile::tempdir()?; + let starting_cd = env::current_dir()?; + let output_path = starting_cd.join(&self.output); + env::set_current_dir(&working_dir)?; + + // TODO: encapsulate compile code + + #[cfg(not(windows))] + let wasm_object_path = PathBuf::from("wasm.o"); + #[cfg(windows)] + let wasm_object_path = PathBuf::from("wasm.obj"); + + let wasm_module_path = starting_cd.join(&self.path); + + let header_file_path = Path::new("my_wasm.h"); + let module = + Module::from_file(&store, &wasm_module_path).context("failed to compile Wasm")?; + let _ = module.serialize_to_file(&wasm_object_path)?; + + let artifact: &wasmer_engine_object_file::ObjectFileArtifact = + module.artifact().as_ref().downcast_ref().context( + "Engine type is ObjectFile but could not downcast artifact into ObjectFileArtifact", + )?; + let symbol_registry = artifact.symbol_registry(); + let metadata_length = artifact.metadata_length(); + let module_info = module.info(); + let header_file_src = crate::c_gen::object_file_header::generate_header_file( + module_info, + symbol_registry, + metadata_length, + ); + + let header_path = header_file_path.clone(); + // for C code + let mut header = std::fs::OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(&header_path)?; + + use std::io::Write; + header.write(header_file_src.as_bytes())?; + + // auto compilation + // + + // write C src to disk + let c_src_path = Path::new("wasmer_main.c"); + #[cfg(not(windows))] + let c_src_obj = PathBuf::from("wasmer_main.o"); + #[cfg(windows)] + let c_src_obj = PathBuf::from("wasmer_main.obj"); + + { + let mut c_src_file = fs::OpenOptions::new() + .create_new(true) + .write(true) + .open(&c_src_path) + .context("Failed to open C source code file")?; + // TODO: + c_src_file.write_all(WASMER_MAIN_C_SOURCE)?; + } + run_c_compile(&c_src_path, &c_src_obj).context("Failed to compile C source code")?; + LinkCode { + object_paths: vec![c_src_obj, wasm_object_path], + output_path, + additional_libraries: self.libraries.clone(), + ..Default::default() + } + .run() + .context("Failed to link objects together")?; + + eprintln!( + "✔ Native executable compiled successfully to `{}`.", + self.output.display(), + ); + + Ok(()) + } +} + +fn get_wasmer_include_directory() -> anyhow::Result { + let mut path = PathBuf::from(env::var("WASMER_DIR")?); + path.push("include"); + Ok(path) +} + +fn get_libwasmer_path() -> anyhow::Result { + let mut path = PathBuf::from(env::var("WASMER_DIR")?); + path.push("lib"); + + #[cfg(not(windows))] + path.push("libwasmer.a"); + #[cfg(windows)] + path.push("libwasmer.lib"); + + Ok(path) +} + +/// Compile the C code. +fn run_c_compile(path_to_c_src: &Path, output_name: &Path) -> anyhow::Result<()> { + #[cfg(not(windows))] + let c_compiler = "cc"; + #[cfg(windows)] + let c_compiler = "clang++"; + + let output = Command::new(c_compiler) + .arg("-O2") + .arg("-c") + .arg(path_to_c_src) + .arg("-I") + .arg(get_wasmer_include_directory()?) + .arg("-o") + .arg(output_name) + .output()?; + + if !output.status.success() { + bail!( + "C code compile failed with: stdout: {}\n\nstderr: {}", + std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"), + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + Ok(()) +} + +/// Data used to run a linking command for generated artifacts. +#[derive(Debug)] +struct LinkCode { + /// Path to the linker used to run the linking command. + linker_path: PathBuf, + /// String used as an optimization flag. + optimization_flag: String, + /// Paths of objects to link. + object_paths: Vec, + /// Additional libraries to link against. + additional_libraries: Vec, + /// Path to the output target. + output_path: PathBuf, + /// Path to the static libwasmer library. + libwasmer_path: PathBuf, +} + +impl Default for LinkCode { + fn default() -> Self { + #[cfg(not(windows))] + let linker = "cc"; + #[cfg(windows)] + let linker = "clang"; + Self { + linker_path: PathBuf::from(linker), + optimization_flag: String::from("-O2"), + object_paths: vec![], + additional_libraries: vec![], + output_path: PathBuf::from("a.out"), + libwasmer_path: get_libwasmer_path().unwrap(), + } + } +} + +impl LinkCode { + // TODO: `wasmer create-exe` needs a command line flag for extra libraries to link aganist + // or perhaps we just want to add a flag for passing things to the linker + fn run(&self) -> anyhow::Result<()> { + let mut command = Command::new(&self.linker_path); + let command = command + .arg(&self.optimization_flag) + .args( + self.object_paths + .iter() + .map(|path| path.canonicalize().unwrap()), + ) + .arg( + &self + .libwasmer_path + .canonicalize() + .context("Failed to find libwasmer")?, + ); + #[cfg(windows)] + let command = command.arg("-luserenv").arg("-lWs2_32").arg("-ladvapi32"); + #[cfg(not(windows))] + let command = command.arg("-ldl").arg("-lm").arg("-pthread"); + let link_aganist_extra_libs = self + .additional_libraries + .iter() + .map(|lib| format!("-l{}", lib)); + let command = command.args(link_aganist_extra_libs); + let output = command.arg("-o").arg(&self.output_path).output()?; + + if !output.status.success() { + bail!( + "linking failed with: stdout: {}\n\nstderr: {}", + std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"), + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + Ok(()) + } +} diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c new file mode 100644 index 000000000..ec00f9179 --- /dev/null +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -0,0 +1,89 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include "wasmer_wasm.h" +#include "wasm.h" +#include "my_wasm.h" + +#include +#include + +#ifdef __cplusplus +} +#endif + +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char* error_str = (char*) malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +int main() { + printf("Initializing...\n"); + wasm_config_t* config = wasm_config_new(); + wasm_config_set_engine(config, OBJECT_FILE); + wasm_engine_t* engine = wasm_engine_new_with_config(config); + wasm_store_t* store = wasm_store_new(engine); + + wasm_module_t* module = wasmer_object_file_engine_new(store, "qjs.wasm"); + if (! module) { + printf("Failed to create module\n"); + print_wasmer_error(); + return -1; + } + + // We have now finished the memory buffer book keeping and we have a valid Module. + + // In this example we're passing some JavaScript source code as a command line argument + // to a WASI module that can evaluate JavaScript. + wasi_config_t* wasi_config = wasi_config_new("constant_value_here"); + const char* js_string = "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));"; + wasi_config_arg(wasi_config, "--eval"); + wasi_config_arg(wasi_config, js_string); + wasi_env_t* wasi_env = wasi_env_new(wasi_config); + if (!wasi_env) { + printf("> Error building WASI env!\n"); + print_wasmer_error(); + return 1; + } + + wasm_importtype_vec_t import_types; + wasm_module_imports(module, &import_types); + int num_imports = import_types.size; + wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*)); + wasm_importtype_vec_delete(&import_types); + + bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports); + if (!get_imports_result) { + printf("> Error getting WASI imports!\n"); + print_wasmer_error(); + return 1; + } + + wasm_instance_t* instance = wasm_instance_new(store, module, (const wasm_extern_t* const*) imports, NULL); + if (! instance) { + printf("Failed to create instance\n"); + print_wasmer_error(); + return -1; + } + wasi_env_set_instance(wasi_env, instance); + + // WASI is now set up. + + void* vmctx = wasm_instance_get_vmctx_ptr(instance); + wasm_val_t* inout[2] = { NULL, NULL }; + + fflush(stdout); + // We're able to call our compiled function directly through a trampoline. + wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout); + + wasm_instance_delete(instance); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + return 0; +} diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 795fcf792..9404ac2bc 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -14,8 +14,27 @@ use wasmer::*; use wasmer_compiler::CompilerConfig; #[derive(Debug, Clone, StructOpt)] -/// The compiler options +/// The compiler and engine options pub struct StoreOptions { + #[structopt(flatten)] + compiler: CompilerOptions, + + /// Use JIT Engine. + #[structopt(long, conflicts_with_all = &["native", "object_file"])] + jit: bool, + + /// Use Native Engine. + #[structopt(long, conflicts_with_all = &["jit", "object_file"])] + native: bool, + + /// Use ObjectFile Engine. + #[structopt(long, conflicts_with_all = &["jit", "native"])] + object_file: bool, +} + +#[derive(Debug, Clone, StructOpt)] +/// The compiler options +pub struct CompilerOptions { /// Use Singlepass compiler. #[structopt(long, conflicts_with_all = &["cranelift", "llvm", "backend"])] singlepass: bool, @@ -36,19 +55,7 @@ pub struct StoreOptions { #[structopt(long, parse(from_os_str))] llvm_debug_dir: Option, - /// Use JIT Engine. - #[structopt(long, conflicts_with_all = &["native", "object_file"])] - jit: bool, - - /// Use Native Engine. - #[structopt(long, conflicts_with_all = &["jit", "object_file"])] - native: bool, - - /// Use ObjectFile Engine. - #[structopt(long, conflicts_with_all = &["jit", "native"])] - object_file: bool, - - /// The deprecated backend flag - Please not use + /// The deprecated backend flag - Please do not use #[structopt(long = "backend", hidden = true, conflicts_with_all = &["singlepass", "cranelift", "llvm"])] backend: Option, @@ -56,80 +63,8 @@ pub struct StoreOptions { features: WasmFeatures, } -/// The compiler used for the store -#[derive(Debug, PartialEq, Eq)] -pub enum CompilerType { - /// Singlepass compiler - Singlepass, - /// Cranelift compiler - Cranelift, - /// LLVM compiler - LLVM, - /// Headless compiler - Headless, -} - -impl CompilerType { - /// Return all enabled compilers - pub fn enabled() -> Vec { - vec![ - #[cfg(feature = "singlepass")] - Self::Singlepass, - #[cfg(feature = "cranelift")] - Self::Cranelift, - #[cfg(feature = "llvm")] - Self::LLVM, - ] - } -} - -impl ToString for CompilerType { - fn to_string(&self) -> String { - match self { - Self::Singlepass => "singlepass".to_string(), - Self::Cranelift => "cranelift".to_string(), - Self::LLVM => "llvm".to_string(), - Self::Headless => "headless".to_string(), - } - } -} - -impl FromStr for CompilerType { - type Err = Error; - fn from_str(s: &str) -> Result { - match s { - "singlepass" => Ok(Self::Singlepass), - "cranelift" => Ok(Self::Cranelift), - "llvm" => Ok(Self::LLVM), - "headless" => Ok(Self::Headless), - backend => bail!("The `{}` compiler does not exist.", backend), - } - } -} - -/// The engine used for the store -#[derive(Debug, PartialEq, Eq)] -pub enum EngineType { - /// JIT Engine - JIT, - /// Native Engine - Native, - /// Object File Engine - ObjectFile, -} - -impl ToString for EngineType { - fn to_string(&self) -> String { - match self { - Self::JIT => "jit".to_string(), - Self::Native => "native".to_string(), - Self::ObjectFile => "objectfile".to_string(), - } - } -} - -#[cfg(all(feature = "compiler", feature = "engine"))] -impl StoreOptions { +#[cfg(feature = "compiler")] +impl CompilerOptions { fn get_compiler(&self) -> Result { if self.cranelift { Ok(CompilerType::Cranelift) @@ -161,7 +96,7 @@ impl StoreOptions { } } - /// Get the Target architecture + /// Get the enaled Wasm features. pub fn get_features(&self, mut features: Features) -> Result { if self.features.threads || self.features.all { features.threads(true); @@ -181,6 +116,63 @@ impl StoreOptions { Ok(features) } + /// Gets the Store for a given target and engine. + pub fn get_store_for_target_and_engine( + &self, + target: Target, + engine_type: EngineType, + ) -> Result<(Store, CompilerType)> { + let (compiler_config, compiler_type) = self.get_compiler_config()?; + let engine = self.get_engine_by_type(target, compiler_config, engine_type)?; + let store = Store::new(&*engine); + Ok((store, compiler_type)) + } + + fn get_engine_by_type( + &self, + target: Target, + compiler_config: Box, + engine_type: EngineType, + ) -> Result> { + let features = self.get_features(compiler_config.default_features_for_target(&target))?; + let engine: Box = match engine_type { + #[cfg(feature = "jit")] + EngineType::JIT => Box::new( + wasmer_engine_jit::JIT::new(&*compiler_config) + .features(features) + .target(target) + .engine(), + ), + #[cfg(feature = "native")] + EngineType::Native => { + let mut compiler_config = compiler_config; + Box::new( + wasmer_engine_native::Native::new(&mut *compiler_config) + .target(target) + .features(features) + .engine(), + ) + } + #[cfg(feature = "object-file")] + EngineType::ObjectFile => { + let mut compiler_config = compiler_config; + Box::new( + wasmer_engine_object_file::ObjectFile::new(&mut *compiler_config) + .target(target) + .features(features) + .engine(), + ) + } + #[cfg(not(all(feature = "jit", feature = "native", feature = "object-file")))] + engine => bail!( + "The `{}` engine is not included in this binary.", + engine.to_string() + ), + }; + + Ok(engine) + } + /// Get the Compiler Config for the current options #[allow(unused_variables)] pub(crate) fn get_compiler_config(&self) -> Result<(Box, CompilerType)> { @@ -315,7 +307,82 @@ impl StoreOptions { #[allow(unreachable_code)] Ok((compiler_config, compiler)) } +} +/// The compiler used for the store +#[derive(Debug, PartialEq, Eq)] +pub enum CompilerType { + /// Singlepass compiler + Singlepass, + /// Cranelift compiler + Cranelift, + /// LLVM compiler + LLVM, + /// Headless compiler + Headless, +} + +impl CompilerType { + /// Return all enabled compilers + pub fn enabled() -> Vec { + vec![ + #[cfg(feature = "singlepass")] + Self::Singlepass, + #[cfg(feature = "cranelift")] + Self::Cranelift, + #[cfg(feature = "llvm")] + Self::LLVM, + ] + } +} + +impl ToString for CompilerType { + fn to_string(&self) -> String { + match self { + Self::Singlepass => "singlepass".to_string(), + Self::Cranelift => "cranelift".to_string(), + Self::LLVM => "llvm".to_string(), + Self::Headless => "headless".to_string(), + } + } +} + +impl FromStr for CompilerType { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "singlepass" => Ok(Self::Singlepass), + "cranelift" => Ok(Self::Cranelift), + "llvm" => Ok(Self::LLVM), + "headless" => Ok(Self::Headless), + backend => bail!("The `{}` compiler does not exist.", backend), + } + } +} + +/// The engine used for the store +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum EngineType { + /// JIT Engine + JIT, + /// Native Engine + Native, + /// Object File Engine + ObjectFile, +} + +impl ToString for EngineType { + fn to_string(&self) -> String { + match self { + Self::JIT => "jit".to_string(), + Self::Native => "native".to_string(), + Self::ObjectFile => "objectfile".to_string(), + } + } +} + +#[cfg(all(feature = "compiler", feature = "engine"))] +impl StoreOptions { /// Gets the store for the host target, with the engine name and compiler name selected pub fn get_store(&self) -> Result<(Store, EngineType, CompilerType)> { let target = Target::default(); @@ -327,7 +394,7 @@ impl StoreOptions { &self, target: Target, ) -> Result<(Store, EngineType, CompilerType)> { - let (compiler_config, compiler_type) = self.get_compiler_config()?; + let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?; let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?; let store = Store::new(&*engine); Ok((store, engine_type, compiler_type)) @@ -339,41 +406,10 @@ impl StoreOptions { compiler_config: Box, ) -> Result<(Box, EngineType)> { let engine_type = self.get_engine()?; - let features = self.get_features(compiler_config.default_features_for_target(&target))?; - let engine: Box = match engine_type { - #[cfg(feature = "jit")] - EngineType::JIT => Box::new( - wasmer_engine_jit::JIT::new(&*compiler_config) - .features(features) - .target(target) - .engine(), - ), - #[cfg(feature = "native")] - EngineType::Native => { - let mut compiler_config = compiler_config; - Box::new( - wasmer_engine_native::Native::new(&mut *compiler_config) - .target(target) - .features(features) - .engine(), - ) - } - #[cfg(feature = "object-file")] - EngineType::ObjectFile => { - let mut compiler_config = compiler_config; - Box::new( - wasmer_engine_object_file::ObjectFile::new(&mut *compiler_config) - .target(target) - .features(features) - .engine(), - ) - } - #[cfg(not(all(feature = "jit", feature = "native", feature = "object-file")))] - engine => bail!( - "The `{}` engine is not included in this binary.", - engine.to_string() - ), - }; + let engine = self + .compiler + .get_engine_by_type(target, compiler_config, engine_type)?; + Ok((engine, engine_type)) } }