Move asset hashing into the CLI (#3988)
* Fix deduping assets * hash assets in the linker * fix extracting assets * Fix the hashed asset name * deserialize the asset back from the link section * prevent the read from being inlined * remove some extra logs * Fix liveview routing * Only look for direct dependencies on dioxus features * fix wasm split test * On wasm, create a new object file with the asset information * Fix multiple assets * add a regression test for 3467 * fix image link * fix linker out file * fix linux linker args * add windows linker command * fix linux link args * fix windows target triple * Expose an option to build assets in the linker intercept * Fix the standalone optimizer and document it in the dx help command * include the cli opt version in the hash * fix clippy * fix ssg port * add a safety note to the read_volatile call * move found assets log * fix some merge conflicts * fix build * make most env var for the linker optional * guess the target triple when dx acts as the linker intercept * automatically find the linker for normal builds * use the right linker in wasm mode * fix mac linker * look inside archive files (rlibs) * remove out.txt * fix clippy * fix typo * fix clippy * fix build * fix dx link name in docs * ignore libcompiler_builtins warnings * fix playwright * pass typocheck * fix printing sysroot * Modify final binary * fix wasm assets * remove old link section logic * fix tests * forward manganis export args * fail to evaluate relocatable address * retain all exports in the same pattern as other platforms * Fix adding assets during a wasm hot patch * less nesting and remove logs * fix clippy * log pdb files * read from pdb * print data section contents * print with object * normal windows builds working * fix hot patch windows * clean up logs * fix clippy * clean up assets * use the most recent pdb * fix doc test * fix typo * tiny nits * make bundled a reference instead of a pointer * add helpful comment to impl * match on file format * Fix asset hashing comment --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
f865e3f559
commit
58669f892d
45 files changed
+1174
-762
No files matched your search
+65
-79
@@ -1,19 +1,23 @@
|
||||
use anyhow::Context;
|
||||
use manganis_core::linker::LinkSection;
|
||||
use manganis::AssetOptions;
|
||||
use manganis_core::BundledAsset;
|
||||
use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection};
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
mod build_info;
|
||||
mod css;
|
||||
mod file;
|
||||
mod folder;
|
||||
mod hash;
|
||||
mod image;
|
||||
mod js;
|
||||
mod json;
|
||||
|
||||
pub use file::process_file_to;
|
||||
pub use hash::add_hash_to_asset;
|
||||
|
||||
/// A manifest of all assets collected from dependencies
|
||||
///
|
||||
@@ -21,7 +25,7 @@ pub use file::process_file_to;
|
||||
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct AssetManifest {
|
||||
/// Map of bundled asset name to the asset itself
|
||||
pub assets: HashMap<PathBuf, BundledAsset>,
|
||||
assets: HashMap<PathBuf, HashSet<BundledAsset>>,
|
||||
}
|
||||
|
||||
impl AssetManifest {
|
||||
@@ -31,100 +35,82 @@ impl AssetManifest {
|
||||
asset_path: &Path,
|
||||
options: manganis::AssetOptions,
|
||||
) -> anyhow::Result<BundledAsset> {
|
||||
let hash = manganis_core::hash::AssetHash::hash_file_contents(asset_path)
|
||||
.context("Failed to hash file")?;
|
||||
|
||||
let output_path_str = asset_path.to_str().ok_or(anyhow::anyhow!(
|
||||
"Failed to convert wasm bindgen output path to string"
|
||||
))?;
|
||||
|
||||
let bundled_asset =
|
||||
manganis::macro_helpers::create_bundled_asset(output_path_str, hash.bytes(), options);
|
||||
let mut bundled_asset =
|
||||
manganis::macro_helpers::create_bundled_asset(output_path_str, options);
|
||||
add_hash_to_asset(&mut bundled_asset);
|
||||
|
||||
self.assets.insert(asset_path.into(), bundled_asset);
|
||||
self.assets
|
||||
.entry(asset_path.to_path_buf())
|
||||
.or_default()
|
||||
.insert(bundled_asset);
|
||||
|
||||
Ok(bundled_asset)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Insert an existing bundled asset to the manifest
|
||||
pub fn insert_asset(&mut self, asset: BundledAsset) {
|
||||
let asset_path = asset.absolute_source_path();
|
||||
self.assets
|
||||
.entry(asset_path.into())
|
||||
.or_default()
|
||||
.insert(asset);
|
||||
}
|
||||
|
||||
/// Get any assets that are tied to a specific source file
|
||||
pub fn get_assets_for_source(&self, path: &Path) -> Option<&HashSet<BundledAsset>> {
|
||||
self.assets.get(path)
|
||||
}
|
||||
|
||||
/// Check if the manifest contains a specific asset
|
||||
pub fn contains(&self, asset: &BundledAsset) -> bool {
|
||||
self.assets
|
||||
.get(&PathBuf::from(asset.absolute_source_path()))
|
||||
.is_some_and(|assets| assets.contains(asset))
|
||||
}
|
||||
|
||||
/// Iterate over all the assets in the manifest
|
||||
pub fn assets(&self) -> impl Iterator<Item = &BundledAsset> {
|
||||
self.assets.values().flat_map(|assets| assets.iter())
|
||||
}
|
||||
|
||||
pub 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 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);
|
||||
}
|
||||
|
||||
/// Optimize a list of assets in parallel
|
||||
pub fn optimize_all_assets(
|
||||
assets_to_transfer: Vec<(PathBuf, PathBuf, AssetOptions)>,
|
||||
on_optimization_start: impl FnMut(&Path, &Path, &AssetOptions) + Sync + Send,
|
||||
on_optimization_end: impl FnMut(&Path, &Path, &AssetOptions) + Sync + Send,
|
||||
) -> anyhow::Result<()> {
|
||||
let on_optimization_start = Arc::new(RwLock::new(on_optimization_start));
|
||||
let on_optimization_end = Arc::new(RwLock::new(on_optimization_end));
|
||||
assets_to_transfer
|
||||
.par_iter()
|
||||
.try_for_each(|(from, to, options)| {
|
||||
{
|
||||
let mut on_optimization_start = on_optimization_start.write().unwrap();
|
||||
on_optimization_start(from, to, options);
|
||||
}
|
||||
}
|
||||
|
||||
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 res = process_file_to(options, from, to);
|
||||
if let Err(err) = res.as_ref() {
|
||||
tracing::error!("Failed to copy asset {from:?}: {err}");
|
||||
}
|
||||
|
||||
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;
|
||||
let mut on_optimization_end = on_optimization_end.write().unwrap();
|
||||
on_optimization_end(from, to, options);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
res.map(|_| ())
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user