52
52
#include " swift/Subsystems.h"
53
53
#include " clang/AST/ASTContext.h"
54
54
#include " clang/AST/Mangle.h"
55
+ #include " clang/Basic/DiagnosticOptions.h"
55
56
#include " clang/Basic/FileEntry.h"
56
57
#include " clang/Basic/IdentifierTable.h"
57
58
#include " clang/Basic/Module.h"
58
59
#include " clang/Basic/TargetInfo.h"
59
60
#include " clang/Basic/Version.h"
60
61
#include " clang/CodeGen/ObjectFilePCHContainerOperations.h"
61
62
#include " clang/Frontend/FrontendActions.h"
63
+ #include " clang/Frontend/TextDiagnosticPrinter.h"
62
64
#include " clang/Frontend/Utils.h"
63
65
#include " clang/Index/IndexingAction.h"
64
66
#include " clang/Lex/Preprocessor.h"
85
87
#include " llvm/TextAPI/TextAPIReader.h"
86
88
#include < algorithm>
87
89
#include < memory>
90
+ #include < optional>
88
91
#include < string>
89
92
90
93
using namespace swift ;
@@ -1040,19 +1043,13 @@ ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
1040
1043
}
1041
1044
1042
1045
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" );
1044
1049
std::vector<std::string> invocationArgStrs;
1045
1050
// When creating from driver commands, clang expects this to be like an actual
1046
1051
// 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 );
1056
1053
switch (ctx.ClangImporterOpts .Mode ) {
1057
1054
case ClangImporterOptions::Modes::Normal:
1058
1055
case ClangImporterOptions::Modes::PrecompiledModule:
@@ -1066,69 +1063,59 @@ ClangImporter::getClangArguments(ASTContext &ctx, bool ignoreClangTarget) {
1066
1063
return invocationArgStrs;
1067
1064
}
1068
1065
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 ,
1071
1068
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
+ });
1114
1082
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 " ;
1127
1089
}
1128
1090
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;
1132
1119
1133
1120
// FIXME: clang fails to generate a module if there is a `-fmodule-map-file`
1134
1121
// argument pointing to a missing file.
@@ -1155,6 +1142,35 @@ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
1155
1142
}
1156
1143
CI->getFrontendOpts ().ModuleMapFiles = FilteredModuleMapFiles;
1157
1144
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
+
1158
1174
return CI;
1159
1175
}
1160
1176
@@ -1204,21 +1220,24 @@ ClangImporter::create(ASTContext &ctx,
1204
1220
1205
1221
// Create a new Clang compiler invocation.
1206
1222
{
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 )
1210
1229
importer->Impl .ClangArgs .push_back (" -fbuiltin-headers-in-system-modules" );
1211
- }
1230
+
1212
1231
ArrayRef<std::string> invocationArgStrs = importer->Impl .ClangArgs ;
1213
1232
if (importerOpts.DumpClangDiagnostics ) {
1214
- llvm::errs () << " '" ;
1233
+ llvm::errs () << " clang importer cc1 args: '" ;
1215
1234
llvm::interleave (
1216
1235
invocationArgStrs, [](StringRef arg) { llvm::errs () << arg; },
1217
1236
[] { llvm::errs () << " ' '" ; });
1218
1237
llvm::errs () << " '\n " ;
1219
1238
}
1220
1239
importer->Impl .Invocation = createClangInvocation (
1221
- importer.get (), importerOpts, VFS, invocationArgStrs );
1240
+ importer.get (), importerOpts, VFS, importer-> Impl . ClangArgs );
1222
1241
if (!importer->Impl .Invocation )
1223
1242
return nullptr ;
1224
1243
}
@@ -1296,10 +1315,12 @@ ClangImporter::create(ASTContext &ctx,
1296
1315
if (ctx.LangOpts .ClangTarget .has_value ()) {
1297
1316
// If '-clang-target' is set, create a mock invocation with the Swift triple
1298
1317
// 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 ;
1301
1322
auto swiftTargetClangInvocation = createClangInvocation (
1302
- importer.get (), importerOpts, VFS, invocationArgStrs );
1323
+ importer.get (), importerOpts, VFS, *swiftTargetClangArgs );
1303
1324
if (!swiftTargetClangInvocation)
1304
1325
return nullptr ;
1305
1326
importer->Impl .setSwiftTargetInfo (clang::TargetInfo::CreateTargetInfo (
0 commit comments