Skip to content

qt-build-utils: split platform linker setup into another module #1289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,7 @@ extern "C" bool {init_fun}() {{
let qt_modules = self.qt_modules(&dependencies);

// Ensure that the linker is setup correctly for Cargo builds
qt_build_utils::setup_linker();
qt_build_utils::QtPlatformLinker::init();

let header_root = dir::header_root();

Expand Down
68 changes: 3 additions & 65 deletions crates/qt-build-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub use installation::qmake::QtInstallationQMake;
#[cfg(feature = "qmake")]
mod parse_cflags;

mod platform;
pub use platform::QtPlatformLinker;

mod tool;
pub use tool::{
MocArguments, MocProducts, QmlCacheArguments, QmlCacheProducts, QtTool, QtToolMoc,
Expand All @@ -43,75 +46,10 @@ use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
process::Command,
};

use semver::Version;

fn command_help_output(command: &str) -> std::io::Result<std::process::Output> {
Command::new(command).args(["--help"]).output()
}

/// Linking executables (including tests) with Cargo that link to Qt fails to link with GNU ld.bfd,
/// which is the default on most Linux distributions, so use GNU ld.gold, lld, or mold instead.
/// If you are using a C++ build system such as CMake to do the final link of the executable, you do
/// not need to call this function.
///
/// With Apple devices we set -fapple-link-rtlib as we build with -nodefaultlibs
/// otherwise we cannot user helpers from the compiler runtime in Qt
///
/// This does nothing on non-Unix platforms.
pub fn setup_linker() {
if env::var("CARGO_CFG_UNIX").is_err() {
return;
}

if let Ok(vendor) = env::var("CARGO_CFG_TARGET_VENDOR") {
if vendor == "apple" {
// Tell clang link to clang_rt as we build with -nodefaultlibs
// otherwise we cannot use helpers from the compiler runtime in Qt
println!("cargo::rustc-link-arg=-fapple-link-rtlib");
}
}

let flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();
// Don't override custom flags
if !flags.contains("-fuse-ld") {
// ld is the system default linker. On Linux, this is usually GNU ld.bfd, but it may be symlinked to another
// linker. On macOS, Xcode ships lld with the executable named ld.
let ld_help = String::from_utf8(
command_help_output("ld")
.expect("Could not run ld command")
.stdout,
)
.unwrap();
// bfd supports some exotic targets that other linkers do not.
let ld_is_bfd = ld_help.contains("symbolsrec")
|| ld_help.contains("verilog")
|| ld_help.contains("tekhex");

// Whatever linker is being used that's not bfd will likely work.
if !ld_is_bfd {
return;
}

// mold is fastest, but specifing mold with -fuse-ld requires GCC >= 12 or Clang.
// Unfortunately cargo does not provide a means to set the linker driver via build scripts,
// so linking would fail trying to use -fuse-ld=mold with GCC < 12 even if clang is installed.
// So, prefer lld and gold to mold for robustness on the widest range of systems.
// mold can still be used by manually specifying it in ~/.cargo/config.toml or the RUSTFLAGS environment variable.
if command_help_output("lld").is_ok() {
println!("cargo::rustc-link-arg=-fuse-ld=lld");
} else if command_help_output("ld.gold").is_ok() {
println!("cargo::rustc-link-arg=-fuse-ld=gold");
} else if command_help_output("mold").is_ok() {
println!("cargo::rustc-link-arg=-fuse-ld=mold");
} else {
println!("cargo::warning=Neither mold, lld, nor gold linkers were found. Linking with GNU ld.bfd will likely fail.");
}
}
}

/// Paths to C++ files generated by [QtBuild::register_qml_module]
pub struct QmlModuleRegistrationFiles {
/// File generated by [rcc](https://doc.qt.io/qt-6/rcc.html) for the QML plugin. The compiled static library
Expand Down
75 changes: 75 additions & 0 deletions crates/qt-build-utils/src/platform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::{env, process::Command};

fn command_help_output(command: &str) -> std::io::Result<std::process::Output> {
Command::new(command).args(["--help"]).output()
}

/// Linking executables (including tests) with Cargo that link to Qt fails to link with GNU ld.bfd,
/// which is the default on most Linux distributions, so use GNU ld.gold, lld, or mold instead.
/// If you are using a C++ build system such as CMake to do the final link of the executable, you do
/// not need to call this function.
///
/// With Apple devices we set -fapple-link-rtlib as we build with -nodefaultlibs
/// otherwise we cannot user helpers from the compiler runtime in Qt
///
/// This does nothing on non-Unix platforms.
pub struct QtPlatformLinker;

impl QtPlatformLinker {
/// Initialize support for linking executables with Cargo that link to Qt
pub fn init() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't know if we want to refactor this code at all? Or if there is anything else we can setup for any platforms beyond just the linker. Most of the Windows craziness was on the CMake side?

We do have the cc build flags we setup, they are currently done in cxx-qt-build don't know if we have a helper in qt-build-utils or not. As could be like QtPlatform::cc_builder() -> cc::Build with the flags preconfigured

if env::var("CARGO_CFG_UNIX").is_err() {
return;
}

if let Ok(vendor) = env::var("CARGO_CFG_TARGET_VENDOR") {
if vendor == "apple" {
// Tell clang link to clang_rt as we build with -nodefaultlibs
// otherwise we cannot use helpers from the compiler runtime in Qt
println!("cargo::rustc-link-arg=-fapple-link-rtlib");
}
}

let flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();
// Don't override custom flags
if !flags.contains("-fuse-ld") {
// ld is the system default linker. On Linux, this is usually GNU ld.bfd, but it may be symlinked to another
// linker. On macOS, Xcode ships lld with the executable named ld.
let ld_help = String::from_utf8(
command_help_output("ld")
.expect("Could not run ld command")
.stdout,
)
.unwrap();
// bfd supports some exotic targets that other linkers do not.
let ld_is_bfd = ld_help.contains("symbolsrec")
|| ld_help.contains("verilog")
|| ld_help.contains("tekhex");

// Whatever linker is being used that's not bfd will likely work.
if !ld_is_bfd {
return;
}

// mold is fastest, but specifing mold with -fuse-ld requires GCC >= 12 or Clang.
// Unfortunately cargo does not provide a means to set the linker driver via build scripts,
// so linking would fail trying to use -fuse-ld=mold with GCC < 12 even if clang is installed.
// So, prefer lld and gold to mold for robustness on the widest range of systems.
// mold can still be used by manually specifying it in ~/.cargo/config.toml or the RUSTFLAGS environment variable.
if command_help_output("lld").is_ok() {
println!("cargo::rustc-link-arg=-fuse-ld=lld");
} else if command_help_output("ld.gold").is_ok() {
println!("cargo::rustc-link-arg=-fuse-ld=gold");
} else if command_help_output("mold").is_ok() {
println!("cargo::rustc-link-arg=-fuse-ld=mold");
} else {
println!("cargo::warning=Neither mold, lld, nor gold linkers were found. Linking with GNU ld.bfd will likely fail.");
}
}
}
}
Loading