Skip to content

Commit 7698d36

Browse files
[ClangImporter] Re-write how clang-importer is created via args
Re-write and clean up how clang-importer is created from clang arguments. Previously, it is unclear if `getClangArguments` will return CC1 args or driver args and the logic is unnecessarily compilicated when creating clang invocation. Now clang invocation is always created from cc1 arguments, which can be directly provided via direct-cc1-mode or converted from driver args. There is no functional changes in this patch, other than `-dump-clang-diagnostics` now will always print cc1 args, and also driver args if that is applicable.
1 parent 1057441 commit 7698d36

File tree

4 files changed

+111
-86
lines changed

4 files changed

+111
-86
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,19 @@ class ClangImporter final : public ClangModuleLoader {
176176
DWARFImporterDelegate *dwarfImporterDelegate = nullptr);
177177

178178
static std::vector<std::string>
179-
getClangArguments(ASTContext &ctx, bool ignoreClangTarget = false);
179+
getClangDriverArguments(ASTContext &ctx, bool ignoreClangTarget = false);
180+
181+
static std::optional<std::vector<std::string>>
182+
getClangCC1Arguments(ClangImporter *importer, ASTContext &ctx,
183+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
184+
bool ignoreClangTarget = false);
180185

181186
static std::unique_ptr<clang::CompilerInvocation>
182187
createClangInvocation(ClangImporter *importer,
183188
const ClangImporterOptions &importerOpts,
184189
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
185-
ArrayRef<std::string> invocationArgStrs,
186-
std::vector<std::string> *CC1Args = nullptr);
190+
std::vector<std::string> &CC1Args);
191+
187192
ClangImporter(const ClangImporter &) = delete;
188193
ClangImporter(ClangImporter &&) = delete;
189194
ClangImporter &operator=(const ClangImporter &) = delete;

lib/ClangImporter/ClangImporter.cpp

Lines changed: 99 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@
5252
#include "swift/Subsystems.h"
5353
#include "clang/AST/ASTContext.h"
5454
#include "clang/AST/Mangle.h"
55+
#include "clang/Basic/DiagnosticOptions.h"
5556
#include "clang/Basic/FileEntry.h"
5657
#include "clang/Basic/IdentifierTable.h"
5758
#include "clang/Basic/Module.h"
5859
#include "clang/Basic/TargetInfo.h"
5960
#include "clang/Basic/Version.h"
6061
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
6162
#include "clang/Frontend/FrontendActions.h"
63+
#include "clang/Frontend/TextDiagnosticPrinter.h"
6264
#include "clang/Frontend/Utils.h"
6365
#include "clang/Index/IndexingAction.h"
6466
#include "clang/Lex/Preprocessor.h"
@@ -85,6 +87,7 @@
8587
#include "llvm/TextAPI/TextAPIReader.h"
8688
#include <algorithm>
8789
#include <memory>
90+
#include <optional>
8891
#include <string>
8992

9093
using namespace swift;
@@ -1040,19 +1043,13 @@ ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
10401043
}
10411044

10421045
std::vector<std::string>
1043-
ClangImporter::getClangArguments(ASTContext &ctx, bool ignoreClangTarget) {
1046+
ClangImporter::getClangDriverArguments(ASTContext &ctx, bool ignoreClangTarget) {
1047+
assert(!ctx.ClangImporterOpts.DirectClangCC1ModuleBuild &&
1048+
"direct-clang-cc1-module-build should not call this function");
10441049
std::vector<std::string> invocationArgStrs;
10451050
// When creating from driver commands, clang expects this to be like an actual
10461051
// command line. So we need to pass in "clang" for argv[0]
1047-
if (!ctx.ClangImporterOpts.DirectClangCC1ModuleBuild)
1048-
invocationArgStrs.push_back(ctx.ClangImporterOpts.clangPath);
1049-
if (ctx.ClangImporterOpts.ExtraArgsOnly) {
1050-
invocationArgStrs.insert(invocationArgStrs.end(),
1051-
ctx.ClangImporterOpts.ExtraArgs.begin(),
1052-
ctx.ClangImporterOpts.ExtraArgs.end());
1053-
return invocationArgStrs;
1054-
}
1055-
1052+
invocationArgStrs.push_back(ctx.ClangImporterOpts.clangPath);
10561053
switch (ctx.ClangImporterOpts.Mode) {
10571054
case ClangImporterOptions::Modes::Normal:
10581055
case ClangImporterOptions::Modes::PrecompiledModule:
@@ -1066,69 +1063,59 @@ ClangImporter::getClangArguments(ASTContext &ctx, bool ignoreClangTarget) {
10661063
return invocationArgStrs;
10671064
}
10681065

1069-
std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
1070-
ClangImporter *importer, const ClangImporterOptions &importerOpts,
1066+
std::optional<std::vector<std::string>> ClangImporter::getClangCC1Arguments(
1067+
ClangImporter *importer, ASTContext &ctx,
10711068
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
1072-
ArrayRef<std::string> invocationArgStrs,
1073-
std::vector<std::string> *CC1Args) {
1074-
std::vector<const char *> invocationArgs;
1075-
invocationArgs.reserve(invocationArgStrs.size());
1076-
for (auto &argStr : invocationArgStrs)
1077-
invocationArgs.push_back(argStr.c_str());
1078-
1079-
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> clangDiags;
1080-
std::unique_ptr<clang::CompilerInvocation> CI;
1081-
if (importerOpts.DirectClangCC1ModuleBuild) {
1082-
// In this mode, we bypass createInvocationFromCommandLine, which goes
1083-
// through the Clang driver, and use strictly cc1 arguments to instantiate a
1084-
// clang Instance directly, assuming that the set of '-Xcc <X>' frontend flags is
1085-
// fully sufficient to do so.
1086-
1087-
// Because we are bypassing the Clang driver, we must populate
1088-
// the diagnostic options here explicitly.
1089-
std::unique_ptr<clang::DiagnosticOptions> clangDiagOpts =
1090-
clang::CreateAndPopulateDiagOpts(invocationArgs);
1091-
auto *diagClient = new ClangDiagnosticConsumer(
1092-
importer->Impl, *clangDiagOpts, importerOpts.DumpClangDiagnostics);
1093-
clangDiags = clang::CompilerInstance::createDiagnostics(
1094-
clangDiagOpts.release(), diagClient,
1095-
/*owned*/ true);
1096-
1097-
// Finally, use the CC1 command-line and the diagnostic engine
1098-
// to instantiate our Invocation.
1099-
CI = std::make_unique<clang::CompilerInvocation>();
1100-
if (!clang::CompilerInvocation::CreateFromArgs(
1101-
*CI, invocationArgs, *clangDiags, invocationArgs[0]))
1102-
return nullptr;
1103-
} else {
1104-
// Set up a temporary diagnostic client to report errors from parsing the
1105-
// command line, which may be important for Swift clients if, for example,
1106-
// they're using -Xcc options. Unfortunately this diagnostic engine has to
1107-
// use the default options because the /actual/ options haven't been parsed
1108-
// yet.
1109-
//
1110-
// The long-term client for Clang diagnostics is set up below, after the
1111-
// clang::CompilerInstance is created.
1112-
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
1113-
new clang::DiagnosticOptions};
1069+
bool ignoreClangTarget) {
1070+
// If using direct cc1 module build, return extra args only.
1071+
if (ctx.ClangImporterOpts.DirectClangCC1ModuleBuild)
1072+
return ctx.ClangImporterOpts.ExtraArgs;
1073+
1074+
// Otherwise, create cc1 arguments from driver args.
1075+
auto driverArgs = getClangDriverArguments(ctx, ignoreClangTarget);
1076+
1077+
llvm::SmallVector<const char *> invocationArgs;
1078+
invocationArgs.reserve(driverArgs.size());
1079+
llvm::for_each(driverArgs, [&](const std::string &Arg) {
1080+
invocationArgs.push_back(Arg.c_str());
1081+
});
11141082

1115-
auto *tempDiagClient = new ClangDiagnosticConsumer(
1116-
importer->Impl, *tempDiagOpts, importerOpts.DumpClangDiagnostics);
1117-
clangDiags = clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(),
1118-
tempDiagClient,
1119-
/*owned*/ true);
1120-
clang::CreateInvocationOptions CIOpts;
1121-
CIOpts.VFS = VFS;
1122-
CIOpts.Diags = clangDiags;
1123-
CIOpts.RecoverOnError = false;
1124-
CIOpts.CC1Args = CC1Args;
1125-
CIOpts.ProbePrecompiled = true;
1126-
CI = clang::createInvocation(invocationArgs, std::move(CIOpts));
1083+
if (ctx.ClangImporterOpts.DumpClangDiagnostics) {
1084+
llvm::errs() << "clang importer driver args: '";
1085+
llvm::interleave(
1086+
invocationArgs, [](StringRef arg) { llvm::errs() << arg; },
1087+
[] { llvm::errs() << "' '"; });
1088+
llvm::errs() << "'\n";
11271089
}
11281090

1129-
if (!CI) {
1130-
return CI;
1131-
}
1091+
// Set up a temporary diagnostic client to report errors from parsing the
1092+
// command line, which may be important for Swift clients if, for example,
1093+
// they're using -Xcc options. Unfortunately this diagnostic engine has to
1094+
// use the default options because the /actual/ options haven't been parsed
1095+
// yet.
1096+
//
1097+
// The long-term client for Clang diagnostics is set up afterwards, after the
1098+
// clang::CompilerInstance is created.
1099+
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
1100+
new clang::DiagnosticOptions};
1101+
1102+
auto *tempDiagClient =
1103+
new ClangDiagnosticConsumer(importer->Impl, *tempDiagOpts,
1104+
ctx.ClangImporterOpts.DumpClangDiagnostics);
1105+
1106+
auto clangDiags = clang::CompilerInstance::createDiagnostics(
1107+
tempDiagOpts.get(), tempDiagClient,
1108+
/*owned*/ true);
1109+
1110+
clang::CreateInvocationOptions CIOpts;
1111+
CIOpts.VFS = VFS;
1112+
CIOpts.Diags = clangDiags;
1113+
CIOpts.RecoverOnError = false;
1114+
CIOpts.ProbePrecompiled = true;
1115+
auto CI = clang::createInvocation(invocationArgs, std::move(CIOpts));
1116+
1117+
if (!CI)
1118+
return std::nullopt;
11321119

11331120
// FIXME: clang fails to generate a module if there is a `-fmodule-map-file`
11341121
// argument pointing to a missing file.
@@ -1155,6 +1142,35 @@ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
11551142
}
11561143
CI->getFrontendOpts().ModuleMapFiles = FilteredModuleMapFiles;
11571144

1145+
return CI->getCC1CommandLine();
1146+
}
1147+
1148+
std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
1149+
ClangImporter *importer, const ClangImporterOptions &importerOpts,
1150+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
1151+
std::vector<std::string> &CC1Args) {
1152+
std::vector<const char *> invocationArgs;
1153+
invocationArgs.reserve(CC1Args.size());
1154+
llvm::for_each(CC1Args, [&](const std::string &Arg) {
1155+
invocationArgs.push_back(Arg.c_str());
1156+
});
1157+
1158+
// Create a diagnostics engine for creating clang compiler invocation. The
1159+
// option here is either generated by dependency scanner or just round tripped
1160+
// from `getClangCC1Arguments` so we don't expect it to fail. Use a simple
1161+
// printing diagnostics consumer for debugging any unexpected error.
1162+
auto diagOpts = llvm::makeIntrusiveRefCnt<clang::DiagnosticOptions>();
1163+
clang::DiagnosticsEngine clangDiags(
1164+
new clang::DiagnosticIDs(), diagOpts,
1165+
new clang::TextDiagnosticPrinter(llvm::errs(), diagOpts.get()));
1166+
1167+
// Finally, use the CC1 command-line and the diagnostic engine
1168+
// to instantiate our Invocation.
1169+
auto CI = std::make_unique<clang::CompilerInvocation>();
1170+
if (!clang::CompilerInvocation::CreateFromArgs(
1171+
*CI, invocationArgs, clangDiags, importerOpts.clangPath.c_str()))
1172+
return nullptr;
1173+
11581174
return CI;
11591175
}
11601176

@@ -1204,21 +1220,24 @@ ClangImporter::create(ASTContext &ctx,
12041220

12051221
// Create a new Clang compiler invocation.
12061222
{
1207-
importer->Impl.ClangArgs = getClangArguments(ctx);
1208-
if (fileMapping.requiresBuiltinHeadersInSystemModules) {
1209-
importer->Impl.ClangArgs.push_back("-Xclang");
1223+
if (auto ClangArgs = getClangCC1Arguments(importer.get(), ctx, VFS))
1224+
importer->Impl.ClangArgs = *ClangArgs;
1225+
else
1226+
return nullptr;
1227+
1228+
if (fileMapping.requiresBuiltinHeadersInSystemModules)
12101229
importer->Impl.ClangArgs.push_back("-fbuiltin-headers-in-system-modules");
1211-
}
1230+
12121231
ArrayRef<std::string> invocationArgStrs = importer->Impl.ClangArgs;
12131232
if (importerOpts.DumpClangDiagnostics) {
1214-
llvm::errs() << "'";
1233+
llvm::errs() << "clang importer cc1 args: '";
12151234
llvm::interleave(
12161235
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
12171236
[] { llvm::errs() << "' '"; });
12181237
llvm::errs() << "'\n";
12191238
}
12201239
importer->Impl.Invocation = createClangInvocation(
1221-
importer.get(), importerOpts, VFS, invocationArgStrs);
1240+
importer.get(), importerOpts, VFS, importer->Impl.ClangArgs);
12221241
if (!importer->Impl.Invocation)
12231242
return nullptr;
12241243
}
@@ -1296,10 +1315,12 @@ ClangImporter::create(ASTContext &ctx,
12961315
if (ctx.LangOpts.ClangTarget.has_value()) {
12971316
// If '-clang-target' is set, create a mock invocation with the Swift triple
12981317
// to configure CodeGen and Target options for Swift compilation.
1299-
auto swiftTargetClangArgs = getClangArguments(ctx, true);
1300-
ArrayRef<std::string> invocationArgStrs = swiftTargetClangArgs;
1318+
auto swiftTargetClangArgs =
1319+
getClangCC1Arguments(importer.get(), ctx, VFS, true);
1320+
if (!swiftTargetClangArgs)
1321+
return nullptr;
13011322
auto swiftTargetClangInvocation = createClangInvocation(
1302-
importer.get(), importerOpts, VFS, invocationArgStrs);
1323+
importer.get(), importerOpts, VFS, *swiftTargetClangArgs);
13031324
if (!swiftTargetClangInvocation)
13041325
return nullptr;
13051326
importer->Impl.setSwiftTargetInfo(clang::TargetInfo::CreateTargetInfo(

lib/ClangImporter/ClangModuleDependencyScanner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ static void addSearchPathInvocationArguments(
8686
static std::vector<std::string> getClangDepScanningInvocationArguments(
8787
ASTContext &ctx, llvm::Optional<StringRef> sourceFileName = llvm::None) {
8888
std::vector<std::string> commandLineArgs =
89-
ClangImporter::getClangArguments(ctx);
89+
ClangImporter::getClangDriverArguments(ctx);
9090
addSearchPathInvocationArguments(commandLineArgs, ctx);
9191

9292
auto sourceFilePos = std::find(

test/ClangImporter/pcm-emit-direct-cc1-mode.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// RUN: %empty-directory(%t)
33
// RUN: %swift-frontend -emit-pcm -direct-clang-cc1-module-build -only-use-extra-clang-opts -module-name script -o %t/script.pcm %S/Inputs/custom-modules/module.modulemap -Xcc %S/Inputs/custom-modules/module.modulemap -Xcc -o -Xcc %t/script.pcm -Xcc -fmodules -Xcc -triple -Xcc %target-triple -Xcc -x -Xcc objective-c -dump-clang-diagnostics 2> %t.diags.txt
44

5-
// Sometimes returns a 1 exit code with no stdout or stderr or even an indication of which command failed.
6-
// XFAIL: OS=linux-gnu, OS=linux-android, OS=linux-androideabi
7-
85
// Verify some of the output of the -dump-pcm flag.
96
// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-DUMP
107
// CHECK-DUMP: Information for module file '{{.*}}/script.pcm':
@@ -13,7 +10,9 @@
1310

1411
// Verify that the clang command-line used is cc1
1512
// RUN: %FileCheck -check-prefix CHECK-CLANG -DTRIPLE=%target-triple %s < %t.diags.txt
16-
// CHECK-CLANG: '{{.*[/\\]}}module.modulemap' '-o' '{{.*[/\\]}}script.pcm' '-fmodules' '-triple' '[[TRIPLE]]' '-x' 'objective-c'
13+
// CHECK-CLANG: clang importer cc1 args
14+
// CHECK-CLANG-SAME: '{{.*[/\\]}}module.modulemap' '-o' '{{.*[/\\]}}script.pcm' '-fmodules' '-triple' '[[TRIPLE]]' '-x' 'objective-c'
15+
// CHECK-CLANG-NOT: clang importer driver args
1716

1817
import script
1918
var _ : ScriptTy

0 commit comments

Comments
 (0)