https://github.com/ian-twilightcoder updated https://github.com/llvm/llvm-project/pull/218595
>From 16764e55dc8ef49d5b6e55cf89bbc50af86507ce Mon Sep 17 00:00:00 2001 From: Ian Anderson <[email protected]> Date: Mon, 24 Aug 2026 23:26:03 -0700 Subject: [PATCH] [clang][driver][darwin] Support libtool arguments used by SwiftBuild SwiftBuild is considering using clang as the driver for the static library tool on Darwin, but clang doesn't support a lot of the options that SwiftBuild uses for libtool. -arch_only is already a clang argument, but it's intentionally dropped, and it would be awkward to reconcile if -target or -arch was passed too. Add a --static-lib-target-arch-only to get the appropriate value for -arch_only from -target/-arch. -D and -no_warning_for_no_symbols are currently passed by default, make positive/negative flags to suppress them. Reuse the -syslibroot, -L, and -filelist arguments from the linker. Add -Xstatic-lib for SwiftBuild to pass the rest of the arguments it uses. Assisted-by: Claude Code rdar://185468081 --- clang/include/clang/Options/Options.td | 24 +++++++++ clang/lib/Driver/ToolChains/Darwin.cpp | 72 ++++++++++++++++++------- clang/lib/Driver/ToolChains/Darwin.h | 2 + clang/test/Driver/darwin-static-lib.c | 74 ++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index eb5a009b5628c..2a5f9372b7f53 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -1124,6 +1124,8 @@ def allowable__client : Separate<["-"], "allowable_client">; def ansi : Flag<["-", "--"], "ansi">, Group<CompileOnly_Group>; def arch__errors__fatal : Flag<["-"], "arch_errors_fatal">; def arch : Separate<["-"], "arch">, Flags<[NoXarchOption,TargetSpecific]>; +// Ignored option for gcc compatibility - not used for anything. +// -arch_only is passed to libtool through --static-lib-target-arch-only def arch__only : Separate<["-"], "arch_only">; def autocomplete : Joined<["--"], "autocomplete=">; def bind__at__load : Flag<["-"], "bind_at_load">; @@ -1434,6 +1436,28 @@ defm cuda_short_ptr : BoolOptionWithoutMarshalling< def emit_static_lib : Flag<["--"], "emit-static-lib">, HelpText<"Enable linker job to emit a static library.">; +// Options controlling the static library tool. Most of these are specific +// to Apple's libtool. +def static_lib_target_arch_only : Flag<["--"], + "static-lib-target-arch-only">, + Visibility<[ClangOption]>, + HelpText<"Restrict the static library to the target architecture, ignoring " + "other architectures present in universal inputs (Darwin only)">; +defm static_lib_deterministic : BoolOptionWithoutMarshalling<"", + "static-lib-deterministic", + PosFlag<SetTrue, [], [ClangOption], + "Set static library member metadata to fixed values so that " + "identical inputs produce identical output (default)">, + NegFlag<SetFalse, [], [ClangOption]>, BothFlags<[]>, ["--"]>; +defm static_lib_warn_no_symbols : BoolOptionWithoutMarshalling<"", + "static-lib-warn-no-symbols", + PosFlag<SetTrue, [], [ClangOption], + "Warn about static library members that define no symbols">, + NegFlag<SetFalse, [], [ClangOption]>, BothFlags<[]>, ["--"]>; +def Xstatic_lib : Separate<["-"], "Xstatic-lib">, + Visibility<[ClangOption]>, + HelpText<"Pass <arg> to the static library tool">, MetaVarName<"<arg>">; + def mprintf_kind_EQ : Joined<["-"], "mprintf-kind=">, Group<m_Group>, HelpText<"Specify the printf lowering scheme (AMDGPU only), allowed values are " "\"hostcall\"(printing happens during kernel execution, this scheme " diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 98ace0720343f..656bcc4d2145d 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -192,6 +192,39 @@ void darwin::MachOTool::AddMachOArch(const ArgList &Args, CmdArgs.push_back("-force_cpusubtype_ALL"); } +void darwin::MachOTool::AddMachOArchOnly(const ArgList &Args, + ArgStringList &CmdArgs) const { + const toolchains::MachO &MachOTC = getMachOToolChain(); + + StringRef ArchName; + if (MachOTC.getTriple().isOSFirmware()) + // Firmware uses the full triple as the arch name. + ArchName = MachOTC.getEffectiveTriple().getTriple(); + else + ArchName = MachOTC.getMachOArchName(Args); + + CmdArgs.push_back("-arch_only"); + CmdArgs.push_back(Args.MakeArgString(ArchName)); +} + +static void AddMachOSysLibRoot(Compilation &C, const ArgList &Args, + ArgStringList &CmdArgs) { + // Give --sysroot= preference, over the Apple specific behavior to also use + // --isysroot as the syslibroot. + // We check `OPT__sysroot_EQ` directly instead of `getSysRoot` to make sure we + // prioritise command line arguments over configuration of `DEFAULT_SYSROOT`. + if (const Arg *A = Args.getLastArg(options::OPT__sysroot_EQ)) { + CmdArgs.push_back("-syslibroot"); + CmdArgs.push_back(A->getValue()); + } else if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { + CmdArgs.push_back("-syslibroot"); + CmdArgs.push_back(A->getValue()); + } else if (StringRef sysroot = C.getSysRoot(); sysroot != "") { + CmdArgs.push_back("-syslibroot"); + CmdArgs.push_back(C.getArgs().MakeArgString(sysroot)); + } +} + bool darwin::Linker::NeedsTempPath(const InputInfoList &Inputs) const { // We only need to generate a temp path for LTO if we aren't compiling object // files. When compiling source files, we run 'dsymutil' after linking. We @@ -439,20 +472,7 @@ void darwin::Linker::AddLinkArgs(Compilation &C, const ArgList &Args, Args.AddAllArgs(CmdArgs, options::OPT_sub__library); Args.AddAllArgs(CmdArgs, options::OPT_sub__umbrella); - // Give --sysroot= preference, over the Apple specific behavior to also use - // --isysroot as the syslibroot. - // We check `OPT__sysroot_EQ` directly instead of `getSysRoot` to make sure we - // prioritise command line arguments over configuration of `DEFAULT_SYSROOT`. - if (const Arg *A = Args.getLastArg(options::OPT__sysroot_EQ)) { - CmdArgs.push_back("-syslibroot"); - CmdArgs.push_back(A->getValue()); - } else if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { - CmdArgs.push_back("-syslibroot"); - CmdArgs.push_back(A->getValue()); - } else if (StringRef sysroot = C.getSysRoot(); sysroot != "") { - CmdArgs.push_back("-syslibroot"); - CmdArgs.push_back(C.getArgs().MakeArgString(sysroot)); - } + AddMachOSysLibRoot(C, Args, CmdArgs); Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace); Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace__hints); @@ -894,15 +914,31 @@ void darwin::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA, // Silence warnings when linking C code with a C++ '-stdlib' argument. Args.ClaimAllArgs(options::OPT_stdlib_EQ); - // libtool <options> <output_file> <input_files> ArgStringList CmdArgs; - // Create and insert file members with a deterministic index. CmdArgs.push_back("-static"); - CmdArgs.push_back("-D"); - CmdArgs.push_back("-no_warning_for_no_symbols"); + + if (Args.hasArg(options::OPT_static_lib_target_arch_only)) + AddMachOArchOnly(Args, CmdArgs); + + if (Args.hasFlag(options::OPT_static_lib_deterministic, + options::OPT_no_static_lib_deterministic, + /*Default=*/true)) + CmdArgs.push_back("-D"); + + AddMachOSysLibRoot(C, Args, CmdArgs); + Args.AddAllArgs(CmdArgs, options::OPT_L); + + if (!Args.hasFlag(options::OPT_static_lib_warn_no_symbols, + options::OPT_no_static_lib_warn_no_symbols, + /*Default=*/false)) + CmdArgs.push_back("-no_warning_for_no_symbols"); + + Args.AddAllArgValues(CmdArgs, options::OPT_Xstatic_lib); + CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); + Args.AddLastArg(CmdArgs, options::OPT_filelist); for (const auto &II : Inputs) { if (II.isFilename()) { CmdArgs.push_back(II.getFilename()); diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index c41bb6c2eead5..1bcc1492b7c3e 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -39,6 +39,8 @@ class LLVM_LIBRARY_VISIBILITY MachOTool : public Tool { protected: void AddMachOArch(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + void AddMachOArchOnly(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; const toolchains::MachO &getMachOToolChain() const { return reinterpret_cast<const toolchains::MachO &>(getToolChain()); diff --git a/clang/test/Driver/darwin-static-lib.c b/clang/test/Driver/darwin-static-lib.c index 74a010e9656f4..97978c1c6d693 100644 --- a/clang/test/Driver/darwin-static-lib.c +++ b/clang/test/Driver/darwin-static-lib.c @@ -1,5 +1,79 @@ // RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib 2>&1 | FileCheck %s // CHECK: "{{.*}}libtool" "-static" "-D" "-no_warning_for_no_symbols" "-o" "a.out" "{{.*o}}" +// -D and -no_warning_for_no_symbols are on by default, adding the explicit flags +// doesn't double up the arguments passed to libtool. +// RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib \ +// RUN: --static-lib-deterministic --no-static-lib-warn-no-symbols 2>&1 \ +// RUN: | FileCheck %s + +// -D and -no_warning_for_no_symbols can be turned off. +// RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib \ +// RUN: --no-static-lib-deterministic --static-lib-warn-no-symbols 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NEITHER +// NEITHER: "-static" +// NEITHER-NOT: "-D" +// NEITHER-NOT: "-no_warning_for_no_symbols" +// NEITHER: "-o" + +/// Last one wins with contradictory arguments. +// RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib \ +// RUN: --no-static-lib-deterministic --static-lib-deterministic 2>&1 \ +// RUN: | FileCheck %s + +// Explicit output file. // RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib -o libfoo.a 2>&1 | FileCheck %s --check-prefix=OUTPUT // OUTPUT: "{{.*}}libtool" "-static" "-D" "-no_warning_for_no_symbols" "-o" "libfoo.a" "{{.*o}}" + +// -arch_only is derived from the target, and only when asked for. The Mach-O +// arch name is used, not the clang one. i.e. the clang driver turns +// armv7k-apple-watchos8.0 into thumbv7k-apple-watchos8.0, but -arch_only uses +// the MachO name armv7k. -force_cpusubtype_ALL isn't used by libtool. +// RUN: %clang -target armv7k-apple-watchos8.0 %s -### --emit-static-lib \ +// RUN: --static-lib-target-arch-only 2>&1 | FileCheck %s --check-prefix=ARMV7K +// ARMV7K: "-static" "-arch_only" "armv7k" "-D" +// ARMV7K-NOT: "-force_cpusubtype_ALL" + +// Firmware uses the full triple. +// RUN: %clang -target armv7em-apple-firmware1.0 %s -### --emit-static-lib \ +// RUN: --static-lib-target-arch-only 2>&1 | FileCheck %s --check-prefix=FIRMWARE +// FIRMWARE: "-arch_only" "thumbv7em-apple-firmware1.0.0" + +// -arch_only on the clang command line is ignored and not passed on. +// RUN: %clang -target armv7k-apple-watchos8.0 %s -### --emit-static-lib \ +// RUN: --static-lib-target-arch-only -arch_only arm32_64 2>&1 | \ +// RUN: FileCheck %s --check-prefix=ARCH-ONLY +// ARCH-ONLY: warning: argument unused during compilation: '-arch_only arm32_64' +// ARCH-ONLY: "-static" "-arch_only" +// ARCH-ONLY-NOT: "arm32_64" +// ARCH-ONLY: "armv7k" "-D" + +/// sysroot becomes -syslibroot, with --sysroot= taking priority over -isysroot. +// RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib \ +// RUN: -isysroot %S/Inputs/MacOSX15.1.sdk --sysroot=/tmp/sysroot 2>&1 \ +// RUN: | FileCheck %s --check-prefix=SYSROOT +// SYSROOT: "-syslibroot" "/tmp/sysroot" + +// RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib \ +// RUN: -isysroot %S/Inputs/MacOSX15.1.sdk 2>&1 | FileCheck %s --check-prefix=ISYSROOT +// ISYSROOT: "-syslibroot" "{{.*}}MacOSX15.1.sdk" + +/// -L, -filelist are forwarded as is. -filelist doesn't get doubled up as an input file. -Xstatic-lib passes through. +/// These are independent of each other, so their relative order isn't checked. +/// The order within each one is, since libtool honours -L order and the +/// passthrough must not reshuffle its values. +// RUN: %clang -target i386-apple-darwin9 %s -### --emit-static-lib \ +// RUN: -L/tmp/first -L/tmp/second -Xstatic-lib -dependency_info -Xstatic-lib deps.dat \ +// RUN: -filelist objs.txt 2>&1 | FileCheck %s --check-prefix=PASSTHROUGH +// PASSTHROUGH-DAG: "-L/tmp/first" "-L/tmp/second" +// PASSTHROUGH-DAG: "-dependency_info" "deps.dat" +// PASSTHROUGH-DAG: "-filelist" "objs.txt" +// PASSTHROUGH-DAG: "{{.*}}darwin-static-lib-{{.*}}.o" +// PASSTHROUGH-NOT: "-filelist" + +// Multiple -arch produces one libtool job per arch plus a lipo. +// RUN: %clang -target x86_64-apple-macos14 %s -### --emit-static-lib \ +// RUN: -arch x86_64 -arch arm64 -o libfoo.a 2>&1 | FileCheck %s --check-prefix=UNIVERSAL +// UNIVERSAL: "{{.*}}libtool" "-static" +// UNIVERSAL: "{{.*}}libtool" "-static" +// UNIVERSAL: "{{.*}}lipo" "-create" "-output" "libfoo.a" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
