Skip to content

Commit 5dd0010

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 443cac2 commit 5dd0010

File tree

4 files changed

+109
-80
lines changed

4 files changed

+109
-80
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: 97 additions & 75 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,17 +1220,21 @@ 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
ArrayRef<std::string> invocationArgStrs = importer->Impl.ClangArgs;
12091229
if (importerOpts.DumpClangDiagnostics) {
1210-
llvm::errs() << "'";
1230+
llvm::errs() << "clang importer cc1 args: '";
12111231
llvm::interleave(
12121232
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
12131233
[] { llvm::errs() << "' '"; });
12141234
llvm::errs() << "'\n";
12151235
}
12161236
importer->Impl.Invocation = createClangInvocation(
1217-
importer.get(), importerOpts, VFS, invocationArgStrs);
1237+
importer.get(), importerOpts, VFS, importer->Impl.ClangArgs);
12181238
if (!importer->Impl.Invocation)
12191239
return nullptr;
12201240
}
@@ -1292,10 +1312,12 @@ ClangImporter::create(ASTContext &ctx,
12921312
if (ctx.LangOpts.ClangTarget.has_value()) {
12931313
// If '-clang-target' is set, create a mock invocation with the Swift triple
12941314
// to configure CodeGen and Target options for Swift compilation.
1295-
auto swiftTargetClangArgs = getClangArguments(ctx, true);
1296-
ArrayRef<std::string> invocationArgStrs = swiftTargetClangArgs;
1315+
auto swiftTargetClangArgs =
1316+
getClangCC1Arguments(importer.get(), ctx, VFS, true);
1317+
if (!swiftTargetClangArgs)
1318+
return nullptr;
12971319
auto swiftTargetClangInvocation = createClangInvocation(
1298-
importer.get(), importerOpts, VFS, invocationArgStrs);
1320+
importer.get(), importerOpts, VFS, *swiftTargetClangArgs);
12991321
if (!swiftTargetClangInvocation)
13001322
return nullptr;
13011323
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
@@ -10,7 +10,9 @@
1010

1111
// Verify that the clang command-line used is cc1
1212
// RUN: %FileCheck -check-prefix CHECK-CLANG -DTRIPLE=%target-triple %s < %t.diags.txt
13-
// 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
1416

1517
import script
1618
var _ : ScriptTy

0 commit comments

Comments
 (0)