wasmer_cli/commands/
gen_c_header.rs1use std::path::PathBuf;
2
3use anyhow::{Context, Error};
4use bytes::Bytes;
5use clap::Parser;
6use wasmer_compiler::{
7 Artifact, object::ObjectMetadataBuilder, types::symbols::ModuleMetadataSymbolRegistry,
8};
9use wasmer_package::{package::WasmerPackageError, utils::from_bytes};
10use wasmer_types::target::{CpuFeature, Triple};
11use webc::{Container, ContainerError, DetectError, compat::SharedBytes};
12
13use crate::backend::RuntimeOptions;
14
15#[derive(Debug, Parser)]
16pub struct GenCHeader {
18 #[clap(name = "FILE")]
20 path: PathBuf,
21
22 #[clap(long)]
24 prefix: Option<String>,
25
26 #[clap(long)]
28 atom: Option<String>,
29
30 #[clap(name = "OUTPUT PATH", short = 'o')]
32 output: PathBuf,
33
34 #[clap(long = "target")]
46 target_triple: Option<Triple>,
47
48 #[clap(long, short = 'm', number_of_values = 1)]
49 cpu_features: Vec<CpuFeature>,
50}
51
52impl GenCHeader {
53 pub fn execute(&self) -> Result<(), Error> {
55 let file: Bytes = std::fs::read(&self.path)
56 .with_context(|| format!("Unable to read \"{}\"", self.path.display()))?
57 .into();
58 let prefix = match self.prefix.as_deref() {
59 Some(s) => s.to_string(),
60 None => crate::commands::PrefixMapCompilation::hash_for_bytes(&file),
61 };
62
63 let atom = match from_bytes(file.clone()) {
64 Ok(webc) => self.get_atom(&webc)?,
65 Err(WasmerPackageError::ContainerError(ContainerError::Detect(
66 DetectError::InvalidMagic { .. },
67 ))) => {
68 file.into()
70 }
71 Err(other) => {
72 return Err(Error::new(other).context("Unable to parse the webc file"));
73 }
74 };
75
76 let target_triple = self.target_triple.clone().unwrap_or_else(Triple::host);
77 let target = crate::commands::create_exe::utils::target_triple_to_target(
78 &target_triple,
79 &self.cpu_features,
80 );
81 let engine =
82 RuntimeOptions::default().get_sys_compiler_engine_for_target(target.clone())?;
83 if !engine.is_sys() {
84 anyhow::bail!(
85 "Cannot use this engine to generate c-headers! Please, use one of `cranelift`, `llvm` or `singlepass`."
86 );
87 }
88 let engine = engine.into_sys();
89
90 let engine_inner = engine.inner();
91 let compiler = engine_inner.compiler()?;
92 let features = engine_inner.features();
93 let tunables = engine.tunables();
94 let (metadata, _, _) = Artifact::metadata(
95 compiler,
96 &atom,
97 Some(prefix.as_str()),
98 &target,
99 tunables,
100 features,
101 )
102 .map_err(|e| anyhow::anyhow!("could not generate metadata: {e}"))?;
103
104 let metadata_builder = ObjectMetadataBuilder::new(&metadata, &target_triple)
105 .map_err(|e| anyhow::anyhow!("failed to create relocs builder: {e}"))?;
106 let metadata_length = metadata_builder.placeholder_data().len();
107
108 let header_file_src = crate::c_gen::staticlib_header::generate_header_file(
109 &prefix,
110 &metadata.compile_info.module,
111 &ModuleMetadataSymbolRegistry {
112 prefix: prefix.clone(),
113 },
114 metadata_length,
115 );
116
117 let output = crate::common::normalize_path(&self.output.display().to_string());
118
119 std::fs::write(&output, header_file_src)
120 .map_err(|e| anyhow::anyhow!("{e}"))
121 .with_context(|| anyhow::anyhow!("{output}"))?;
122
123 Ok(())
124 }
125
126 fn get_atom(&self, pirita: &Container) -> Result<SharedBytes, Error> {
127 let atoms = pirita.atoms();
128 let atom_names: Vec<_> = atoms.keys().map(|s| s.as_str()).collect();
129
130 match *atom_names.as_slice() {
131 [] => Err(Error::msg("The file doesn't contain any atoms")),
132 [name] => Ok(atoms[name].clone()),
133 [..] => match &self.atom {
134 Some(name) => atoms
135 .get(name)
136 .cloned()
137 .with_context(|| format!("The file doesn't contain a \"{name}\" atom"))
138 .with_context(|| {
139 format!("-> note: available atoms are: {}", atom_names.join(", "))
140 }),
141 None => {
142 let err = Error::msg("file has multiple atoms, please specify which atom to generate the header file for")
143 .context(format!("-> note: available atoms are: {}", atom_names.join(", ")));
144 Err(err)
145 }
146 },
147 }
148 }
149}