qt_build_utils/platform.rs
1// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6use std::{env, process::Command};
7
8fn command_help_output(command: &str) -> std::io::Result<std::process::Output> {
9 Command::new(command).args(["--help"]).output()
10}
11
12/// Linking executables (including tests) with Cargo that link to Qt fails to link with GNU ld.bfd,
13/// which is the default on most Linux distributions, so use GNU ld.gold, lld, or mold instead.
14/// If you are using a C++ build system such as CMake to do the final link of the executable, you do
15/// not need to call this function.
16///
17/// With Apple devices we set -fapple-link-rtlib as we build with -nodefaultlibs
18/// otherwise we cannot user helpers from the compiler runtime in Qt
19///
20/// This does nothing on non-Unix platforms.
21pub struct QtPlatformLinker;
22
23impl QtPlatformLinker {
24 /// Initialize support for linking executables with Cargo that link to Qt
25 pub fn init() {
26 if env::var("CARGO_CFG_UNIX").is_err() {
27 return;
28 }
29
30 if let Ok(vendor) = env::var("CARGO_CFG_TARGET_VENDOR") {
31 if vendor == "apple" {
32 // Tell clang link to clang_rt as we build with -nodefaultlibs
33 // otherwise we cannot use helpers from the compiler runtime in Qt
34 println!("cargo::rustc-link-arg=-fapple-link-rtlib");
35 }
36 }
37
38 let flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();
39 // Don't override custom flags
40 if !flags.contains("-fuse-ld") {
41 // ld is the system default linker. On Linux, this is usually GNU ld.bfd, but it may be symlinked to another
42 // linker. On macOS, Xcode ships lld with the executable named ld.
43 let ld_help = String::from_utf8(
44 command_help_output("ld")
45 .expect("Could not run ld command")
46 .stdout,
47 )
48 .unwrap();
49 // bfd supports some exotic targets that other linkers do not.
50 let ld_is_bfd = ld_help.contains("symbolsrec")
51 || ld_help.contains("verilog")
52 || ld_help.contains("tekhex");
53
54 // Whatever linker is being used that's not bfd will likely work.
55 if !ld_is_bfd {
56 return;
57 }
58
59 // mold is fastest, but specifing mold with -fuse-ld requires GCC >= 12 or Clang.
60 // Unfortunately cargo does not provide a means to set the linker driver via build scripts,
61 // so linking would fail trying to use -fuse-ld=mold with GCC < 12 even if clang is installed.
62 // So, prefer lld and gold to mold for robustness on the widest range of systems.
63 // mold can still be used by manually specifying it in ~/.cargo/config.toml or the RUSTFLAGS environment variable.
64 if command_help_output("lld").is_ok() {
65 println!("cargo::rustc-link-arg=-fuse-ld=lld");
66 } else if command_help_output("ld.gold").is_ok() {
67 println!("cargo::rustc-link-arg=-fuse-ld=gold");
68 } else if command_help_output("mold").is_ok() {
69 println!("cargo::rustc-link-arg=-fuse-ld=mold");
70 } else {
71 println!("cargo::warning=Neither mold, lld, nor gold linkers were found. Linking with GNU ld.bfd will likely fail.");
72 }
73 }
74 }
75}