1use crate::cmd::{cfg_spinner, run_stage};
2use crate::install::Tools;
3use crate::parse::{BuildOpts, Opts};
4use crate::thread::{spawn_thread, ThreadHandle};
5use crate::{errors::*, get_user_crate_name};
6use console::{style, Emoji};
7use indicatif::{MultiProgress, ProgressBar};
8use std::path::PathBuf;
9
10static GENERATING: Emoji<'_, '_> = Emoji("🔨", "");
12static BUILDING: Emoji<'_, '_> = Emoji("🏗️ ", ""); macro_rules! handle_exit_code {
16 ($code:expr) => {
17 let (_, _, code) = $code;
18 if code != 0 {
19 return ::std::result::Result::Ok(code);
20 }
21 };
22}
23
24#[allow(clippy::type_complexity)]
30pub fn build_internal(
31 dir: PathBuf,
32 spinners: &MultiProgress,
33 num_steps: u8,
34 is_release: bool,
35 tools: &Tools,
36 global_opts: &Opts,
37) -> Result<
38 (
39 ThreadHandle<impl FnOnce() -> Result<i32, ExecutionError>, Result<i32, ExecutionError>>,
40 ThreadHandle<impl FnOnce() -> Result<i32, ExecutionError>, Result<i32, ExecutionError>>,
41 ),
42 ExecutionError,
43> {
44 let tools = tools.clone();
46 let Opts {
47 mut wasm_release_rustflags,
48 cargo_engine_args,
49 cargo_browser_args,
50 wasm_bindgen_args,
51 wasm_opt_args,
52 verbose,
53 ..
54 } = global_opts.clone();
55 wasm_release_rustflags.push_str(" --cfg=client");
56
57 let crate_name = get_user_crate_name(&dir)?;
58 let sg_msg = format!(
60 "{} {} Generating your app",
61 style(format!("[1/{}]", num_steps)).bold().dim(),
62 GENERATING
63 );
64 let wb_msg = format!(
66 "{} {} Building your app to Wasm",
67 style(format!("[2/{}]", num_steps)).bold().dim(),
68 BUILDING
69 );
70
71 let sg_spinner = spinners.insert(0, ProgressBar::new_spinner());
75 let sg_spinner = cfg_spinner(sg_spinner, &sg_msg);
76 let sg_dir = dir.clone();
77 let wb_spinner = spinners.insert(1, ProgressBar::new_spinner());
78 let wb_spinner = cfg_spinner(wb_spinner, &wb_msg);
79 let wb_dir = dir;
80 let cargo_engine_exec = tools.cargo_engine.clone();
81 let sg_thread = spawn_thread(
82 move || {
83 handle_exit_code!(run_stage(
84 vec![&format!(
85 "{} run {} {}",
86 cargo_engine_exec,
87 if is_release { "--release" } else { "" },
88 cargo_engine_args
89 )],
90 &sg_dir,
91 &sg_spinner,
92 &sg_msg,
93 vec![
94 ("PERSEUS_ENGINE_OPERATION", "build"),
95 ("CARGO_TARGET_DIR", "dist/target_engine"),
96 ("RUSTFLAGS", "--cfg=engine"),
97 ("CARGO_TERM_COLOR", "always")
98 ],
99 verbose,
100 )?);
101
102 Ok(0)
103 },
104 global_opts.sequential,
105 );
106 let wb_thread = spawn_thread(
107 move || {
108 let mut cmds = vec![
109 format!(
111 "{} build --target wasm32-unknown-unknown {} {}",
112 tools.cargo_browser,
113 if is_release { "--release" } else { "" },
114 cargo_browser_args
115 ),
116 format!(
118 "{cmd} ./dist/target_wasm/wasm32-unknown-unknown/{profile}/{crate_name}.wasm --out-dir dist/pkg --out-name perseus_engine --target web {args}",
119 cmd=tools.wasm_bindgen,
120 profile={ if is_release { "release" } else { "debug" } },
121 args=wasm_bindgen_args,
122 crate_name=crate_name
123 )
124 ];
125 if is_release {
127 cmds.push(format!(
128 "{cmd} -Oz ./dist/pkg/perseus_engine_bg.wasm -o ./dist/pkg/perseus_engine_bg.wasm {args}",
129 cmd=tools.wasm_opt,
130 args=wasm_opt_args
131 ));
132 }
133 let cmds = cmds.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
134 handle_exit_code!(run_stage(
135 cmds,
136 &wb_dir,
137 &wb_spinner,
138 &wb_msg,
139 if is_release {
140 vec![
141 ("CARGO_TARGET_DIR", "dist/target_wasm"),
142 ("RUSTFLAGS", &wasm_release_rustflags),
143 ("CARGO_TERM_COLOR", "always"),
144 ]
145 } else {
146 vec![
147 ("CARGO_TARGET_DIR", "dist/target_wasm"),
148 ("RUSTFLAGS", "--cfg=client"),
149 ("CARGO_TERM_COLOR", "always"),
150 ]
151 },
152 verbose,
153 )?);
154
155 Ok(0)
156 },
157 global_opts.sequential,
158 );
159
160 Ok((sg_thread, wb_thread))
161}
162
163pub fn build(
166 dir: PathBuf,
167 opts: &BuildOpts,
168 tools: &Tools,
169 global_opts: &Opts,
170) -> Result<i32, ExecutionError> {
171 let spinners = MultiProgress::new();
172
173 let (sg_thread, wb_thread) =
174 build_internal(dir, &spinners, 2, opts.release, tools, global_opts)?;
175 let sg_res = sg_thread
176 .join()
177 .map_err(|_| ExecutionError::ThreadWaitFailed)??;
178 if sg_res != 0 {
179 return Ok(sg_res);
180 }
181 let wb_res = wb_thread
182 .join()
183 .map_err(|_| ExecutionError::ThreadWaitFailed)??;
184 if wb_res != 0 {
185 return Ok(wb_res);
186 }
187
188 Ok(0)
190}