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"
8587#include " llvm/TextAPI/TextAPIReader.h"
8688#include < algorithm>
8789#include < memory>
90+ #include < optional>
8891#include < string>
8992
9093using namespace swift ;
@@ -1040,19 +1043,13 @@ ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
10401043}
10411044
10421045std::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 (
0 commit comments