diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index b088e9b70d2d9..d82f8a430ba07 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -106,6 +106,8 @@ WARNING(warning_cannot_multithread_batch_mode,none, "ignoring -num-threads argument; cannot multithread batch mode", ()) ERROR(error_cannot_explicit_interface_build_in_mode,none, "'-explicit-interface-module-build' only supported when building a module from interface ('-compile-module-from-interface')'", ()) +ERROR(error_cannot_direct_cc1_pcm_build_in_mode,none, + "'-direct-clang-cc1-module-build' only supported when building a PCM ('-emit-pcm')'", ()) ERROR(error_unsupported_option_argument,none, "unsupported argument '%1' to option '%0'", (StringRef, StringRef)) ERROR(error_immediate_mode_missing_stdlib,none, diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 23587d0701b68..e81db66d70de2 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -808,6 +808,11 @@ namespace swift { /// contains the full option set. bool ExtraArgsOnly = false; + /// When building a PCM, rely on the Swift frontend's command-line -Xcc flags + /// to build the Clang module via Clang frontend directly, + /// and completly bypass the Clang driver. + bool DirectClangCC1ModuleBuild = false; + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index fbb1cc503df62..2d998953a0a6b 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -889,6 +889,10 @@ def explicit_interface_module_build : Flag<["-"], "explicit-interface-module-build">, HelpText<"Use the specified command-line to build the module from interface, instead of flags specified in the interface">; +def direct_clang_cc1_module_build : + Flag<["-"], "direct-clang-cc1-module-build">, + HelpText<"Use the specified -Xcc options to build a PCM by using Clang frontend directly, bypassing the Clang driver">; + def build_module_from_parseable_interface : Flag<["-"], "build-module-from-parseable-interface">, Alias, diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 0f8b57167ba19..8e2f14797b1e2 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -993,27 +993,51 @@ std::unique_ptr ClangImporter::createClangInvocation( invocationArgs.reserve(invocationArgStrs.size()); for (auto &argStr : invocationArgStrs) invocationArgs.push_back(argStr.c_str()); - // Set up a temporary diagnostic client to report errors from parsing the - // command line, which may be important for Swift clients if, for example, - // they're using -Xcc options. Unfortunately this diagnostic engine has to - // use the default options because the /actual/ options haven't been parsed - // yet. - // - // The long-term client for Clang diagnostics is set up below, after the - // clang::CompilerInstance is created. - llvm::IntrusiveRefCntPtr tempDiagOpts{ - new clang::DiagnosticOptions - }; - ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts, - importerOpts.DumpClangDiagnostics}; - llvm::IntrusiveRefCntPtr tempClangDiags = - clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(), - &tempDiagClient, - /*owned*/false); + llvm::IntrusiveRefCntPtr clangDiags; + std::unique_ptr CI; + if (importerOpts.DirectClangCC1ModuleBuild) { + // In this mode, we bypass createInvocationFromCommandLine, which goes + // through the Clang driver, and use strictly cc1 arguments to instantiate a + // clang Instance directly, assuming that the set of '-Xcc ' frontend flags is + // fully sufficient to do so. + + // Because we are bypassing the Clang driver, we must populate + // the diagnostic options here explicitly. + std::unique_ptr clangDiagOpts = + clang::CreateAndPopulateDiagOpts(invocationArgs); + ClangDiagnosticConsumer diagClient{importer->Impl, *clangDiagOpts, + importerOpts.DumpClangDiagnostics}; + clangDiags = clang::CompilerInstance::createDiagnostics( + clangDiagOpts.release(), &diagClient, + /*owned*/ false); + + // Finally, use the CC1 command-line and the diagnostic engine + // to instantiate our Invocation. + CI = std::make_unique(); + if (!clang::CompilerInvocation::CreateFromArgs( + *CI, invocationArgs, *clangDiags, invocationArgs[0])) + return nullptr; + } else { + // Set up a temporary diagnostic client to report errors from parsing the + // command line, which may be important for Swift clients if, for example, + // they're using -Xcc options. Unfortunately this diagnostic engine has to + // use the default options because the /actual/ options haven't been parsed + // yet. + // + // The long-term client for Clang diagnostics is set up below, after the + // clang::CompilerInstance is created. + llvm::IntrusiveRefCntPtr tempDiagOpts{ + new clang::DiagnosticOptions}; - auto CI = clang::createInvocationFromCommandLine( - invocationArgs, tempClangDiags, VFS, false, CC1Args); + ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts, + importerOpts.DumpClangDiagnostics}; + clangDiags = clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(), + &tempDiagClient, + /*owned*/ false); + CI = clang::createInvocationFromCommandLine(invocationArgs, clangDiags, VFS, + false, CC1Args); + } if (!CI) { return CI; @@ -1030,8 +1054,9 @@ std::unique_ptr ClangImporter::createClangInvocation( // rdar://77516546 is tracking that the clang importer should be more // resilient and provide a module even if there were building it. auto TempVFS = clang::createVFSFromCompilerInvocation( - *CI, *tempClangDiags, + *CI, *clangDiags, VFS ? VFS : importer->Impl.SwiftContext.SourceMgr.getFileSystem()); + std::vector FilteredModuleMapFiles; for (auto ModuleMapFile : CI->getFrontendOpts().ModuleMapFiles) { if (TempVFS->exists(ModuleMapFile)) { @@ -1058,13 +1083,11 @@ ClangImporter::create(ASTContext &ctx, if (importerOpts.DumpClangDiagnostics) { llvm::errs() << "'"; llvm::interleave( - invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; }, - [] { llvm::errs() << "' '"; }); + invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; }, + [] { llvm::errs() << "' '"; }); llvm::errs() << "'\n"; } - - if (isPCHFilenameExtension(importerOpts.BridgingHeader)) { importer->Impl.setSinglePCHImport(importerOpts.BridgingHeader); importer->Impl.IsReadingBridgingPCH = true; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 467a3017c0136..2a19e051f892b 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1273,6 +1273,7 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, } Opts.ExtraArgsOnly |= Args.hasArg(OPT_extra_clang_options_only); + Opts.DirectClangCC1ModuleBuild |= Args.hasArg(OPT_direct_clang_cc1_module_build); if (const Arg *A = Args.getLastArg(OPT_pch_output_dir)) { Opts.PrecompiledHeaderOutputDir = A->getValue(); diff --git a/test/ClangImporter/pcm-emit-direct-cc1-mode.swift b/test/ClangImporter/pcm-emit-direct-cc1-mode.swift new file mode 100644 index 0000000000000..75f09f06da779 --- /dev/null +++ b/test/ClangImporter/pcm-emit-direct-cc1-mode.swift @@ -0,0 +1,16 @@ +// Emit the explicit module. +// RUN: %empty-directory(%t) +// RUN: %swift-frontend -emit-pcm -direct-clang-cc1-module-build -only-use-extra-clang-opts -module-name script -o %t/script.pcm %S/Inputs/custom-modules/module.map -Xcc %S/Inputs/custom-modules/module.map -Xcc -o -Xcc %t/script.pcm -Xcc -fmodules -Xcc -triple -Xcc %target-triple -Xcc -x -Xcc objective-c -dump-clang-diagnostics 2> %t.diags.txt + +// Verify some of the output of the -dump-pcm flag. +// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-DUMP +// CHECK-DUMP: Information for module file '{{.*}}/script.pcm': +// CHECK-DUMP: Module name: script +// CHECK-DUMP: Module map file: {{.*[/\\]}}Inputs{{/|\\}}custom-modules{{/|\\}}module.map + +// Verify that the clang command-line used is cc1 +// RUN: %FileCheck -check-prefix CHECK-CLANG %s < %t.diags.txt +// CHECK-CLANG: '{{.*[/\\]}}clang'{{.*}}'-fmodules' + +import script +var _ : ScriptTy