1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#![allow(dead_code)]
//! Create a standalone native executable for a given Wasm file.

use std::{env, path::PathBuf};

use anyhow::{Context, Result};
use clap::Parser;
use wasmer::*;
use wasmer_package::utils::from_disk;

use crate::store::CompilerOptions;

#[derive(Debug, Parser)]
/// The options for the `wasmer create-exe` subcommand
pub struct CreateObj {
    /// Input file
    #[clap(name = "FILE")]
    path: PathBuf,

    /// Output file or directory if the input is a pirita file
    #[clap(name = "OUTPUT_PATH", short = 'o')]
    output: PathBuf,

    /// Optional directorey used for debugging: if present, will
    /// output the files to a debug instead of a temp directory
    #[clap(long, name = "DEBUG PATH")]
    debug_dir: Option<PathBuf>,

    /// Prefix for the function names in the input file in the compiled object file.
    ///
    /// Default value = sha256 of the input file
    #[clap(long, name = "PREFIX")]
    prefix: Option<String>,

    /// Atom name to compile when compiling multi-atom pirita files
    #[clap(long, name = "ATOM")]
    atom: Option<String>,

    /// Compilation Target triple
    ///
    /// Accepted target triple values must follow the
    /// ['target_lexicon'](https://crates.io/crates/target-lexicon) crate format.
    ///
    /// The recommended targets we try to support are:
    ///
    /// - "x86_64-linux-gnu"
    /// - "aarch64-linux-gnu"
    /// - "x86_64-apple-darwin"
    /// - "arm64-apple-darwin"
    /// - "x86_64-windows-gnu"
    #[clap(long = "target")]
    target_triple: Option<Triple>,

    #[clap(long, short = 'm', number_of_values = 1)]
    cpu_features: Vec<CpuFeature>,

    #[clap(flatten)]
    compiler: CompilerOptions,
}

impl CreateObj {
    /// Runs logic for the `create-obj` subcommand
    pub fn execute(&self) -> Result<()> {
        let path = crate::common::normalize_path(&format!("{}", self.path.display()));
        let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host);
        let starting_cd = env::current_dir()?;
        let input_path = starting_cd.join(path);
        let temp_dir = tempfile::tempdir();
        let output_directory_path = match self.debug_dir.as_ref() {
            Some(s) => s.clone(),
            None => temp_dir?.path().to_path_buf(),
        };
        std::fs::create_dir_all(&output_directory_path)?;
        let prefix = match self.prefix.as_ref() {
            Some(s) => vec![s.clone()],
            None => Vec::new(),
        };

        let target = crate::commands::create_exe::utils::target_triple_to_target(
            &target_triple,
            &self.cpu_features,
        );
        let (_, compiler_type) = self.compiler.get_store_for_target(target.clone())?;
        println!("Compiler: {compiler_type}");
        println!("Target: {}", target.triple());

        let atoms = if let Ok(webc) = from_disk(&input_path) {
            crate::commands::create_exe::compile_pirita_into_directory(
                &webc,
                &output_directory_path,
                &self.compiler,
                &self.cpu_features,
                &target_triple,
                &prefix,
                crate::commands::AllowMultiWasm::Reject(self.atom.clone()),
                self.debug_dir.is_some(),
            )
        } else {
            crate::commands::create_exe::prepare_directory_from_single_wasm_file(
                &input_path,
                &output_directory_path,
                &self.compiler,
                &target_triple,
                &self.cpu_features,
                &prefix,
                self.debug_dir.is_some(),
            )
        }?;

        // Copy output files into target path, depending on whether
        // there are one or many files being compiled
        let file_paths = std::fs::read_dir(output_directory_path.join("atoms"))
            .map_err(|e| {
                anyhow::anyhow!(
                    "could not read {}: {e}",
                    output_directory_path.join("atoms").display()
                )
            })?
            .filter_map(|path| path.ok()?.path().canonicalize().ok())
            .collect::<Vec<_>>();

        if file_paths.is_empty() {
            return Err(anyhow::anyhow!(
                "could not compile object file: no output objects in {}",
                output_directory_path.join("atoms").display()
            ));
        }

        if file_paths.len() == 1 {
            if let Some(parent) = self.output.parent() {
                std::fs::create_dir_all(parent)?;
            }
            std::fs::copy(
                std::env::current_dir().unwrap().join(&file_paths[0]),
                std::env::current_dir().unwrap().join(&self.output),
            )
            .map_err(|e| {
                anyhow::anyhow!(
                    "{} -> {}: {e}",
                    &file_paths[0].display(),
                    self.output.display()
                )
            })?;
        } else {
            let keys = atoms
                .iter()
                .map(|(name, _)| name.clone())
                .collect::<Vec<_>>();
            return Err(anyhow::anyhow!(
                "where <ATOM> is one of: {}",
                keys.join(", ")
            ))
            .context(anyhow::anyhow!(
                "note: use --atom <ATOM> to specify which atom to compile"
            ))
            .context(anyhow::anyhow!(
                "cannot compile more than one atom at a time"
            ));
        }

        let output_file = self.output.canonicalize().unwrap().display().to_string();
        let output_file = output_file
            .strip_prefix(r"\\?\")
            .unwrap_or(&output_file)
            .to_string();

        eprintln!("✔ Object compiled successfully to `{output_file}`");

        Ok(())
    }
}