CLI fixes for hotpatching the playground (#4715)

* add component subcommand

* add dx component add, remove and list commands

* handle rust dependencies

* handle component dependencies

* fetch remote registries

* sort components before listing them

* remove local dependency

* move te component manifest into the dioxus workspace

* add additional instalation instructions the first time the component
module is created

* fix mod name

* fix clippy

* improve docs and move to the main branch of components

* fix typos

* more typos

* undo const-serialize changes

* remove a few unwraps

* Allow adding multiple components at once and add a `--all` flag

* add options to the config for the base path, global asset path and
default registry

* component_path -> component_dir

* component_dir is always relative to the crate root

* fix clippy

* fix hot patching with a base path

* read the hot patch file from stdin instead of a CLI arg to allow for longer inputs

* fix request imports

* wip: code style changes

* rename to "components" instead of "component"

* bump lock

* move to method on request

* read until eof in the case of pretty json

* strip is now copy

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2025-10-20 17:50:27 -04:00
1 parent 79613f9db9
commit 987e41de16
6 files changed
+1715 -1739

No files matched your search

Generated
+1654 -1698
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -250,7 +250,7 @@ futures-util = { version = "0.3", default-features = false }
rustc-hash = "2.1.1"
wasm-bindgen = "0.2.100"
wasm-bindgen-futures = "0.4.50"
js-sys = "0.3.77"
js-sys = "0.3"
web-sys = { version = "0.3.77", default-features = false }
html_parser = "0.7.0"
thiserror = "2.0.12"
+1 -12
View File
@@ -640,7 +640,6 @@ impl AppBuilder {
) -> Result<JumpTable> {
let original = self.build.main_exe();
let new = self.build.patch_exe(res.time_start);
let triple = self.build.triple.clone();
let asset_dir = self.build.asset_dir();
// Hotpatch asset!() calls
@@ -688,7 +687,7 @@ impl AppBuilder {
tracing::debug!("Patching {} -> {}", original.display(), new.display());
let mut jump_table = crate::build::create_jump_table(&new, &triple, cache)?;
let mut jump_table = self.build.create_jump_table(&new, cache)?;
// If it's android, we need to copy the assets to the device and then change the location of the patch
if self.build.bundle == BundleFormat::Android {
@@ -697,16 +696,6 @@ impl AppBuilder {
.await?;
}
// Rebase the wasm binary to be relocatable once the jump table is generated
if triple.architecture == target_lexicon::Architecture::Wasm32 {
// Make sure we use the dir relative to the public dir, so the web can load it as a proper URL
//
// ie we would've shipped `/Users/foo/Projects/dioxus/target/dx/project/debug/web/public/wasm/lib.wasm`
// but we want to ship `/wasm/lib.wasm`
jump_table.lib =
PathBuf::from("/").join(jump_table.lib.strip_prefix(self.build.root_dir()).unwrap())
}
let changed_files = match &res.mode {
BuildMode::Thin { changed_files, .. } => changed_files.clone(),
_ => vec![],
+3 -20
View File
@@ -290,24 +290,7 @@ impl HotpatchModuleCache {
}
}
/// Create a jump table for the given original and patch files.
pub fn create_jump_table(
patch: &Path,
triple: &Triple,
cache: &HotpatchModuleCache,
) -> Result<JumpTable> {
// Symbols are stored differently based on the platform, so we need to handle them differently.
// - Wasm requires the walrus crate and actually modifies the patch file
// - windows requires the pdb crate and pdb files
// - nix requires the object crate
match triple.operating_system {
OperatingSystem::Windows => create_windows_jump_table(patch, cache),
_ if triple.architecture == Architecture::Wasm32 => create_wasm_jump_table(patch, cache),
_ => create_native_jump_table(patch, triple, cache),
}
}
fn create_windows_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Result<JumpTable> {
pub fn create_windows_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Result<JumpTable> {
use pdb::FallibleIterator;
let old_name_to_addr = &cache.symbol_table;
@@ -361,7 +344,7 @@ fn create_windows_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resul
///
/// This does not work for WASM since the `object` crate does not support emitting the WASM format,
/// and because WASM requires more logic to handle the wasm-bindgen transformations.
fn create_native_jump_table(
pub fn create_native_jump_table(
patch: &Path,
triple: &Triple,
cache: &HotpatchModuleCache,
@@ -416,7 +399,7 @@ fn create_native_jump_table(
/// to manually satisfy them here, removing their need to be imported.
///
/// <https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md>
fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Result<JumpTable> {
pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Result<JumpTable> {
let name_to_ifunc_old = &cache.symbol_ifunc_map;
let old = &cache.old_wasm;
let old_symbols =
+44 -1
View File
@@ -349,6 +349,7 @@ use std::{
},
time::{SystemTime, UNIX_EPOCH},
};
use subsecond_types::JumpTable;
use target_lexicon::{Architecture, OperatingSystem, Triple};
use tempfile::TempDir;
use tokio::{io::AsyncBufReadExt, process::Command};
@@ -2334,6 +2335,48 @@ impl BuildRequest {
Ok(())
}
pub(crate) fn create_jump_table(
&self,
patch: &Path,
cache: &HotpatchModuleCache,
) -> Result<JumpTable> {
use crate::build::patch::{
create_native_jump_table, create_wasm_jump_table, create_windows_jump_table,
};
let root_dir = self.root_dir();
let base_path = self.base_path();
let triple = &self.triple;
// Symbols are stored differently based on the platform, so we need to handle them differently.
// - Wasm requires the walrus crate and actually modifies the patch file
// - windows requires the pdb crate and pdb files
// - nix requires the object crate
let mut jump_table = match triple.operating_system {
OperatingSystem::Windows => create_windows_jump_table(patch, cache)?,
_ if triple.architecture == Architecture::Wasm32 => {
create_wasm_jump_table(patch, cache)?
}
_ => create_native_jump_table(patch, triple, cache)?,
};
// root_dir: &Path,
// base_path: Option<&str>,
// Rebase the wasm binary to be relocatable once the jump table is generated
if triple.architecture == target_lexicon::Architecture::Wasm32 {
// Make sure we use the dir relative to the public dir, so the web can load it as a proper URL
//
// ie we would've shipped `/Users/foo/Projects/dioxus/target/dx/project/debug/web/public/wasm/lib.wasm`
// but we want to ship `/wasm/lib.wasm`
jump_table.lib = PathBuf::from(
"/".to_string() + base_path.unwrap_or_default().trim_start_matches('/'),
)
.join(jump_table.lib.strip_prefix(root_dir).unwrap())
}
Ok(jump_table)
}
/// Automatically detect the linker flavor based on the target triple and any custom linkers.
///
/// This tries to replicate what rustc does when selecting the linker flavor based on the linker
@@ -3567,7 +3610,7 @@ impl BuildRequest {
// Get the strip setting from the profile or the profile it inherits from
fn get_strip(profile: &Profile, profiles: &Profiles) -> Option<StripSetting> {
profile.strip.clone().or_else(|| {
profile.strip.or_else(|| {
// If we can't find the strip setting, check if we inherit from another profile
profile.inherits.as_ref().and_then(|inherits| {
let profile = match inherits.as_str() {
+12 -7
View File
@@ -5,11 +5,15 @@ use crate::{
use anyhow::Context;
use clap::Parser;
use dioxus_dx_wire_format::StructuredBuildArtifacts;
use std::io::Read;
use std::sync::Arc;
const HELP_HEADING: &str = "Hotpatching a binary";
/// Patches a single binary, but takes the same arguments as a `cargo build`.
/// Patches a single binary, but takes the same arguments as a `cargo build` and the serialized output from `dx build --fat-binary` as input.
///
/// This is intended to be used with something like `dx build --fat-binary >> output.json` and then
/// `cat output.json | dx hotpatch --aslr-reference 0x12345678` to produce a hotpatched binary.
///
/// By default, patches the client, but you can set patch_server to true to patch the server instead.
#[derive(Clone, Debug, Parser)]
@@ -18,10 +22,6 @@ pub struct HotpatchTip {
#[clap(long, num_args = 0..=1, default_missing_value="true", help_heading = HELP_HEADING)]
pub patch_server: Option<bool>,
/// The serialized output from the `dx build --fat-binary` command
#[clap(long, help_heading = HELP_HEADING)]
pub serialized_artifacts: String,
/// The ASLR reference of the running app being patched. Used to generate sensible offsets for patched code.
#[clap(long, help_heading = HELP_HEADING)]
pub aslr_reference: u64,
@@ -49,8 +49,13 @@ impl HotpatchTip {
&targets.client
};
let mut serialized_artifacts = String::new();
std::io::stdin()
.lock()
.read_to_string(&mut serialized_artifacts)
.context("Failed to read serialized build artifacts from stdin")?;
let structured_build_artifacts =
serde_json::from_str::<StructuredBuildArtifacts>(&self.serialized_artifacts)
serde_json::from_str::<StructuredBuildArtifacts>(&serialized_artifacts)
.context("Failed to parse structured build artifacts")?;
let StructuredBuildArtifacts {
@@ -82,7 +87,7 @@ impl HotpatchTip {
let patch_exe = request.patch_exe(artifacts.time_start);
Ok(StructuredOutput::Hotpatch {
jump_table: crate::build::create_jump_table(&patch_exe, &request.triple, &cache)?,
jump_table: request.create_jump_table(&patch_exe, &cache)?,
artifacts: artifacts.into_structured_output(),
})
}