//===--- ClangModuleDependencyScanner.cpp - Dependency Scanning -----------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements dependency scanning for Clang modules. // //===----------------------------------------------------------------------===// #include "ImporterImpl.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ModuleDependencies.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Basic/Assertions.h" #include "clang/Basic/Diagnostic.h" #include "clang/CAS/CASOptions.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Path.h" #include "llvm/Support/StringSaver.h" using namespace swift; using namespace clang::tooling; using namespace clang::tooling::dependencies; static void addScannerPrefixMapperInvocationArguments( std::vector &invocationArgStrs, ASTContext &ctx) { for (const auto &arg : ctx.SearchPathOpts.ScannerPrefixMapper) { invocationArgStrs.push_back("-fdepscan-prefix-map"); invocationArgStrs.push_back(arg.first); invocationArgStrs.push_back(arg.second); } } /// Create the command line for Clang dependency scanning. std::vector ClangImporter::getClangDepScanningInvocationArguments( ASTContext &ctx) { std::vector commandLineArgs = getClangDriverArguments(ctx); addScannerPrefixMapperInvocationArguments(commandLineArgs, ctx); // HACK! Drop the -fmodule-format= argument and the one that // precedes it. { auto moduleFormatPos = std::find_if(commandLineArgs.begin(), commandLineArgs.end(), [](StringRef arg) { return arg.starts_with("-fmodule-format="); }); assert(moduleFormatPos != commandLineArgs.end()); assert(moduleFormatPos != commandLineArgs.begin()); commandLineArgs.erase(moduleFormatPos-1, moduleFormatPos+1); } // Use `-fsyntax-only` to do dependency scanning and assert if not there. assert(std::find(commandLineArgs.begin(), commandLineArgs.end(), "-fsyntax-only") != commandLineArgs.end() && "missing -fsyntax-only option"); // The Clang modules produced by ClangImporter are always embedded in an // ObjectFilePCHContainer and contain -gmodules debug info. commandLineArgs.push_back("-gmodules"); // To use -gmodules we need to have a real path for the PCH; this option has // no effect if caching is disabled. commandLineArgs.push_back("-Xclang"); commandLineArgs.push_back("-finclude-tree-preserve-pch-path"); return commandLineArgs; } void ClangImporter::getBridgingHeaderOptions( const ASTContext &ctx, const clang::tooling::dependencies::TranslationUnitDeps &deps, std::vector &swiftArgs) { auto addClangArg = [&](Twine arg) { swiftArgs.push_back("-Xcc"); swiftArgs.push_back(arg.str()); }; // We are using Swift frontend mode. swiftArgs.push_back("-frontend"); // Swift frontend action: -emit-pcm swiftArgs.push_back("-emit-pch"); // Ensure that the resulting PCM build invocation uses Clang frontend // directly swiftArgs.push_back("-direct-clang-cc1-module-build"); // Add args reported by the scanner. // Round-trip clang args to canonicalize and clear the options that swift // compiler doesn't need. clang::CompilerInvocation depsInvocation; clang::DiagnosticOptions diagOpts; clang::DiagnosticsEngine clangDiags(new clang::DiagnosticIDs(), diagOpts, new clang::IgnoringDiagConsumer()); llvm::SmallVector clangArgs; llvm::for_each(deps.Commands[0].Arguments, [&](const std::string &Arg) { clangArgs.push_back(Arg.c_str()); }); bool success = clang::CompilerInvocation::CreateFromArgs( depsInvocation, clangArgs, clangDiags); (void)success; assert(success && "clang option from dep scanner round trip failed"); // Clear the cache key for module. The module key is computed from clang // invocation, not swift invocation. depsInvocation.getFrontendOpts().ProgramAction = clang::frontend::ActionKind::GeneratePCH; depsInvocation.getFrontendOpts().ModuleCacheKeys.clear(); depsInvocation.getFrontendOpts().PathPrefixMappings.clear(); depsInvocation.getFrontendOpts().OutputFile.clear(); llvm::BumpPtrAllocator allocator; llvm::StringSaver saver(allocator); clangArgs.clear(); depsInvocation.generateCC1CommandLine( clangArgs, [&saver](const llvm::Twine &T) { return saver.save(T).data(); }); llvm::for_each(clangArgs, addClangArg); ctx.CASOpts.enumerateCASConfigurationFlags( [&](StringRef Arg) { swiftArgs.push_back(Arg.str()); }); if (auto Tree = deps.IncludeTreeID) { swiftArgs.push_back("-clang-include-tree-root"); swiftArgs.push_back(*Tree); } }