Skip to content

Commit 4d09879

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 6812e61 commit 4d09879

File tree

4 files changed

+110
-81
lines changed

4 files changed

+110
-81
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: 98 additions & 76 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,25 @@ ClangImporter::create(ASTContext &ctx,
12041220

12051221
// Create a new Clang compiler invocation.
12061222
{
1207-
importer->Impl.ClangArgs = getClangArguments(ctx);
1223+
if (auto ClangArgs = getClangCC1Arguments(importer.get(), ctx, VFS))
1224+
importer->Impl.ClangArgs = *ClangArgs;
1225+
else
1226+
return nullptr;
1227+
12081228
if (fileMapping.requiresBuiltinHeadersInSystemModules) {
12091229
importer->Impl.ClangArgs.push_back("-Xclang");
12101230
importer->Impl.ClangArgs.push_back("-fbuiltin-headers-in-system-modules");
1211-
}
1231+
12121232
ArrayRef<std::string> invocationArgStrs = importer->Impl.ClangArgs;
12131233
if (importerOpts.DumpClangDiagnostics) {
1214-
llvm::errs() << "'";
1234+
llvm::errs() << "clang importer cc1 args: '";
12151235
llvm::interleave(
12161236
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
12171237
[] { llvm::errs() << "' '"; });
12181238
llvm::errs() << "'\n";
12191239
}
12201240
importer->Impl.Invocation = createClangInvocation(
1221-
importer.get(), importerOpts, VFS, invocationArgStrs);
1241+
importer.get(), importerOpts, VFS, importer->Impl.ClangArgs);
12221242
if (!importer->Impl.Invocation)
12231243
return nullptr;
12241244
}
@@ -1296,10 +1316,12 @@ ClangImporter::create(ASTContext &ctx,
12961316
if (ctx.LangOpts.ClangTarget.has_value()) {
12971317
// If '-clang-target' is set, create a mock invocation with the Swift triple
12981318
// to configure CodeGen and Target options for Swift compilation.
1299-
auto swiftTargetClangArgs = getClangArguments(ctx, true);
1300-
ArrayRef<std::string> invocationArgStrs = swiftTargetClangArgs;
1319+
auto swiftTargetClangArgs =
1320+
getClangCC1Arguments(importer.get(), ctx, VFS, true);
1321+
if (!swiftTargetClangArgs)
1322+
return nullptr;
13011323
auto swiftTargetClangInvocation = createClangInvocation(
1302-
importer.get(), importerOpts, VFS, invocationArgStrs);
1324+
importer.get(), importerOpts, VFS, *swiftTargetClangArgs);
13031325
if (!swiftTargetClangInvocation)
13041326
return nullptr;
13051327
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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
// Verify that the clang command-line used is cc1
1515
// 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'
16+
// CHECK-CLANG: clang importer cc1 args
17+
// CHECK-CLANG-SAME: '{{.*[/\\]}}module.modulemap' '-o' '{{.*[/\\]}}script.pcm' '-fmodules' '-triple' '[[TRIPLE]]' '-x' 'objective-c'
18+
// CHECK-CLANG-NOT: clang importer driver args
1719

1820
import script
1921
var _ : ScriptTy

0 commit comments

Comments
 (0)