1#![doc(html_logo_url = "https://gear-tech.io/logo.png")]
20#![doc(html_favicon_url = "https://gear-tech.io/favicon.ico")]
21#![cfg_attr(docsrs, feature(doc_cfg))]
22
23pub use gear_wasm_optimizer::{self as optimize, CargoCommand};
24pub use wasm_project::{PreProcessor, PreProcessorResult, PreProcessorTarget};
25
26use crate::wasm_project::WasmProject;
27use anyhow::Result;
28use regex::Regex;
29use std::{env, path::PathBuf, process};
30
31mod builder_error;
32pub mod code_validator;
33mod crate_info;
34mod multiple_crate_versions;
35mod smart_fs;
36mod wasm_project;
37
38pub const TARGET: &str = env!("TARGET");
39
40pub struct WasmBuilder {
42 wasm_project: WasmProject,
43 cargo: CargoCommand,
44 excluded_features: Vec<&'static str>,
45}
46
47impl WasmBuilder {
48 pub fn new() -> Self {
50 WasmBuilder::create(WasmProject::new())
51 }
52
53 fn create(wasm_project: WasmProject) -> Self {
54 WasmBuilder {
55 wasm_project,
56 cargo: CargoCommand::new(),
57 excluded_features: vec![],
58 }
59 }
60
61 pub fn exclude_features(mut self, features: impl Into<Vec<&'static str>>) -> Self {
63 self.excluded_features = features.into();
64 self
65 }
66
67 pub fn with_pre_processor(mut self, pre_processor: Box<dyn PreProcessor>) -> Self {
69 self.wasm_project.add_preprocessor(pre_processor);
70 self
71 }
72
73 pub fn with_recommended_toolchain(mut self) -> Self {
75 self.cargo.set_check_recommended_toolchain(true);
76 self
77 }
78
79 #[doc(hidden)]
86 pub fn with_forced_recommended_toolchain(mut self) -> Self {
87 self.cargo.set_force_recommended_toolchain(true);
88 self
89 }
90
91 pub fn build(self) -> Option<(PathBuf, PathBuf)> {
97 if env::var("__GEAR_WASM_BUILDER_NO_BUILD").is_ok() || is_intellij_sync() {
98 _ = self.wasm_project.provide_dummy_wasm_binary_if_not_exist();
99 return None;
100 }
101
102 match self.build_project() {
103 Err(e) => {
104 eprintln!("error: {e}");
105 e.chain()
106 .skip(1)
107 .for_each(|cause| eprintln!("| {cause}"));
108 process::exit(1);
109 }
110 Ok(r) => r,
111 }
112 }
113
114 fn build_project(mut self) -> Result<Option<(PathBuf, PathBuf)>> {
115 self.wasm_project.generate()?;
116
117 self.cargo
118 .set_manifest_path(self.wasm_project.manifest_path());
119 self.cargo.set_target_dir(self.wasm_project.target_dir());
120 let profile = self.wasm_project.profile();
121 let profile = if profile == "debug" { "dev" } else { profile };
122 self.cargo.set_profile(profile.to_string());
123 self.cargo.set_features(&self.enabled_features()?);
124
125 self.cargo.run()?;
126 self.wasm_project.postprocess()
127 }
128
129 fn manifest_path(&self) -> Result<String> {
130 let manifest_path = env::var("CARGO_MANIFEST_DIR")?;
131 Ok(manifest_path)
132 }
133
134 fn enabled_features(&self) -> Result<Vec<String>> {
136 let project_features = self.wasm_project.features();
137 let enabled_features_iter = env::vars().filter_map(|(key, _)| {
138 key.strip_prefix("CARGO_FEATURE_")
139 .map(|feature| feature.to_lowercase())
140 });
141 let mut matched_features = Vec::new();
142 let mut unmatched_features = Vec::new();
143 for enabled_feature in enabled_features_iter {
144 let enabled_feature_regex =
147 Regex::new(&format!("^{}$", enabled_feature.replace('_', "[-_]")))?;
148 if self
149 .excluded_features
150 .iter()
151 .any(|excluded_feature| enabled_feature_regex.is_match(excluded_feature))
152 {
153 continue;
154 }
155 if let Some(project_feature) = project_features
156 .iter()
157 .find(|project_feature| enabled_feature_regex.is_match(project_feature))
158 {
159 matched_features.push(project_feature.clone());
160 } else {
161 unmatched_features.push(enabled_feature);
162 }
163 }
164
165 if !unmatched_features.is_empty() && unmatched_features != ["default"] {
174 println!(
175 "cargo:warning=Package {}: features `{}` are not available and will be ignored",
176 self.manifest_path()?,
177 unmatched_features.join(", ")
178 );
179 }
180
181 Ok(matched_features
188 .into_iter()
189 .filter(|feature| feature != "gcli")
190 .collect())
191 }
192}
193
194impl Default for WasmBuilder {
195 fn default() -> Self {
196 Self::new()
197 }
198}
199
200fn is_intellij_sync() -> bool {
201 env::var("RUSTC_WRAPPER")
203 .unwrap_or_default()
204 .contains("intellij")
205}
206
207const FEATURES_TO_EXCLUDE_BY_DEFAULT: &[&str] = &["std"];
211
212pub fn build() -> Option<(PathBuf, PathBuf)> {
216 WasmBuilder::new()
217 .exclude_features(FEATURES_TO_EXCLUDE_BY_DEFAULT.to_vec())
218 .build()
219}
220
221pub fn recommended_nightly() -> Option<(PathBuf, PathBuf)> {
225 WasmBuilder::new()
226 .exclude_features(FEATURES_TO_EXCLUDE_BY_DEFAULT.to_vec())
227 .with_recommended_toolchain()
228 .build()
229}