swiftPackages.swift{,-unwrapped,-driver}: ignore $NIX_CC

Swift needs Clang to link with. The linker patch breaks linking
when using Swift with a GCC‐based standard environment.

Similarly, the internal Clang headers in the resource directory are
coupled to the corresponding version of Clang. The resource root patch
caused Swift’s Clang to use the resource directory from the version
of Clang used in the build environment, which is only compatible if
the versions match.

Instead, hard‐code the Clang path in the patches and wrappers.
This commit is contained in:
Emily
2025-08-11 15:36:06 +01:00
parent 1884f3de79
commit 809f7d037c
8 changed files with 65 additions and 134 deletions

View File

@@ -111,6 +111,18 @@ let
"swift-remote-mirror-headers"
];
clangForWrappers = clang.override (prev: {
extraBuildCommands =
prev.extraBuildCommands
# We need to use the resource directory corresponding to Swifts
# version of Clang instead of passing along the one from the
# `cc-wrapper` flags.
+ ''
substituteInPlace $out/nix-support/cc-cflags \
--replace-fail " -resource-dir=$out/resource-root" ""
'';
});
# Build a tool used during the build to create a custom clang wrapper, with
# which we wrap the clang produced by the swift build.
#
@@ -130,7 +142,7 @@ let
unwrappedClang="$targetFile-unwrapped"
mv "$targetFile" "$unwrappedClang"
sed < '${clang}/bin/clang' > "$targetFile" \
sed < '${clangForWrappers}/bin/clang' > "$targetFile" \
-e 's|^\s*exec|exec -a "$0"|g' \
-e 's|^\[\[ "${clang.cc}/bin/clang" = \*++ ]]|[[ "$0" = *++ ]]|' \
-e "s|${clang.cc}/bin/clang|$unwrappedClang|g" \
@@ -146,13 +158,12 @@ let
# executable uses $0 to detect what tool is called.
wrapperParams = {
inherit bintools;
default_cc_wrapper = clang; # Instead of `@out@` in the original.
coreutils_bin = lib.getBin coreutils;
gnugrep_bin = gnugrep;
suffixSalt = lib.replaceStrings [ "-" "." ] [ "_" "_" ] targetPlatform.config;
use_response_file_by_default = 1;
swiftDriver = "";
# NOTE: @prog@ needs to be filled elsewhere.
# NOTE: @cc_wrapper@ and @prog@ need to be filled elsewhere.
};
swiftWrapper = runCommand "swift-wrapper.sh" wrapperParams ''
# Make empty to avoid adding the SDKs modules in the bootstrap wrapper. Otherwise, the SDK conflicts with the
@@ -168,6 +179,7 @@ let
mv "$targetFile" "$unwrappedSwift"
sed < '${swiftWrapper}' > "$targetFile" \
-e "s|@prog@|'$unwrappedSwift'|g" \
-e 's|@cc_wrapper@|${clangForWrappers}|g' \
-e 's|exec "$prog"|exec -a "$0" "$prog"|g'
chmod a+x "$targetFile"
'';
@@ -308,9 +320,12 @@ stdenv.mkDerivation {
patch -p1 -d swift -i ${./patches/swift-cmake-3.25-compat.patch}
patch -p1 -d swift -i ${./patches/swift-wrap.patch}
patch -p1 -d swift -i ${./patches/swift-nix-resource-root.patch}
patch -p1 -d swift -i ${./patches/swift-linux-fix-libc-paths.patch}
patch -p1 -d swift -i ${./patches/swift-linux-fix-linking.patch}
patch -p1 -d swift -i ${
replaceVars ./patches/swift-linux-fix-linking.patch {
inherit clang;
}
}
patch -p1 -d swift -i ${./patches/swift-darwin-libcxx-flags.patch}
patch -p1 -d swift -i ${
replaceVars ./patches/swift-darwin-plistbuddy-workaround.patch {
@@ -507,6 +522,10 @@ stdenv.mkDerivation {
"
buildProject llvm llvm-project/llvm
# Ensure that the built Clang can find the runtime libraries by
# copying the symlinks from the main wrapper.
cp -P ${clang}/resource-root/{lib,share} $SWIFT_BUILD_ROOT/llvm/lib/clang/15.0.0/
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# Add appleSwiftCore to the search paths. Adding the whole SDK results in build failures.
@@ -714,14 +733,8 @@ stdenv.mkDerivation {
ln -s $lib/lib/swift $out/lib/swift
# Swift has a separate resource root from Clang, but locates the Clang
# resource root via subdir or symlink. Provide a default here, but we also
# patch Swift to prefer NIX_CC if set.
#
# NOTE: We don't symlink directly here, because that'd add a run-time dep
# on the full Clang compiler to every Swift executable. The copy here is
# just copying the 3 symlinks inside to smaller closures.
mkdir $lib/lib/swift/clang
cp -P ${clang}/resource-root/* $lib/lib/swift/clang/
# resource root via subdir or symlink.
mv $SWIFT_BUILD_ROOT/llvm/lib/clang/15.0.0 $lib/lib/swift/clang
'';
preFixup = lib.optionalString stdenv.hostPlatform.isLinux ''

View File

@@ -1,21 +1,17 @@
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index c0ee9217e8..bf7737d6fa 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -1475,7 +1475,17 @@ const char *ToolChain::getClangLinkerDriver(
// If there is a linker driver in the toolchain folder, use that instead.
if (auto tool = llvm::sys::findProgramByName(LinkerDriver, {toolchainPath}))
- LinkerDriver = Args.MakeArgString(tool.get());
+ return Args.MakeArgString(tool.get());
+ }
+
+ // For Nix, prefer linking using the wrapped system clang, instead of using
+ // the unwrapped clang packaged with swift. The latter is unable to link, but
+ // we still want to use it for other purposes (clang importer).
+ if (auto nixCC = llvm::sys::Process::GetEnv("NIX_CC")) {
+ llvm::SmallString<128> binDir(nixCC.getValue());
+ llvm::sys::path::append(binDir, "bin");
+ if (auto tool = llvm::sys::findProgramByName(LinkerDriver, {binDir.str()}))
+ return Args.MakeArgString(tool.get());
@@ -1489,6 +1489,12 @@
LinkerDriver = Args.MakeArgString(tool.get());
}
+ // For Nix, prefer linking using the wrapped Nixpkgs clang, instead of using
+ // the unwrapped clang packaged with swift. The latter is unable to link, but
+ // we still want to use it for other purposes (clang importer).
+ if (auto tool = llvm::sys::findProgramByName(LinkerDriver, {"@clang@/bin"}))
+ return Args.MakeArgString(tool.get());
+
return LinkerDriver;
}

View File

@@ -1,67 +0,0 @@
Swift normally looks for the Clang resource dir in a subdir/symlink of its own
resource dir. We provide a symlink to the Swift build-time Clang as a default
there, but we also here patch two checks to try locate it via NIX_CC.
The first (ClangImporter.cpp) happens when Swift code imports C modules. The
second (ToolChains.cpp) happens when Swift is used to link the final product.
--- a/lib/ClangImporter/ClangImporter.cpp
+++ b/lib/ClangImporter/ClangImporter.cpp
@@ -73,6 +73,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/YAMLParser.h"
#include <algorithm>
@@ -786,6 +787,17 @@ importer::addCommonInvocationArguments(
const std::string &overrideResourceDir = importerOpts.OverrideResourceDir;
if (overrideResourceDir.empty()) {
+ // Prefer the Clang resource directory from NIX_CC, to allow swapping in a
+ // different stdenv.
+ // TODO: Figure out how to provide a user override for this. Probably a
+ // niche use case, though, and for now a user can unset NIX_CC to work
+ // around it if necessary.
+ if (auto nixCC = llvm::sys::Process::GetEnv("NIX_CC")) {
+ llvm::SmallString<128> resourceDir(nixCC.getValue());
+ llvm::sys::path::append(resourceDir, "resource-root");
+ invocationArgStrs.push_back("-resource-dir");
+ invocationArgStrs.push_back(std::string(resourceDir.str()));
+ } else {
llvm::SmallString<128> resourceDir(searchPathOpts.RuntimeResourcePath);
// Adjust the path to refer to our copy of the Clang resource directory
@@ -801,6 +813,7 @@ importer::addCommonInvocationArguments(
// Set the Clang resource directory to the path we computed.
invocationArgStrs.push_back("-resource-dir");
invocationArgStrs.push_back(std::string(resourceDir.str()));
+ } // nixCC
} else {
invocationArgStrs.push_back("-resource-dir");
invocationArgStrs.push_back(overrideResourceDir);
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -1393,10 +1393,20 @@ void ToolChain::getClangLibraryPath(const ArgList &Args,
SmallString<128> &LibPath) const {
const llvm::Triple &T = getTriple();
+ // Nix: We provide a `clang` symlink in the default Swift resource root, but
+ // prefer detecting the Clang resource root via NIX_CC, to allow swapping in
+ // a different stdenv. However, always honor a user-provided `-resource-dir`.
+ auto nixCC = llvm::sys::Process::GetEnv("NIX_CC");
+ if (nixCC && !Args.hasArgNoClaim(options::OPT_resource_dir)) {
+ LibPath.assign(nixCC.getValue());
+ llvm::sys::path::append(LibPath, "resource-root");
+ } else {
getResourceDirPath(LibPath, Args, /*Shared=*/true);
// Remove platform name.
llvm::sys::path::remove_filename(LibPath);
- llvm::sys::path::append(LibPath, "clang", "lib",
+ llvm::sys::path::append(LibPath, "clang");
+ } // nixCC
+ llvm::sys::path::append(LibPath, "lib",
T.isOSDarwin() ? "darwin"
: getPlatformNameForTriple(T));
}

View File

@@ -10,6 +10,7 @@
XCTest,
sqlite,
ncurses,
clang,
replaceVars,
}:
let
@@ -40,9 +41,10 @@ stdenv.mkDerivation {
];
patches = [
./patches/nix-resource-root.patch
./patches/disable-catalyst.patch
./patches/linux-fix-linking.patch
(replaceVars ./patches/linux-fix-linking.patch {
inherit clang;
})
# TODO: Replace with branch patch once merged:
# https://github.com/apple/swift-driver/pull/1197
(fetchpatch {

View File

@@ -1,3 +1,5 @@
diff --git a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift
index a4a735f498..381522cc1f 100644
--- a/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift
+++ b/Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift
@@ -10,6 +10,7 @@
@@ -8,19 +10,17 @@
import SwiftOptions
import func TSCBasic.lookupExecutablePath
@@ -120,7 +121,20 @@ extension GenericUnixToolchain {
@@ -120,7 +121,18 @@
// just using `clang` and avoid a dependency on the C++ runtime.
let clangTool: Tool =
parsedOptions.hasArgument(.enableExperimentalCxxInterop) ? .clangxx : .clang
- var clangPath = try getToolPath(clangTool)
+
+ // For Nix, prefer linking using the wrapped system clang, instead of using
+ // For Nix, prefer linking using the wrapped Nixpkgs clang, instead of using
+ // the unwrapped clang packaged with swift. The latter is unable to link, but
+ // we still want to use it for other purposes (clang importer).
+ var clangPath: AbsolutePath
+ let env = ProcessInfo.processInfo.environment
+ if let nixCC = env["NIX_CC"],
+ let binPath = try? AbsolutePath(validating: "\(nixCC)/bin"),
+ if let binPath = try? AbsolutePath(validating: "@clang@/bin"),
+ let tool = lookupExecutablePath(filename: parsedOptions.hasArgument(.enableExperimentalCxxInterop)
+ ? "clang++" : "clang",
+ searchPaths: [binPath]) {
@@ -30,11 +30,11 @@
if let toolsDirPath = parsedOptions.getLastArgument(.toolsDirectory) {
// FIXME: What if this isn't an absolute path?
let toolsDir = try AbsolutePath(validating: toolsDirPath.asSingle)
@@ -136,6 +150,7 @@ extension GenericUnixToolchain {
@@ -136,6 +148,7 @@
commandLine.appendFlag("-B")
commandLine.appendPath(toolsDir)
}
+ } // nixCC
+ } // Nix
// Executables on Linux get -pie
if targetTriple.os == .linux && linkerOutputType == .executable {

View File

@@ -1,28 +0,0 @@
Swift normally looks for the Clang resource dir in a subdir/symlink of its own
resource dir. We provide a symlink to the Swift build-time Clang as a default
there, but we also here patch a check to try locate it via NIX_CC.
--- a/Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift
+++ b/Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+import Foundation
import SwiftOptions
import protocol TSCBasic.FileSystem
@@ -26,6 +27,13 @@ extension Toolchain {
for targetInfo: FrontendTargetInfo,
parsedOptions: inout ParsedOptions
) throws -> VirtualPath {
+ let env = ProcessInfo.processInfo.environment
+ if let nixCC = env["NIX_CC"] {
+ return try VirtualPath(path: nixCC)
+ .appending(components: "resource-root", "lib",
+ targetInfo.target.triple.platformName(conflatingDarwin: true)!)
+ }
+
return VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
.appending(components: "clang", "lib",
targetInfo.target.triple.platformName(conflatingDarwin: true)!)

View File

@@ -4,6 +4,7 @@
swift,
useSwiftDriver ? true,
swift-driver,
clang,
}:
stdenv.mkDerivation (
@@ -28,6 +29,20 @@ stdenv.mkDerivation (
swiftStaticLibSubdir
;
swiftDriver = lib.optionalString useSwiftDriver "${swift-driver}/bin/swift-driver";
cc_wrapper = clang.override (prev: {
extraBuildCommands =
prev.extraBuildCommands
# We need to use the resource directory corresponding to Swifts
# version of Clang instead of passing along the one from the
# `cc-wrapper` flags.
+ ''
rm -r $out/resource-root
substituteInPlace $out/nix-support/cc-cflags \
--replace-fail \
"-resource-dir=$out/resource-root" \
"-resource-dir=${lib.getLib swift}/lib/swift/clang"
'';
});
env.darwinMinVersion = lib.optionalString stdenv.targetPlatform.isDarwin (
stdenv.targetPlatform.darwinMinVersion

View File

@@ -8,7 +8,7 @@ if (( "${NIX_DEBUG:-0}" >= 7 )); then
set -x
fi
cc_wrapper="${NIX_CC:-@default_cc_wrapper@}"
cc_wrapper="@cc_wrapper@"
source $cc_wrapper/nix-support/utils.bash