wasmer_cli/commands/
create_obj.rs

1#![allow(dead_code)]
2//! Create a standalone native executable for a given Wasm file.
3
4use std::{env, path::PathBuf};
5
6use anyhow::{Context, Result};
7use clap::Parser;
8use wasmer::sys::*;
9use wasmer_package::utils::from_disk;
10
11use crate::backend::RuntimeOptions;
12
13#[derive(Debug, Parser)]
14/// The options for the `wasmer create-exe` subcommand
15pub struct CreateObj {
16    /// Input file
17    #[clap(name = "FILE")]
18    path: PathBuf,
19
20    /// Output file or directory if the input is a pirita file
21    #[clap(name = "OUTPUT_PATH", short = 'o')]
22    output: PathBuf,
23
24    /// Optional directorey used for debugging: if present, will
25    /// output the files to a debug instead of a temp directory
26    #[clap(long, name = "DEBUG PATH")]
27    debug_dir: Option<PathBuf>,
28
29    /// Prefix for the function names in the input file in the compiled object file.
30    ///
31    /// Default value = sha256 of the input file
32    #[clap(long, name = "PREFIX")]
33    prefix: Option<String>,
34
35    /// Atom name to compile when compiling multi-atom pirita files
36    #[clap(long, name = "ATOM")]
37    atom: Option<String>,
38
39    /// Compilation Target triple
40    ///
41    /// Accepted target triple values must follow the
42    /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format.
43    ///
44    /// The recommended targets we try to support are:
45    ///
46    /// - "x86_64-linux-gnu"
47    /// - "aarch64-linux-gnu"
48    /// - "x86_64-apple-darwin"
49    /// - "arm64-apple-darwin"
50    /// - "x86_64-windows-gnu"
51    #[clap(long = "target")]
52    target_triple: Option<Triple>,
53
54    #[clap(long, short = 'm', number_of_values = 1)]
55    cpu_features: Vec<CpuFeature>,
56
57    #[clap(flatten)]
58    rt: RuntimeOptions,
59}
60
61impl CreateObj {
62    /// Runs logic for the `create-obj` subcommand
63    pub fn execute(&self) -> Result<()> {
64        let path = crate::common::normalize_path(&format!("{}", self.path.display()));
65        let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host);
66        let starting_cd = env::current_dir()?;
67        let input_path = starting_cd.join(path);
68        let temp_dir = tempfile::tempdir();
69        let output_directory_path = match self.debug_dir.as_ref() {
70            Some(s) => s.clone(),
71            None => temp_dir?.path().to_path_buf(),
72        };
73        std::fs::create_dir_all(&output_directory_path)?;
74        let prefix = match self.prefix.as_ref() {
75            Some(s) => vec![s.clone()],
76            None => Vec::new(),
77        };
78
79        let target = crate::commands::create_exe::utils::target_triple_to_target(
80            &target_triple,
81            &self.cpu_features,
82        );
83        // let compiler_type = self.rt.get_available_backends()?.get(0).unwrap();
84        // match compiler_type {
85        //     crate::backend::BackendType::Cranelift
86        //     | crate::backend::BackendType::LLVM
87        //     | crate::backend::BackendType::Singlepass=> {
88        //     },
89        //     _ => {
90        //         anyhow::bail!("Cannot produce objects with {compiler_type}!")
91        //     }
92        // }
93        // println!("Compiler: {compiler_type}");
94
95        println!("Target: {}", target.triple());
96
97        let atoms = if let Ok(webc) = from_disk(&input_path) {
98            crate::commands::create_exe::compile_pirita_into_directory(
99                &webc,
100                &output_directory_path,
101                &self.rt,
102                &self.cpu_features,
103                &target_triple,
104                &prefix,
105                crate::commands::AllowMultiWasm::Reject(self.atom.clone()),
106                self.debug_dir.is_some(),
107            )
108        } else {
109            crate::commands::create_exe::prepare_directory_from_single_wasm_file(
110                &input_path,
111                &output_directory_path,
112                &self.rt,
113                &target_triple,
114                &self.cpu_features,
115                &prefix,
116                self.debug_dir.is_some(),
117            )
118        }?;
119
120        // Copy output files into target path, depending on whether
121        // there are one or many files being compiled
122        let file_paths = std::fs::read_dir(output_directory_path.join("atoms"))
123            .map_err(|e| {
124                anyhow::anyhow!(
125                    "could not read {}: {e}",
126                    output_directory_path.join("atoms").display()
127                )
128            })?
129            .filter_map(|path| path.ok()?.path().canonicalize().ok())
130            .collect::<Vec<_>>();
131
132        if file_paths.is_empty() {
133            return Err(anyhow::anyhow!(
134                "could not compile object file: no output objects in {}",
135                output_directory_path.join("atoms").display()
136            ));
137        }
138
139        if file_paths.len() == 1 {
140            if let Some(parent) = self.output.parent() {
141                std::fs::create_dir_all(parent)?;
142            }
143            std::fs::copy(
144                std::env::current_dir().unwrap().join(&file_paths[0]),
145                std::env::current_dir().unwrap().join(&self.output),
146            )
147            .map_err(|e| {
148                anyhow::anyhow!(
149                    "{} -> {}: {e}",
150                    &file_paths[0].display(),
151                    self.output.display()
152                )
153            })?;
154        } else {
155            let keys = atoms
156                .iter()
157                .map(|(name, _)| name.clone())
158                .collect::<Vec<_>>();
159            return Err(anyhow::anyhow!(
160                "where <ATOM> is one of: {}",
161                keys.join(", ")
162            ))
163            .context(anyhow::anyhow!(
164                "note: use --atom <ATOM> to specify which atom to compile"
165            ))
166            .context(anyhow::anyhow!(
167                "cannot compile more than one atom at a time"
168            ));
169        }
170
171        let output_file = self.output.canonicalize().unwrap().display().to_string();
172        let output_file = output_file
173            .strip_prefix(r"\\?\")
174            .unwrap_or(&output_file)
175            .to_string();
176
177        eprintln!("✔ Object compiled successfully to `{output_file}`");
178
179        Ok(())
180    }
181}