|
@@ -316,8 +316,9 @@
|
|
|
//! - xbuild: <https://github.com/rust-mobile/xbuild/blob/master/xbuild/src/command/build.rs>
|
|
|
|
|
|
use crate::{
|
|
|
- AndroidTools, BuildContext, DioxusConfig, Error, LinkAction, Platform, Result, RustcArgs,
|
|
|
- TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace, DX_RUSTC_WRAPPER_ENV_VAR,
|
|
|
+ AndroidTools, BuildContext, DioxusConfig, Error, LinkAction, LinkerFlavor, Platform, Result,
|
|
|
+ RustcArgs, TargetArgs, TraceSrc, WasmBindgen, WasmOptConfig, Workspace,
|
|
|
+ DX_RUSTC_WRAPPER_ENV_VAR,
|
|
|
};
|
|
|
use anyhow::Context;
|
|
|
use cargo_metadata::diagnostic::Diagnostic;
|
|
@@ -377,7 +378,7 @@ pub(crate) struct BuildRequest {
|
|
|
pub(crate) extra_cargo_args: Vec<String>,
|
|
|
pub(crate) extra_rustc_args: Vec<String>,
|
|
|
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) wasm_split: 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))
|
|
|
} else {
|
|
|
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!)
|
|
|
let link_args_file = Arc::new(
|
|
|
NamedTempFile::with_suffix(".txt")
|
|
@@ -677,11 +692,16 @@ impl BuildRequest {
|
|
|
• link_args_file: {},
|
|
|
• link_err_file: {},
|
|
|
• rustc_wrapper_args_file: {},
|
|
|
- • session_cache_dir: {}"#,
|
|
|
+ • session_cache_dir: {}
|
|
|
+ • linker: {:?}
|
|
|
+ • target_dir: {:?}
|
|
|
+ "#,
|
|
|
link_args_file.path().display(),
|
|
|
link_err_file.path().display(),
|
|
|
rustc_wrapper_args_file.path().display(),
|
|
|
session_cache_dir.path().display(),
|
|
|
+ custom_linker,
|
|
|
+ target_dir,
|
|
|
);
|
|
|
|
|
|
Ok(Self {
|
|
@@ -696,7 +716,7 @@ impl BuildRequest {
|
|
|
workspace,
|
|
|
config,
|
|
|
enabled_platforms,
|
|
|
- custom_target_dir: None,
|
|
|
+ target_dir,
|
|
|
custom_linker,
|
|
|
link_args_file,
|
|
|
link_err_file,
|
|
@@ -739,6 +759,9 @@ impl BuildRequest {
|
|
|
self.write_executable(ctx, &artifacts.exe, &mut artifacts.assets)
|
|
|
.await
|
|
|
.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)
|
|
|
.await
|
|
|
.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.
|
|
|
let mut direct_rustc = RustcArgs::default();
|
|
|
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 let Ok(linker_warnings) = std::fs::read_to_string(self.link_err_file.path()) {
|
|
|
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
|
|
|
if matches!(ctx.mode, BuildMode::Fat) {
|
|
|
+ let link_start = SystemTime::now();
|
|
|
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)?;
|
|
@@ -901,7 +943,11 @@ impl BuildRequest {
|
|
|
let mode = ctx.mode.clone();
|
|
|
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 {
|
|
|
time_end,
|
|
@@ -991,12 +1037,6 @@ impl BuildRequest {
|
|
|
| Platform::Ios
|
|
|
| Platform::Liveview
|
|
|
| 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::copy(exe, self.main_exe())?;
|
|
|
}
|
|
@@ -1005,6 +1045,52 @@ impl BuildRequest {
|
|
|
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
|
|
|
///
|
|
|
/// 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
|
|
|
// -Wl,-all_load
|
|
|
// ```
|
|
|
+ let mut dylibs = vec![];
|
|
|
let mut object_files = args
|
|
|
.iter()
|
|
|
.filter(|arg| arg.ends_with(".rcgu.o"))
|
|
@@ -1233,6 +1320,15 @@ impl BuildRequest {
|
|
|
let patch_file = self.main_exe().with_file_name("stub.o");
|
|
|
std::fs::write(&patch_file, stub_bytes)?;
|
|
|
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
|
|
@@ -1251,6 +1347,7 @@ impl BuildRequest {
|
|
|
// does it since it uses llvm-objcopy into the `target/debug/` folder.
|
|
|
let res = Command::new(linker)
|
|
|
.args(object_files.iter())
|
|
|
+ .args(dylibs.iter())
|
|
|
.args(self.thin_link_args(&args)?)
|
|
|
.args(out_arg)
|
|
|
.env_clear()
|
|
@@ -1298,12 +1395,9 @@ impl BuildRequest {
|
|
|
/// This is basically just stripping away the rlibs and other libraries that will be satisfied
|
|
|
/// by our stub step.
|
|
|
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![];
|
|
|
|
|
|
- match triple.operating_system {
|
|
|
+ match self.linker_flavor() {
|
|
|
// wasm32-unknown-unknown -> use wasm-ld (gnu-lld)
|
|
|
//
|
|
|
// 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
|
|
|
// fully relocatable modules with no host coordination in lieu of sending out
|
|
|
// the aslr slide at runtime.
|
|
|
- OperatingSystem::Unknown if self.platform == Platform::Web => {
|
|
|
+ LinkerFlavor::WasmLld => {
|
|
|
out_args.extend([
|
|
|
"--fatal-warnings".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
|
|
|
// 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()]);
|
|
|
|
|
|
// Preserve the original args. We only preserve:
|
|
@@ -1367,7 +1461,7 @@ impl BuildRequest {
|
|
|
// android/linux need to be compatible with lld
|
|
|
//
|
|
|
// android currently drags along its own libraries and other zany flags
|
|
|
- OperatingSystem::Linux => {
|
|
|
+ LinkerFlavor::Gnu => {
|
|
|
out_args.extend([
|
|
|
"-shared".to_string(),
|
|
|
"-Wl,--eh-frame-hdr".to_string(),
|
|
@@ -1391,13 +1485,14 @@ impl BuildRequest {
|
|
|
if arg.starts_with("-l")
|
|
|
|| arg.starts_with("-m")
|
|
|
|| arg.starts_with("-Wl,--target=")
|
|
|
+ || arg.starts_with("-Wl,-fuse-ld")
|
|
|
{
|
|
|
out_args.push(arg.to_string());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- OperatingSystem::Windows => {
|
|
|
+ LinkerFlavor::Msvc => {
|
|
|
out_args.extend([
|
|
|
"shlwapi.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> {
|
|
@@ -1460,15 +1557,12 @@ impl BuildRequest {
|
|
|
.unwrap_or(0),
|
|
|
));
|
|
|
|
|
|
- let extension = match self.triple.operating_system {
|
|
|
- OperatingSystem::Darwin(_) => "dylib",
|
|
|
- OperatingSystem::MacOSX(_) => "dylib",
|
|
|
- OperatingSystem::IOS(_) => "dylib",
|
|
|
- OperatingSystem::Windows => "dll",
|
|
|
- OperatingSystem::Linux => "so",
|
|
|
- OperatingSystem::Wasi => "wasm",
|
|
|
- OperatingSystem::Unknown if self.platform == Platform::Web => "wasm",
|
|
|
- _ => "",
|
|
|
+ let extension = match self.linker_flavor() {
|
|
|
+ LinkerFlavor::Darwin => "dylib",
|
|
|
+ LinkerFlavor::Gnu => "so",
|
|
|
+ LinkerFlavor::WasmLld => "wasm",
|
|
|
+ LinkerFlavor::Msvc => "dll",
|
|
|
+ LinkerFlavor::Unsupported => "",
|
|
|
};
|
|
|
|
|
|
path.with_extension(extension)
|
|
@@ -1512,38 +1606,65 @@ impl BuildRequest {
|
|
|
) -> Result<()> {
|
|
|
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
|
|
|
- let rlibs = args
|
|
|
+ let rlibs = rustc_args
|
|
|
+ .link_args
|
|
|
.iter()
|
|
|
.filter(|arg| arg.ends_with(".rlib"))
|
|
|
.map(PathBuf::from)
|
|
|
.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(
|
|
|
&Uuid::NAMESPACE_OID,
|
|
|
rlibs
|
|
|
.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>()
|
|
|
.as_bytes(),
|
|
|
- );
|
|
|
+ )
|
|
|
+ .to_string()
|
|
|
+ .chars()
|
|
|
+ .take(8)
|
|
|
+ .collect::<String>();
|
|
|
|
|
|
// 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
|
|
|
// 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
|
|
|
// 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 out_ar = ar::Builder::new(&mut bytes);
|
|
|
for rlib in &rlibs {
|
|
@@ -1573,7 +1694,11 @@ impl BuildRequest {
|
|
|
}
|
|
|
|
|
|
// 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());
|
|
|
continue;
|
|
|
}
|
|
@@ -1582,6 +1707,7 @@ impl BuildRequest {
|
|
|
tracing::debug!("Unknown object file in rlib: {:?}", name);
|
|
|
}
|
|
|
|
|
|
+ archive_has_contents = true;
|
|
|
out_ar
|
|
|
.append(&object_file.header().clone(), object_file)
|
|
|
.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,
|
|
|
// but is necessary for some linkers to work properly.
|
|
|
// We ignore its error in case it doesn't recognize the architecture
|
|
|
- if let Some(ranlib) = self.select_ranlib() {
|
|
|
- _ = Command::new(ranlib).arg(&out_ar_path).output().await;
|
|
|
+ if self.linker_flavor() == LinkerFlavor::Darwin {
|
|
|
+ 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
|
|
|
//
|
|
|
// 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<_>>();
|
|
|
- if let Some(first_rlib) = args.iter().position(|arg| arg.ends_with(".rlib")) {
|
|
|
- match self.triple.operating_system {
|
|
|
- OperatingSystem::Unknown if self.platform == Platform::Web => {
|
|
|
- // We need to use the --whole-archive flag for wasm
|
|
|
- args[first_rlib] = "--whole-archive".to_string();
|
|
|
- args.insert(first_rlib + 1, out_ar_path.display().to_string());
|
|
|
- args.insert(first_rlib + 2, "--no-whole-archive".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());
|
|
|
+ let mut args = rustc_args.link_args.clone();
|
|
|
+ if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) {
|
|
|
+ if archive_has_contents {
|
|
|
+ match self.linker_flavor() {
|
|
|
+ LinkerFlavor::WasmLld => {
|
|
|
+ args.insert(last_object, "--whole-archive".to_string());
|
|
|
+ args.insert(last_object + 1, out_ar_path.display().to_string());
|
|
|
+ args.insert(last_object + 2, "--no-whole-archive".to_string());
|
|
|
+ args.retain(|arg| !arg.ends_with(".rlib"));
|
|
|
+ for rlib in compiler_rlibs.iter().rev() {
|
|
|
+ args.insert(last_object + 3, rlib.display().to_string());
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // Subtle difference - on linux and android we go through clang and thus pass `-Wl,` prefix
|
|
|
- OperatingSystem::Linux => {
|
|
|
- args[first_rlib] = "-Wl,--whole-archive".to_string();
|
|
|
- args.insert(first_rlib + 1, out_ar_path.display().to_string());
|
|
|
- args.insert(first_rlib + 2, "-Wl,--no-whole-archive".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::Gnu => {
|
|
|
+ args.insert(last_object, "-Wl,--whole-archive".to_string());
|
|
|
+ args.insert(last_object + 1, out_ar_path.display().to_string());
|
|
|
+ args.insert(last_object + 2, "-Wl,--no-whole-archive".to_string());
|
|
|
+ args.retain(|arg| !arg.ends_with(".rlib"));
|
|
|
+ for rlib in compiler_rlibs.iter().rev() {
|
|
|
+ args.insert(last_object + 3, rlib.display().to_string());
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- OperatingSystem::Darwin(_) | OperatingSystem::IOS(_) => {
|
|
|
- args[first_rlib] = "-Wl,-force_load".to_string();
|
|
|
- args.insert(first_rlib + 1, out_ar_path.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 + 2, rlib.display().to_string());
|
|
|
+ LinkerFlavor::Darwin => {
|
|
|
+ args.insert(last_object, "-Wl,-force_load".to_string());
|
|
|
+ args.insert(last_object + 1, out_ar_path.display().to_string());
|
|
|
+ args.retain(|arg| !arg.ends_with(".rlib"));
|
|
|
+ for rlib in compiler_rlibs.iter().rev() {
|
|
|
+ args.insert(last_object + 2, rlib.display().to_string());
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- args.insert(first_rlib + 3, "-Wl,-all_load".to_string());
|
|
|
- }
|
|
|
-
|
|
|
- OperatingSystem::Windows => {
|
|
|
- args[first_rlib] = format!("/WHOLEARCHIVE:{}", out_ar_path.display());
|
|
|
- args.retain(|arg| !arg.ends_with(".rlib"));
|
|
|
-
|
|
|
- // add back the compiler rlibs
|
|
|
- for rlib in compiler_rlibs.iter().rev() {
|
|
|
- args.insert(first_rlib + 1, rlib.display().to_string());
|
|
|
+ LinkerFlavor::Msvc => {
|
|
|
+ args.insert(
|
|
|
+ last_object,
|
|
|
+ format!("/WHOLEARCHIVE:{}", out_ar_path.display()),
|
|
|
+ );
|
|
|
+ args.retain(|arg| !arg.ends_with(".rlib"));
|
|
|
+ for rlib in compiler_rlibs.iter().rev() {
|
|
|
+ args.insert(last_object + 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
|
|
@@ -1690,6 +1827,7 @@ impl BuildRequest {
|
|
|
let linker = self.select_linker()?;
|
|
|
|
|
|
tracing::trace!("Fat linking with args: {:?} {:#?}", linker, args);
|
|
|
+ tracing::trace!("Fat linking with env: {:#?}", rustc_args.envs);
|
|
|
|
|
|
// Run the linker directly!
|
|
|
let out_arg = match self.triple.operating_system {
|
|
@@ -1724,9 +1862,64 @@ impl BuildRequest {
|
|
|
_ = 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(())
|
|
|
}
|
|
|
|
|
|
+ /// 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.
|
|
|
///
|
|
|
/// 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));
|
|
|
}
|
|
|
|
|
|
- let cc = match self.triple.operating_system {
|
|
|
- OperatingSystem::Unknown if self.platform == Platform::Web => self.workspace.wasm_ld(),
|
|
|
+ if let Some(linker) = self.custom_linker.clone() {
|
|
|
+ return Ok(linker);
|
|
|
+ }
|
|
|
|
|
|
- // The android clang linker is *special* and has some android-specific flags that we need
|
|
|
- //
|
|
|
- // Note that this is *clang*, not `lld`.
|
|
|
- OperatingSystem::Linux if self.platform == Platform::Android => {
|
|
|
- self.workspace.android_tools()?.android_cc(&self.triple)
|
|
|
- }
|
|
|
+ let cc = match self.linker_flavor() {
|
|
|
+ LinkerFlavor::WasmLld => self.workspace.wasm_ld(),
|
|
|
|
|
|
// 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.
|
|
|
//
|
|
|
// 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
|
|
|
// 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,
|
|
|
// 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
|
|
|
// the form of `-Wl,<args>` for them to make it to the linker. This matches how rust does it
|
|
|
// which is confusing.
|
|
|
- _ => self.workspace.cc(),
|
|
|
+ LinkerFlavor::Unsupported => self.workspace.cc(),
|
|
|
};
|
|
|
|
|
|
Ok(cc)
|
|
@@ -1827,7 +2020,7 @@ impl BuildRequest {
|
|
|
|
|
|
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)
|
|
|
}
|
|
@@ -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.
|
|
|
//
|
|
|
// 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.
|
|
|
// todo: should we even be doing this? might be better being a build.rs or something else.
|
|
|
if self.release {
|
|
@@ -2510,7 +2714,7 @@ impl BuildRequest {
|
|
|
/// is "distributed" after building an application (configurable in the
|
|
|
/// `Dioxus.toml`).
|
|
|
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();
|
|
|
dir
|
|
|
}
|