Author: Ian Anderson Date: 2026-08-28T09:22:08-07:00 New Revision: a0b0b4e447b61ae6a8d6b9f9a04947d559ee1d79
URL: https://github.com/llvm/llvm-project/commit/a0b0b4e447b61ae6a8d6b9f9a04947d559ee1d79 DIFF: https://github.com/llvm/llvm-project/commit/a0b0b4e447b61ae6a8d6b9f9a04947d559ee1d79.diff LOG: [clang][driver][darwin] Support libtool arguments used by SwiftBuild (#218595) 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 Added: clang/test/Driver/linux-ar-args.c Modified: clang/include/clang/Options/Options.td clang/lib/Driver/ToolChains/BareMetal.cpp clang/lib/Driver/ToolChains/Darwin.cpp clang/lib/Driver/ToolChains/Darwin.h clang/lib/Driver/ToolChains/Fuchsia.cpp clang/lib/Driver/ToolChains/Gnu.cpp clang/test/Driver/baremetal.cpp clang/test/Driver/darwin-static-lib.c clang/test/Driver/fuchsia.c Removed: ################################################################################ diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 7c17adb1bc06e..c383265e9f092 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -1124,7 +1124,11 @@ 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">; def bundle__loader : Separate<["-"], "bundle_loader">; @@ -1434,6 +1438,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_tool : Separate<["-"], "Xstatic-lib-tool">, + 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/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp index ba454acbf755c..19fbb6b07cebe 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -490,6 +490,7 @@ void baremetal::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA, ArgStringList CmdArgs; // Create and insert file members with a deterministic index. CmdArgs.push_back("rcsD"); + Args.AddAllArgValues(CmdArgs, options::OPT_Xstatic_lib_tool); CmdArgs.push_back(Output.getFilename()); for (const auto &II : Inputs) { diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 98ace0720343f..a0d7d31f1bf15 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_tool); + 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/lib/Driver/ToolChains/Fuchsia.cpp b/clang/lib/Driver/ToolChains/Fuchsia.cpp index abde9fa10482d..028d94b3fa103 100644 --- a/clang/lib/Driver/ToolChains/Fuchsia.cpp +++ b/clang/lib/Driver/ToolChains/Fuchsia.cpp @@ -217,6 +217,7 @@ void fuchsia::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA, ArgStringList CmdArgs; // Create and insert file members with a deterministic index. CmdArgs.push_back("rcsD"); + Args.AddAllArgValues(CmdArgs, options::OPT_Xstatic_lib_tool); CmdArgs.push_back(Output.getFilename()); for (const auto &II : Inputs) { diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp index 72affac131701..04f3b8d2200d6 100644 --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -244,6 +244,7 @@ void tools::gnutools::StaticLibTool::ConstructJob( ArgStringList CmdArgs; // Create and insert file members with a deterministic index. CmdArgs.push_back("rcsD"); + Args.AddAllArgValues(CmdArgs, options::OPT_Xstatic_lib_tool); CmdArgs.push_back(Output.getFilename()); for (const auto &II : Inputs) { diff --git a/clang/test/Driver/baremetal.cpp b/clang/test/Driver/baremetal.cpp index f36faf59f55d6..aa2aa9696c08e 100644 --- a/clang/test/Driver/baremetal.cpp +++ b/clang/test/Driver/baremetal.cpp @@ -1,8 +1,17 @@ // UNSUPPORTED: system-windows -// RUN: %clang -### %s --target=armv6-none-eabi --emit-static-lib 2>&1 \ +// RUN: %clang -### %s --target=armv6-none-eabi --emit-static-lib \ +// RUN: -Xstatic-lib-tool -U -Xstatic-lib-tool --format=gnu -o %t.out 2>&1 \ // RUN: | FileCheck -check-prefixes=CHECK-STATIC-LIB %s -// CHECK-STATIC-LIB: {{.*}}llvm-ar{{.*}}" "rcsD" +// CHECK-STATIC-LIB: {{.*}}llvm-ar{{.*}}" "rcsD" "-U" "--format=gnu" "{{.*}}.out" + +// RUN: %clang -### %s --target=armv6-none-eabi --emit-static-lib \ +// RUN: --static-lib-target-arch-only 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHECK-LIBTOOL-ARG %s +// CHECK-LIBTOOL-ARG: warning: argument unused during compilation: '--static-lib-target-arch-only' +// CHECK-LIBTOOL-ARG: {{.*}}llvm-ar{{.*}}" "rcsD" +// CHECK-LIBTOOL-ARG-NOT: "--static-lib-target-arch-only" +// CHECK-LIBTOOL-ARG-NOT: "-arch_only" // RUN: %clang %s -### --target=arm-none-eabi -o %t.out 2>&1 \ // RUN: --sysroot=%S/Inputs/multiarch-sysroot-tree \ diff --git a/clang/test/Driver/darwin-static-lib.c b/clang/test/Driver/darwin-static-lib.c index 74a010e9656f4..7975d78b7b5f2 100644 --- a/clang/test/Driver/darwin-static-lib.c +++ b/clang/test/Driver/darwin-static-lib.c @@ -1,5 +1,80 @@ +// -D and -no_warning_for_no_symbols are on by default. // 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}}" // 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}}" + +// RUN: touch %t1.o %t2.o + +// Adding the explicit flags for -D and -no_warning_for_no_symbols doesn't +// double up the arguments passed to libtool. +// RUN: %clang -target i386-apple-darwin9 -### --emit-static-lib %t1.o %t2.o \ +// RUN: --static-lib-deterministic --no-static-lib-warn-no-symbols 2>&1 \ +// RUN: | FileCheck %s --check-prefix=DEFAULT +// DEFAULT: "{{.*}}libtool" "-static" "-D" "-no_warning_for_no_symbols" "-o" "a.out" "{{.*}}1.o" "{{.*}}2.o" + +// Last one wins with contradictory arguments. +// RUN: %clang -target i386-apple-darwin9 -### --emit-static-lib %t1.o %t2.o \ +// RUN: --no-static-lib-deterministic --static-lib-deterministic 2>&1 \ +// RUN: | FileCheck %s --check-prefix=DEFAULT + +// -D and -no_warning_for_no_symbols can be turned off. +// RUN: %clang -target i386-apple-darwin9 -### --emit-static-lib %t1.o %t2.o \ +// 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" + +// -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.0, but -arch_only uses +// the MachO name armv7k. -force_cpusubtype_ALL isn't used by libtool. +// RUN: %clang -target armv7k-apple-watchos8.0 -### --emit-static-lib %t1.o %t2.o \ +// 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 -### --emit-static-lib %t1.o %t2.o \ +// 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 -### --emit-static-lib %t1.o %t2.o \ +// 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 -### --emit-static-lib %t1.o %t2.o \ +// 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 -### --emit-static-lib %t1.o %t2.o \ +// 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-tool passes through. +// RUN: %clang -target i386-apple-darwin9 -### --emit-static-lib %t1.o %t2.o \ +// RUN: -L/tmp/first -L/tmp/second -Xstatic-lib-tool -dependency_info -Xstatic-lib-tool 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: "{{.*}}1.o" "{{.*}}2.o" +// PASSTHROUGH-NOT: "-filelist" + +// Multiple -arch produces one libtool job per arch plus a lipo. +// RUN: %clang -target x86_64-apple-macos14 -### --emit-static-lib %t1.o %t2.o \ +// 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" diff --git a/clang/test/Driver/fuchsia.c b/clang/test/Driver/fuchsia.c index d7ee59696737c..10135b204ffff 100644 --- a/clang/test/Driver/fuchsia.c +++ b/clang/test/Driver/fuchsia.c @@ -64,9 +64,19 @@ // CHECK-NOT: crtend.o // CHECK-NOT: crtn.o -// RUN: %clang -### %s --target=x86_64-unknown-fuchsia \ -// RUN: --emit-static-lib 2>&1 | FileCheck -check-prefixes=CHECK-STATIC-LIB %s -// CHECK-STATIC-LIB: {{.*}}llvm-ar{{.*}}" "rcsD" +// RUN: %clang -### %s --target=x86_64-unknown-fuchsia --emit-static-lib \ +// RUN: -Xstatic-lib-tool -U -Xstatic-lib-tool --format=gnu -o %t.out 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHECK-STATIC-LIB %s +// CHECK-STATIC-LIB: {{.*}}llvm-ar{{.*}}" "rcsD" "-U" "--format=gnu" "{{.*}}.out" + +// The libtool-only options do not apply to ar(1) and are reported unused. +// RUN: %clang -### %s --target=x86_64-unknown-fuchsia --emit-static-lib \ +// RUN: --static-lib-target-arch-only 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHECK-LIBTOOL-ARG %s +// CHECK-LIBTOOL-ARG: warning: argument unused during compilation: '--static-lib-target-arch-only' +// CHECK-LIBTOOL-ARG: {{.*}}llvm-ar{{.*}}" "rcsD" +// CHECK-LIBTOOL-ARG-NOT: "--static-lib-target-arch-only" +// CHECK-LIBTOOL-ARG-NOT: "-arch_only" // RUN: %clang -### %s --target=x86_64-unknown-fuchsia 2>&1 \ // RUN: | FileCheck %s -check-prefix=CHECK-FP-ALL diff --git a/clang/test/Driver/linux-ar-args.c b/clang/test/Driver/linux-ar-args.c new file mode 100644 index 0000000000000..8341e7168a809 --- /dev/null +++ b/clang/test/Driver/linux-ar-args.c @@ -0,0 +1,11 @@ +// RUN: %clang --target=x86_64-unknown-linux-gnu %s -### --emit-static-lib \ +// RUN: -Xstatic-lib-tool -U -Xstatic-lib-tool --format=gnu 2>&1 \ +// RUN: | FileCheck %s +// CHECK: "{{.*}}llvm-ar" "rcsD" "-U" "--format=gnu" "a.out" "{{.*}}linux-ar-args-{{.*}}.o" + +// RUN: %clang --target=x86_64-unknown-linux-gnu %s -### --emit-static-lib \ +// RUN: --static-lib-target-arch-only 2>&1 | FileCheck %s --check-prefix=LIBTOOL-ARG +// LIBTOOL-ARG: warning: argument unused during compilation: '--static-lib-target-arch-only' +// LIBTOOL-ARG: "{{.*}}llvm-ar" "rcsD" +// LIBTOOL-ARG-NOT: "--static-lib-target-arch-only" +// LIBTOOL-ARG-NOT: "-arch_only" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
