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:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2025-05-31 21:45:12 +02:00
1 parent f865e3f559
commit 58669f892d
45 files changed
+1174 -762

No files matched your search

+75 -33
View File
@@ -1,23 +1,13 @@
use crate::AssetOptions;
use const_serialize::{ConstStr, SerializeConst};
use std::path::PathBuf;
use const_serialize::{deserialize_const, ConstStr, ConstVec, SerializeConst};
use std::{fmt::Debug, hash::Hash, path::PathBuf};
/// An asset that should be copied by the bundler with some options. This type will be
/// serialized into the binary and added to the link section [`LinkSection::CURRENT`](crate::linker::LinkSection::CURRENT).
/// serialized into the binary.
/// CLIs that support manganis, should pull out the assets from the link section, optimize,
/// and write them to the filesystem at [`BundledAsset::bundled_path`] for the application
/// to use.
#[derive(
Debug,
PartialEq,
PartialOrd,
Clone,
Copy,
Hash,
SerializeConst,
serde::Serialize,
serde::Deserialize,
)]
#[derive(Debug, Eq, Clone, Copy, SerializeConst, serde::Serialize, serde::Deserialize)]
pub struct BundledAsset {
/// The absolute path of the asset
absolute_source_path: ConstStr,
@@ -27,13 +17,46 @@ pub struct BundledAsset {
options: AssetOptions,
}
impl PartialEq for BundledAsset {
fn eq(&self, other: &Self) -> bool {
self.absolute_source_path == other.absolute_source_path
&& self.bundled_path == other.bundled_path
&& self.options == other.options
}
}
impl PartialOrd for BundledAsset {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self
.absolute_source_path
.partial_cmp(&other.absolute_source_path)
{
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.bundled_path.partial_cmp(&other.bundled_path) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.options.partial_cmp(&other.options)
}
}
impl Hash for BundledAsset {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.absolute_source_path.hash(state);
self.bundled_path.hash(state);
self.options.hash(state);
}
}
impl BundledAsset {
#[doc(hidden)]
/// This should only be called from the macro
/// Create a new asset
pub const fn new(
absolute_source_path: &'static str,
bundled_path: &'static str,
absolute_source_path: &str,
bundled_path: &str,
options: AssetOptions,
) -> Self {
Self {
@@ -83,6 +106,7 @@ impl BundledAsset {
pub fn absolute_source_path(&self) -> &str {
self.absolute_source_path.as_str()
}
/// Get the options for the asset
pub const fn options(&self) -> &AssetOptions {
&self.options
@@ -101,28 +125,49 @@ impl BundledAsset {
/// img { src: ASSET }
/// };
/// ```
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(PartialEq, Clone, Copy)]
pub struct Asset {
/// The bundled asset
bundled: BundledAsset,
/// The link section for the asset
keep_link_section: fn() -> u8,
/// A pointer to the bundled asset. This will be resolved after the linker has run and
/// put into the lazy asset
///
/// WARNING: Don't read this directly. Reads can get optimized away at compile time before
/// the data for this is filled in by the CLI after the binary is built. Instead, use
/// [`std::ptr::read_volatile`] to read the data.
bundled: &'static [u8],
}
impl Debug for Asset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.resolve().fmt(f)
}
}
unsafe impl Send for Asset {}
unsafe impl Sync for Asset {}
impl Asset {
#[doc(hidden)]
/// This should only be called from the macro
/// Create a new asset from the bundled form of the asset and the link section
pub const fn new(bundled: BundledAsset, keep_link_section: fn() -> u8) -> Self {
Self {
bundled,
keep_link_section,
}
pub const fn new(bundled: &'static [u8]) -> Self {
Self { bundled }
}
/// Get the bundled asset
pub const fn bundled(&self) -> &BundledAsset {
&self.bundled
pub fn bundled(&self) -> BundledAsset {
let len = self.bundled.len();
let ptr = self.bundled as *const [u8] as *const u8;
if ptr.is_null() {
panic!("Tried to use an asset that was not bundled. Make sure you are compiling dx as the linker");
}
let mut bytes = ConstVec::new();
for byte in 0..len {
// SAFETY: We checked that the pointer was not null above. The pointer is valid for reads and
// since we are reading a u8 there are no alignment requirements
bytes = bytes.push(unsafe { std::ptr::read_volatile(ptr.add(byte)) });
}
let read = bytes.read();
deserialize_const!(BundledAsset, read).expect("Failed to deserialize asset. Make sure you built with the matching version of the Dioxus CLI").1
}
/// Return a canonicalized path to the asset
@@ -130,13 +175,10 @@ impl Asset {
/// Attempts to resolve it against an `assets` folder in the current directory.
/// If that doesn't exist, it will resolve against the cargo manifest dir
pub fn resolve(&self) -> PathBuf {
// Force a volatile read of the asset link section to ensure the symbol makes it into the binary
(self.keep_link_section)();
#[cfg(feature = "dioxus")]
// If the asset is relative, we resolve the asset at the current directory
if !dioxus_core_types::is_bundled_app() {
return PathBuf::from(self.bundled.absolute_source_path.as_str());
return PathBuf::from(self.bundled().absolute_source_path.as_str());
}
#[cfg(feature = "dioxus")]
@@ -156,7 +198,7 @@ impl Asset {
// Otherwise presumably we're bundled and we can use the bundled path
bundle_root.join(PathBuf::from(
self.bundled.bundled_path.as_str().trim_start_matches('/'),
self.bundled().bundled_path.as_str().trim_start_matches('/'),
))
}
}