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