wasmer_cli/commands/run/
target.rs

1use std::{
2    fs::File,
3    io::Read as _,
4    path::{Path, PathBuf},
5    sync::Arc,
6};
7
8use anyhow::{Context as _, Error, bail};
9use indicatif::ProgressBar;
10use wasmer::Module;
11#[cfg(feature = "compiler")]
12use wasmer_compiler::ArtifactBuild;
13use wasmer_types::ModuleHash;
14use wasmer_wasix::{
15    Runtime,
16    bin_factory::BinaryPackage,
17    runtime::{module_cache::HashedModuleData, task_manager::VirtualTaskManagerExt as _},
18};
19
20/// We've been given the path for a file... What does it contain and how should
21/// that be run?
22#[derive(Debug, Clone)]
23pub enum TargetOnDisk {
24    WebAssemblyBinary,
25    Wat,
26    LocalWebc,
27    Artifact,
28}
29
30impl TargetOnDisk {
31    pub fn from_file(path: &Path) -> Result<TargetOnDisk, Error> {
32        // Normally the first couple hundred bytes is enough to figure
33        // out what type of file this is.
34        let mut buffer = [0_u8; 512];
35
36        let mut f = File::open(path)
37            .with_context(|| format!("Unable to open \"{}\" for reading", path.display()))?;
38        let bytes_read = f.read(&mut buffer)?;
39
40        let leading_bytes = &buffer[..bytes_read];
41
42        if wasmer::is_wasm(leading_bytes) {
43            return Ok(TargetOnDisk::WebAssemblyBinary);
44        }
45
46        if webc::detect(leading_bytes).is_ok() {
47            return Ok(TargetOnDisk::LocalWebc);
48        }
49
50        #[cfg(feature = "compiler")]
51        if ArtifactBuild::is_deserializable(leading_bytes) {
52            return Ok(TargetOnDisk::Artifact);
53        }
54
55        // If we can't figure out the file type based on its content, fall back
56        // to checking the extension.
57
58        match path.extension().and_then(|s| s.to_str()) {
59            Some("wat") => Ok(TargetOnDisk::Wat),
60            Some("wasm") => Ok(TargetOnDisk::WebAssemblyBinary),
61            Some("webc") => Ok(TargetOnDisk::LocalWebc),
62            Some("wasmu") => Ok(TargetOnDisk::WebAssemblyBinary),
63            _ => bail!("Unable to determine how to execute \"{}\"", path.display()),
64        }
65    }
66}
67
68#[allow(clippy::large_enum_variant)]
69#[derive(Debug, Clone)]
70pub enum ExecutableTarget {
71    WebAssembly {
72        module: Module,
73        module_hash: ModuleHash,
74        path: PathBuf,
75    },
76    Package(Box<BinaryPackage>),
77}
78
79impl ExecutableTarget {
80    /// Try to load a Wasmer package from a directory containing a `wasmer.toml`
81    /// file.
82    #[tracing::instrument(level = "debug", skip_all)]
83    pub fn from_dir(
84        dir: &Path,
85        runtime: &Arc<dyn Runtime + Send + Sync>,
86        pb: &ProgressBar,
87    ) -> Result<Self, Error> {
88        pb.set_message(format!("Loading \"{}\" into memory", dir.display()));
89        pb.set_message("Resolving dependencies");
90        let inner_runtime = runtime.clone();
91        let pkg = runtime.task_manager().spawn_and_block_on({
92            let path = dir.to_path_buf();
93
94            async move { BinaryPackage::from_dir(&path, inner_runtime.as_ref()).await }
95        })??;
96
97        Ok(ExecutableTarget::Package(Box::new(pkg)))
98    }
99
100    /// Try to load a file into something that can be used to run it.
101    #[tracing::instrument(level = "debug", skip_all)]
102    pub fn from_file(
103        path: &Path,
104        runtime: &Arc<dyn Runtime + Send + Sync>,
105        pb: &ProgressBar,
106    ) -> Result<Self, Error> {
107        pb.set_message(format!("Loading from \"{}\"", path.display()));
108
109        match TargetOnDisk::from_file(path)? {
110            TargetOnDisk::WebAssemblyBinary | TargetOnDisk::Wat => {
111                let wasm = std::fs::read(path)?;
112                let module_data = HashedModuleData::new(wasm);
113                let module_hash = *module_data.hash();
114
115                pb.set_message("Compiling to WebAssembly");
116                let module = runtime
117                    .load_hashed_module_sync(module_data, None)
118                    .with_context(|| format!("Unable to compile \"{}\"", path.display()))?;
119
120                Ok(ExecutableTarget::WebAssembly {
121                    module,
122                    module_hash,
123                    path: path.to_path_buf(),
124                })
125            }
126            TargetOnDisk::Artifact => {
127                let engine = runtime.engine();
128                pb.set_message("Deserializing pre-compiled WebAssembly module");
129                let module = unsafe { Module::deserialize_from_file(&engine, path)? };
130
131                let module_hash = module.info().hash.ok_or_else(|| {
132                    anyhow::Error::msg("module hash is not present in the artifact")
133                })?;
134
135                Ok(ExecutableTarget::WebAssembly {
136                    module,
137                    module_hash,
138                    path: path.to_path_buf(),
139                })
140            }
141            TargetOnDisk::LocalWebc => {
142                let container = wasmer_package::utils::from_disk(path)?;
143                pb.set_message("Resolving dependencies");
144
145                let inner_runtime = runtime.clone();
146                let pkg = runtime.task_manager().spawn_and_block_on(async move {
147                    BinaryPackage::from_webc(&container, inner_runtime.as_ref()).await
148                })??;
149                Ok(ExecutableTarget::Package(Box::new(pkg)))
150            }
151        }
152    }
153}