https://github.com/ian-twilightcoder updated 
https://github.com/llvm/llvm-project/pull/218595

>From 6649d4c0efd7b54c3d7471b2276a40646638bf76 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  | 71 +++++++++++++++++++++++++
 4 files changed, 151 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..c69174fa490d4 100644
--- a/clang/test/Driver/darwin-static-lib.c
+++ b/clang/test/Driver/darwin-static-lib.c
@@ -1,5 +1,76 @@
 // 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.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.
+// 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

Reply via email to