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.
@@ -1146,7 +1133,12 @@ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
11461133
11471134 std::vector<std::string> FilteredModuleMapFiles;
11481135 for (auto ModuleMapFile : CI->getFrontendOpts ().ModuleMapFiles ) {
1149- if (TempVFS->exists (ModuleMapFile)) {
1136+ if (ctx.ClangImporterOpts .UseClangIncludeTree ) {
1137+ // There is no need to add any module map file here. Issue a warning and
1138+ // drop the option.
1139+ importer->Impl .diagnose (SourceLoc (), diag::module_map_ignored,
1140+ ModuleMapFile);
1141+ } else if (TempVFS->exists (ModuleMapFile)) {
11501142 FilteredModuleMapFiles.push_back (ModuleMapFile);
11511143 } else {
11521144 importer->Impl .diagnose (SourceLoc (), diag::module_map_not_found,
@@ -1155,6 +1147,35 @@ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
11551147 }
11561148 CI->getFrontendOpts ().ModuleMapFiles = FilteredModuleMapFiles;
11571149
1150+ return CI->getCC1CommandLine ();
1151+ }
1152+
1153+ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation (
1154+ ClangImporter *importer, const ClangImporterOptions &importerOpts,
1155+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
1156+ std::vector<std::string> &CC1Args) {
1157+ std::vector<const char *> invocationArgs;
1158+ invocationArgs.reserve (CC1Args.size ());
1159+ llvm::for_each (CC1Args, [&](const std::string &Arg) {
1160+ invocationArgs.push_back (Arg.c_str ());
1161+ });
1162+
1163+ // Create a diagnostics engine for creating clang compiler invocation. The
1164+ // option here is either generated by dependency scanner or just round tripped
1165+ // from `getClangCC1Arguments` so we don't expect it to fail. Use a simple
1166+ // printing diagnostics consumer for debugging any unexpected error.
1167+ auto diagOpts = llvm::makeIntrusiveRefCnt<clang::DiagnosticOptions>();
1168+ clang::DiagnosticsEngine clangDiags (
1169+ new clang::DiagnosticIDs (), diagOpts,
1170+ new clang::TextDiagnosticPrinter (llvm::errs (), diagOpts.get ()));
1171+
1172+ // Finally, use the CC1 command-line and the diagnostic engine
1173+ // to instantiate our Invocation.
1174+ auto CI = std::make_unique<clang::CompilerInvocation>();
1175+ if (!clang::CompilerInvocation::CreateFromArgs (
1176+ *CI, invocationArgs, clangDiags, importerOpts.clangPath .c_str ()))
1177+ return nullptr ;
1178+
11581179 return CI;
11591180}
11601181
@@ -1204,21 +1225,24 @@ ClangImporter::create(ASTContext &ctx,
12041225
12051226 // Create a new Clang compiler invocation.
12061227 {
1207- importer->Impl .ClangArgs = getClangArguments (ctx);
1208- if (fileMapping.requiresBuiltinHeadersInSystemModules ) {
1209- importer->Impl .ClangArgs .push_back (" -Xclang" );
1228+ if (auto ClangArgs = getClangCC1Arguments (importer.get (), ctx, VFS))
1229+ importer->Impl .ClangArgs = *ClangArgs;
1230+ else
1231+ return nullptr ;
1232+
1233+ if (fileMapping.requiresBuiltinHeadersInSystemModules )
12101234 importer->Impl .ClangArgs .push_back (" -fbuiltin-headers-in-system-modules" );
1211- }
1235+
12121236 ArrayRef<std::string> invocationArgStrs = importer->Impl .ClangArgs ;
12131237 if (importerOpts.DumpClangDiagnostics ) {
1214- llvm::errs () << " '" ;
1238+ llvm::errs () << " clang importer cc1 args: '" ;
12151239 llvm::interleave (
12161240 invocationArgStrs, [](StringRef arg) { llvm::errs () << arg; },
12171241 [] { llvm::errs () << " ' '" ; });
12181242 llvm::errs () << " '\n " ;
12191243 }
12201244 importer->Impl .Invocation = createClangInvocation (
1221- importer.get (), importerOpts, VFS, invocationArgStrs );
1245+ importer.get (), importerOpts, VFS, importer-> Impl . ClangArgs );
12221246 if (!importer->Impl .Invocation )
12231247 return nullptr ;
12241248 }
@@ -1296,10 +1320,12 @@ ClangImporter::create(ASTContext &ctx,
12961320 if (ctx.LangOpts .ClangTarget .has_value ()) {
12971321 // If '-clang-target' is set, create a mock invocation with the Swift triple
12981322 // to configure CodeGen and Target options for Swift compilation.
1299- auto swiftTargetClangArgs = getClangArguments (ctx, true );
1300- ArrayRef<std::string> invocationArgStrs = swiftTargetClangArgs;
1323+ auto swiftTargetClangArgs =
1324+ getClangCC1Arguments (importer.get (), ctx, VFS, true );
1325+ if (!swiftTargetClangArgs)
1326+ return nullptr ;
13011327 auto swiftTargetClangInvocation = createClangInvocation (
1302- importer.get (), importerOpts, VFS, invocationArgStrs );
1328+ importer.get (), importerOpts, VFS, *swiftTargetClangArgs );
13031329 if (!swiftTargetClangInvocation)
13041330 return nullptr ;
13051331 importer->Impl .setSwiftTargetInfo (clang::TargetInfo::CreateTargetInfo (
0 commit comments