Skip to content

Commit 6650a89

Browse files
committed
ClangImporter: run Clang with a proper executable path
Clang deduces its installation directory from the `argv[0]` parameter (see clang/lib/Frontend/CreateInvocationFromCommandLine.cpp), and the default include search paths are computed based on the installation directory. This change allows compiling Swift code that imports the C++ stdlib without having to manually specify the include search path of `std` headers.
1 parent 0b31642 commit 6650a89

File tree

7 files changed

+37
-2
lines changed

7 files changed

+37
-2
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,10 @@ namespace swift {
597597
/// Options for controlling the behavior of the Clang importer.
598598
class ClangImporterOptions final {
599599
public:
600+
/// The path to the Clang compiler executable.
601+
/// Used to detect the default include paths.
602+
std::string clangPath = "clang";
603+
600604
/// The module cache path which the Clang importer should use.
601605
std::string ModuleCachePath;
602606

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ ClangImporter::getClangArguments(ASTContext &ctx) {
949949
std::vector<std::string> invocationArgStrs;
950950
// Clang expects this to be like an actual command line. So we need to pass in
951951
// "clang" for argv[0]
952-
invocationArgStrs.push_back("clang");
952+
invocationArgStrs.push_back(ctx.ClangImporterOpts.clangPath);
953953
switch (ctx.ClangImporterOpts.Mode) {
954954
case ClangImporterOptions::Modes::Normal:
955955
case ClangImporterOptions::Modes::PrecompiledModule:

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
255255
inputArgs.AddLastArg(arguments, options::OPT_swift_version);
256256
inputArgs.AddLastArg(arguments, options::OPT_enforce_exclusivity_EQ);
257257
inputArgs.AddLastArg(arguments, options::OPT_stats_output_dir);
258+
inputArgs.AddLastArg(arguments, options::OPT_tools_directory);
258259
inputArgs.AddLastArg(arguments, options::OPT_trace_stats_events);
259260
inputArgs.AddLastArg(arguments, options::OPT_profile_stats_events);
260261
inputArgs.AddLastArg(arguments, options::OPT_profile_stats_entities);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ void CompilerInvocation::setMainExecutablePath(StringRef Path) {
6666
Path, FrontendOpts.UseSharedResourceFolder, LibPath);
6767
setRuntimeResourcePath(LibPath.str());
6868

69+
llvm::SmallString<128> clangPath(Path);
70+
llvm::sys::path::remove_filename(clangPath);
71+
llvm::sys::path::append(clangPath, "clang");
72+
ClangImporterOpts.clangPath = std::string(clangPath);
73+
6974
llvm::SmallString<128> DiagnosticDocsPath(Path);
7075
llvm::sys::path::remove_filename(DiagnosticDocsPath); // Remove /swift
7176
llvm::sys::path::remove_filename(DiagnosticDocsPath); // Remove /bin
@@ -858,6 +863,18 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts,
858863
StringRef workingDirectory) {
859864
using namespace options;
860865

866+
if (const Arg *a = Args.getLastArg(OPT_tools_directory)) {
867+
// If a custom tools directory is specified, try to find Clang there.
868+
// This is useful when the Swift executable is located in a different
869+
// directory than the Clang/LLVM executables, for example, when building
870+
// the Swift project itself.
871+
llvm::SmallString<128> clangPath(a->getValue());
872+
llvm::sys::path::append(clangPath, "clang");
873+
if (llvm::sys::fs::exists(clangPath)) {
874+
Opts.clangPath = std::string(clangPath);
875+
}
876+
}
877+
861878
if (const Arg *A = Args.getLastArg(OPT_module_cache_path)) {
862879
Opts.ModuleCachePath = A->getValue();
863880
}

stdlib/cmake/modules/SwiftSource.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ function(_compile_swift_files
395395
compute_library_subdir(library_subdir
396396
"${library_subdir_sdk}" "${SWIFTFILE_ARCHITECTURE}")
397397

398+
list(APPEND swift_flags "-tools-directory" "${SWIFT_NATIVE_CLANG_TOOLS_PATH}")
399+
398400
# If we have a custom module cache path, use it.
399401
if (SWIFT_MODULE_CACHE_PATH)
400402
list(APPEND swift_flags "-module-cache-path" "${SWIFT_MODULE_CACHE_PATH}")

test/ScanDependencies/module_deps.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ import SubE
115115
// CHECK-NEXT: "-frontend"
116116
// CHECK-NEXT: "-only-use-extra-clang-opts"
117117
// CHECK-NEXT: "-Xcc"
118-
// CHECK-NEXT: "clang"
118+
// CHECK-NEXT: "BUILD_DIR/bin/clang"
119119
// CHECK: "-fsystem-module",
120120
// CHECK-NEXT: "-emit-pcm",
121121
// CHECK-NEXT: "-module-name",

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ static llvm::cl::list<std::string>
276276
SwiftVersion("swift-version", llvm::cl::desc("Swift version"),
277277
llvm::cl::cat(Category));
278278

279+
static llvm::cl::opt<std::string>
280+
ToolsDirectory("tools-directory",
281+
llvm::cl::desc("Path to external executables (ld, clang, binutils)"),
282+
llvm::cl::cat(Category));
283+
279284
static llvm::cl::list<std::string>
280285
ModuleCachePath("module-cache-path", llvm::cl::desc("Clang module cache path"),
281286
llvm::cl::cat(Category));
@@ -3871,6 +3876,12 @@ int main(int argc, char *argv[]) {
38713876
InitInvok.getLangOptions().EffectiveLanguageVersion = actual.getValue();
38723877
}
38733878
}
3879+
if (!options::ToolsDirectory.empty()) {
3880+
SmallString<128> toolsDir(options::ToolsDirectory);
3881+
llvm::sys::path::append(toolsDir, "clang");
3882+
InitInvok.getClangImporterOptions().clangPath =
3883+
std::string(toolsDir);
3884+
}
38743885
if (!options::ModuleCachePath.empty()) {
38753886
// Honor the *last* -module-cache-path specified.
38763887
InitInvok.getClangImporterOptions().ModuleCachePath =

0 commit comments

Comments
 (0)