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}