feat: copy frameworks to Frameworks folder, detect linkers, custom target dirs (#4174)
* feat: copy frameworks to Frameworks folder * add dylib linking to patching * remove logging * rename __aslr_offset * fix android/linux * support linker flavor * support custom target dirs * custom cargo target dir support * whoops ios * dlsym doesn't exist on windows * look for the right main symbol per platform * distinction between mains * export main on windows * enbable rlib caching * add dx hash * adjust linking warnings * update tui with hotreload state * implement symbol flags * add flags * only run ranlib on darwin * enable lld * paths * add harness test for tls * wip: revert launch mobile * wip * fix fullstack mobile * fix a few bugs - long paths? - reverting back to original * pass-thru fuse-ld args * fix nan in render, fix empty archives * consider existing dep files if archive exists * fmt * drop msrv
This commit is contained in:
1 parent
751f1920c3
commit
0982c1ff8b
25 files changed
+1044
-315
No files matched your search
Generated
+30
@@ -2022,6 +2022,18 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-config2"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dc3749a36e0423c991f1e7a3e4ab0c36a1f489658313db4b187d401d79cc461"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"toml_edit 0.22.26",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo-generate"
|
name = "cargo-generate"
|
||||||
version = "0.23.3"
|
version = "0.23.3"
|
||||||
@@ -2943,6 +2955,14 @@ dependencies = [
|
|||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cross-tls-crate"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cross-tls-crate-dylib"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam"
|
name = "crossbeam"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@@ -3662,6 +3682,7 @@ dependencies = [
|
|||||||
"backtrace",
|
"backtrace",
|
||||||
"brotli 7.0.0",
|
"brotli 7.0.0",
|
||||||
"built",
|
"built",
|
||||||
|
"cargo-config2",
|
||||||
"cargo-generate",
|
"cargo-generate",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
@@ -13468,6 +13489,15 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subsecond-tls-harness"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cross-tls-crate",
|
||||||
|
"cross-tls-crate-dylib",
|
||||||
|
"dioxus-devtools",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subsecond-types"
|
name = "subsecond-types"
|
||||||
version = "0.7.0-alpha.0"
|
version = "0.7.0-alpha.0"
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ members = [
|
|||||||
# subsecond
|
# subsecond
|
||||||
"packages/subsecond/subsecond",
|
"packages/subsecond/subsecond",
|
||||||
"packages/subsecond/subsecond-types",
|
"packages/subsecond/subsecond-types",
|
||||||
|
"packages/subsecond/subsecond-tests/cross-tls-crate",
|
||||||
|
"packages/subsecond/subsecond-tests/cross-tls-crate-dylib",
|
||||||
|
"packages/subsecond/subsecond-tests/cross-tls-test",
|
||||||
|
|
||||||
# Full project examples
|
# Full project examples
|
||||||
"example-projects/fullstack-hackernews",
|
"example-projects/fullstack-hackernews",
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ wasm-bindgen-externref-xform = "0.2.100"
|
|||||||
pdb = "0.8.0"
|
pdb = "0.8.0"
|
||||||
self_update = { version = "0.42.0", features = ["archive-tar", "archive-zip", "compression-flate2", "compression-zip-deflate"] }
|
self_update = { version = "0.42.0", features = ["archive-tar", "archive-zip", "compression-flate2", "compression-zip-deflate"] }
|
||||||
self-replace = "1.5.0"
|
self-replace = "1.5.0"
|
||||||
|
cargo-config2 = { workspace = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
built = { version = "0.7.5", features = ["git2"] }
|
built = { version = "0.7.5", features = ["git2"] }
|
||||||
|
|||||||
@@ -208,12 +208,12 @@ impl AppBuilder {
|
|||||||
self.bundling_progress = 0.0;
|
self.bundling_progress = 0.0;
|
||||||
}
|
}
|
||||||
BuildStage::Starting { crate_count, .. } => {
|
BuildStage::Starting { crate_count, .. } => {
|
||||||
self.expected_crates = *crate_count;
|
self.expected_crates = *crate_count.max(&1);
|
||||||
}
|
}
|
||||||
BuildStage::InstallingTooling => {}
|
BuildStage::InstallingTooling => {}
|
||||||
BuildStage::Compiling { current, total, .. } => {
|
BuildStage::Compiling { current, total, .. } => {
|
||||||
self.compiled_crates = *current;
|
self.compiled_crates = *current;
|
||||||
self.expected_crates = *total;
|
self.expected_crates = *total.max(&1);
|
||||||
|
|
||||||
if self.compile_start.is_none() {
|
if self.compile_start.is_none() {
|
||||||
self.compile_start = Some(Instant::now());
|
self.compile_start = Some(Instant::now());
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use itertools::Itertools;
|
|||||||
use object::{
|
use object::{
|
||||||
macho::{self},
|
macho::{self},
|
||||||
read::File,
|
read::File,
|
||||||
write::{MachOBuildVersion, StandardSection, Symbol, SymbolSection},
|
write::{MachOBuildVersion, SectionId, StandardSection, Symbol, SymbolId, SymbolSection},
|
||||||
Endianness, Object, ObjectSymbol, SymbolKind, SymbolScope,
|
Endianness, Object, ObjectSymbol, SymbolFlags, SymbolKind, SymbolScope,
|
||||||
};
|
};
|
||||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -84,6 +84,7 @@ pub struct CachedSymbol {
|
|||||||
pub is_undefined: bool,
|
pub is_undefined: bool,
|
||||||
pub is_weak: bool,
|
pub is_weak: bool,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
|
pub flags: SymbolFlags<SectionId, SymbolId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for HotpatchModuleCache {
|
impl PartialEq for HotpatchModuleCache {
|
||||||
@@ -139,6 +140,7 @@ impl HotpatchModuleCache {
|
|||||||
is_undefined,
|
is_undefined,
|
||||||
is_weak: false,
|
is_weak: false,
|
||||||
size: 0,
|
size: 0,
|
||||||
|
flags: SymbolFlags::None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -158,6 +160,7 @@ impl HotpatchModuleCache {
|
|||||||
is_undefined,
|
is_undefined,
|
||||||
is_weak: false,
|
is_weak: false,
|
||||||
size: 0,
|
size: 0,
|
||||||
|
flags: SymbolFlags::None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -252,6 +255,15 @@ impl HotpatchModuleCache {
|
|||||||
let symbol_table = obj
|
let symbol_table = obj
|
||||||
.symbols()
|
.symbols()
|
||||||
.filter_map(|s| {
|
.filter_map(|s| {
|
||||||
|
let flags = match s.flags() {
|
||||||
|
SymbolFlags::None => SymbolFlags::None,
|
||||||
|
SymbolFlags::Elf { st_info, st_other } => {
|
||||||
|
SymbolFlags::Elf { st_info, st_other }
|
||||||
|
}
|
||||||
|
SymbolFlags::MachO { n_desc } => SymbolFlags::MachO { n_desc },
|
||||||
|
_ => SymbolFlags::None,
|
||||||
|
};
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
s.name().ok()?.to_string(),
|
s.name().ok()?.to_string(),
|
||||||
CachedSymbol {
|
CachedSymbol {
|
||||||
@@ -260,6 +272,7 @@ impl HotpatchModuleCache {
|
|||||||
is_weak: s.is_weak(),
|
is_weak: s.is_weak(),
|
||||||
kind: s.kind(),
|
kind: s.kind(),
|
||||||
size: s.size(),
|
size: s.size(),
|
||||||
|
flags,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
@@ -326,9 +339,9 @@ fn create_windows_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resul
|
|||||||
.context("failed to find 'main' symbol in patch")?;
|
.context("failed to find 'main' symbol in patch")?;
|
||||||
|
|
||||||
let aslr_reference = old_name_to_addr
|
let aslr_reference = old_name_to_addr
|
||||||
.get("__aslr_reference")
|
.get("main")
|
||||||
.map(|s| s.address)
|
.map(|s| s.address)
|
||||||
.context("failed to find '_aslr_reference' symbol in original module")?;
|
.context("failed to find '_main' symbol in original module")?;
|
||||||
|
|
||||||
Ok(JumpTable {
|
Ok(JumpTable {
|
||||||
lib: patch.to_path_buf(),
|
lib: patch.to_path_buf(),
|
||||||
@@ -371,31 +384,15 @@ fn create_native_jump_table(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_base_address = match triple.operating_system {
|
let sentinel = main_sentinel(triple);
|
||||||
// The symbol in the symtab is called "_main" but in the dysymtab it is called "main"
|
let new_base_address = new_name_to_addr
|
||||||
OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
|
.get(sentinel)
|
||||||
*new_name_to_addr
|
.cloned()
|
||||||
.get("_main")
|
.context("failed to find 'main' symbol in base - are deubg symbols enabled?")?;
|
||||||
.context("failed to find '_main' symbol in patch")?
|
|
||||||
}
|
|
||||||
|
|
||||||
// No distincation between the two on these platforms
|
|
||||||
OperatingSystem::Freebsd
|
|
||||||
| OperatingSystem::Openbsd
|
|
||||||
| OperatingSystem::Linux
|
|
||||||
| OperatingSystem::Windows => *new_name_to_addr
|
|
||||||
.get("main")
|
|
||||||
.context("failed to find 'main' symbol in patch")?,
|
|
||||||
|
|
||||||
// On wasm, it doesn't matter what the address is since the binary is PIC
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let aslr_reference = old_name_to_addr
|
let aslr_reference = old_name_to_addr
|
||||||
.get("___aslr_reference")
|
.get(sentinel)
|
||||||
.or_else(|| old_name_to_addr.get("__aslr_reference"))
|
|
||||||
.map(|s| s.address)
|
.map(|s| s.address)
|
||||||
.context("failed to find '___aslr_reference' symbol in original module")?;
|
.context("failed to find 'main' symbol in original module - are debug symbols enabled?")?;
|
||||||
|
|
||||||
Ok(JumpTable {
|
Ok(JumpTable {
|
||||||
lib: patch.to_path_buf(),
|
lib: patch.to_path_buf(),
|
||||||
@@ -831,22 +828,31 @@ pub fn create_undefined_symbol_stub(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbol_table = &cache.symbol_table;
|
// Get the offset from the main module and adjust the addresses by the slide;
|
||||||
|
let aslr_ref_address = cache
|
||||||
|
.symbol_table
|
||||||
|
.get(main_sentinel(triple))
|
||||||
|
.context("failed to find '_main' symbol in patch")?
|
||||||
|
.address;
|
||||||
|
|
||||||
|
if aslr_reference < aslr_ref_address {
|
||||||
|
return Err(PatchError::InvalidModule(
|
||||||
|
format!(
|
||||||
|
"ASLR reference is less than the main module's address - is there a `main`?. {:x} < {:x}", aslr_reference, aslr_ref_address )
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Get the offset from the main module and adjust the addresses by the slide
|
|
||||||
let aslr_ref_address = symbol_table
|
|
||||||
.get("___aslr_reference")
|
|
||||||
.or_else(|| symbol_table.get("__aslr_reference"))
|
|
||||||
.map(|s| s.address)
|
|
||||||
.context("Failed to find ___aslr_reference symbol")?;
|
|
||||||
let aslr_offset = aslr_reference - aslr_ref_address;
|
let aslr_offset = aslr_reference - aslr_ref_address;
|
||||||
|
|
||||||
// we need to assemble a PLT/GOT so direct calls to the patch symbols work
|
// we need to assemble a PLT/GOT so direct calls to the patch symbols work
|
||||||
// for each symbol we either write the address directly (as a symbol) or create a PLT/GOT entry
|
// for each symbol we either write the address directly (as a symbol) or create a PLT/GOT entry
|
||||||
let text_section = obj.section_id(StandardSection::Text);
|
let text_section = obj.section_id(StandardSection::Text);
|
||||||
for name in undefined_symbols {
|
for name in undefined_symbols {
|
||||||
let Some(sym) = symbol_table.get(name.as_str().trim_start_matches("__imp_")) else {
|
let Some(sym) = cache
|
||||||
tracing::error!("Symbol not found: {}", name);
|
.symbol_table
|
||||||
|
.get(name.as_str().trim_start_matches("__imp_"))
|
||||||
|
else {
|
||||||
|
tracing::debug!("Symbol not found: {}", name);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -899,7 +905,7 @@ pub fn create_undefined_symbol_stub(
|
|||||||
kind: SymbolKind::Data, // Always Data for IAT entries
|
kind: SymbolKind::Data, // Always Data for IAT entries
|
||||||
weak: false,
|
weak: false,
|
||||||
section: SymbolSection::Section(data_section),
|
section: SymbolSection::Section(data_section),
|
||||||
flags: object::SymbolFlags::None,
|
flags: SymbolFlags::None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1028,7 +1034,7 @@ pub fn create_undefined_symbol_stub(
|
|||||||
kind: SymbolKind::Text,
|
kind: SymbolKind::Text,
|
||||||
weak: false,
|
weak: false,
|
||||||
section: SymbolSection::Section(text_section),
|
section: SymbolSection::Section(text_section),
|
||||||
flags: object::SymbolFlags::None,
|
flags: SymbolFlags::None, // ignore for these stubs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1097,7 +1103,7 @@ pub fn create_undefined_symbol_stub(
|
|||||||
kind: SymbolKind::Tls,
|
kind: SymbolKind::Tls,
|
||||||
weak: false,
|
weak: false,
|
||||||
section: SymbolSection::Section(tls_section),
|
section: SymbolSection::Section(tls_section),
|
||||||
flags: object::SymbolFlags::None,
|
flags: SymbolFlags::None, // ignore for these stubs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,6 +1114,15 @@ pub fn create_undefined_symbol_stub(
|
|||||||
SymbolKind::Unknown => SymbolKind::Data,
|
SymbolKind::Unknown => SymbolKind::Data,
|
||||||
k => k,
|
k => k,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// plain linux *wants* these flags, but android doesn't.
|
||||||
|
// unsure what's going on here, but this is special cased for now.
|
||||||
|
// I think the more advanced linkers don't want these flags, but the default linux linker (ld) does.
|
||||||
|
let flags = match triple.environment {
|
||||||
|
target_lexicon::Environment::Android => SymbolFlags::None,
|
||||||
|
_ => sym.flags,
|
||||||
|
};
|
||||||
|
|
||||||
obj.add_symbol(Symbol {
|
obj.add_symbol(Symbol {
|
||||||
name: name.as_bytes()[name_offset..].to_vec(),
|
name: name.as_bytes()[name_offset..].to_vec(),
|
||||||
value: abs_addr,
|
value: abs_addr,
|
||||||
@@ -1116,7 +1131,7 @@ pub fn create_undefined_symbol_stub(
|
|||||||
kind,
|
kind,
|
||||||
weak: sym.is_weak,
|
weak: sym.is_weak,
|
||||||
section: SymbolSection::Absolute,
|
section: SymbolSection::Absolute,
|
||||||
flags: object::SymbolFlags::None,
|
flags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1439,3 +1454,18 @@ fn parse_module_with_ids(bindgened: &[u8]) -> Result<ParsedModule> {
|
|||||||
symbols,
|
symbols,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the main sentinel symbol for the given target triple
|
||||||
|
///
|
||||||
|
/// We need to special case darwin since `main` is the entrypoint but `_main` is the actual symbol.
|
||||||
|
/// The entrypoint ends up outside the text section, seemingly, and breaks our aslr detection.
|
||||||
|
fn main_sentinel(triple: &Triple) -> &'static str {
|
||||||
|
match triple.operating_system {
|
||||||
|
// The symbol in the symtab is called "_main" but in the dysymtab it is called "main"
|
||||||
|
OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
|
||||||
|
"_main"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => "main",
|
||||||
|
}
|
||||||
|
}
|
||||||
+319
-115
@@ -316,8 +316,9 @@
|
|||||||
//! - xbuild: <https://github.com/rust-mobile/xbuild/blob/master/xbuild/src/command/build.rs>
|
//! - xbuild: <https://github.com/rust-mobile/xbuild/blob/master/xbuild/src/command/build.rs>
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AndroidTools, BuildContext, DioxusConfig, Error, LinkAction, Platform, Result, RustcArgs,
|
AndroidTools, BuildContext, DioxusConfig, Error, LinkAction, LinkerFlavor, Platform, Result,
|
||||||
TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR,
|
RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace,
|
||||||
|
DX_RUSTC_WRAPPER_ENV_VAR,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use cargo_metadata::diagnostic::Diagnostic;
|
use cargo_metadata::diagnostic::Diagnostic;
|
||||||
@@ -377,7 +378,7 @@ pub(crate) struct BuildRequest {
|
|||||||
pub(crate) extra_cargo_args: Vec<String>,
|
pub(crate) extra_cargo_args: Vec<String>,
|
||||||
pub(crate) extra_rustc_args: Vec<String>,
|
pub(crate) extra_rustc_args: Vec<String>,
|
||||||
pub(crate) no_default_features: bool,
|
pub(crate) no_default_features: bool,
|
||||||
pub(crate) custom_target_dir: Option<PathBuf>,
|
pub(crate) target_dir: PathBuf,
|
||||||
pub(crate) skip_assets: bool,
|
pub(crate) skip_assets: bool,
|
||||||
pub(crate) wasm_split: bool,
|
pub(crate) wasm_split: bool,
|
||||||
pub(crate) debug_symbols: bool,
|
pub(crate) debug_symbols: bool,
|
||||||
@@ -643,12 +644,26 @@ impl BuildRequest {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let custom_linker = if platform == Platform::Android {
|
// Somethings we override are also present in the user's config.
|
||||||
|
// If we can't get them by introspecting cargo, then we need to get them from the config
|
||||||
|
let cargo_config = cargo_config2::Config::load().unwrap();
|
||||||
|
|
||||||
|
let mut custom_linker = if platform == Platform::Android {
|
||||||
Some(workspace.android_tools()?.android_cc(&triple))
|
Some(workspace.android_tools()?.android_cc(&triple))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Ok(Some(linker)) = cargo_config.linker(triple.to_string()) {
|
||||||
|
custom_linker = Some(linker);
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_dir = std::env::var("CARGO_TARGET_DIR")
|
||||||
|
.ok()
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| cargo_config.build.target_dir.clone())
|
||||||
|
.unwrap_or_else(|| workspace.workspace_root().join("target"));
|
||||||
|
|
||||||
// Set up some tempfiles so we can do some IPC between us and the linker/rustc wrapper (which is occasionally us!)
|
// Set up some tempfiles so we can do some IPC between us and the linker/rustc wrapper (which is occasionally us!)
|
||||||
let link_args_file = Arc::new(
|
let link_args_file = Arc::new(
|
||||||
NamedTempFile::with_suffix(".txt")
|
NamedTempFile::with_suffix(".txt")
|
||||||
@@ -677,11 +692,16 @@ impl BuildRequest {
|
|||||||
• link_args_file: {},
|
• link_args_file: {},
|
||||||
• link_err_file: {},
|
• link_err_file: {},
|
||||||
• rustc_wrapper_args_file: {},
|
• rustc_wrapper_args_file: {},
|
||||||
• session_cache_dir: {}"#,
|
• session_cache_dir: {}
|
||||||
|
• linker: {:?}
|
||||||
|
• target_dir: {:?}
|
||||||
|
"#,
|
||||||
link_args_file.path().display(),
|
link_args_file.path().display(),
|
||||||
link_err_file.path().display(),
|
link_err_file.path().display(),
|
||||||
rustc_wrapper_args_file.path().display(),
|
rustc_wrapper_args_file.path().display(),
|
||||||
session_cache_dir.path().display(),
|
session_cache_dir.path().display(),
|
||||||
|
custom_linker,
|
||||||
|
target_dir,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -696,7 +716,7 @@ impl BuildRequest {
|
|||||||
workspace,
|
workspace,
|
||||||
config,
|
config,
|
||||||
enabled_platforms,
|
enabled_platforms,
|
||||||
custom_target_dir: None,
|
target_dir,
|
||||||
custom_linker,
|
custom_linker,
|
||||||
link_args_file,
|
link_args_file,
|
||||||
link_err_file,
|
link_err_file,
|
||||||
@@ -739,6 +759,9 @@ impl BuildRequest {
|
|||||||
self.write_executable(ctx, &artifacts.exe, &mut artifacts.assets)
|
self.write_executable(ctx, &artifacts.exe, &mut artifacts.assets)
|
||||||
.await
|
.await
|
||||||
.context("Failed to write main executable")?;
|
.context("Failed to write main executable")?;
|
||||||
|
self.write_frameworks(ctx, &artifacts.direct_rustc)
|
||||||
|
.await
|
||||||
|
.context("Failed to write frameworks")?;
|
||||||
self.write_assets(ctx, &artifacts.assets)
|
self.write_assets(ctx, &artifacts.assets)
|
||||||
.await
|
.await
|
||||||
.context("Failed to write assets")?;
|
.context("Failed to write assets")?;
|
||||||
@@ -874,8 +897,6 @@ impl BuildRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let exe = output_location.context("Cargo build failed - no output location. Toggle tracing mode (press `t`) for more information.")?;
|
|
||||||
|
|
||||||
// Accumulate the rustc args from the wrapper, if they exist and can be parsed.
|
// Accumulate the rustc args from the wrapper, if they exist and can be parsed.
|
||||||
let mut direct_rustc = RustcArgs::default();
|
let mut direct_rustc = RustcArgs::default();
|
||||||
if let Ok(res) = std::fs::read_to_string(self.rustc_wrapper_args_file.path()) {
|
if let Ok(res) = std::fs::read_to_string(self.rustc_wrapper_args_file.path()) {
|
||||||
@@ -887,13 +908,34 @@ impl BuildRequest {
|
|||||||
// If there's any warnings from the linker, we should print them out
|
// If there's any warnings from the linker, we should print them out
|
||||||
if let Ok(linker_warnings) = std::fs::read_to_string(self.link_err_file.path()) {
|
if let Ok(linker_warnings) = std::fs::read_to_string(self.link_err_file.path()) {
|
||||||
if !linker_warnings.is_empty() {
|
if !linker_warnings.is_empty() {
|
||||||
tracing::warn!("Linker warnings: {}", linker_warnings);
|
if output_location.is_none() {
|
||||||
|
tracing::error!("Linker warnings: {}", linker_warnings);
|
||||||
|
} else {
|
||||||
|
tracing::debug!("Linker warnings: {}", linker_warnings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect the linker args from the and update the rustc args
|
||||||
|
direct_rustc.link_args = std::fs::read_to_string(self.link_args_file.path())
|
||||||
|
.context("Failed to read link args from file")?
|
||||||
|
.lines()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let exe = output_location.context("Cargo build failed - no output location. Toggle tracing mode (press `t`) for more information.")?;
|
||||||
|
|
||||||
// Fat builds need to be linked with the fat linker. Would also like to link here for thin builds
|
// Fat builds need to be linked with the fat linker. Would also like to link here for thin builds
|
||||||
if matches!(ctx.mode, BuildMode::Fat) {
|
if matches!(ctx.mode, BuildMode::Fat) {
|
||||||
|
let link_start = SystemTime::now();
|
||||||
self.run_fat_link(ctx, &exe, &direct_rustc).await?;
|
self.run_fat_link(ctx, &exe, &direct_rustc).await?;
|
||||||
|
tracing::debug!(
|
||||||
|
"Fat linking completed in {}us",
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(link_start)
|
||||||
|
.unwrap()
|
||||||
|
.as_micros()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let assets = self.collect_assets(&exe, ctx)?;
|
let assets = self.collect_assets(&exe, ctx)?;
|
||||||
@@ -901,7 +943,11 @@ impl BuildRequest {
|
|||||||
let mode = ctx.mode.clone();
|
let mode = ctx.mode.clone();
|
||||||
let platform = self.platform;
|
let platform = self.platform;
|
||||||
|
|
||||||
tracing::debug!("Build completed successfully: {:?}", exe);
|
tracing::debug!(
|
||||||
|
"Build completed successfully in {}us: {:?}",
|
||||||
|
time_end.duration_since(time_start).unwrap().as_micros(),
|
||||||
|
exe
|
||||||
|
);
|
||||||
|
|
||||||
Ok(BuildArtifacts {
|
Ok(BuildArtifacts {
|
||||||
time_end,
|
time_end,
|
||||||
@@ -991,12 +1037,6 @@ impl BuildRequest {
|
|||||||
| Platform::Ios
|
| Platform::Ios
|
||||||
| Platform::Liveview
|
| Platform::Liveview
|
||||||
| Platform::Server => {
|
| Platform::Server => {
|
||||||
// We wipe away the dir completely, which is not great behavior :/
|
|
||||||
// Don't wipe server since web will need this folder too.
|
|
||||||
if self.platform != Platform::Server {
|
|
||||||
_ = std::fs::remove_dir_all(self.exe_dir());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::fs::create_dir_all(self.exe_dir())?;
|
std::fs::create_dir_all(self.exe_dir())?;
|
||||||
std::fs::copy(exe, self.main_exe())?;
|
std::fs::copy(exe, self.main_exe())?;
|
||||||
}
|
}
|
||||||
@@ -1005,6 +1045,52 @@ impl BuildRequest {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn write_frameworks(&self, _ctx: &BuildContext, direct_rustc: &RustcArgs) -> Result<()> {
|
||||||
|
let framework_dir = self.frameworks_folder();
|
||||||
|
_ = std::fs::create_dir_all(&framework_dir);
|
||||||
|
|
||||||
|
for arg in &direct_rustc.link_args {
|
||||||
|
// todo - how do we handle windows dlls? we don't want to bundle the system dlls
|
||||||
|
// for now, we don't do anything with dlls, and only use .dylibs and .so files
|
||||||
|
if arg.ends_with(".dylib") | arg.ends_with(".so") {
|
||||||
|
let from = PathBuf::from(arg);
|
||||||
|
let to = framework_dir.join(from.file_name().unwrap());
|
||||||
|
_ = std::fs::remove_file(&to);
|
||||||
|
|
||||||
|
tracing::debug!("Copying framework from {from:?} to {to:?}");
|
||||||
|
|
||||||
|
// in dev and on normal oses, we want to symlink the file
|
||||||
|
// otherwise, just copy it (since in release you want to distribute the framework)
|
||||||
|
if cfg!(any(windows, unix)) && !self.release {
|
||||||
|
#[cfg(windows)]
|
||||||
|
std::os::windows::fs::symlink_file(from, to).with_context(|| {
|
||||||
|
"Failed to symlink framework into bundle: {from:?} -> {to:?}"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
std::os::unix::fs::symlink(from, to).with_context(|| {
|
||||||
|
"Failed to symlink framework into bundle: {from:?} -> {to:?}"
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
std::fs::copy(from, to)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frameworks_folder(&self) -> PathBuf {
|
||||||
|
match self.triple.operating_system {
|
||||||
|
OperatingSystem::Darwin(_) | OperatingSystem::MacOSX(_) => {
|
||||||
|
self.root_dir().join("Contents").join("Frameworks")
|
||||||
|
}
|
||||||
|
OperatingSystem::IOS(_) => self.root_dir().join("Frameworks"),
|
||||||
|
OperatingSystem::Linux | OperatingSystem::Windows => self.root_dir(),
|
||||||
|
_ => self.root_dir(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy the assets out of the manifest and into the target location
|
/// Copy the assets out of the manifest and into the target location
|
||||||
///
|
///
|
||||||
/// Should be the same on all platforms - just copy over the assets from the manifest into the output directory
|
/// Should be the same on all platforms - just copy over the assets from the manifest into the output directory
|
||||||
@@ -1200,6 +1286,7 @@ impl BuildRequest {
|
|||||||
// -nodefaultlibs
|
// -nodefaultlibs
|
||||||
// -Wl,-all_load
|
// -Wl,-all_load
|
||||||
// ```
|
// ```
|
||||||
|
let mut dylibs = vec![];
|
||||||
let mut object_files = args
|
let mut object_files = args
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|arg| arg.ends_with(".rcgu.o"))
|
.filter(|arg| arg.ends_with(".rcgu.o"))
|
||||||
@@ -1233,6 +1320,15 @@ impl BuildRequest {
|
|||||||
let patch_file = self.main_exe().with_file_name("stub.o");
|
let patch_file = self.main_exe().with_file_name("stub.o");
|
||||||
std::fs::write(&patch_file, stub_bytes)?;
|
std::fs::write(&patch_file, stub_bytes)?;
|
||||||
object_files.push(patch_file);
|
object_files.push(patch_file);
|
||||||
|
|
||||||
|
// Add the dylibs/sos to the linker args
|
||||||
|
// Make sure to use the one in the bundle, not the ones in the target dir or system.
|
||||||
|
for arg in &rustc_args.link_args {
|
||||||
|
if arg.ends_with(".dylib") || arg.ends_with(".so") {
|
||||||
|
let path = PathBuf::from(arg);
|
||||||
|
dylibs.push(self.frameworks_folder().join(path.file_name().unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// And now we can run the linker with our new args
|
// And now we can run the linker with our new args
|
||||||
@@ -1251,6 +1347,7 @@ impl BuildRequest {
|
|||||||
// does it since it uses llvm-objcopy into the `target/debug/` folder.
|
// does it since it uses llvm-objcopy into the `target/debug/` folder.
|
||||||
let res = Command::new(linker)
|
let res = Command::new(linker)
|
||||||
.args(object_files.iter())
|
.args(object_files.iter())
|
||||||
|
.args(dylibs.iter())
|
||||||
.args(self.thin_link_args(&args)?)
|
.args(self.thin_link_args(&args)?)
|
||||||
.args(out_arg)
|
.args(out_arg)
|
||||||
.env_clear()
|
.env_clear()
|
||||||
@@ -1298,12 +1395,9 @@ impl BuildRequest {
|
|||||||
/// This is basically just stripping away the rlibs and other libraries that will be satisfied
|
/// This is basically just stripping away the rlibs and other libraries that will be satisfied
|
||||||
/// by our stub step.
|
/// by our stub step.
|
||||||
fn thin_link_args(&self, original_args: &[&str]) -> Result<Vec<String>> {
|
fn thin_link_args(&self, original_args: &[&str]) -> Result<Vec<String>> {
|
||||||
use target_lexicon::OperatingSystem;
|
|
||||||
|
|
||||||
let triple = self.triple.clone();
|
|
||||||
let mut out_args = vec![];
|
let mut out_args = vec![];
|
||||||
|
|
||||||
match triple.operating_system {
|
match self.linker_flavor() {
|
||||||
// wasm32-unknown-unknown -> use wasm-ld (gnu-lld)
|
// wasm32-unknown-unknown -> use wasm-ld (gnu-lld)
|
||||||
//
|
//
|
||||||
// We need to import a few things - namely the memory and ifunc table.
|
// We need to import a few things - namely the memory and ifunc table.
|
||||||
@@ -1323,7 +1417,7 @@ impl BuildRequest {
|
|||||||
// I think we can make relocation-model=pic work for non-wasm platforms, enabling
|
// I think we can make relocation-model=pic work for non-wasm platforms, enabling
|
||||||
// fully relocatable modules with no host coordination in lieu of sending out
|
// fully relocatable modules with no host coordination in lieu of sending out
|
||||||
// the aslr slide at runtime.
|
// the aslr slide at runtime.
|
||||||
OperatingSystem::Unknown if self.platform == Platform::Web => {
|
LinkerFlavor::WasmLld => {
|
||||||
out_args.extend([
|
out_args.extend([
|
||||||
"--fatal-warnings".to_string(),
|
"--fatal-warnings".to_string(),
|
||||||
"--verbose".to_string(),
|
"--verbose".to_string(),
|
||||||
@@ -1344,7 +1438,7 @@ impl BuildRequest {
|
|||||||
//
|
//
|
||||||
// Most importantly, we want to pass `-dylib` to both CC and the linker to indicate that
|
// Most importantly, we want to pass `-dylib` to both CC and the linker to indicate that
|
||||||
// we want to generate the shared library instead of an executable.
|
// we want to generate the shared library instead of an executable.
|
||||||
OperatingSystem::IOS(_) | OperatingSystem::MacOSX(_) | OperatingSystem::Darwin(_) => {
|
LinkerFlavor::Darwin => {
|
||||||
out_args.extend(["-Wl,-dylib".to_string()]);
|
out_args.extend(["-Wl,-dylib".to_string()]);
|
||||||
|
|
||||||
// Preserve the original args. We only preserve:
|
// Preserve the original args. We only preserve:
|
||||||
@@ -1367,7 +1461,7 @@ impl BuildRequest {
|
|||||||
// android/linux need to be compatible with lld
|
// android/linux need to be compatible with lld
|
||||||
//
|
//
|
||||||
// android currently drags along its own libraries and other zany flags
|
// android currently drags along its own libraries and other zany flags
|
||||||
OperatingSystem::Linux => {
|
LinkerFlavor::Gnu => {
|
||||||
out_args.extend([
|
out_args.extend([
|
||||||
"-shared".to_string(),
|
"-shared".to_string(),
|
||||||
"-Wl,--eh-frame-hdr".to_string(),
|
"-Wl,--eh-frame-hdr".to_string(),
|
||||||
@@ -1391,13 +1485,14 @@ impl BuildRequest {
|
|||||||
if arg.starts_with("-l")
|
if arg.starts_with("-l")
|
||||||
|| arg.starts_with("-m")
|
|| arg.starts_with("-m")
|
||||||
|| arg.starts_with("-Wl,--target=")
|
|| arg.starts_with("-Wl,--target=")
|
||||||
|
|| arg.starts_with("-Wl,-fuse-ld")
|
||||||
{
|
{
|
||||||
out_args.push(arg.to_string());
|
out_args.push(arg.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OperatingSystem::Windows => {
|
LinkerFlavor::Msvc => {
|
||||||
out_args.extend([
|
out_args.extend([
|
||||||
"shlwapi.lib".to_string(),
|
"shlwapi.lib".to_string(),
|
||||||
"kernel32.lib".to_string(),
|
"kernel32.lib".to_string(),
|
||||||
@@ -1415,7 +1510,9 @@ impl BuildRequest {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => return Err(anyhow::anyhow!("Unsupported platform for thin linking").into()),
|
LinkerFlavor::Unsupported => {
|
||||||
|
return Err(anyhow::anyhow!("Unsupported platform for thin linking").into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let extract_value = |arg: &str| -> Option<String> {
|
let extract_value = |arg: &str| -> Option<String> {
|
||||||
@@ -1460,15 +1557,12 @@ impl BuildRequest {
|
|||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
));
|
));
|
||||||
|
|
||||||
let extension = match self.triple.operating_system {
|
let extension = match self.linker_flavor() {
|
||||||
OperatingSystem::Darwin(_) => "dylib",
|
LinkerFlavor::Darwin => "dylib",
|
||||||
OperatingSystem::MacOSX(_) => "dylib",
|
LinkerFlavor::Gnu => "so",
|
||||||
OperatingSystem::IOS(_) => "dylib",
|
LinkerFlavor::WasmLld => "wasm",
|
||||||
OperatingSystem::Windows => "dll",
|
LinkerFlavor::Msvc => "dll",
|
||||||
OperatingSystem::Linux => "so",
|
LinkerFlavor::Unsupported => "",
|
||||||
OperatingSystem::Wasi => "wasm",
|
|
||||||
OperatingSystem::Unknown if self.platform == Platform::Web => "wasm",
|
|
||||||
_ => "",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
path.with_extension(extension)
|
path.with_extension(extension)
|
||||||
@@ -1512,38 +1606,65 @@ impl BuildRequest {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
ctx.status_starting_link();
|
ctx.status_starting_link();
|
||||||
|
|
||||||
let raw_args = std::fs::read_to_string(self.link_args_file.path())
|
|
||||||
.context("Failed to read link args from file")?;
|
|
||||||
let args = raw_args.lines().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Filter out the rlib files from the arguments
|
// Filter out the rlib files from the arguments
|
||||||
let rlibs = args
|
let rlibs = rustc_args
|
||||||
|
.link_args
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|arg| arg.ends_with(".rlib"))
|
.filter(|arg| arg.ends_with(".rlib"))
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Acquire a hash from the rlib names
|
// Acquire a hash from the rlib names, sizes, modified times, and dx's git commit hash
|
||||||
|
// This ensures that any changes in dx or the rlibs will cause a new hash to be generated
|
||||||
|
// The hash relies on both dx and rustc hashes, so it should be thoroughly unique. Keep it
|
||||||
|
// short to avoid long file names.
|
||||||
let hash_id = Uuid::new_v5(
|
let hash_id = Uuid::new_v5(
|
||||||
&Uuid::NAMESPACE_OID,
|
&Uuid::NAMESPACE_OID,
|
||||||
rlibs
|
rlibs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.file_name().unwrap().to_string_lossy())
|
.map(|p| {
|
||||||
|
format!(
|
||||||
|
"{}-{}-{}-{}",
|
||||||
|
p.file_name().unwrap().to_string_lossy(),
|
||||||
|
p.metadata().map(|m| m.len()).unwrap_or_default(),
|
||||||
|
p.metadata()
|
||||||
|
.ok()
|
||||||
|
.and_then(|m| m.modified().ok())
|
||||||
|
.and_then(|f| f.duration_since(UNIX_EPOCH).map(|f| f.as_secs()).ok())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
crate::dx_build_info::GIT_COMMIT_HASH.unwrap_or_default()
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
);
|
)
|
||||||
|
.to_string()
|
||||||
|
.chars()
|
||||||
|
.take(8)
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
// Check if we already have a cached object file
|
// Check if we already have a cached object file
|
||||||
let out_ar_path = exe.with_file_name(format!("libfatdependencies-{hash_id}.a"));
|
let out_ar_path = exe.with_file_name(format!("libdeps-{hash_id}.a",));
|
||||||
|
let out_rlibs_list = exe.with_file_name(format!("rlibs-{hash_id}.txt"));
|
||||||
|
let mut archive_has_contents = out_ar_path.exists();
|
||||||
|
|
||||||
let mut compiler_rlibs = vec![];
|
// Use the rlibs list if it exists
|
||||||
|
let mut compiler_rlibs = std::fs::read_to_string(&out_rlibs_list)
|
||||||
|
.ok()
|
||||||
|
.map(|s| s.lines().map(PathBuf::from).collect::<Vec<_>>())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Create it by dumping all the rlibs into it
|
// Create it by dumping all the rlibs into it
|
||||||
// This will include the std rlibs too, which can severely bloat the size of the archive
|
// This will include the std rlibs too, which can severely bloat the size of the archive
|
||||||
//
|
//
|
||||||
// The nature of this process involves making extremely fat archives, so we should try and
|
// The nature of this process involves making extremely fat archives, so we should try and
|
||||||
// speed up the future linking process by caching the archive.
|
// speed up the future linking process by caching the archive.
|
||||||
if !crate::devcfg::should_cache_dep_lib(&out_ar_path) {
|
//
|
||||||
|
// Since we're using the git hash for the CLI entropy, debug builds should always regenerate
|
||||||
|
// the archive since their hash might not change, but the logic might.
|
||||||
|
if !archive_has_contents || cfg!(debug_assertions) {
|
||||||
|
compiler_rlibs.clear();
|
||||||
|
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
let mut out_ar = ar::Builder::new(&mut bytes);
|
let mut out_ar = ar::Builder::new(&mut bytes);
|
||||||
for rlib in &rlibs {
|
for rlib in &rlibs {
|
||||||
@@ -1573,7 +1694,11 @@ impl BuildRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rlibs might contain dlls/sos/lib files which we don't want to include
|
// rlibs might contain dlls/sos/lib files which we don't want to include
|
||||||
if name.ends_with(".dll") || name.ends_with(".so") || name.ends_with(".lib") {
|
if name.ends_with(".dll")
|
||||||
|
|| name.ends_with(".so")
|
||||||
|
|| name.ends_with(".lib")
|
||||||
|
|| name.ends_with(".dylib")
|
||||||
|
{
|
||||||
compiler_rlibs.push(rlib.to_owned());
|
compiler_rlibs.push(rlib.to_owned());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1582,6 +1707,7 @@ impl BuildRequest {
|
|||||||
tracing::debug!("Unknown object file in rlib: {:?}", name);
|
tracing::debug!("Unknown object file in rlib: {:?}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
archive_has_contents = true;
|
||||||
out_ar
|
out_ar
|
||||||
.append(&object_file.header().clone(), object_file)
|
.append(&object_file.header().clone(), object_file)
|
||||||
.context("Failed to add object file to archive")?;
|
.context("Failed to add object file to archive")?;
|
||||||
@@ -1595,8 +1721,10 @@ impl BuildRequest {
|
|||||||
// Run the ranlib command to index the archive. This slows down this process a bit,
|
// Run the ranlib command to index the archive. This slows down this process a bit,
|
||||||
// but is necessary for some linkers to work properly.
|
// but is necessary for some linkers to work properly.
|
||||||
// We ignore its error in case it doesn't recognize the architecture
|
// We ignore its error in case it doesn't recognize the architecture
|
||||||
if let Some(ranlib) = self.select_ranlib() {
|
if self.linker_flavor() == LinkerFlavor::Darwin {
|
||||||
_ = Command::new(ranlib).arg(&out_ar_path).output().await;
|
if let Some(ranlib) = self.select_ranlib() {
|
||||||
|
_ = Command::new(ranlib).arg(&out_ar_path).output().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1606,62 +1734,71 @@ impl BuildRequest {
|
|||||||
// And then remove the rest of the rlibs
|
// And then remove the rest of the rlibs
|
||||||
//
|
//
|
||||||
// We also need to insert the -force_load flag to force the linker to load the archive
|
// We also need to insert the -force_load flag to force the linker to load the archive
|
||||||
let mut args = args.iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
let mut args = rustc_args.link_args.clone();
|
||||||
if let Some(first_rlib) = args.iter().position(|arg| arg.ends_with(".rlib")) {
|
if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) {
|
||||||
match self.triple.operating_system {
|
if archive_has_contents {
|
||||||
OperatingSystem::Unknown if self.platform == Platform::Web => {
|
match self.linker_flavor() {
|
||||||
// We need to use the --whole-archive flag for wasm
|
LinkerFlavor::WasmLld => {
|
||||||
args[first_rlib] = "--whole-archive".to_string();
|
args.insert(last_object, "--whole-archive".to_string());
|
||||||
args.insert(first_rlib + 1, out_ar_path.display().to_string());
|
args.insert(last_object + 1, out_ar_path.display().to_string());
|
||||||
args.insert(first_rlib + 2, "--no-whole-archive".to_string());
|
args.insert(last_object + 2, "--no-whole-archive".to_string());
|
||||||
args.retain(|arg| !arg.ends_with(".rlib"));
|
args.retain(|arg| !arg.ends_with(".rlib"));
|
||||||
|
for rlib in compiler_rlibs.iter().rev() {
|
||||||
// add back the compiler rlibs
|
args.insert(last_object + 3, rlib.display().to_string());
|
||||||
for rlib in compiler_rlibs.iter().rev() {
|
}
|
||||||
args.insert(first_rlib + 3, rlib.display().to_string());
|
|
||||||
}
|
}
|
||||||
}
|
LinkerFlavor::Gnu => {
|
||||||
|
args.insert(last_object, "-Wl,--whole-archive".to_string());
|
||||||
// Subtle difference - on linux and android we go through clang and thus pass `-Wl,` prefix
|
args.insert(last_object + 1, out_ar_path.display().to_string());
|
||||||
OperatingSystem::Linux => {
|
args.insert(last_object + 2, "-Wl,--no-whole-archive".to_string());
|
||||||
args[first_rlib] = "-Wl,--whole-archive".to_string();
|
args.retain(|arg| !arg.ends_with(".rlib"));
|
||||||
args.insert(first_rlib + 1, out_ar_path.display().to_string());
|
for rlib in compiler_rlibs.iter().rev() {
|
||||||
args.insert(first_rlib + 2, "-Wl,--no-whole-archive".to_string());
|
args.insert(last_object + 3, rlib.display().to_string());
|
||||||
args.retain(|arg| !arg.ends_with(".rlib"));
|
}
|
||||||
|
|
||||||
// add back the compiler rlibs
|
|
||||||
for rlib in compiler_rlibs.iter().rev() {
|
|
||||||
args.insert(first_rlib + 3, rlib.display().to_string());
|
|
||||||
}
|
}
|
||||||
}
|
LinkerFlavor::Darwin => {
|
||||||
|
args.insert(last_object, "-Wl,-force_load".to_string());
|
||||||
OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
|
args.insert(last_object + 1, out_ar_path.display().to_string());
|
||||||
args[first_rlib] = "-Wl,-force_load".to_string();
|
args.retain(|arg| !arg.ends_with(".rlib"));
|
||||||
args.insert(first_rlib + 1, out_ar_path.display().to_string());
|
for rlib in compiler_rlibs.iter().rev() {
|
||||||
args.retain(|arg| !arg.ends_with(".rlib"));
|
args.insert(last_object + 2, rlib.display().to_string());
|
||||||
|
}
|
||||||
// add back the compiler rlibs
|
|
||||||
for rlib in compiler_rlibs.iter().rev() {
|
|
||||||
args.insert(first_rlib + 2, rlib.display().to_string());
|
|
||||||
}
|
}
|
||||||
|
LinkerFlavor::Msvc => {
|
||||||
args.insert(first_rlib + 3, "-Wl,-all_load".to_string());
|
args.insert(
|
||||||
}
|
last_object,
|
||||||
|
format!("/WHOLEARCHIVE:{}", out_ar_path.display()),
|
||||||
OperatingSystem::Windows => {
|
);
|
||||||
args[first_rlib] = format!("/WHOLEARCHIVE:{}", out_ar_path.display());
|
args.retain(|arg| !arg.ends_with(".rlib"));
|
||||||
args.retain(|arg| !arg.ends_with(".rlib"));
|
for rlib in compiler_rlibs.iter().rev() {
|
||||||
|
args.insert(last_object + 1, rlib.display().to_string());
|
||||||
// add back the compiler rlibs
|
}
|
||||||
for rlib in compiler_rlibs.iter().rev() {
|
|
||||||
args.insert(first_rlib + 1, rlib.display().to_string());
|
|
||||||
}
|
}
|
||||||
|
LinkerFlavor::Unsupported => {
|
||||||
|
tracing::error!("Unsupported platform for fat linking");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
args.insert(first_rlib, "/HIGHENTROPYVA:NO".to_string());
|
// Add custom args to the linkers
|
||||||
}
|
match self.linker_flavor() {
|
||||||
|
LinkerFlavor::Gnu => {
|
||||||
|
// Export `main` so subsecond can use it for a reference point
|
||||||
|
args.push("-Wl,--export-dynamic-symbol,main".to_string());
|
||||||
|
}
|
||||||
|
LinkerFlavor::Darwin => {
|
||||||
|
// `-all_load` is an extra step to ensure that all symbols are loaded (different than force_load)
|
||||||
|
args.push("-Wl,-all_load".to_string());
|
||||||
|
}
|
||||||
|
LinkerFlavor::Msvc => {
|
||||||
|
// Prevent alsr from overflowing 32 bits
|
||||||
|
args.push("/HIGHENTROPYVA:NO".to_string());
|
||||||
|
|
||||||
_ => {}
|
// Export `main` so subsecond can use it for a reference point
|
||||||
};
|
args.push("/EXPORT:main".to_string());
|
||||||
|
}
|
||||||
|
LinkerFlavor::WasmLld | LinkerFlavor::Unsupported => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We also need to remove the `-o` flag since we want the linker output to end up in the
|
// We also need to remove the `-o` flag since we want the linker output to end up in the
|
||||||
@@ -1690,6 +1827,7 @@ impl BuildRequest {
|
|||||||
let linker = self.select_linker()?;
|
let linker = self.select_linker()?;
|
||||||
|
|
||||||
tracing::trace!("Fat linking with args: {:?} {:#?}", linker, args);
|
tracing::trace!("Fat linking with args: {:?} {:#?}", linker, args);
|
||||||
|
tracing::trace!("Fat linking with env: {:#?}", rustc_args.envs);
|
||||||
|
|
||||||
// Run the linker directly!
|
// Run the linker directly!
|
||||||
let out_arg = match self.triple.operating_system {
|
let out_arg = match self.triple.operating_system {
|
||||||
@@ -1724,9 +1862,64 @@ impl BuildRequest {
|
|||||||
_ = std::fs::remove_file(f);
|
_ = std::fs::remove_file(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache the rlibs list
|
||||||
|
_ = std::fs::write(
|
||||||
|
&out_rlibs_list,
|
||||||
|
compiler_rlibs
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.display().to_string())
|
||||||
|
.join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// and triple.
|
||||||
|
fn linker_flavor(&self) -> LinkerFlavor {
|
||||||
|
if let Some(custom) = self.custom_linker.as_ref() {
|
||||||
|
let name = custom.file_name().unwrap().to_ascii_lowercase();
|
||||||
|
match name.to_str() {
|
||||||
|
Some("lld-link") => return LinkerFlavor::Msvc,
|
||||||
|
Some("lld-link.exe") => return LinkerFlavor::Msvc,
|
||||||
|
Some("wasm-ld") => return LinkerFlavor::WasmLld,
|
||||||
|
Some("ld64.lld") => return LinkerFlavor::Darwin,
|
||||||
|
Some("ld.lld") => return LinkerFlavor::Gnu,
|
||||||
|
Some("ld.gold") => return LinkerFlavor::Gnu,
|
||||||
|
Some("mold") => return LinkerFlavor::Gnu,
|
||||||
|
Some("sold") => return LinkerFlavor::Gnu,
|
||||||
|
Some("wild") => return LinkerFlavor::Gnu,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.triple.environment {
|
||||||
|
target_lexicon::Environment::Gnu
|
||||||
|
| target_lexicon::Environment::Gnuabi64
|
||||||
|
| target_lexicon::Environment::Gnueabi
|
||||||
|
| target_lexicon::Environment::Gnueabihf
|
||||||
|
| target_lexicon::Environment::GnuLlvm => LinkerFlavor::Gnu,
|
||||||
|
target_lexicon::Environment::Musl => LinkerFlavor::Gnu,
|
||||||
|
target_lexicon::Environment::Android => LinkerFlavor::Gnu,
|
||||||
|
target_lexicon::Environment::Msvc => LinkerFlavor::Msvc,
|
||||||
|
target_lexicon::Environment::Macabi => LinkerFlavor::Darwin,
|
||||||
|
_ => match self.triple.operating_system {
|
||||||
|
OperatingSystem::Darwin(_) => LinkerFlavor::Darwin,
|
||||||
|
OperatingSystem::IOS(_) => LinkerFlavor::Darwin,
|
||||||
|
OperatingSystem::MacOSX(_) => LinkerFlavor::Darwin,
|
||||||
|
OperatingSystem::Linux => LinkerFlavor::Gnu,
|
||||||
|
OperatingSystem::Windows => LinkerFlavor::Msvc,
|
||||||
|
_ => match self.triple.architecture {
|
||||||
|
target_lexicon::Architecture::Wasm32 => LinkerFlavor::WasmLld,
|
||||||
|
target_lexicon::Architecture::Wasm64 => LinkerFlavor::WasmLld,
|
||||||
|
_ => LinkerFlavor::Unsupported,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Select the linker to use for this platform.
|
/// Select the linker to use for this platform.
|
||||||
///
|
///
|
||||||
/// We prefer to use the rust-lld linker when we can since it's usually there.
|
/// We prefer to use the rust-lld linker when we can since it's usually there.
|
||||||
@@ -1750,25 +1943,25 @@ impl BuildRequest {
|
|||||||
return Ok(PathBuf::from(linker));
|
return Ok(PathBuf::from(linker));
|
||||||
}
|
}
|
||||||
|
|
||||||
let cc = match self.triple.operating_system {
|
if let Some(linker) = self.custom_linker.clone() {
|
||||||
OperatingSystem::Unknown if self.platform == Platform::Web => self.workspace.wasm_ld(),
|
return Ok(linker);
|
||||||
|
}
|
||||||
|
|
||||||
// The android clang linker is *special* and has some android-specific flags that we need
|
let cc = match self.linker_flavor() {
|
||||||
//
|
LinkerFlavor::WasmLld => self.workspace.wasm_ld(),
|
||||||
// Note that this is *clang*, not `lld`.
|
|
||||||
OperatingSystem::Linux if self.platform == Platform::Android => {
|
|
||||||
self.workspace.android_tools()?.android_cc(&self.triple)
|
|
||||||
}
|
|
||||||
|
|
||||||
// On macOS, we use the system linker since it's usually there.
|
// On macOS, we use the system linker since it's usually there.
|
||||||
// We could also use `lld` here, but it might not be installed by default.
|
// We could also use `lld` here, but it might not be installed by default.
|
||||||
//
|
//
|
||||||
// Note that this is *clang*, not `lld`.
|
// Note that this is *clang*, not `lld`.
|
||||||
OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => self.workspace.cc(),
|
LinkerFlavor::Darwin => self.workspace.cc(),
|
||||||
|
|
||||||
|
// On Linux, we use the system linker since it's usually there.
|
||||||
|
LinkerFlavor::Gnu => self.workspace.cc(),
|
||||||
|
|
||||||
// On windows, instead of trying to find the system linker, we just go with the lld.link
|
// On windows, instead of trying to find the system linker, we just go with the lld.link
|
||||||
// that rustup provides. It's faster and more stable then reyling on link.exe in path.
|
// that rustup provides. It's faster and more stable then reyling on link.exe in path.
|
||||||
OperatingSystem::Windows => self.workspace.lld_link(),
|
LinkerFlavor::Msvc => self.workspace.lld_link(),
|
||||||
|
|
||||||
// The rest of the platforms use `cc` as the linker which should be available in your path,
|
// The rest of the platforms use `cc` as the linker which should be available in your path,
|
||||||
// provided you have build-tools setup. On mac/linux this is the default, but on Windows
|
// provided you have build-tools setup. On mac/linux this is the default, but on Windows
|
||||||
@@ -1782,7 +1975,7 @@ impl BuildRequest {
|
|||||||
// Note that "cc" is *not* a linker. It's a compiler! The arguments we pass need to be in
|
// Note that "cc" is *not* a linker. It's a compiler! The arguments we pass need to be in
|
||||||
// the form of `-Wl,<args>` for them to make it to the linker. This matches how rust does it
|
// the form of `-Wl,<args>` for them to make it to the linker. This matches how rust does it
|
||||||
// which is confusing.
|
// which is confusing.
|
||||||
_ => self.workspace.cc(),
|
LinkerFlavor::Unsupported => self.workspace.cc(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(cc)
|
Ok(cc)
|
||||||
@@ -1827,7 +2020,7 @@ impl BuildRequest {
|
|||||||
|
|
||||||
cmd.envs(rustc_args.envs.iter().cloned());
|
cmd.envs(rustc_args.envs.iter().cloned());
|
||||||
|
|
||||||
tracing::trace!("Setting env vars: {:#?}", rustc_args.envs);
|
// tracing::trace!("Setting env vars: {:#?}", rustc_args.envs);
|
||||||
|
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
@@ -1935,6 +2128,21 @@ impl BuildRequest {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle frameworks/dylibs by setting the rpath
|
||||||
|
// This is dependent on the bundle structure - in this case, appimage and appbundle for mac/linux
|
||||||
|
// todo: we need to figure out what to do for windows
|
||||||
|
match self.triple.operating_system {
|
||||||
|
OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
|
||||||
|
cargo_args.push("-Clink-arg=-Wl,-rpath,@executable_path/../Frameworks".to_string());
|
||||||
|
cargo_args.push("-Clink-arg=-Wl,-rpath,@executable_path".to_string());
|
||||||
|
}
|
||||||
|
OperatingSystem::Linux => {
|
||||||
|
cargo_args.push("-Clink-arg=-Wl,-rpath,$ORIGIN/../lib".to_string());
|
||||||
|
cargo_args.push("-Clink-arg=-Wl,-rpath,$ORIGIN".to_string());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
// Our fancy hot-patching engine needs a lot of customization to work properly.
|
// Our fancy hot-patching engine needs a lot of customization to work properly.
|
||||||
//
|
//
|
||||||
// These args are mostly intended to be passed when *fat* linking but are generally fine to
|
// These args are mostly intended to be passed when *fat* linking but are generally fine to
|
||||||
@@ -2051,10 +2259,6 @@ impl BuildRequest {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(target_dir) = self.custom_target_dir.as_ref() {
|
|
||||||
env_vars.push(("CARGO_TARGET_DIR", target_dir.display().to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a release build, bake the base path and title into the binary with env vars.
|
// If this is a release build, bake the base path and title into the binary with env vars.
|
||||||
// todo: should we even be doing this? might be better being a build.rs or something else.
|
// todo: should we even be doing this? might be better being a build.rs or something else.
|
||||||
if self.release {
|
if self.release {
|
||||||
@@ -2510,7 +2714,7 @@ impl BuildRequest {
|
|||||||
/// is "distributed" after building an application (configurable in the
|
/// is "distributed" after building an application (configurable in the
|
||||||
/// `Dioxus.toml`).
|
/// `Dioxus.toml`).
|
||||||
fn internal_out_dir(&self) -> PathBuf {
|
fn internal_out_dir(&self) -> PathBuf {
|
||||||
let dir = self.workspace_dir().join("target").join("dx");
|
let dir = self.target_dir.join("dx");
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ pub enum LinkerFlavor {
|
|||||||
Gnu,
|
Gnu,
|
||||||
Darwin,
|
Darwin,
|
||||||
WasmLld,
|
WasmLld,
|
||||||
Unix,
|
|
||||||
Msvc,
|
Msvc,
|
||||||
|
Unsupported, // a catch-all for unsupported linkers, usually the stripped-down unix ones
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinkAction {
|
impl LinkAction {
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
//! Configuration of the CLI at runtime to enable certain experimental features.
|
//! Configuration of the CLI at runtime to enable certain experimental features.
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
/// Should we cache the dependency library?
|
|
||||||
///
|
|
||||||
/// When the `DIOXUS_CACHE_DEP_LIB` environment variable is set, we will cache the dependency library
|
|
||||||
/// built from the target's dependencies.
|
|
||||||
pub(crate) fn should_cache_dep_lib(lib: &Path) -> bool {
|
|
||||||
std::env::var("DIOXUS_CACHE_DEP_LIB").is_ok() && lib.exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should we force the entropy to be used on the main exe?
|
/// Should we force the entropy to be used on the main exe?
|
||||||
///
|
///
|
||||||
/// This is used to verify that binaries are copied with different names such that they don't collide
|
/// This is used to verify that binaries are copied with different names such that they don't collide
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub fn is_wrapping_rustc() -> bool {
|
|||||||
pub struct RustcArgs {
|
pub struct RustcArgs {
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
pub envs: Vec<(String, String)>,
|
pub envs: Vec<(String, String)>,
|
||||||
|
pub link_args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run rustc directly, but output the result to a file.
|
/// Run rustc directly, but output the result to a file.
|
||||||
@@ -50,6 +51,7 @@ pub async fn run_rustc() {
|
|||||||
let rustc_args = RustcArgs {
|
let rustc_args = RustcArgs {
|
||||||
args: args().skip(1).collect::<Vec<_>>(),
|
args: args().skip(1).collect::<Vec<_>>(),
|
||||||
envs: vars().collect::<_>(),
|
envs: vars().collect::<_>(),
|
||||||
|
link_args: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
std::fs::create_dir_all(var_file.parent().expect("Failed to get parent dir"))
|
std::fs::create_dir_all(var_file.parent().expect("Failed to get parent dir"))
|
||||||
|
|||||||
@@ -721,7 +721,13 @@ impl Output {
|
|||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new(Line::from(vec![
|
Paragraph::new(Line::from(vec![
|
||||||
"Hotreload: ".gray(),
|
"Hotreload: ".gray(),
|
||||||
"rsx and assets".yellow(),
|
if !state.runner.automatic_rebuilds {
|
||||||
|
"disabled".dark_gray()
|
||||||
|
} else if state.runner.use_hotpatch_engine {
|
||||||
|
"hot-patching".yellow()
|
||||||
|
} else {
|
||||||
|
"rsx and assets".yellow()
|
||||||
|
},
|
||||||
])),
|
])),
|
||||||
meta_list[3],
|
meta_list[3],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -138,9 +138,10 @@ pub(crate) fn proxy_to(
|
|||||||
match res {
|
match res {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// log assets at a different log level
|
// log assets at a different log level
|
||||||
if uri.path().starts_with("/assets")
|
if uri.path().starts_with("/assets/")
|
||||||
|| uri.path().starts_with("/_dioxus")
|
|| uri.path().starts_with("/_dioxus/")
|
||||||
|| uri.path().starts_with("/public")
|
|| uri.path().starts_with("/public/")
|
||||||
|
|| uri.path().starts_with("/wasm/")
|
||||||
{
|
{
|
||||||
tracing::trace!(dx_src = ?TraceSrc::Dev, "[{}] {}", res.status().as_u16(), uri);
|
tracing::trace!(dx_src = ?TraceSrc::Dev, "[{}] {}", res.status().as_u16(), uri);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -341,15 +341,15 @@ impl AppServer {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update the most recent version of the file, so when we force a rebuild, we keep operating on the most recent version
|
||||||
|
cached_file.most_recent = Some(new_contents);
|
||||||
|
|
||||||
// This assumes the two files are structured similarly. If they're not, we can't diff them
|
// This assumes the two files are structured similarly. If they're not, we can't diff them
|
||||||
let Some(changed_rsx) = dioxus_rsx_hotreload::diff_rsx(&new_file, &old_file) else {
|
let Some(changed_rsx) = dioxus_rsx_hotreload::diff_rsx(&new_file, &old_file) else {
|
||||||
needs_full_rebuild = true;
|
needs_full_rebuild = true;
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the most recent version of the file, so when we force a rebuild, we keep operating on the most recent version
|
|
||||||
cached_file.most_recent = Some(new_contents);
|
|
||||||
|
|
||||||
for ChangedRsx { old, new } in changed_rsx {
|
for ChangedRsx { old, new } in changed_rsx {
|
||||||
let old_start = old.span().start();
|
let old_start = old.span().start();
|
||||||
|
|
||||||
|
|||||||
+261
-107
@@ -1,3 +1,228 @@
|
|||||||
|
// use crate::Config;
|
||||||
|
// use crate::{
|
||||||
|
// app::App,
|
||||||
|
// ipc::{IpcMethod, UserWindowEvent},
|
||||||
|
// };
|
||||||
|
// use dioxus_core::*;
|
||||||
|
// use dioxus_document::eval;
|
||||||
|
// use std::any::Any;
|
||||||
|
// use tao::event::{Event, StartCause, WindowEvent};
|
||||||
|
|
||||||
|
// /// Launches the WebView and runs the event loop
|
||||||
|
// pub fn launch(root: fn() -> Element) {
|
||||||
|
// launch_cfg(root, vec![], vec![]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Launches the WebView and runs the event loop, with configuration and root props.
|
||||||
|
// pub fn launch_cfg(
|
||||||
|
// root: fn() -> Element,
|
||||||
|
// contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
|
// platform_config: Vec<Box<dyn Any>>,
|
||||||
|
// ) {
|
||||||
|
// let mut virtual_dom = VirtualDom::new(root);
|
||||||
|
|
||||||
|
// for context in contexts {
|
||||||
|
// virtual_dom.insert_any_root_context(context());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let platform_config = *platform_config
|
||||||
|
// .into_iter()
|
||||||
|
// .find_map(|cfg| cfg.downcast::<Config>().ok())
|
||||||
|
// .unwrap_or_default();
|
||||||
|
|
||||||
|
// launch_virtual_dom(virtual_dom, platform_config)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Launches the WebView and runs the event loop, with configuration and root props.
|
||||||
|
// pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
|
||||||
|
// #[cfg(feature = "tokio_runtime")]
|
||||||
|
// {
|
||||||
|
// tokio::runtime::Builder::new_multi_thread()
|
||||||
|
// .enable_all()
|
||||||
|
// .build()
|
||||||
|
// .unwrap()
|
||||||
|
// .block_on(tokio::task::unconstrained(async move {
|
||||||
|
// launch_virtual_dom_blocking(virtual_dom, desktop_config)
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// unreachable!("The desktop launch function will never exit")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[cfg(not(feature = "tokio_runtime"))]
|
||||||
|
// {
|
||||||
|
// launch_virtual_dom_blocking(virtual_dom, desktop_config);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Launch the WebView and run the event loop, with configuration and root props.
|
||||||
|
// ///
|
||||||
|
// /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
|
||||||
|
// /// and is equivalent to calling launch_with_props with the tokio feature disabled.
|
||||||
|
// pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config: Config) -> ! {
|
||||||
|
// let mut custom_event_handler = desktop_config.custom_event_handler.take();
|
||||||
|
// let (event_loop, mut app) = App::new(desktop_config, virtual_dom);
|
||||||
|
|
||||||
|
// event_loop.run(move |window_event, event_loop, control_flow| {
|
||||||
|
// // Set the control flow and check if any events need to be handled in the app itself
|
||||||
|
// app.tick(&window_event);
|
||||||
|
|
||||||
|
// if let Some(ref mut f) = custom_event_handler {
|
||||||
|
// f(&window_event, event_loop)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// match window_event {
|
||||||
|
// Event::NewEvents(StartCause::Init) => app.handle_start_cause_init(),
|
||||||
|
// Event::LoopDestroyed => app.handle_loop_destroyed(),
|
||||||
|
// Event::WindowEvent {
|
||||||
|
// event, window_id, ..
|
||||||
|
// } => match event {
|
||||||
|
// WindowEvent::CloseRequested => app.handle_close_requested(window_id),
|
||||||
|
// WindowEvent::Destroyed { .. } => app.window_destroyed(window_id),
|
||||||
|
// WindowEvent::Resized(new_size) => app.resize_window(window_id, new_size),
|
||||||
|
// _ => {}
|
||||||
|
// },
|
||||||
|
|
||||||
|
// Event::UserEvent(event) => match event {
|
||||||
|
// UserWindowEvent::Poll(id) => app.poll_vdom(id),
|
||||||
|
// UserWindowEvent::NewWindow => app.handle_new_window(),
|
||||||
|
// UserWindowEvent::CloseWindow(id) => app.handle_close_msg(id),
|
||||||
|
// UserWindowEvent::Shutdown => app.control_flow = tao::event_loop::ControlFlow::Exit,
|
||||||
|
|
||||||
|
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
// UserWindowEvent::GlobalHotKeyEvent(evnt) => app.handle_global_hotkey(evnt),
|
||||||
|
|
||||||
|
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
// UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt),
|
||||||
|
|
||||||
|
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
// UserWindowEvent::TrayMenuEvent(evnt) => app.handle_tray_menu_event(evnt),
|
||||||
|
|
||||||
|
// #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
// UserWindowEvent::TrayIconEvent(evnt) => app.handle_tray_icon_event(evnt),
|
||||||
|
|
||||||
|
// #[cfg(all(feature = "devtools", debug_assertions))]
|
||||||
|
// UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
|
||||||
|
|
||||||
|
// // Windows-only drag-n-drop fix events. We need to call the interpreter drag-n-drop code.
|
||||||
|
// UserWindowEvent::WindowsDragDrop(id) => {
|
||||||
|
// if let Some(webview) = app.webviews.get(&id) {
|
||||||
|
// webview.dom.in_runtime(|| {
|
||||||
|
// ScopeId::ROOT.in_runtime(|| {
|
||||||
|
// eval("window.interpreter.handleWindowsDragDrop();");
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// UserWindowEvent::WindowsDragLeave(id) => {
|
||||||
|
// if let Some(webview) = app.webviews.get(&id) {
|
||||||
|
// webview.dom.in_runtime(|| {
|
||||||
|
// ScopeId::ROOT.in_runtime(|| {
|
||||||
|
// eval("window.interpreter.handleWindowsDragLeave();");
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// UserWindowEvent::WindowsDragOver(id, x_pos, y_pos) => {
|
||||||
|
// if let Some(webview) = app.webviews.get(&id) {
|
||||||
|
// webview.dom.in_runtime(|| {
|
||||||
|
// ScopeId::ROOT.in_runtime(|| {
|
||||||
|
// let e = eval(
|
||||||
|
// r#"
|
||||||
|
// const xPos = await dioxus.recv();
|
||||||
|
// const yPos = await dioxus.recv();
|
||||||
|
// window.interpreter.handleWindowsDragOver(xPos, yPos)
|
||||||
|
// "#,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// _ = e.send(x_pos);
|
||||||
|
// _ = e.send(y_pos);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// UserWindowEvent::Ipc { id, msg } => match msg.method() {
|
||||||
|
// IpcMethod::Initialize => app.handle_initialize_msg(id),
|
||||||
|
// IpcMethod::FileDialog => app.handle_file_dialog_msg(msg, id),
|
||||||
|
// IpcMethod::UserEvent => {}
|
||||||
|
// IpcMethod::Query => app.handle_query_msg(msg, id),
|
||||||
|
// IpcMethod::BrowserOpen => app.handle_browser_open(msg),
|
||||||
|
// IpcMethod::Other(_) => {}
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// *control_flow = app.control_flow;
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Expose the `Java_dev_dioxus_main_WryActivity_create` function to the JNI layer.
|
||||||
|
// /// We hardcode these to have a single trampoline for host Java code to call into.
|
||||||
|
// ///
|
||||||
|
// /// This saves us from having to plumb the top-level package name all the way down into
|
||||||
|
// /// this file. This is better for modularity (ie just call dioxus' main to run the app) as
|
||||||
|
// /// well as cache thrashing since this crate doesn't rely on external env vars.
|
||||||
|
// ///
|
||||||
|
// /// The CLI is expecting to find `dev.dioxus.main` in the final library. If you find a need to
|
||||||
|
// /// change this, you'll need to change the CLI as well.
|
||||||
|
// #[cfg(target_os = "android")]
|
||||||
|
// #[no_mangle]
|
||||||
|
// #[inline(never)]
|
||||||
|
// pub extern "C" fn start_app() {
|
||||||
|
// wry::android_binding!(dev_dioxus, main, wry);
|
||||||
|
// tao::android_binding!(
|
||||||
|
// dev_dioxus,
|
||||||
|
// main,
|
||||||
|
// WryActivity,
|
||||||
|
// wry::android_setup,
|
||||||
|
// dioxus_main_root_fn,
|
||||||
|
// tao
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[cfg(target_os = "android")]
|
||||||
|
// #[doc(hidden)]
|
||||||
|
// pub fn dioxus_main_root_fn() {
|
||||||
|
// // we're going to find the `main` symbol using dlsym directly and call it
|
||||||
|
// unsafe {
|
||||||
|
// let mut main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"main\0".as_ptr() as _);
|
||||||
|
|
||||||
|
// if main_fn_ptr.is_null() {
|
||||||
|
// main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"_main\0".as_ptr() as _);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if main_fn_ptr.is_null() {
|
||||||
|
// panic!("Failed to find main symbol");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Set the env vars that rust code might expect, passed off to us by the android app
|
||||||
|
// // Doing this before main emulates the behavior of a regular executable
|
||||||
|
// if cfg!(target_os = "android") && cfg!(debug_assertions) {
|
||||||
|
// load_env_file_from_session_cache();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let main_fn: extern "C" fn() = std::mem::transmute(main_fn_ptr);
|
||||||
|
// main_fn();
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Load the env file from the session cache if we're in debug mode and on android
|
||||||
|
// ///
|
||||||
|
// /// This is a slightly hacky way of being able to use std::env::var code in android apps without
|
||||||
|
// /// going through their custom java-based system.
|
||||||
|
// #[cfg(target_os = "android")]
|
||||||
|
// fn load_env_file_from_session_cache() {
|
||||||
|
// let env_file = dioxus_cli_config::android_session_cache_dir().join(".env");
|
||||||
|
// if let Some(env_file) = std::fs::read_to_string(&env_file).ok() {
|
||||||
|
// for line in env_file.lines() {
|
||||||
|
// if let Some((key, value)) = line.trim().split_once('=') {
|
||||||
|
// std::env::set_var(key, value);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
@@ -8,52 +233,6 @@ use dioxus_document::eval;
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use tao::event::{Event, StartCause, WindowEvent};
|
use tao::event::{Event, StartCause, WindowEvent};
|
||||||
|
|
||||||
/// Launches the WebView and runs the event loop
|
|
||||||
pub fn launch(root: fn() -> Element) {
|
|
||||||
launch_cfg(root, vec![], vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Launches the WebView and runs the event loop, with configuration and root props.
|
|
||||||
pub fn launch_cfg(
|
|
||||||
root: fn() -> Element,
|
|
||||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
|
||||||
platform_config: Vec<Box<dyn Any>>,
|
|
||||||
) {
|
|
||||||
let mut virtual_dom = VirtualDom::new(root);
|
|
||||||
|
|
||||||
for context in contexts {
|
|
||||||
virtual_dom.insert_any_root_context(context());
|
|
||||||
}
|
|
||||||
|
|
||||||
let platform_config = *platform_config
|
|
||||||
.into_iter()
|
|
||||||
.find_map(|cfg| cfg.downcast::<Config>().ok())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
launch_virtual_dom(virtual_dom, platform_config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Launches the WebView and runs the event loop, with configuration and root props.
|
|
||||||
pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
|
|
||||||
#[cfg(feature = "tokio_runtime")]
|
|
||||||
{
|
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block_on(tokio::task::unconstrained(async move {
|
|
||||||
launch_virtual_dom_blocking(virtual_dom, desktop_config)
|
|
||||||
}));
|
|
||||||
|
|
||||||
unreachable!("The desktop launch function will never exit")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "tokio_runtime"))]
|
|
||||||
{
|
|
||||||
launch_virtual_dom_blocking(virtual_dom, desktop_config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Launch the WebView and run the event loop, with configuration and root props.
|
/// Launch the WebView and run the event loop, with configuration and root props.
|
||||||
///
|
///
|
||||||
/// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
|
/// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
|
||||||
@@ -157,68 +336,43 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expose the `Java_dev_dioxus_main_WryActivity_create` function to the JNI layer.
|
/// Launches the WebView and runs the event loop, with configuration and root props.
|
||||||
/// We hardcode these to have a single trampoline for host Java code to call into.
|
pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! {
|
||||||
///
|
#[cfg(feature = "tokio_runtime")]
|
||||||
/// This saves us from having to plumb the top-level package name all the way down into
|
{
|
||||||
/// this file. This is better for modularity (ie just call dioxus' main to run the app) as
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
/// well as cache thrashing since this crate doesn't rely on external env vars.
|
.enable_all()
|
||||||
///
|
.build()
|
||||||
/// The CLI is expecting to find `dev.dioxus.main` in the final library. If you find a need to
|
.unwrap()
|
||||||
/// change this, you'll need to change the CLI as well.
|
.block_on(tokio::task::unconstrained(async move {
|
||||||
#[cfg(target_os = "android")]
|
launch_virtual_dom_blocking(virtual_dom, desktop_config)
|
||||||
#[no_mangle]
|
}));
|
||||||
#[inline(never)]
|
|
||||||
pub extern "C" fn start_app() {
|
|
||||||
wry::android_binding!(dev_dioxus, main, wry);
|
|
||||||
tao::android_binding!(
|
|
||||||
dev_dioxus,
|
|
||||||
main,
|
|
||||||
WryActivity,
|
|
||||||
wry::android_setup,
|
|
||||||
dioxus_main_root_fn,
|
|
||||||
tao
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
unreachable!("The desktop launch function will never exit")
|
||||||
#[doc(hidden)]
|
}
|
||||||
pub fn dioxus_main_root_fn() {
|
|
||||||
// we're going to find the `main` symbol using dlsym directly and call it
|
|
||||||
unsafe {
|
|
||||||
let mut main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"main\0".as_ptr() as _);
|
|
||||||
|
|
||||||
if main_fn_ptr.is_null() {
|
#[cfg(not(feature = "tokio_runtime"))]
|
||||||
main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"_main\0".as_ptr() as _);
|
{
|
||||||
}
|
launch_virtual_dom_blocking(virtual_dom, desktop_config);
|
||||||
|
|
||||||
if main_fn_ptr.is_null() {
|
|
||||||
panic!("Failed to find main symbol");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the env vars that rust code might expect, passed off to us by the android app
|
|
||||||
// Doing this before main emulates the behavior of a regular executable
|
|
||||||
if cfg!(target_os = "android") && cfg!(debug_assertions) {
|
|
||||||
load_env_file_from_session_cache();
|
|
||||||
}
|
|
||||||
|
|
||||||
let main_fn: extern "C" fn() = std::mem::transmute(main_fn_ptr);
|
|
||||||
main_fn();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the env file from the session cache if we're in debug mode and on android
|
|
||||||
///
|
|
||||||
/// This is a slightly hacky way of being able to use std::env::var code in android apps without
|
|
||||||
/// going through their custom java-based system.
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
fn load_env_file_from_session_cache() {
|
|
||||||
let env_file = dioxus_cli_config::android_session_cache_dir().join(".env");
|
|
||||||
if let Some(env_file) = std::fs::read_to_string(&env_file).ok() {
|
|
||||||
for line in env_file.lines() {
|
|
||||||
if let Some((key, value)) = line.trim().split_once('=') {
|
|
||||||
std::env::set_var(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Launches the WebView and runs the event loop, with configuration and root props.
|
||||||
|
pub fn launch(
|
||||||
|
root: fn() -> Element,
|
||||||
|
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
|
platform_config: Vec<Box<dyn Any>>,
|
||||||
|
) -> ! {
|
||||||
|
let mut virtual_dom = VirtualDom::new(root);
|
||||||
|
|
||||||
|
for context in contexts {
|
||||||
|
virtual_dom.insert_any_root_context(context());
|
||||||
|
}
|
||||||
|
|
||||||
|
let platform_config = *platform_config
|
||||||
|
.into_iter()
|
||||||
|
.find_map(|cfg| cfg.downcast::<Config>().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
launch_virtual_dom(virtual_dom, platform_config)
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ pub fn connect_at(endpoint: String, mut callback: impl FnMut(DevserverMsg) + Sen
|
|||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let uri = format!(
|
let uri = format!(
|
||||||
"{endpoint}?aslr_reference={}&build_id={}",
|
"{endpoint}?aslr_reference={}&build_id={}",
|
||||||
subsecond::__aslr_reference(),
|
subsecond::aslr_reference(),
|
||||||
dioxus_cli_config::build_id()
|
dioxus_cli_config::build_id()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -332,12 +332,12 @@ impl LaunchBuilder {
|
|||||||
|
|
||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
if matches!(platform, KnownPlatform::Mobile) {
|
if matches!(platform, KnownPlatform::Mobile) {
|
||||||
return dioxus_desktop::launch::launch_cfg(app, contexts, configs);
|
return dioxus_mobile::launch_bindings::launch(app, contexts, configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
if matches!(platform, KnownPlatform::Desktop) {
|
if matches!(platform, KnownPlatform::Desktop) {
|
||||||
return dioxus_desktop::launch::launch_cfg(app, contexts, configs);
|
return dioxus_desktop::launch::launch(app, contexts, configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
|
|||||||
+160
-1
@@ -5,6 +5,23 @@
|
|||||||
pub use dioxus_desktop::*;
|
pub use dioxus_desktop::*;
|
||||||
use dioxus_lib::prelude::*;
|
use dioxus_lib::prelude::*;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
pub mod launch_bindings {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
pub fn launch(
|
||||||
|
root: fn() -> Element,
|
||||||
|
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
|
platform_config: Vec<Box<dyn Any>>,
|
||||||
|
) {
|
||||||
|
super::launch_cfg(root, contexts, platform_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn launch_virtual_dom(_virtual_dom: VirtualDom, _desktop_config: Config) -> ! {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Launch via the binding API
|
/// Launch via the binding API
|
||||||
pub fn launch(root: fn() -> Element) {
|
pub fn launch(root: fn() -> Element) {
|
||||||
@@ -16,5 +33,147 @@ pub fn launch_cfg(
|
|||||||
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
platform_config: Vec<Box<dyn Any>>,
|
platform_config: Vec<Box<dyn Any>>,
|
||||||
) {
|
) {
|
||||||
dioxus_desktop::launch::launch_cfg(root, contexts, platform_config);
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
*APP_OBJECTS.lock().unwrap() = Some(BoundLaunchObjects {
|
||||||
|
root,
|
||||||
|
contexts,
|
||||||
|
platform_config,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
{
|
||||||
|
dioxus_desktop::launch::launch(root, contexts, platform_config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We need to store the root function and contexts in a static so that when the tao bindings call
|
||||||
|
/// "start_app", that the original function arguments are still around.
|
||||||
|
///
|
||||||
|
/// If you look closely, you'll notice that we impl Send for this struct. This would normally be
|
||||||
|
/// unsound. However, we know that the thread that created these objects ("main()" - see JNI_OnLoad)
|
||||||
|
/// is finished once `start_app` is called. This is similar to how an Rc<T> is technically safe
|
||||||
|
/// to move between threads if you can prove that no other thread is using the Rc<T> at the same time.
|
||||||
|
/// Crates like https://crates.io/crates/sendable exist that build on this idea but with runtimk,
|
||||||
|
/// validation that the current thread is the one that created the object.
|
||||||
|
///
|
||||||
|
/// Since `main()` completes, the only reader of this data will be `start_app`, so it's okay to
|
||||||
|
/// impl this as Send/Sync.
|
||||||
|
///
|
||||||
|
/// Todo(jon): the visibility of functions in this module is too public. Make sure to hide them before
|
||||||
|
/// releasing 0.7.
|
||||||
|
struct BoundLaunchObjects {
|
||||||
|
root: fn() -> Element,
|
||||||
|
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
|
platform_config: Vec<Box<dyn Any>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for BoundLaunchObjects {}
|
||||||
|
unsafe impl Sync for BoundLaunchObjects {}
|
||||||
|
|
||||||
|
static APP_OBJECTS: Mutex<Option<BoundLaunchObjects>> = Mutex::new(None);
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn root() {
|
||||||
|
let app = APP_OBJECTS
|
||||||
|
.lock()
|
||||||
|
.expect("APP_FN_PTR lock failed")
|
||||||
|
.take()
|
||||||
|
.expect("Android to have set the app trampoline");
|
||||||
|
|
||||||
|
let BoundLaunchObjects {
|
||||||
|
root,
|
||||||
|
contexts,
|
||||||
|
platform_config,
|
||||||
|
} = app;
|
||||||
|
|
||||||
|
dioxus_desktop::launch::launch(root, contexts, platform_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expose the `Java_dev_dioxus_main_WryActivity_create` function to the JNI layer.
|
||||||
|
/// We hardcode these to have a single trampoline for host Java code to call into.
|
||||||
|
///
|
||||||
|
/// This saves us from having to plumb the top-level package name all the way down into
|
||||||
|
/// this file. This is better for modularity (ie just call dioxus' main to run the app) as
|
||||||
|
/// well as cache thrashing since this crate doesn't rely on external env vars.
|
||||||
|
///
|
||||||
|
/// The CLI is expecting to find `dev.dioxus.main` in the final library. If you find a need to
|
||||||
|
/// change this, you'll need to change the CLI as well.
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[no_mangle]
|
||||||
|
#[inline(never)]
|
||||||
|
pub extern "C" fn start_app() {
|
||||||
|
tao::android_binding!(dev_dioxus, main, WryActivity, wry::android_setup, root, tao);
|
||||||
|
wry::android_binding!(dev_dioxus, main, wry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call our `main` function to initialize the rust runtime and set the launch binding trampoline
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[no_mangle]
|
||||||
|
#[inline(never)]
|
||||||
|
pub extern "C" fn JNI_OnLoad(
|
||||||
|
_vm: *mut libc::c_void,
|
||||||
|
_reserved: *mut libc::c_void,
|
||||||
|
) -> jni::sys::jint {
|
||||||
|
// we're going to find the `main` symbol using dlsym directly and call it
|
||||||
|
unsafe {
|
||||||
|
let mut main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"main\0".as_ptr() as _);
|
||||||
|
|
||||||
|
if main_fn_ptr.is_null() {
|
||||||
|
main_fn_ptr = libc::dlsym(libc::RTLD_DEFAULT, b"_main\0".as_ptr() as _);
|
||||||
|
}
|
||||||
|
|
||||||
|
if main_fn_ptr.is_null() {
|
||||||
|
panic!("Failed to find main symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the env vars that rust code might expect, passed off to us by the android app
|
||||||
|
// Doing this before main emulates the behavior of a regular executable
|
||||||
|
if cfg!(target_os = "android") && cfg!(debug_assertions) {
|
||||||
|
load_env_file_from_session_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
let main_fn: extern "C" fn() = std::mem::transmute(main_fn_ptr);
|
||||||
|
main_fn();
|
||||||
|
};
|
||||||
|
|
||||||
|
jni::sys::JNI_VERSION_1_6
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the env file from the session cache if we're in debug mode and on android
|
||||||
|
///
|
||||||
|
/// This is a slightly hacky way of being able to use std::env::var code in android apps without
|
||||||
|
/// going through their custom java-based system.
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
fn load_env_file_from_session_cache() {
|
||||||
|
let env_file = dioxus_cli_config::android_session_cache_dir().join(".env");
|
||||||
|
if let Some(env_file) = std::fs::read_to_string(&env_file).ok() {
|
||||||
|
for line in env_file.lines() {
|
||||||
|
if let Some((key, value)) = line.trim().split_once('=') {
|
||||||
|
std::env::set_var(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #![doc = include_str!("../README.md")]
|
||||||
|
// #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
|
||||||
|
// #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
|
||||||
|
|
||||||
|
// pub use dioxus_desktop::*;
|
||||||
|
// use dioxus_lib::prelude::*;
|
||||||
|
// use std::any::Any;
|
||||||
|
|
||||||
|
// /// Launch via the binding API
|
||||||
|
// pub fn launch(root: fn() -> Element) {
|
||||||
|
// launch_cfg(root, vec![], vec![]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn launch_cfg(
|
||||||
|
// root: fn() -> Element,
|
||||||
|
// contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
|
||||||
|
// platform_config: Vec<Box<dyn Any>>,
|
||||||
|
// ) {
|
||||||
|
// dioxus_desktop::launch::launch_cfg(root, contexts, platform_config);
|
||||||
|
// }
|
||||||
@@ -354,19 +354,18 @@ impl ServeConfigBuilder {
|
|||||||
let index_path = self
|
let index_path = self
|
||||||
.index_path
|
.index_path
|
||||||
.unwrap_or_else(|| public_path.join("index.html"));
|
.unwrap_or_else(|| public_path.join("index.html"));
|
||||||
load_index_path(index_path)?
|
load_index_path(index_path).unwrap_or_else(|_| {
|
||||||
// load_index_path(index_path).unwrap_or_else(|_| {
|
r#"
|
||||||
// r#"
|
<!DOCTYPE html>
|
||||||
// <!DOCTYPE html>
|
<html>
|
||||||
// <html>
|
<head> </head>
|
||||||
// <head> </head>
|
<body>
|
||||||
// <body>
|
<div id="main"></div>
|
||||||
// <div id="main"></div>
|
</body>
|
||||||
// </body>
|
</html>
|
||||||
// </html>
|
"#
|
||||||
// "#
|
.to_string()
|
||||||
// .to_string()
|
})
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "cross-tls-crate-dylib"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
# when testing, use a dylib
|
||||||
|
# crate-type = ["dylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
pub use std::cell::Cell;
|
||||||
|
use std::{cell::RefCell, thread::LocalKey};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StoredItem {
|
||||||
|
pub name: String,
|
||||||
|
pub value: f32,
|
||||||
|
pub items: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static BAZ: RefCell<Option<StoredItem>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_baz() -> &'static LocalKey<RefCell<Option<StoredItem>>> {
|
||||||
|
if BAZ.with(|f| f.borrow().is_none()) {
|
||||||
|
BAZ.set(Some(StoredItem {
|
||||||
|
name: "BAR".to_string(),
|
||||||
|
value: 0.0,
|
||||||
|
items: vec!["item1".to_string(), "item2".to_string()],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
&BAZ
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "cross-tls-crate"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
pub use std::cell::Cell;
|
||||||
|
use std::{cell::RefCell, thread::LocalKey};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StoredItem {
|
||||||
|
pub name: String,
|
||||||
|
pub value: f32,
|
||||||
|
pub items: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static BAR: RefCell<Option<StoredItem>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bar() -> &'static LocalKey<RefCell<Option<StoredItem>>> {
|
||||||
|
if BAR.with(|f| f.borrow().is_none()) {
|
||||||
|
BAR.set(Some(StoredItem {
|
||||||
|
name: "BAR".to_string(),
|
||||||
|
value: 0.0,
|
||||||
|
items: vec!["item1".to_string(), "item2".to_string()],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
&BAR
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "subsecond-tls-harness"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dioxus-devtools = { workspace = true }
|
||||||
|
cross-tls-crate = { path = "../cross-tls-crate" }
|
||||||
|
cross-tls-crate-dylib = { path = "../cross-tls-crate-dylib" }
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
harness for testing cross-crate TLS imports.
|
||||||
|
|
||||||
|
TLS is the hardest to get right with binary patchinig since we need to rewrite initializers.
|
||||||
|
|
||||||
|
this crate relies on two crates - one as a dylib and the other as an rlib
|
||||||
|
|
||||||
|
the dylib should make it into our bundle and the rlib should just be linked.
|
||||||
|
|
||||||
|
both shouldn't cause access errors or segfaults during hotpatch.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run --package dioxus-cli -- serve --hotpatch
|
||||||
|
```
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use std::{cell::Cell, thread, time::Duration};
|
||||||
|
|
||||||
|
use cross_tls_crate::get_bar;
|
||||||
|
use cross_tls_crate_dylib::get_baz;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dioxus_devtools::connect_subsecond();
|
||||||
|
loop {
|
||||||
|
dioxus_devtools::subsecond::call(|| {
|
||||||
|
use cross_tls_crate::BAR;
|
||||||
|
use cross_tls_crate_dylib::BAZ;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static FOO: Cell<f32> = const { Cell::new(2.0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Hello 123s123123s: {}", FOO.get());
|
||||||
|
get_bar().with(|f| println!("Bar: {:?}", f.borrow()));
|
||||||
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
|
FOO.set(2.0);
|
||||||
|
get_bar().with(|f| f.borrow_mut().as_mut().unwrap().value = 3.0);
|
||||||
|
get_baz().with(|f| f.borrow_mut().as_mut().unwrap().value = 4.0);
|
||||||
|
|
||||||
|
BAR.with_borrow(|f| {
|
||||||
|
println!("Bar: {:?}", f);
|
||||||
|
});
|
||||||
|
BAZ.with_borrow(|f| {
|
||||||
|
println!("Baz: {:?}", f);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -495,10 +495,9 @@ pub unsafe fn apply_patch(mut table: JumpTable) -> Result<(), PatchError> {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Use the `__aslr_reference` symbol as a sentinel for the current executable. This is basically a
|
// Use the `main` symbol as a sentinel for the current executable. This is basically a
|
||||||
// cross-platform version of `__mh_execute_header` on macOS that sets a reference point for the
|
// cross-platform version of `__mh_execute_header` on macOS that we can use to base the executable.
|
||||||
// jump table.
|
let old_offset = aslr_reference() - table.aslr_reference as usize;
|
||||||
let old_offset = __aslr_reference() - table.aslr_reference as usize;
|
|
||||||
|
|
||||||
// Use the `main` symbol as a sentinel for the loaded library. Might want to move away
|
// Use the `main` symbol as a sentinel for the loaded library. Might want to move away
|
||||||
// from this at some point, or make it configurable
|
// from this at some point, or make it configurable
|
||||||
@@ -672,22 +671,49 @@ pub enum PatchError {
|
|||||||
AndroidMemfd(String),
|
AndroidMemfd(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function returns its own address, providing a stable reference point for hot-patch engine
|
/// This function returns the address of the main function in the current executable. This is used as
|
||||||
/// to hook onto. If you were to write an object file for this function, it would amount to:
|
/// an anchor to reference the current executable's base address.
|
||||||
///
|
|
||||||
/// ```asm
|
|
||||||
/// __aslr_reference:
|
|
||||||
/// mov rax, qword ptr [rip + __aslr_reference@GOTPCREL] // notice the @GOTPCREL relocation
|
|
||||||
/// ret
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// The point here being that we have a stable address both at runtime and compile time, making it
|
/// The point here being that we have a stable address both at runtime and compile time, making it
|
||||||
/// possible to calculate the ASLR offset from within the process to correct the jump table.
|
/// possible to calculate the ASLR offset from within the process to correct the jump table.
|
||||||
|
///
|
||||||
|
/// It should only be called from the main executable *first* and not from a shared library since it
|
||||||
|
/// self-initializes.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline(never)]
|
pub fn aslr_reference() -> usize {
|
||||||
#[no_mangle]
|
#[cfg(target_family = "wasm")]
|
||||||
pub extern "C" fn __aslr_reference() -> usize {
|
return 0;
|
||||||
__aslr_reference as *const () as usize
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
unsafe {
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
// The first call to this function should occur in the
|
||||||
|
static mut MAIN_PTR: *mut c_void = std::ptr::null_mut();
|
||||||
|
|
||||||
|
if MAIN_PTR.is_null() {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
MAIN_PTR = libc::dlsym(libc::RTLD_DEFAULT, c"main".as_ptr() as _);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
extern "system" {
|
||||||
|
fn GetModuleHandleA(lpModuleName: *const i8) -> *mut std::ffi::c_void;
|
||||||
|
fn GetProcAddress(
|
||||||
|
hModule: *mut std::ffi::c_void,
|
||||||
|
lpProcName: *const i8,
|
||||||
|
) -> *mut std::ffi::c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
MAIN_PTR =
|
||||||
|
GetProcAddress(GetModuleHandleA(std::ptr::null()), c"main".as_ptr() as _) as _;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MAIN_PTR as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// On Android, we can't dlopen libraries that aren't placed inside /data/data/<package_name>/lib/
|
/// On Android, we can't dlopen libraries that aren't placed inside /data/data/<package_name>/lib/
|
||||||
|
|||||||
Reference in new issue
Block a user