Fix outdir not work when run dx bundle --platform web --outdir mydir (#3572)
* Make legacy asset dir optional * properly implement outdir --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com> Co-authored-by: Nathy-bajo <bajon7680@gmail.com>
This commit is contained in:
9 files changed
+152
-143
No files matched your search
Generated
+35
@@ -2855,6 +2855,19 @@ dependencies = [
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.13"
|
||||
@@ -3478,6 +3491,7 @@ dependencies = [
|
||||
"dioxus-rsx",
|
||||
"dioxus-rsx-hotreload",
|
||||
"dioxus-rsx-rosetta",
|
||||
"dircpy",
|
||||
"dirs",
|
||||
"dunce",
|
||||
"env_logger 0.11.5",
|
||||
@@ -4242,6 +4256,17 @@ dependencies = [
|
||||
"tower-http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dircpy"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a88521b0517f5f9d51d11925d8ab4523497dcf947073fa3231a311b63941131c"
|
||||
dependencies = [
|
||||
"jwalk",
|
||||
"log",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
@@ -7206,6 +7231,16 @@ dependencies = [
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jwalk"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56"
|
||||
dependencies = [
|
||||
"crossbeam",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jzon"
|
||||
version = "0.12.5"
|
||||
|
||||
@@ -119,6 +119,7 @@ tauri-bundler = { workspace = true }
|
||||
include_dir = "0.7.4"
|
||||
flate2 = "1.0.35"
|
||||
tar = "0.4.43"
|
||||
dircpy = "0.3.19"
|
||||
|
||||
[build-dependencies]
|
||||
built = { version = "=0.7.4", features = ["git2"] }
|
||||
|
||||
@@ -154,8 +154,12 @@ impl BuildRequest {
|
||||
// If the path is absolute, make it relative to the current directory before we join it
|
||||
// The path is actually a web path which is relative to the root of the website
|
||||
let path = path.strip_prefix("/").unwrap_or(path);
|
||||
let asset_dir_path = self.krate.legacy_asset_dir().join(path);
|
||||
if let Ok(absolute_path) = asset_dir_path.canonicalize() {
|
||||
let asset_dir_path = self
|
||||
.krate
|
||||
.legacy_asset_dir()
|
||||
.map(|dir| dir.join(path).canonicalize());
|
||||
|
||||
if let Some(Ok(absolute_path)) = asset_dir_path {
|
||||
let absolute_crate_root = self.krate.crate_dir().canonicalize().unwrap();
|
||||
PathBuf::from("./")
|
||||
.join(absolute_path.strip_prefix(absolute_crate_root).unwrap())
|
||||
@@ -179,7 +183,7 @@ impl BuildRequest {
|
||||
ResourceType::Script => "web.resource.script",
|
||||
};
|
||||
|
||||
tracing::debug!(
|
||||
tracing::warn!(
|
||||
"{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{AppBundle, BuildArgs, Builder, DioxusCrate, Platform};
|
||||
use anyhow::Context;
|
||||
use path_absolutize::Absolutize;
|
||||
use std::collections::HashMap;
|
||||
use tauri_bundler::{BundleBinary, BundleSettings, PackageSettings, SettingsBuilder};
|
||||
|
||||
@@ -25,12 +26,13 @@ pub struct Bundle {
|
||||
|
||||
/// The directory in which the final bundle will be placed.
|
||||
///
|
||||
/// Relative paths will be placed relative to the current working directory.
|
||||
/// Relative paths will be placed relative to the current working directory if specified.
|
||||
/// Otherwise, the out_dir path specified in Dioxus.toml will be used (relative to the crate root).
|
||||
///
|
||||
/// We will flatten the artifacts into this directory - there will be no differentiation between
|
||||
/// artifacts produced by different platforms.
|
||||
#[clap(long)]
|
||||
pub outdir: Option<PathBuf>,
|
||||
pub out_dir: Option<PathBuf>,
|
||||
|
||||
/// The arguments for the dioxus build
|
||||
#[clap(flatten)]
|
||||
@@ -54,72 +56,38 @@ impl Bundle {
|
||||
.finish()
|
||||
.await?;
|
||||
|
||||
tracing::info!("Copying app to output directory...");
|
||||
|
||||
// If we're building for iOS, we need to bundle the iOS bundle
|
||||
if self.build_arguments.platform() == Platform::Ios && self.package_types.is_none() {
|
||||
self.package_types = Some(vec![crate::PackageType::IosBundle]);
|
||||
}
|
||||
|
||||
let mut cmd_result = StructuredOutput::Success;
|
||||
let mut bundles = vec![];
|
||||
|
||||
// Copy the server over if it exists
|
||||
if bundle.build.build.fullstack {
|
||||
bundles.push(bundle.server_exe().unwrap());
|
||||
}
|
||||
|
||||
// Create a list of bundles that we might need to copy
|
||||
match self.build_arguments.platform() {
|
||||
// By default, mac/win/linux work with tauri bundle
|
||||
Platform::MacOS | Platform::Linux | Platform::Windows => {
|
||||
let bundles = self.bundle_desktop(krate, bundle)?;
|
||||
|
||||
tracing::info!("Bundled app successfully!");
|
||||
tracing::info!("App produced {} outputs:", bundles.len());
|
||||
tracing::debug!("Bundling produced bundles: {:#?}", bundles);
|
||||
|
||||
// Copy the bundles to the output directory and log their locations
|
||||
let mut bundle_paths = vec![];
|
||||
for bundle in bundles {
|
||||
for src in bundle.bundle_paths {
|
||||
let src = if let Some(outdir) = &self.outdir {
|
||||
let dest = outdir.join(src.file_name().expect("Filename to exist"));
|
||||
crate::fastfs::copy_asset(&src, &dest)
|
||||
.context("Failed to copy or compress optimized asset")?;
|
||||
dest
|
||||
} else {
|
||||
src.clone()
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
"{} - [{}]",
|
||||
bundle.package_type.short_name(),
|
||||
src.display()
|
||||
);
|
||||
|
||||
bundle_paths.push(src);
|
||||
}
|
||||
tracing::info!("Running desktop bundler...");
|
||||
for bundle in self.bundle_desktop(&krate, &bundle)? {
|
||||
bundles.extend(bundle.bundle_paths);
|
||||
}
|
||||
|
||||
cmd_result = StructuredOutput::BundleOutput {
|
||||
bundles: bundle_paths,
|
||||
};
|
||||
}
|
||||
|
||||
Platform::Web => {
|
||||
tracing::info!("App available at: {}", bundle.build.root_dir().display());
|
||||
}
|
||||
|
||||
// Web/ios can just use their root_dir
|
||||
Platform::Web => bundles.push(bundle.build.root_dir()),
|
||||
Platform::Ios => {
|
||||
tracing::warn!("Signed iOS bundles are not yet supported");
|
||||
tracing::info!(
|
||||
"The bundle is available at: {}",
|
||||
bundle.build.root_dir().display()
|
||||
);
|
||||
tracing::warn!("iOS bundles are not currently codesigned! You will need to codesign the app before distributing.");
|
||||
bundles.push(bundle.build.root_dir())
|
||||
}
|
||||
Platform::Server => bundles.push(bundle.build.root_dir()),
|
||||
Platform::Liveview => bundles.push(bundle.build.root_dir()),
|
||||
|
||||
Platform::Server => {
|
||||
tracing::info!("Server available at: {}", bundle.build.root_dir().display())
|
||||
}
|
||||
Platform::Liveview => tracing::info!(
|
||||
"Liveview server available at: {}",
|
||||
bundle.build.root_dir().display()
|
||||
),
|
||||
|
||||
// todo(jon): we can technically create apks (already do...) just need to expose it
|
||||
Platform::Android => {
|
||||
return Err(Error::UnsupportedFeature(
|
||||
"Android bundles are not yet supported".into(),
|
||||
@@ -127,13 +95,54 @@ impl Bundle {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(cmd_result)
|
||||
// Copy the bundles to the output directory if one was specified
|
||||
let crate_outdir = bundle.build.krate.crate_out_dir();
|
||||
if let Some(outdir) = self.out_dir.clone().or(crate_outdir) {
|
||||
let outdir = outdir
|
||||
.absolutize()
|
||||
.context("Failed to absolutize output directory")?;
|
||||
|
||||
tracing::info!("Copying bundles to output directory: {}", outdir.display());
|
||||
|
||||
std::fs::create_dir_all(&outdir)?;
|
||||
|
||||
for bundle_path in bundles.iter_mut() {
|
||||
let destination = outdir.join(bundle_path.file_name().unwrap());
|
||||
|
||||
tracing::debug!(
|
||||
"Copying from {} to {}",
|
||||
bundle_path.display(),
|
||||
destination.display()
|
||||
);
|
||||
|
||||
if bundle_path.is_dir() {
|
||||
dircpy::CopyBuilder::new(&bundle_path, &destination)
|
||||
.overwrite(true)
|
||||
.run_par()
|
||||
.context("Failed to copy the app to output directory")?;
|
||||
} else {
|
||||
std::fs::copy(&bundle_path, &destination)
|
||||
.context("Failed to copy the app to output directory")?;
|
||||
}
|
||||
|
||||
*bundle_path = destination;
|
||||
}
|
||||
}
|
||||
|
||||
for bundle_path in bundles.iter() {
|
||||
tracing::info!(
|
||||
"Bundled app at: {}",
|
||||
bundle_path.absolutize().unwrap().display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(StructuredOutput::BundleOutput { bundles })
|
||||
}
|
||||
|
||||
fn bundle_desktop(
|
||||
&self,
|
||||
krate: DioxusCrate,
|
||||
bundle: AppBundle,
|
||||
krate: &DioxusCrate,
|
||||
bundle: &AppBundle,
|
||||
) -> Result<Vec<tauri_bundler::Bundle>, Error> {
|
||||
_ = std::fs::remove_dir_all(krate.bundle_dir(self.build_arguments.platform()));
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@ use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct ApplicationConfig {
|
||||
#[serde(default = "asset_dir_default")]
|
||||
pub(crate) asset_dir: PathBuf,
|
||||
#[serde(default)]
|
||||
pub(crate) asset_dir: Option<PathBuf>,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) sub_package: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn asset_dir_default() -> PathBuf {
|
||||
PathBuf::from("assets")
|
||||
#[serde(default)]
|
||||
pub(crate) out_dir: Option<PathBuf>,
|
||||
}
|
||||
@@ -22,8 +22,9 @@ impl Default for DioxusConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
application: ApplicationConfig {
|
||||
asset_dir: asset_dir_default(),
|
||||
asset_dir: None,
|
||||
sub_package: None,
|
||||
out_dir: None,
|
||||
},
|
||||
web: WebConfig {
|
||||
app: WebAppConfig {
|
||||
|
||||
@@ -115,17 +115,28 @@ impl DioxusCrate {
|
||||
})
|
||||
}
|
||||
|
||||
/// Compose an asset directory. Represents the typical "public" directory
|
||||
/// with publicly available resources (configurable in the `Dioxus.toml`).
|
||||
pub(crate) fn legacy_asset_dir(&self) -> PathBuf {
|
||||
self.crate_dir().join(&self.config.application.asset_dir)
|
||||
/// The asset dir we used to support before manganis became the default.
|
||||
/// This generally was just a folder in your Dioxus.toml called "assets" or "public" where users
|
||||
/// would store their assets.
|
||||
///
|
||||
/// With manganis you now use `asset!()` and we pick it up automatically.
|
||||
pub(crate) fn legacy_asset_dir(&self) -> Option<PathBuf> {
|
||||
self.config
|
||||
.application
|
||||
.asset_dir
|
||||
.clone()
|
||||
.map(|dir| self.crate_dir().join(dir))
|
||||
}
|
||||
|
||||
/// Get the list of files in the "legacy" asset directory
|
||||
pub(crate) fn legacy_asset_dir_files(&self) -> Vec<PathBuf> {
|
||||
let mut files = vec![];
|
||||
|
||||
let Ok(read_dir) = self.legacy_asset_dir().read_dir() else {
|
||||
let Some(legacy_asset_dir) = self.legacy_asset_dir() else {
|
||||
return files;
|
||||
};
|
||||
|
||||
let Ok(read_dir) = legacy_asset_dir.read_dir() else {
|
||||
return files;
|
||||
};
|
||||
|
||||
@@ -136,10 +147,20 @@ impl DioxusCrate {
|
||||
files
|
||||
}
|
||||
|
||||
/// Get the outdir specified by the Dioxus.toml, relative to the crate directory.
|
||||
/// We don't support workspaces yet since that would cause a collision of bundles per project.
|
||||
pub(crate) fn crate_out_dir(&self) -> Option<PathBuf> {
|
||||
self.config
|
||||
.application
|
||||
.out_dir
|
||||
.as_ref()
|
||||
.map(|out_dir| self.crate_dir().join(out_dir))
|
||||
}
|
||||
|
||||
/// Compose an out directory. Represents the typical "dist" directory that
|
||||
/// is "distributed" after building an application (configurable in the
|
||||
/// `Dioxus.toml`).
|
||||
fn out_dir(&self) -> PathBuf {
|
||||
fn internal_out_dir(&self) -> PathBuf {
|
||||
let dir = self.workspace_dir().join("target").join("dx");
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
dir
|
||||
@@ -153,7 +174,7 @@ impl DioxusCrate {
|
||||
/// target/dx/build/app/web/public/
|
||||
/// target/dx/build/app/web/server.exe
|
||||
pub(crate) fn build_dir(&self, platform: Platform, release: bool) -> PathBuf {
|
||||
self.out_dir()
|
||||
self.internal_out_dir()
|
||||
.join(self.executable_name())
|
||||
.join(if release { "release" } else { "debug" })
|
||||
.join(platform.build_folder_name())
|
||||
@@ -164,7 +185,7 @@ impl DioxusCrate {
|
||||
/// target/dx/bundle/app/blah.exe
|
||||
/// target/dx/bundle/app/public/
|
||||
pub(crate) fn bundle_dir(&self, platform: Platform) -> PathBuf {
|
||||
self.out_dir()
|
||||
self.internal_out_dir()
|
||||
.join(self.executable_name())
|
||||
.join("bundle")
|
||||
.join(platform.build_folder_name())
|
||||
|
||||
@@ -11,68 +11,6 @@ use std::{
|
||||
use brotli::enc::BrotliEncoderParams;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub fn copy_asset(src: &Path, dest: &Path) -> std::io::Result<()> {
|
||||
if src.is_dir() {
|
||||
copy_dir_to(src, dest, false)?;
|
||||
} else {
|
||||
std::fs::copy(src, dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn copy_dir_to(
|
||||
src_dir: &Path,
|
||||
dest_dir: &Path,
|
||||
pre_compress: bool,
|
||||
) -> std::io::Result<()> {
|
||||
let entries = std::fs::read_dir(src_dir)?;
|
||||
let mut children: Vec<std::thread::JoinHandle<std::io::Result<()>>> = Vec::new();
|
||||
|
||||
for entry in entries.flatten() {
|
||||
let entry_path = entry.path();
|
||||
let path_relative_to_src = entry_path.strip_prefix(src_dir).unwrap();
|
||||
let output_file_location = dest_dir.join(path_relative_to_src);
|
||||
children.push(std::thread::spawn(move || {
|
||||
if entry.file_type()?.is_dir() {
|
||||
// If the file is a directory, recursively copy it into the output directory
|
||||
if let Err(err) = copy_dir_to(&entry_path, &output_file_location, pre_compress) {
|
||||
tracing::error!(
|
||||
"Failed to pre-compress directory {}: {}",
|
||||
entry_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Make sure the directory exists
|
||||
std::fs::create_dir_all(output_file_location.parent().unwrap())?;
|
||||
// Copy the file to the output directory
|
||||
std::fs::copy(&entry_path, &output_file_location)?;
|
||||
|
||||
// Then pre-compress the file if needed
|
||||
if pre_compress {
|
||||
if let Err(err) = pre_compress_file(&output_file_location) {
|
||||
tracing::error!(
|
||||
"Failed to pre-compress static assets {}: {}",
|
||||
output_file_location.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
// If pre-compression isn't enabled, we should remove the old compressed file if it exists
|
||||
} else if let Some(compressed_path) = compressed_path(&output_file_location) {
|
||||
_ = std::fs::remove_file(compressed_path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
for child in children {
|
||||
child.join().unwrap()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the path to the compressed version of a file
|
||||
fn compressed_path(path: &Path) -> Option<PathBuf> {
|
||||
let new_extension = match path.extension() {
|
||||
|
||||
@@ -172,14 +172,15 @@ impl AppHandle {
|
||||
tracing::debug!("Hotreloading asset {changed_file:?} in target {asset_dir:?}");
|
||||
|
||||
// If the asset shares the same name in the bundle, reload that
|
||||
let legacy_asset_dir = self.app.build.krate.legacy_asset_dir();
|
||||
if changed_file.starts_with(&legacy_asset_dir) {
|
||||
tracing::debug!("Hotreloading legacy asset {changed_file:?}");
|
||||
let trimmed = changed_file.strip_prefix(legacy_asset_dir).unwrap();
|
||||
let res = std::fs::copy(changed_file, asset_dir.join(trimmed));
|
||||
bundled_name = Some(trimmed.to_path_buf());
|
||||
if let Err(e) = res {
|
||||
tracing::debug!("Failed to hotreload legacy asset {e}");
|
||||
if let Some(legacy_asset_dir) = self.app.build.krate.legacy_asset_dir() {
|
||||
if changed_file.starts_with(&legacy_asset_dir) {
|
||||
tracing::debug!("Hotreloading legacy asset {changed_file:?}");
|
||||
let trimmed = changed_file.strip_prefix(legacy_asset_dir).unwrap();
|
||||
let res = std::fs::copy(changed_file, asset_dir.join(trimmed));
|
||||
bundled_name = Some(trimmed.to_path_buf());
|
||||
if let Err(e) = res {
|
||||
tracing::debug!("Failed to hotreload legacy asset {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user