builder.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. use crate::{
  2. config::{CrateConfig, ExecutableType},
  3. error::{Error, Result},
  4. tools::Tool,
  5. DioxusConfig,
  6. };
  7. use cargo_metadata::{diagnostic::Diagnostic, Message};
  8. use indicatif::{ProgressBar, ProgressStyle};
  9. use serde::Serialize;
  10. use std::{
  11. fs::{copy, create_dir_all, File},
  12. io::Read,
  13. panic,
  14. path::PathBuf,
  15. process::Command,
  16. time::Duration,
  17. };
  18. use wasm_bindgen_cli_support::Bindgen;
  19. #[derive(Serialize, Debug, Clone)]
  20. pub struct BuildResult {
  21. pub warnings: Vec<Diagnostic>,
  22. pub elapsed_time: u128,
  23. }
  24. pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
  25. // [1] Build the project with cargo, generating a wasm32-unknown-unknown target (is there a more specific, better target to leverage?)
  26. // [2] Generate the appropriate build folders
  27. // [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm
  28. // [4] Wasm-opt the .wasm file with whatever optimizations need to be done
  29. // [5][OPTIONAL] Builds the Tailwind CSS file using the Tailwind standalone binary
  30. // [6] Link up the html page to the wasm module
  31. let CrateConfig {
  32. out_dir,
  33. crate_dir,
  34. target_dir,
  35. asset_dir,
  36. executable,
  37. dioxus_config,
  38. ..
  39. } = config;
  40. // start to build the assets
  41. let ignore_files = build_assets(config)?;
  42. let t_start = std::time::Instant::now();
  43. // [1] Build the .wasm module
  44. log::info!("🚅 Running build command...");
  45. let cmd = subprocess::Exec::cmd("cargo");
  46. let cmd = cmd
  47. .cwd(&crate_dir)
  48. .arg("build")
  49. .arg("--target")
  50. .arg("wasm32-unknown-unknown")
  51. .arg("--message-format=json");
  52. let cmd = if config.release {
  53. cmd.arg("--release")
  54. } else {
  55. cmd
  56. };
  57. let cmd = if config.verbose {
  58. cmd.arg("--verbose")
  59. } else {
  60. cmd
  61. };
  62. let cmd = if quiet { cmd.arg("--quiet") } else { cmd };
  63. let cmd = if config.custom_profile.is_some() {
  64. let custom_profile = config.custom_profile.as_ref().unwrap();
  65. cmd.arg("--profile").arg(custom_profile)
  66. } else {
  67. cmd
  68. };
  69. let cmd = if config.features.is_some() {
  70. let features_str = config.features.as_ref().unwrap().join(" ");
  71. cmd.arg("--features").arg(features_str)
  72. } else {
  73. cmd
  74. };
  75. let cmd = match executable {
  76. ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
  77. ExecutableType::Lib(name) => cmd.arg("--lib").arg(name),
  78. ExecutableType::Example(name) => cmd.arg("--example").arg(name),
  79. };
  80. let warning_messages = prettier_build(cmd)?;
  81. // [2] Establish the output directory structure
  82. let bindgen_outdir = out_dir.join("assets").join("dioxus");
  83. let release_type = match config.release {
  84. true => "release",
  85. false => "debug",
  86. };
  87. let input_path = match executable {
  88. ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir
  89. .join(format!("wasm32-unknown-unknown/{}", release_type))
  90. .join(format!("{}.wasm", name)),
  91. ExecutableType::Example(name) => target_dir
  92. .join(format!("wasm32-unknown-unknown/{}/examples", release_type))
  93. .join(format!("{}.wasm", name)),
  94. };
  95. let bindgen_result = panic::catch_unwind(move || {
  96. // [3] Bindgen the final binary for use easy linking
  97. let mut bindgen_builder = Bindgen::new();
  98. bindgen_builder
  99. .input_path(input_path)
  100. .web(true)
  101. .unwrap()
  102. .debug(true)
  103. .demangle(true)
  104. .keep_debug(true)
  105. .remove_name_section(false)
  106. .remove_producers_section(false)
  107. .out_name(&dioxus_config.application.name)
  108. .generate(&bindgen_outdir)
  109. .unwrap();
  110. });
  111. if bindgen_result.is_err() {
  112. return Err(Error::BuildFailed("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.81` Bindgen crate.".to_string()));
  113. }
  114. // check binaryen:wasm-opt tool
  115. let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default();
  116. if dioxus_tools.contains_key("binaryen") {
  117. let info = dioxus_tools.get("binaryen").unwrap();
  118. let binaryen = crate::tools::Tool::Binaryen;
  119. if binaryen.is_installed() {
  120. if let Some(sub) = info.as_table() {
  121. if sub.contains_key("wasm_opt")
  122. && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false)
  123. {
  124. log::info!("Optimizing WASM size with wasm-opt...");
  125. let target_file = out_dir
  126. .join("assets")
  127. .join("dioxus")
  128. .join(format!("{}_bg.wasm", dioxus_config.application.name));
  129. if target_file.is_file() {
  130. let mut args = vec![
  131. target_file.to_str().unwrap(),
  132. "-o",
  133. target_file.to_str().unwrap(),
  134. ];
  135. if config.release == true {
  136. args.push("-Oz");
  137. }
  138. binaryen.call("wasm-opt", args)?;
  139. }
  140. }
  141. }
  142. } else {
  143. log::warn!(
  144. "Binaryen tool not found, you can use `dioxus tool add binaryen` to install it."
  145. );
  146. }
  147. }
  148. // [5][OPTIONAL] If tailwind is enabled and installed we run it to generate the CSS
  149. if dioxus_tools.contains_key("tailwindcss") {
  150. let info = dioxus_tools.get("tailwindcss").unwrap();
  151. let tailwind = crate::tools::Tool::Tailwind;
  152. if tailwind.is_installed() {
  153. if let Some(sub) = info.as_table() {
  154. log::info!("Building Tailwind bundle CSS file...");
  155. let input_path = match sub.get("input") {
  156. Some(val) => val.as_str().unwrap(),
  157. None => "./public",
  158. };
  159. let config_path = match sub.get("config") {
  160. Some(val) => val.as_str().unwrap(),
  161. None => "./src/tailwind.config.js",
  162. };
  163. let mut args = vec![
  164. "-i",
  165. input_path,
  166. "-o",
  167. "dist/tailwind.css",
  168. "-c",
  169. config_path,
  170. ];
  171. if config.release == true {
  172. args.push("--minify");
  173. }
  174. tailwind.call("tailwindcss", args)?;
  175. }
  176. } else {
  177. log::warn!(
  178. "Tailwind tool not found, you can use `dioxus tool add tailwindcss` to install it."
  179. );
  180. }
  181. }
  182. // this code will copy all public file to the output dir
  183. let copy_options = fs_extra::dir::CopyOptions {
  184. overwrite: true,
  185. skip_exist: false,
  186. buffer_size: 64000,
  187. copy_inside: false,
  188. content_only: false,
  189. depth: 0,
  190. };
  191. if asset_dir.is_dir() {
  192. for entry in std::fs::read_dir(&asset_dir)? {
  193. let path = entry?.path();
  194. if path.is_file() {
  195. std::fs::copy(&path, out_dir.join(path.file_name().unwrap()))?;
  196. } else {
  197. match fs_extra::dir::copy(&path, out_dir, &copy_options) {
  198. Ok(_) => {}
  199. Err(_e) => {
  200. log::warn!("Error copying dir: {}", _e);
  201. }
  202. }
  203. for ignore in &ignore_files {
  204. let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
  205. let ignore = config.out_dir.join(ignore);
  206. if ignore.is_file() {
  207. std::fs::remove_file(ignore)?;
  208. }
  209. }
  210. }
  211. }
  212. }
  213. let t_end = std::time::Instant::now();
  214. Ok(BuildResult {
  215. warnings: warning_messages,
  216. elapsed_time: (t_end - t_start).as_millis(),
  217. })
  218. }
  219. pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<()> {
  220. log::info!("🚅 Running build [Desktop] command...");
  221. let ignore_files = build_assets(config)?;
  222. let mut cmd = Command::new("cargo");
  223. cmd.current_dir(&config.crate_dir)
  224. .arg("build")
  225. .stdout(std::process::Stdio::inherit())
  226. .stderr(std::process::Stdio::inherit());
  227. if config.release {
  228. cmd.arg("--release");
  229. }
  230. if config.verbose {
  231. cmd.arg("--verbose");
  232. }
  233. if config.custom_profile.is_some() {
  234. let custom_profile = config.custom_profile.as_ref().unwrap();
  235. cmd.arg("--profile");
  236. cmd.arg(custom_profile);
  237. }
  238. if config.features.is_some() {
  239. let features_str = config.features.as_ref().unwrap().join(" ");
  240. cmd.arg("--features");
  241. cmd.arg(features_str);
  242. }
  243. match &config.executable {
  244. crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
  245. crate::ExecutableType::Lib(name) => cmd.arg("--lib").arg(name),
  246. crate::ExecutableType::Example(name) => cmd.arg("--example").arg(name),
  247. };
  248. let output = cmd.output()?;
  249. if !output.status.success() {
  250. return Err(Error::BuildFailed("Program build failed.".into()));
  251. }
  252. if output.status.success() {
  253. let release_type = match config.release {
  254. true => "release",
  255. false => "debug",
  256. };
  257. let file_name: String;
  258. let mut res_path = match &config.executable {
  259. crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => {
  260. file_name = name.clone();
  261. config.target_dir.join(release_type).join(name)
  262. }
  263. crate::ExecutableType::Example(name) => {
  264. file_name = name.clone();
  265. config
  266. .target_dir
  267. .join(release_type)
  268. .join("examples")
  269. .join(name)
  270. }
  271. };
  272. let target_file = if cfg!(windows) {
  273. res_path.set_extension("exe");
  274. format!("{}.exe", &file_name)
  275. } else {
  276. file_name
  277. };
  278. if !config.out_dir.is_dir() {
  279. create_dir_all(&config.out_dir)?;
  280. }
  281. copy(res_path, &config.out_dir.join(target_file))?;
  282. // this code will copy all public file to the output dir
  283. if config.asset_dir.is_dir() {
  284. let copy_options = fs_extra::dir::CopyOptions {
  285. overwrite: true,
  286. skip_exist: false,
  287. buffer_size: 64000,
  288. copy_inside: false,
  289. content_only: false,
  290. depth: 0,
  291. };
  292. for entry in std::fs::read_dir(&config.asset_dir)? {
  293. let path = entry?.path();
  294. if path.is_file() {
  295. std::fs::copy(&path, &config.out_dir.join(path.file_name().unwrap()))?;
  296. } else {
  297. match fs_extra::dir::copy(&path, &config.out_dir, &copy_options) {
  298. Ok(_) => {}
  299. Err(e) => {
  300. log::warn!("Error copying dir: {}", e);
  301. }
  302. }
  303. for ignore in &ignore_files {
  304. let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
  305. let ignore = config.out_dir.join(ignore);
  306. if ignore.is_file() {
  307. std::fs::remove_file(ignore)?;
  308. }
  309. }
  310. }
  311. }
  312. }
  313. log::info!(
  314. "🚩 Build completed: [./{}]",
  315. config
  316. .dioxus_config
  317. .application
  318. .out_dir
  319. .clone()
  320. .unwrap_or_else(|| PathBuf::from("dist"))
  321. .display()
  322. );
  323. }
  324. Ok(())
  325. }
  326. fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result<Vec<Diagnostic>> {
  327. let mut warning_messages: Vec<Diagnostic> = vec![];
  328. let pb = ProgressBar::new_spinner();
  329. pb.enable_steady_tick(Duration::from_millis(200));
  330. pb.set_style(
  331. ProgressStyle::with_template("{spinner:.dim.bold} {wide_msg}")
  332. .unwrap()
  333. .tick_chars("/|\\- "),
  334. );
  335. pb.set_message("💼 Waiting to start build the project...");
  336. struct StopSpinOnDrop(ProgressBar);
  337. impl Drop for StopSpinOnDrop {
  338. fn drop(&mut self) {
  339. self.0.finish_and_clear();
  340. }
  341. }
  342. StopSpinOnDrop(pb.clone());
  343. let stdout = cmd.detached().stream_stdout()?;
  344. let reader = std::io::BufReader::new(stdout);
  345. for message in cargo_metadata::Message::parse_stream(reader) {
  346. match message.unwrap() {
  347. Message::CompilerMessage(msg) => {
  348. let message = msg.message;
  349. match message.level {
  350. cargo_metadata::diagnostic::DiagnosticLevel::Error => {
  351. return {
  352. Err(anyhow::anyhow!(message
  353. .rendered
  354. .unwrap_or("Unknown".into())))
  355. };
  356. }
  357. cargo_metadata::diagnostic::DiagnosticLevel::Warning => {
  358. warning_messages.push(message.clone());
  359. }
  360. _ => {}
  361. }
  362. }
  363. Message::CompilerArtifact(artifact) => {
  364. pb.set_message(format!("Compiling {} ", artifact.package_id));
  365. pb.tick();
  366. }
  367. Message::BuildScriptExecuted(script) => {
  368. let _package_id = script.package_id.to_string();
  369. }
  370. Message::BuildFinished(finished) => {
  371. if finished.success {
  372. log::info!("👑 Build done.");
  373. } else {
  374. std::process::exit(1);
  375. }
  376. }
  377. _ => (), // Unknown message
  378. }
  379. }
  380. Ok(warning_messages)
  381. }
  382. pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
  383. let crate_root = crate::cargo::crate_root().unwrap();
  384. let custom_html_file = crate_root.join("index.html");
  385. let mut html = if custom_html_file.is_file() {
  386. let mut buf = String::new();
  387. let mut file = File::open(custom_html_file).unwrap();
  388. if file.read_to_string(&mut buf).is_ok() {
  389. buf
  390. } else {
  391. String::from(include_str!("./assets/index.html"))
  392. }
  393. } else {
  394. String::from(include_str!("./assets/index.html"))
  395. };
  396. let resouces = config.web.resource.clone();
  397. let mut style_list = resouces.style.unwrap_or_default();
  398. let mut script_list = resouces.script.unwrap_or_default();
  399. if serve {
  400. let mut dev_style = resouces.dev.style.clone().unwrap_or_default();
  401. let mut dev_script = resouces.dev.script.unwrap_or_default();
  402. style_list.append(&mut dev_style);
  403. script_list.append(&mut dev_script);
  404. }
  405. let mut style_str = String::new();
  406. for style in style_list {
  407. style_str.push_str(&format!(
  408. "<link rel=\"stylesheet\" href=\"{}\">\n",
  409. &style.to_str().unwrap(),
  410. ))
  411. }
  412. if config
  413. .application
  414. .tools
  415. .clone()
  416. .unwrap_or_default()
  417. .contains_key("tailwindcss")
  418. {
  419. style_str.push_str("<link rel=\"stylesheet\" href=\"tailwind.css\">\n");
  420. }
  421. replace_or_insert_before("{style_include}", &style_str, "</head", &mut html);
  422. let mut script_str = String::new();
  423. for script in script_list {
  424. script_str.push_str(&format!(
  425. "<script src=\"{}\"></script>\n",
  426. &script.to_str().unwrap(),
  427. ))
  428. }
  429. replace_or_insert_before("{script_include}", &script_str, "</body", &mut html);
  430. if serve {
  431. html += &format!(
  432. "<script>{}</script>",
  433. include_str!("./assets/autoreload.js")
  434. );
  435. }
  436. let base_path = match &config.web.app.base_path {
  437. Some(path) => path,
  438. None => ".",
  439. };
  440. let app_name = &config.application.name;
  441. // Check if a script already exists
  442. if html.contains("{app_name}") && html.contains("{base_path}") {
  443. html = html.replace("{app_name}", app_name);
  444. html = html.replace("{base_path}", base_path);
  445. } else {
  446. // If not, insert the script
  447. html = html.replace(
  448. "</body",
  449. &format!(
  450. r#"<script type="module">
  451. import init from "/{base_path}/assets/dioxus/{app_name}.js";
  452. init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {{
  453. if (wasm.__wbindgen_start == undefined) {{
  454. wasm.main();
  455. }}
  456. }});
  457. </script>
  458. </body"#
  459. ),
  460. );
  461. }
  462. let title = config
  463. .web
  464. .app
  465. .title
  466. .clone()
  467. .unwrap_or_else(|| "dioxus | ⛺".into());
  468. replace_or_insert_before("{app_title}", &title, "</title", &mut html);
  469. html
  470. }
  471. fn replace_or_insert_before(
  472. replace: &str,
  473. with: &str,
  474. or_insert_before: &str,
  475. content: &mut String,
  476. ) {
  477. if content.contains(replace) {
  478. *content = content.replace(replace, with);
  479. } else {
  480. *content = content.replace(or_insert_before, &format!("{}{}", with, or_insert_before));
  481. }
  482. }
  483. // this function will build some assets file
  484. // like sass tool resources
  485. // this function will return a array which file don't need copy to out_dir.
  486. fn build_assets(config: &CrateConfig) -> Result<Vec<PathBuf>> {
  487. let mut result = vec![];
  488. let dioxus_config = &config.dioxus_config;
  489. let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default();
  490. // check sass tool state
  491. let sass = Tool::Sass;
  492. if sass.is_installed() && dioxus_tools.contains_key("sass") {
  493. let sass_conf = dioxus_tools.get("sass").unwrap();
  494. if let Some(tab) = sass_conf.as_table() {
  495. let source_map = tab.contains_key("source_map");
  496. let source_map = if source_map && tab.get("source_map").unwrap().is_bool() {
  497. if tab.get("source_map").unwrap().as_bool().unwrap_or_default() {
  498. "--source-map"
  499. } else {
  500. "--no-source-map"
  501. }
  502. } else {
  503. "--source-map"
  504. };
  505. if tab.contains_key("input") {
  506. if tab.get("input").unwrap().is_str() {
  507. let file = tab.get("input").unwrap().as_str().unwrap().trim();
  508. if file == "*" {
  509. // if the sass open auto, we need auto-check the assets dir.
  510. let asset_dir = config.asset_dir.clone();
  511. if asset_dir.is_dir() {
  512. for entry in walkdir::WalkDir::new(&asset_dir)
  513. .into_iter()
  514. .filter_map(|e| e.ok())
  515. {
  516. let temp = entry.path();
  517. if temp.is_file() {
  518. let suffix = temp.extension();
  519. if suffix.is_none() {
  520. continue;
  521. }
  522. let suffix = suffix.unwrap().to_str().unwrap();
  523. if suffix == "scss" || suffix == "sass" {
  524. // if file suffix is `scss` / `sass` we need transform it.
  525. let out_file = format!(
  526. "{}.css",
  527. temp.file_stem().unwrap().to_str().unwrap()
  528. );
  529. let target_path = config
  530. .out_dir
  531. .join(
  532. temp.strip_prefix(&asset_dir)
  533. .unwrap()
  534. .parent()
  535. .unwrap(),
  536. )
  537. .join(out_file);
  538. let res = sass.call(
  539. "sass",
  540. vec![
  541. temp.to_str().unwrap(),
  542. target_path.to_str().unwrap(),
  543. source_map,
  544. ],
  545. );
  546. if res.is_ok() {
  547. result.push(temp.to_path_buf());
  548. }
  549. }
  550. }
  551. }
  552. }
  553. } else {
  554. // just transform one file.
  555. let relative_path = if &file[0..1] == "/" {
  556. &file[1..file.len()]
  557. } else {
  558. file
  559. };
  560. let path = config.asset_dir.join(relative_path);
  561. let out_file =
  562. format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
  563. let target_path = config
  564. .out_dir
  565. .join(PathBuf::from(relative_path).parent().unwrap())
  566. .join(out_file);
  567. if path.is_file() {
  568. let res = sass.call(
  569. "sass",
  570. vec![
  571. path.to_str().unwrap(),
  572. target_path.to_str().unwrap(),
  573. source_map,
  574. ],
  575. );
  576. if res.is_ok() {
  577. result.push(path);
  578. } else {
  579. log::error!("{:?}", res);
  580. }
  581. }
  582. }
  583. } else if tab.get("input").unwrap().is_array() {
  584. // check files list.
  585. let list = tab.get("input").unwrap().as_array().unwrap();
  586. for i in list {
  587. if i.is_str() {
  588. let path = i.as_str().unwrap();
  589. let relative_path = if &path[0..1] == "/" {
  590. &path[1..path.len()]
  591. } else {
  592. path
  593. };
  594. let path = config.asset_dir.join(relative_path);
  595. let out_file =
  596. format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
  597. let target_path = config
  598. .out_dir
  599. .join(PathBuf::from(relative_path).parent().unwrap())
  600. .join(out_file);
  601. if path.is_file() {
  602. let res = sass.call(
  603. "sass",
  604. vec![
  605. path.to_str().unwrap(),
  606. target_path.to_str().unwrap(),
  607. source_map,
  608. ],
  609. );
  610. if res.is_ok() {
  611. result.push(path);
  612. }
  613. }
  614. }
  615. }
  616. }
  617. }
  618. }
  619. }
  620. // SASS END
  621. Ok(result)
  622. }
  623. // use binary_install::{Cache, Download};
  624. // /// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a
  625. // /// precompiled binary.
  626. // ///
  627. // /// Returns `Some` if a binary was found or it was successfully downloaded.
  628. // /// Returns `None` if a binary wasn't found in `PATH` and this platform doesn't
  629. // /// have precompiled binaries. Returns an error if we failed to download the
  630. // /// binary.
  631. // pub fn find_wasm_opt(
  632. // cache: &Cache,
  633. // install_permitted: bool,
  634. // ) -> Result<install::Status, failure::Error> {
  635. // // First attempt to look up in PATH. If found assume it works.
  636. // if let Ok(path) = which::which("wasm-opt") {
  637. // PBAR.info(&format!("found wasm-opt at {:?}", path));
  638. // match path.as_path().parent() {
  639. // Some(path) => return Ok(install::Status::Found(Download::at(path))),
  640. // None => {}
  641. // }
  642. // }
  643. // let version = "version_78";
  644. // Ok(install::download_prebuilt(
  645. // &install::Tool::WasmOpt,
  646. // cache,
  647. // version,
  648. // install_permitted,
  649. // )?)
  650. // }