improve build times by extracting asset opt into its own crate (#3273)

* improve build types by extracing asset opt as a dylib
* dont use dylib, but let regular workspace caching do the heavy lifting
* add prebuild to cli for better lock contention
* hoist deps
* disable slow tests
* re-enable sequential, debug=0 for dev
* use vfs for windows
* dont cache on failure, fix windows vfs caceh
* free disk space re-order
* add clippy + fmt to windows channel
* yoink makefile
* proper rustfmt/clippy on toolchain
* set incremental to zero since cargo-cache disables it
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-12-02 16:52:40 -08:00
1 parent 79a2f023e7
commit 1cbfcde6cd
25 files changed
+550 -554

No files matched your search

-42
View File
@@ -1,42 +0,0 @@
use std::path::Path;
use anyhow::Context;
use lightningcss::{
printer::PrinterOptions,
stylesheet::{MinifyOptions, ParserOptions, StyleSheet},
};
use manganis_core::CssAssetOptions;
pub(crate) fn process_css(
css_options: &CssAssetOptions,
source: &Path,
output_path: &Path,
) -> anyhow::Result<()> {
let css = std::fs::read_to_string(source)?;
let css = if css_options.minified() {
minify_css(&css)
} else {
css
};
std::fs::write(output_path, css).with_context(|| {
format!(
"Failed to write css to output location: {}",
output_path.display()
)
})?;
Ok(())
}
pub(crate) fn minify_css(css: &str) -> String {
let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap();
stylesheet.minify(MinifyOptions::default()).unwrap();
let printer = PrinterOptions {
minify: true,
..Default::default()
};
let res = stylesheet.to_css(printer).unwrap();
res.code
}
-76
View File
@@ -1,76 +0,0 @@
use anyhow::Context;
use manganis_core::{AssetOptions, CssAssetOptions, ImageAssetOptions, JsAssetOptions};
use std::path::Path;
use super::{
css::process_css, folder::process_folder, image::process_image, js::process_js,
json::process_json,
};
/// Process a specific file asset with the given options reading from the source and writing to the output path
pub(crate) fn process_file_to(
options: &AssetOptions,
source: &Path,
output_path: &Path,
) -> anyhow::Result<()> {
// If the file already exists, then we must have a file with the same hash
// already. The hash has the file contents and options, so if we find a file
// with the same hash, we probably already created this file in the past
if output_path.exists() {
return Ok(());
}
if let Some(parent) = output_path.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent)?;
}
}
match options {
AssetOptions::Unknown => match source.extension().map(|e| e.to_string_lossy()).as_deref() {
Some("css") => {
process_css(&CssAssetOptions::new(), source, output_path)?;
}
Some("js") => {
process_js(&JsAssetOptions::new(), source, output_path)?;
}
Some("json") => {
process_json(source, output_path)?;
}
Some("jpg" | "jpeg" | "png" | "webp" | "avif") => {
process_image(&ImageAssetOptions::new(), source, output_path)?;
}
Some(_) | None => {
if source.is_dir() {
process_folder(source, output_path)?;
} else {
let source_file = std::fs::File::open(source)?;
let mut reader = std::io::BufReader::new(source_file);
let output_file = std::fs::File::create(output_path)?;
let mut writer = std::io::BufWriter::new(output_file);
std::io::copy(&mut reader, &mut writer).with_context(|| {
format!(
"Failed to write file to output location: {}",
output_path.display()
)
})?;
}
}
},
AssetOptions::Css(options) => {
process_css(options, source, output_path)?;
}
AssetOptions::Js(options) => {
process_js(options, source, output_path)?;
}
AssetOptions::Image(options) => {
process_image(options, source, output_path)?;
}
AssetOptions::Folder(_) => {
process_folder(source, output_path)?;
}
_ => {
tracing::warn!("Unknown asset options: {:?}", options);
}
}
Ok(())
}
-41
View File
@@ -1,41 +0,0 @@
use std::path::Path;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use super::file::process_file_to;
/// Process a folder, optimizing and copying all assets into the output folder
pub fn process_folder(source: &Path, output_folder: &Path) -> anyhow::Result<()> {
// Create the folder
std::fs::create_dir_all(output_folder)?;
// Then optimize children
let files: Vec<_> = std::fs::read_dir(source)
.into_iter()
.flatten()
.flatten()
.collect();
files.par_iter().try_for_each(|file| {
let file = file.path();
let metadata = file.metadata()?;
let output_path = output_folder.join(file.strip_prefix(source)?);
if metadata.is_dir() {
process_folder(&file, &output_path)
} else {
process_file_minimal(&file, &output_path)
}
})?;
Ok(())
}
/// Optimize a file without changing any of its contents significantly (e.g. by changing the extension)
fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> {
process_file_to(
&manganis_core::AssetOptions::Unknown,
input_path,
output_path,
)?;
Ok(())
}
-23
View File
@@ -1,23 +0,0 @@
use image::{DynamicImage, EncodableLayout};
use std::{
io::{BufWriter, Write},
path::Path,
};
pub(crate) fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> {
let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX);
let width = image.width() as usize;
let height = image.height() as usize;
comp.set_size(width, height);
let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work
comp.write_scanlines(image.to_rgba8().as_bytes())?;
let jpeg_bytes = comp.finish()?;
let file = std::fs::File::create(output_location)?;
let w = &mut BufWriter::new(file);
w.write_all(&jpeg_bytes)?;
Ok(())
}
-62
View File
@@ -1,62 +0,0 @@
use std::path::Path;
use anyhow::Context;
use jpg::compress_jpg;
use manganis_core::{ImageAssetOptions, ImageFormat, ImageSize};
use png::compress_png;
mod jpg;
mod png;
pub(crate) fn process_image(
image_options: &ImageAssetOptions,
source: &Path,
output_path: &Path,
) -> anyhow::Result<()> {
let mut image = image::ImageReader::new(std::io::Cursor::new(&*std::fs::read(source)?))
.with_guessed_format()?
.decode();
if let Ok(image) = &mut image {
if let ImageSize::Manual { width, height } = image_options.size() {
*image = image.resize_exact(width, height, image::imageops::FilterType::Lanczos3);
}
}
match (image, image_options.format()) {
(image, ImageFormat::Png) => {
compress_png(image?, output_path);
}
(image, ImageFormat::Jpg) => {
compress_jpg(image?, output_path)?;
}
(Ok(image), ImageFormat::Avif) => {
if let Err(error) = image.save(output_path) {
tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display());
}
}
(Ok(image), ImageFormat::Webp) => {
if let Err(err) = image.save(output_path) {
tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err);
}
}
(Ok(image), _) => {
image.save(output_path)?;
}
// If we can't decode the image or it is of an unknown type, we just copy the file
_ => {
let source_file = std::fs::File::open(source)?;
let mut reader = std::io::BufReader::new(source_file);
let output_file = std::fs::File::create(output_path)?;
let mut writer = std::io::BufWriter::new(output_file);
std::io::copy(&mut reader, &mut writer).with_context(|| {
format!(
"Failed to write image to output location: {}",
output_path.display()
)
})?;
}
}
Ok(())
}
-52
View File
@@ -1,52 +0,0 @@
use std::{io::BufWriter, path::Path};
use image::DynamicImage;
pub(crate) fn compress_png(image: DynamicImage, output_location: &Path) {
// Image loading/saving is outside scope of this library
let width = image.width() as usize;
let height = image.height() as usize;
let bitmap: Vec<_> = image
.into_rgba8()
.pixels()
.map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3]))
.collect();
// Configure the library
let mut liq = imagequant::new();
liq.set_speed(5).unwrap();
liq.set_quality(0, 99).unwrap();
// Describe the bitmap
let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap();
// The magic happens in quantize()
let mut res = match liq.quantize(&mut img) {
Ok(res) => res,
Err(err) => panic!("Quantization failed, because: {err:?}"),
};
let (palette, pixels) = res.remapped(&mut img).unwrap();
let file = std::fs::File::create(output_location).unwrap();
let w = &mut BufWriter::new(file);
let mut encoder = png::Encoder::new(w, width as u32, height as u32);
encoder.set_color(png::ColorType::Rgba);
let mut flattened_palette = Vec::new();
let mut alpha_palette = Vec::new();
for px in palette {
flattened_palette.push(px.r);
flattened_palette.push(px.g);
flattened_palette.push(px.b);
alpha_palette.push(px.a);
}
encoder.set_palette(flattened_palette);
encoder.set_trns(alpha_palette);
encoder.set_depth(png::BitDepth::Eight);
encoder.set_color(png::ColorType::Indexed);
encoder.set_compression(png::Compression::Best);
let mut writer = encoder.write_header().unwrap();
writer.write_image_data(&pixels).unwrap();
writer.finish().unwrap();
}
-69
View File
@@ -1,69 +0,0 @@
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use anyhow::Context;
use manganis_core::JsAssetOptions;
use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig};
use swc_common::{sync::Lrc, FileName};
use swc_common::{SourceMap, GLOBALS};
pub(crate) fn minify_js(source: &Path) -> anyhow::Result<String> {
let mut source_file = std::fs::File::open(source)?;
let cm = Arc::new(SourceMap::default());
let mut js = String::new();
source_file.read_to_string(&mut js)?;
let c = swc::Compiler::new(cm.clone());
let output = GLOBALS
.set(&Default::default(), || {
try_with_handler(cm.clone(), Default::default(), |handler| {
let filename = Lrc::new(FileName::Real(source.to_path_buf()));
let fm = cm.new_source_file(filename, js.to_string());
c.minify(
fm,
handler,
&JsMinifyOptions {
compress: BoolOrDataConfig::from_bool(true),
mangle: BoolOrDataConfig::from_bool(true),
..Default::default()
},
)
.context("failed to minify javascript")
})
})
.map(|output| output.code);
match output {
Ok(output) => Ok(output),
Err(err) => {
tracing::error!("Failed to minify javascript: {}", err);
Ok(js)
}
}
}
pub(crate) fn process_js(
js_options: &JsAssetOptions,
source: &Path,
output_path: &Path,
) -> anyhow::Result<()> {
let js = if js_options.minified() {
minify_js(source)?
} else {
let mut source_file = std::fs::File::open(source)?;
let mut source = String::new();
source_file.read_to_string(&mut source)?;
source
};
std::fs::write(output_path, js).with_context(|| {
format!(
"Failed to write js to output location: {}",
output_path.display()
)
})?;
Ok(())
}
-33
View File
@@ -1,33 +0,0 @@
use std::{io::Read, path::Path};
use anyhow::Context;
pub(crate) fn minify_json(source: &str) -> anyhow::Result<String> {
// First try to parse the json
let json: serde_json::Value = serde_json::from_str(source)?;
// Then print it in a minified format
let json = serde_json::to_string(&json)?;
Ok(json)
}
pub(crate) fn process_json(source: &Path, output_path: &Path) -> anyhow::Result<()> {
let mut source_file = std::fs::File::open(source)?;
let mut source = String::new();
source_file.read_to_string(&mut source)?;
let json = match minify_json(&source) {
Ok(json) => json,
Err(err) => {
tracing::error!("Failed to minify json: {}", err);
source
}
};
std::fs::write(output_path, json).with_context(|| {
format!(
"Failed to write json to output location: {}",
output_path.display()
)
})?;
Ok(())
}
-109
View File
@@ -1,109 +0,0 @@
use anyhow::Context;
use manganis_core::linker::LinkSection;
use manganis_core::BundledAsset;
use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection};
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::{collections::HashMap, path::PathBuf};
mod css;
mod file;
mod folder;
mod image;
mod js;
mod json;
pub(crate) use file::process_file_to;
/// A manifest of all assets collected from dependencies
///
/// This will be filled in primarily by incremental compilation artifacts.
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize)]
pub(crate) struct AssetManifest {
/// Map of bundled asset name to the asset itself
pub(crate) assets: HashMap<PathBuf, BundledAsset>,
}
impl AssetManifest {
#[allow(dead_code)]
pub(crate) fn load_from_file(path: &Path) -> anyhow::Result<Self> {
let src = std::fs::read_to_string(path)?;
serde_json::from_str(&src)
.with_context(|| format!("Failed to parse asset manifest from {path:?}\n{src}"))
}
/// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted
pub(crate) fn add_from_object_path(&mut self, path: &Path) -> anyhow::Result<()> {
let data = std::fs::read(path)?;
match path.extension().and_then(|ext| ext.to_str()) {
// Parse an rlib as a collection of objects
Some("rlib") => {
if let Ok(archive) = object::read::archive::ArchiveFile::parse(&*data) {
self.add_from_archive_file(&archive, &data)?;
}
}
_ => {
if let Ok(object) = object::File::parse(&*data) {
self.add_from_object_file(&object)?;
}
}
}
Ok(())
}
/// Fill this manifest from an rlib / ar file that contains many object files and their entries
fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) -> object::Result<()> {
// Look through each archive member for object files.
// Read the archive member's binary data (we know it's an object file)
// And parse it with the normal `object::File::parse` to find the manganis string.
for member in archive.members() {
let member = member?;
let name = String::from_utf8_lossy(member.name()).to_string();
// Check if the archive member is an object file and parse it.
if name.ends_with(".o") {
let data = member.data(data)?;
let object = object::File::parse(data)?;
_ = self.add_from_object_file(&object);
}
}
Ok(())
}
/// Fill this manifest with whatever tables might come from the object file
fn add_from_object_file(&mut self, obj: &ObjectFile) -> anyhow::Result<()> {
for section in obj.sections() {
let Ok(section_name) = section.name() else {
continue;
};
// Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling
let matches = LinkSection::ALL
.iter()
.any(|x| x.link_section == section_name);
if !matches {
continue;
}
let bytes = section
.uncompressed_data()
.context("Could not read uncompressed data from object file")?;
let mut buffer = const_serialize::ConstReadBuffer::new(&bytes);
while let Some((remaining_buffer, asset)) =
const_serialize::deserialize_const!(BundledAsset, buffer)
{
self.assets
.insert(asset.absolute_source_path().into(), asset);
buffer = remaining_buffer;
}
}
Ok(())
}
}
+2 -3
View File
@@ -1,8 +1,7 @@
use crate::assets::process_file_to;
use crate::Result;
use crate::{assets::AssetManifest, TraceSrc};
use crate::{BuildRequest, Platform};
use crate::{Result, TraceSrc};
use anyhow::Context;
use dioxus_cli_opt::{process_file_to, AssetManifest};
use manganis_core::AssetOptions;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use std::collections::HashSet;
+12 -4
View File
@@ -1,11 +1,10 @@
use super::{progress::ProgressTx, BuildArtifacts};
use crate::dioxus_crate::DioxusCrate;
use crate::Result;
use crate::{assets::AssetManifest, TraceSrc};
use crate::{link::LinkAction, BuildArgs};
use crate::{AppBundle, Platform};
use crate::{AppBundle, Platform, Result, TraceSrc};
use anyhow::Context;
use dioxus_cli_config::{APP_TITLE_ENV, ASSET_ROOT_ENV};
use dioxus_cli_opt::AssetManifest;
use serde::Deserialize;
use std::{
path::{Path, PathBuf},
@@ -46,7 +45,10 @@ impl BuildRequest {
pub(crate) async fn build_all(self) -> Result<AppBundle> {
tracing::debug!("Running build command...");
let (app, server) = self.build_concurrent().await?;
let (app, server) = match self.build.force_sequential {
true => self.build_sequential().await?,
false => self.build_concurrent().await?,
};
AppBundle::new(self, app, server).await
}
@@ -59,6 +61,12 @@ impl BuildRequest {
Ok((app, server))
}
async fn build_sequential(&self) -> Result<(BuildArtifacts, Option<BuildArtifacts>)> {
let app = self.build_app().await?;
let server = self.build_server().await?;
Ok((app, server))
}
pub(crate) async fn build_app(&self) -> Result<BuildArtifacts> {
tracing::debug!("Building app...");
+159 -157
View File
@@ -223,191 +223,193 @@ fn remove_triple_newlines(string: &str) -> String {
new_string
}
#[cfg(test)]
pub(crate) mod tests {
use escargot::{CargoBuild, CargoRun};
use once_cell::sync::Lazy;
use std::fs::{create_dir_all, read_to_string};
use std::path::{Path, PathBuf};
use std::process::Command;
use tempfile::tempdir;
use toml::Value;
// todo: re-enable these tests with better parallelization
//
// #[cfg(test)]
// pub(crate) mod tests {
// use escargot::{CargoBuild, CargoRun};
// use once_cell::sync::Lazy;
// use std::fs::{create_dir_all, read_to_string};
// use std::path::{Path, PathBuf};
// use std::process::Command;
// use tempfile::tempdir;
// use toml::Value;
static BINARY: Lazy<CargoRun> = Lazy::new(|| {
CargoBuild::new()
.bin(env!("CARGO_BIN_NAME"))
.current_release()
.run()
.expect("Couldn't build the binary for tests.")
});
// static BINARY: Lazy<CargoRun> = Lazy::new(|| {
// CargoBuild::new()
// .bin(env!("CARGO_BIN_NAME"))
// .current_release()
// .run()
// .expect("Couldn't build the binary for tests.")
// });
// Note: tests below (at least 6 of them) were written to mainly test
// correctness of project's directory and its name, because previously it
// was broken and tests bring a peace of mind. And also so that I don't have
// to run my local hand-made tests every time.
// // Note: tests below (at least 6 of them) were written to mainly test
// // correctness of project's directory and its name, because previously it
// // was broken and tests bring a peace of mind. And also so that I don't have
// // to run my local hand-made tests every time.
pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
// pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub(crate) fn subcommand(name: &str) -> Command {
let mut command = BINARY.command();
command.arg(name).arg("--yes"); // Skip any questions by choosing default answers.
command
}
// pub(crate) fn subcommand(name: &str) -> Command {
// let mut command = BINARY.command();
// command.arg(name).arg("--yes"); // Skip any questions by choosing default answers.
// command
// }
pub(crate) fn get_cargo_toml_path(project_path: &Path) -> PathBuf {
project_path.join("Cargo.toml")
}
// pub(crate) fn get_cargo_toml_path(project_path: &Path) -> PathBuf {
// project_path.join("Cargo.toml")
// }
pub(crate) fn get_project_name(cargo_toml_path: &Path) -> Result<String> {
Ok(toml::from_str::<Value>(&read_to_string(cargo_toml_path)?)?
.get("package")
.unwrap()
.get("name")
.unwrap()
.as_str()
.unwrap()
.to_string())
}
// pub(crate) fn get_project_name(cargo_toml_path: &Path) -> Result<String> {
// Ok(toml::from_str::<Value>(&read_to_string(cargo_toml_path)?)?
// .get("package")
// .unwrap()
// .get("name")
// .unwrap()
// .as_str()
// .unwrap()
// .to_string())
// }
fn subcommand_new() -> Command {
subcommand("new")
}
// fn subcommand_new() -> Command {
// subcommand("new")
// }
#[test]
fn test_subcommand_new_with_dot_path() -> Result<()> {
let project_dir = "dir";
let project_name = project_dir;
// #[test]
// fn test_subcommand_new_with_dot_path() -> Result<()> {
// let project_dir = "dir";
// let project_name = project_dir;
let temp_dir = tempdir()?;
// Make current dir's name deterministic.
let current_dir = temp_dir.path().join(project_dir);
create_dir_all(&current_dir)?;
let project_path = &current_dir;
assert!(project_path.exists());
// let temp_dir = tempdir()?;
// // Make current dir's name deterministic.
// let current_dir = temp_dir.path().join(project_dir);
// create_dir_all(&current_dir)?;
// let project_path = &current_dir;
// assert!(project_path.exists());
assert!(subcommand_new()
.arg(".")
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_new()
// .arg(".")
// .current_dir(&current_dir)
// .status()
// .is_ok());
let cargo_toml_path = get_cargo_toml_path(project_path);
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let cargo_toml_path = get_cargo_toml_path(project_path);
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_new_with_1_dir_path() -> Result<()> {
let project_dir = "dir";
let project_name = project_dir;
// #[test]
// fn test_subcommand_new_with_1_dir_path() -> Result<()> {
// let project_dir = "dir";
// let project_name = project_dir;
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_new()
.arg(project_dir)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_new()
// .arg(project_dir)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_new_with_2_dir_path() -> Result<()> {
let project_dir = "a/b";
let project_name = "b";
// #[test]
// fn test_subcommand_new_with_2_dir_path() -> Result<()> {
// let project_dir = "a/b";
// let project_name = "b";
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_new()
.arg(project_dir)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_new()
// .arg(project_dir)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_new_with_dot_path_and_custom_name() -> Result<()> {
let project_dir = "dir";
let project_name = "project";
// #[test]
// fn test_subcommand_new_with_dot_path_and_custom_name() -> Result<()> {
// let project_dir = "dir";
// let project_name = "project";
let temp_dir = tempdir()?;
// Make current dir's name deterministic.
let current_dir = temp_dir.path().join(project_dir);
create_dir_all(&current_dir)?;
let project_path = &current_dir;
assert!(project_path.exists());
// let temp_dir = tempdir()?;
// // Make current dir's name deterministic.
// let current_dir = temp_dir.path().join(project_dir);
// create_dir_all(&current_dir)?;
// let project_path = &current_dir;
// assert!(project_path.exists());
assert!(subcommand_new()
.arg("--name")
.arg(project_name)
.arg(".")
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_new()
// .arg("--name")
// .arg(project_name)
// .arg(".")
// .current_dir(&current_dir)
// .status()
// .is_ok());
let cargo_toml_path = get_cargo_toml_path(project_path);
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let cargo_toml_path = get_cargo_toml_path(project_path);
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_new_with_1_dir_path_and_custom_name() -> Result<()> {
let project_dir = "dir";
let project_name = "project";
// #[test]
// fn test_subcommand_new_with_1_dir_path_and_custom_name() -> Result<()> {
// let project_dir = "dir";
// let project_name = "project";
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_new()
.arg(project_dir)
.arg("--name")
.arg(project_name)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_new()
// .arg(project_dir)
// .arg("--name")
// .arg(project_name)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_new_with_2_dir_path_and_custom_name() -> Result<()> {
let project_dir = "a/b";
let project_name = "project";
// #[test]
// fn test_subcommand_new_with_2_dir_path_and_custom_name() -> Result<()> {
// let project_dir = "a/b";
// let project_name = "project";
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_new()
.arg(project_dir)
.arg("--name")
.arg(project_name)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_new()
// .arg(project_dir)
// .arg("--name")
// .arg(project_name)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
// }
+124 -122
View File
@@ -78,152 +78,154 @@ impl Init {
}
}
#[cfg(test)]
mod tests {
use std::{fs::create_dir_all, process::Command};
use tempfile::tempdir;
// todo: re-enable these tests with better parallelization
//
// #[cfg(test)]
// mod tests {
// use std::{fs::create_dir_all, process::Command};
// use tempfile::tempdir;
use super::create::tests::*;
// use super::create::tests::*;
// Note: tests below (at least 6 of them) were written to mainly test
// correctness of project's directory and its name, because previously it
// was broken and tests bring a peace of mind. And also so that I don't have
// to run my local hand-made tests every time.
// // Note: tests below (at least 6 of them) were written to mainly test
// // correctness of project's directory and its name, because previously it
// // was broken and tests bring a peace of mind. And also so that I don't have
// // to run my local hand-made tests every time.
fn subcommand_init() -> Command {
subcommand("init")
}
// fn subcommand_init() -> Command {
// subcommand("init")
// }
#[test]
fn test_subcommand_init_with_default_path() -> Result<()> {
let project_dir = "dir";
let project_name = project_dir;
// #[test]
// fn test_subcommand_init_with_default_path() -> Result<()> {
// let project_dir = "dir";
// let project_name = project_dir;
let temp_dir = tempdir()?;
// Make current dir's name deterministic.
let current_dir = temp_dir.path().join(project_dir);
create_dir_all(&current_dir)?;
let project_path = &current_dir;
assert!(project_path.exists());
// let temp_dir = tempdir()?;
// // Make current dir's name deterministic.
// let current_dir = temp_dir.path().join(project_dir);
// create_dir_all(&current_dir)?;
// let project_path = &current_dir;
// assert!(project_path.exists());
assert!(subcommand_init().current_dir(&current_dir).status().is_ok());
// assert!(subcommand_init().current_dir(&current_dir).status().is_ok());
let cargo_toml_path = get_cargo_toml_path(project_path);
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let cargo_toml_path = get_cargo_toml_path(project_path);
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_init_with_1_dir_path() -> Result<()> {
let project_dir = "dir";
let project_name = project_dir;
// #[test]
// fn test_subcommand_init_with_1_dir_path() -> Result<()> {
// let project_dir = "dir";
// let project_name = project_dir;
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_init()
.arg(project_dir)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_init()
// .arg(project_dir)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_init_with_2_dir_path() -> Result<()> {
let project_dir = "a/b";
let project_name = "b";
// #[test]
// fn test_subcommand_init_with_2_dir_path() -> Result<()> {
// let project_dir = "a/b";
// let project_name = "b";
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_init()
.arg(project_dir)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_init()
// .arg(project_dir)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_init_with_default_path_and_custom_name() -> Result<()> {
let project_dir = "dir";
let project_name = "project";
// #[test]
// fn test_subcommand_init_with_default_path_and_custom_name() -> Result<()> {
// let project_dir = "dir";
// let project_name = "project";
let temp_dir = tempdir()?;
// Make current dir's name deterministic.
let current_dir = temp_dir.path().join(project_dir);
create_dir_all(&current_dir)?;
let project_path = &current_dir;
assert!(project_path.exists());
// let temp_dir = tempdir()?;
// // Make current dir's name deterministic.
// let current_dir = temp_dir.path().join(project_dir);
// create_dir_all(&current_dir)?;
// let project_path = &current_dir;
// assert!(project_path.exists());
assert!(subcommand_init()
.arg("--name")
.arg(project_name)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_init()
// .arg("--name")
// .arg(project_name)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let cargo_toml_path = get_cargo_toml_path(project_path);
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let cargo_toml_path = get_cargo_toml_path(project_path);
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_init_with_1_dir_path_and_custom_name() -> Result<()> {
let project_dir = "dir";
let project_name = "project";
// #[test]
// fn test_subcommand_init_with_1_dir_path_and_custom_name() -> Result<()> {
// let project_dir = "dir";
// let project_name = "project";
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_init()
.arg(project_dir)
.arg("--name")
.arg(project_name)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_init()
// .arg(project_dir)
// .arg("--name")
// .arg(project_name)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
#[test]
fn test_subcommand_init_with_2_dir_path_and_custom_name() -> Result<()> {
let project_dir = "a/b";
let project_name = "project";
// #[test]
// fn test_subcommand_init_with_2_dir_path_and_custom_name() -> Result<()> {
// let project_dir = "a/b";
// let project_name = "project";
let current_dir = tempdir()?;
// let current_dir = tempdir()?;
assert!(subcommand_init()
.arg(project_dir)
.arg("--name")
.arg(project_name)
.current_dir(&current_dir)
.status()
.is_ok());
// assert!(subcommand_init()
// .arg(project_dir)
// .arg("--name")
// .arg(project_name)
// .current_dir(&current_dir)
// .status()
// .is_ok());
let project_path = current_dir.path().join(project_dir);
let cargo_toml_path = get_cargo_toml_path(&project_path);
assert!(project_path.exists());
assert!(cargo_toml_path.exists());
assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
Ok(())
}
}
// let project_path = current_dir.path().join(project_dir);
// let cargo_toml_path = get_cargo_toml_path(&project_path);
// assert!(project_path.exists());
// assert!(cargo_toml_path.exists());
// assert_eq!(get_project_name(&cargo_toml_path)?, project_name);
// Ok(())
// }
// }
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::assets::AssetManifest;
use dioxus_cli_opt::AssetManifest;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
-1
View File
@@ -3,7 +3,6 @@
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod assets;
mod build;
mod bundle_utils;
mod cli;