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 directory used for debugging: if present, will
25    /// output the files to a debug instead of a temporary 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    /// - "arm64-apple-darwin"
49    /// - "x86_64-windows-gnu"
50    #[clap(long = "target")]
51    target_triple: Option<Triple>,
52
53    #[clap(long, short = 'm', number_of_values = 1)]
54    cpu_features: Vec<CpuFeature>,
55
56    #[clap(flatten)]
57    rt: RuntimeOptions,
58}
59
60impl CreateObj {
61    /// Runs logic for the `create-obj` subcommand
62    pub fn execute(&self) -> Result<()> {
63        let path = crate::common::normalize_path(&format!("{}", self.path.display()));
64        let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host);
65        let starting_cd = env::current_dir()?;
66        let input_path = starting_cd.join(path);
67        let temp_dir = tempfile::tempdir();
68        let output_directory_path = match self.debug_dir.as_ref() {
69            Some(s) => s.clone(),
70            None => temp_dir?.path().to_path_buf(),
71        };
72        std::fs::create_dir_all(&output_directory_path)?;
73        let prefix = match self.prefix.as_ref() {
74            Some(s) => vec![s.clone()],
75            None => Vec::new(),
76        };
77
78        let target = crate::commands::create_exe::utils::target_triple_to_target(
79            &target_triple,
80            &self.cpu_features,
81        );
82        // let compiler_type = self.rt.get_available_backends()?.get(0).unwrap();
83        // match compiler_type {
84        //     crate::backend::BackendType::Cranelift
85        //     | crate::backend::BackendType::LLVM
86        //     | crate::backend::BackendType::Singlepass=> {
87        //     },
88        //     _ => {
89        //         anyhow::bail!("Cannot produce objects with {compiler_type}!")
90        //     }
91        // }
92        // println!("Compiler: {compiler_type}");
93
94        println!("Target: {}", target.triple());
95
96        let atoms = if let Ok(webc) = from_disk(&input_path) {
97            crate::commands::create_exe::compile_pirita_into_directory(
98                &webc,
99                &output_directory_path,
100                &self.rt,
101                &self.cpu_features,
102                &target_triple,
103                &prefix,
104                crate::commands::AllowMultiWasm::Reject(self.atom.clone()),
105                self.debug_dir.is_some(),
106            )
107        } else {
108            crate::commands::create_exe::prepare_directory_from_single_wasm_file(
109                &input_path,
110                &output_directory_path,
111                &self.rt,
112                &target_triple,
113                &self.cpu_features,
114                &prefix,
115                self.debug_dir.is_some(),
116            )
117        }?;
118
119        // Copy output files into target path, depending on whether
120        // there are one or many files being compiled
121        let file_paths = std::fs::read_dir(output_directory_path.join("atoms"))
122            .map_err(|e| {
123                anyhow::anyhow!(
124                    "could not read {}: {e}",
125                    output_directory_path.join("atoms").display()
126                )
127            })?
128            .filter_map(|path| path.ok()?.path().canonicalize().ok())
129            .collect::<Vec<_>>();
130
131        if file_paths.is_empty() {
132            return Err(anyhow::anyhow!(
133                "could not compile object file: no output objects in {}",
134                output_directory_path.join("atoms").display()
135            ));
136        }
137
138        if file_paths.len() == 1 {
139            if let Some(parent) = self.output.parent() {
140                std::fs::create_dir_all(parent)?;
141            }
142            std::fs::copy(
143                std::env::current_dir().unwrap().join(&file_paths[0]),
144                std::env::current_dir().unwrap().join(&self.output),
145            )
146            .map_err(|e| {
147                anyhow::anyhow!(
148                    "{} -> {}: {e}",
149                    &file_paths[0].display(),
150                    self.output.display()
151                )
152            })?;
153        } else {
154            let keys = atoms
155                .iter()
156                .map(|(name, _)| name.clone())
157                .collect::<Vec<_>>();
158            return Err(anyhow::anyhow!(
159                "where <ATOM> is one of: {}",
160                keys.join(", ")
161            ))
162            .context(anyhow::anyhow!(
163                "note: use --atom <ATOM> to specify which atom to compile"
164            ))
165            .context(anyhow::anyhow!(
166                "cannot compile more than one atom at a time"
167            ));
168        }
169
170        let output_file = self.output.canonicalize().unwrap().display().to_string();
171        let output_file = output_file
172            .strip_prefix(r"\\?\")
173            .unwrap_or(&output_file)
174            .to_string();
175
176        eprintln!("✔ Object compiled successfully to `{output_file}`");
177
178        Ok(())
179    }
180}