From d663c11df761172f4a83db1cda373a1cd60ca6c6 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sat, 29 Oct 2022 17:32:25 -0700 Subject: [PATCH] IDE: Move compiler invocation utils to break cycle with Frontend. Move compiler invocation utilities up into the IDETool library which is designed to be dependent on the Frontend and FrontendTool libraries. --- include/swift/IDE/Utils.h | 25 +- include/swift/IDETool/CompileInstance.h | 1 - include/swift/IDETool/CompilerInvocation.h | 41 ++ lib/Frontend/CMakeLists.txt | 2 +- lib/FrontendTool/CMakeLists.txt | 1 + lib/IDE/Utils.cpp | 379 +---------------- lib/IDETool/CMakeLists.txt | 5 +- lib/IDETool/CompilerInvocation.cpp | 394 ++++++++++++++++++ .../lib/SwiftLang/SwiftASTManager.cpp | 3 +- .../lib/SwiftLang/SwiftEditorInterfaceGen.cpp | 2 +- tools/swift-ide-test/swift-ide-test.cpp | 1 + 11 files changed, 450 insertions(+), 404 deletions(-) create mode 100644 include/swift/IDETool/CompilerInvocation.h create mode 100644 lib/IDETool/CompilerInvocation.cpp diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index a549d3ecb425f..fab001e9f59b7 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -13,21 +13,20 @@ #ifndef SWIFT_IDE_UTILS_H #define SWIFT_IDE_UTILS_H -#include "llvm/ADT/PointerIntPair.h" -#include "swift/Basic/LLVM.h" #include "swift/AST/ASTNode.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/DeclNameLoc.h" #include "swift/AST/Effects.h" #include "swift/AST/Module.h" -#include "swift/AST/ASTPrinter.h" -#include "swift/Frontend/FrontendOptions.h" +#include "swift/Basic/LLVM.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/Parse/Token.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" +#include #include #include -#include #include namespace llvm { @@ -41,10 +40,8 @@ namespace clang { } namespace swift { - class ModuleDecl; class ValueDecl; class ASTContext; - class CompilerInvocation; class SourceFile; class TypeDecl; class SourceLoc; @@ -84,20 +81,6 @@ SourceCompleteResult isSourceInputComplete(std::unique_ptr MemBuf, SourceFileKind SFKind); SourceCompleteResult isSourceInputComplete(StringRef Text, SourceFileKind SFKind); -bool initCompilerInvocation( - CompilerInvocation &Invocation, ArrayRef OrigArgs, - FrontendOptions::ActionType Action, DiagnosticEngine &Diags, - StringRef UnresolvedPrimaryFile, - llvm::IntrusiveRefCntPtr FileSystem, - const std::string &swiftExecutablePath, - const std::string &runtimeResourcePath, - const std::string &diagnosticDocumentationPath, time_t sessionTimestamp, - std::string &Error); - -bool initInvocationByClangArguments(ArrayRef ArgList, - CompilerInvocation &Invok, - std::string &Error); - /// Visits all overridden declarations exhaustively from VD, including protocol /// conformances and clang declarations. void walkOverriddenDecls(const ValueDecl *VD, diff --git a/include/swift/IDETool/CompileInstance.h b/include/swift/IDETool/CompileInstance.h index 728e66336b10d..4c39a9b5e52a1 100644 --- a/include/swift/IDETool/CompileInstance.h +++ b/include/swift/IDETool/CompileInstance.h @@ -24,7 +24,6 @@ namespace swift { class CompilerInstance; -class CompilerInvocation; class DiagnosticConsumer; namespace ide { diff --git a/include/swift/IDETool/CompilerInvocation.h b/include/swift/IDETool/CompilerInvocation.h new file mode 100644 index 0000000000000..4b15f322f9933 --- /dev/null +++ b/include/swift/IDETool/CompilerInvocation.h @@ -0,0 +1,41 @@ +//===--- CompilerInvocation.h ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_COMPILERINVOCATION_H +#define SWIFT_IDE_COMPILERINVOCATION_H + +#include "swift/Frontend/Frontend.h" + +namespace swift { + +class CompilerInvocation; + +namespace ide { + +bool initCompilerInvocation( + CompilerInvocation &Invocation, ArrayRef OrigArgs, + FrontendOptions::ActionType Action, DiagnosticEngine &Diags, + StringRef UnresolvedPrimaryFile, + llvm::IntrusiveRefCntPtr FileSystem, + const std::string &swiftExecutablePath, + const std::string &runtimeResourcePath, + const std::string &diagnosticDocumentationPath, time_t sessionTimestamp, + std::string &Error); + +bool initInvocationByClangArguments(ArrayRef ArgList, + CompilerInvocation &Invok, + std::string &Error); + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_COMPILERINVOCATION_H diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 5b183f732d2df..ba2de920558dc 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -19,8 +19,8 @@ add_dependencies(swiftFrontend target_link_libraries(swiftFrontend PRIVATE swiftAST swiftConstExtract + swiftIDE swiftSIL - swiftMigrator swiftOption swiftSILGen swiftSILOptimizer diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 114b6e28bdbb4..3fcc7a6867b1e 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -20,6 +20,7 @@ target_link_libraries(swiftFrontendTool PRIVATE swiftImmediate swiftIndex swiftIRGen + swiftMigrator swiftOption swiftPrintAsClang swiftSerialization diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index a5bad2394d4c9..028d8e42ee424 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -12,22 +12,13 @@ #include "swift/IDE/Utils.h" #include "swift/Basic/Edit.h" -#include "swift/Basic/SourceManager.h" #include "swift/Basic/Platform.h" +#include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangModule.h" -#include "swift/Driver/FrontendUtil.h" -#include "swift/Frontend/Frontend.h" #include "swift/Parse/Parser.h" #include "swift/Subsystems.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/TextDiagnosticBuffer.h" -#include "clang/Lex/PreprocessorOptions.h" #include "clang/Rewrite/Core/RewriteBuffer.h" -#include "clang/Serialization/ASTReader.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" @@ -190,374 +181,6 @@ ide::isSourceInputComplete(StringRef Text,SourceFileKind SFKind) { SFKind); } -static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( - FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile, - llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { - assert(FileSystem); - - llvm::SmallString<128> PrimaryFile; - if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile)) - PrimaryFile = UnresolvedPrimaryFile; - - unsigned primaryCount = 0; - // FIXME: The frontend should be dealing with symlinks, maybe similar to - // clang's FileManager ? - FrontendInputsAndOutputs replacementInputsAndOutputs; - for (const InputFile &input : inputsAndOutputs.getAllInputs()) { - llvm::SmallString<128> newFilename; - if (auto err = FileSystem->getRealPath(input.getFileName(), newFilename)) - newFilename = input.getFileName(); - llvm::sys::path::native(newFilename); - bool newIsPrimary = input.isPrimary() || - (!PrimaryFile.empty() && PrimaryFile == newFilename); - if (newIsPrimary) { - ++primaryCount; - } - assert(primaryCount < 2 && "cannot handle multiple primaries"); - - replacementInputsAndOutputs.addInput( - InputFile(newFilename.str(), newIsPrimary, input.getBuffer())); - } - - if (PrimaryFile.empty() || primaryCount == 1) { - return replacementInputsAndOutputs; - } - - llvm::SmallString<64> Err; - llvm::raw_svector_ostream OS(Err); - OS << "'" << PrimaryFile << "' is not part of the input files"; - Error = std::string(OS.str()); - return replacementInputsAndOutputs; -} - -static void disableExpensiveSILOptions(SILOptions &Opts) { - // Disable the sanitizers. - Opts.Sanitizers = {}; - - // Disable PGO and code coverage. - Opts.GenerateProfile = false; - Opts.EmitProfileCoverageMapping = false; - Opts.UseProfile = ""; -} - -namespace { -class StreamDiagConsumer : public DiagnosticConsumer { - llvm::raw_ostream &OS; - -public: - StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {} - - void handleDiagnostic(SourceManager &SM, - const DiagnosticInfo &Info) override { - // FIXME: Print location info if available. - switch (Info.Kind) { - case DiagnosticKind::Error: - OS << "error: "; - break; - case DiagnosticKind::Warning: - OS << "warning: "; - break; - case DiagnosticKind::Note: - OS << "note: "; - break; - case DiagnosticKind::Remark: - OS << "remark: "; - break; - } - DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, - Info.FormatArgs); - } -}; -} // end anonymous namespace - -bool ide::initCompilerInvocation( - CompilerInvocation &Invocation, ArrayRef OrigArgs, - FrontendOptions::ActionType Action, DiagnosticEngine &Diags, - StringRef UnresolvedPrimaryFile, - llvm::IntrusiveRefCntPtr FileSystem, - const std::string &swiftExecutablePath, - const std::string &runtimeResourcePath, - const std::string &diagnosticDocumentationPath, time_t sessionTimestamp, - std::string &Error) { - SmallVector Args; - // Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at - // the top to allow overriding them with the passed in arguments. - Args.push_back("-resource-dir"); - Args.push_back(runtimeResourcePath.c_str()); - Args.push_back("-Xfrontend"); - Args.push_back("-diagnostic-documentation-path"); - Args.push_back("-Xfrontend"); - Args.push_back(diagnosticDocumentationPath.c_str()); - Args.append(OrigArgs.begin(), OrigArgs.end()); - - SmallString<32> ErrStr; - llvm::raw_svector_ostream ErrOS(ErrStr); - StreamDiagConsumer DiagConsumer(ErrOS); - Diags.addConsumer(DiagConsumer); - - bool InvocationCreationFailed = - driver::getSingleFrontendInvocationFromDriverArguments( - Args, Diags, - [&](ArrayRef FrontendArgs) { - return Invocation.parseArgs( - FrontendArgs, Diags, /*ConfigurationFileBuffers=*/nullptr, - /*workingDirectory=*/"", swiftExecutablePath); - }, - /*ForceNoOutputs=*/true); - - // Remove the StreamDiagConsumer as it's no longer needed. - Diags.removeConsumer(DiagConsumer); - - Error = std::string(ErrOS.str()); - if (InvocationCreationFailed) { - return true; - } - - std::string SymlinkResolveError; - Invocation.getFrontendOptions().InputsAndOutputs = - resolveSymbolicLinksInInputs( - Invocation.getFrontendOptions().InputsAndOutputs, - UnresolvedPrimaryFile, FileSystem, SymlinkResolveError); - - // SourceKit functionalities want to proceed even if there are missing inputs. - Invocation.getFrontendOptions().InputsAndOutputs - .setShouldRecoverMissingInputs(); - - if (!SymlinkResolveError.empty()) { - // resolveSymbolicLinksInInputs fails if the unresolved primary file is not - // in the input files. We can't recover from that. - Error += SymlinkResolveError; - return true; - } - - ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions(); - ImporterOpts.DetailedPreprocessingRecord = true; - - assert(!Invocation.getModuleName().empty()); - - auto &LangOpts = Invocation.getLangOptions(); - LangOpts.AttachCommentsToDecls = true; - LangOpts.DiagnosticsEditorMode = true; - LangOpts.CollectParsedToken = true; - if (LangOpts.PlaygroundTransform) { - // The playground instrumenter changes the AST in ways that disrupt the - // SourceKit functionality. Since we don't need the instrumenter, and all we - // actually need is the playground semantics visible to the user, like - // silencing the "expression resolves to an unused l-value" error, disable it. - LangOpts.PlaygroundTransform = false; - } - - // Disable the index-store functionality for the sourcekitd requests. - auto &FrontendOpts = Invocation.getFrontendOptions(); - FrontendOpts.IndexStorePath.clear(); - ImporterOpts.IndexStorePath.clear(); - - FrontendOpts.RequestedAction = Action; - - // We don't care about LLVMArgs - FrontendOpts.LLVMArgs.clear(); - - // To save the time for module validation, consider the lifetime of ASTManager - // as a single build session. - // NOTE: Do this only if '-disable-modules-validate-system-headers' is *not* - // explicitly enabled. - auto &SearchPathOpts = Invocation.getSearchPathOptions(); - if (!SearchPathOpts.DisableModulesValidateSystemDependencies) { - // NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may - // cause unnecessary validations if they happens within one second - // from the SourceKit startup. - ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" + - std::to_string(sessionTimestamp - 1)); - ImporterOpts.ExtraArgs.push_back( - "-fmodules-validate-once-per-build-session"); - - SearchPathOpts.DisableModulesValidateSystemDependencies = true; - } - - // Disable expensive SIL options to reduce time spent in SILGen. - disableExpensiveSILOptions(Invocation.getSILOptions()); - - return false; -} - -// Adjust the cc1 triple string we got from clang, to make sure it will be -// accepted when it goes through the swift clang importer. -static std::string adjustClangTriple(StringRef TripleStr) { - std::string Result; - llvm::raw_string_ostream OS(Result); - - llvm::Triple Triple(TripleStr); - switch (Triple.getSubArch()) { - case llvm::Triple::SubArchType::ARMSubArch_v7: - OS << "armv7"; break; - case llvm::Triple::SubArchType::ARMSubArch_v7s: - OS << "armv7s"; break; - case llvm::Triple::SubArchType::ARMSubArch_v7k: - OS << "armv7k"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6: - OS << "armv6"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6m: - OS << "armv6m"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6k: - OS << "armv6k"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6t2: - OS << "armv6t2"; break; - case llvm::Triple::SubArchType::ARMSubArch_v5: - OS << "armv5"; break; - case llvm::Triple::SubArchType::ARMSubArch_v5te: - OS << "armv5te"; break; - default: - // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386. - if ((Triple.getOS() == llvm::Triple::MacOSX || - Triple.getOS() == llvm::Triple::Darwin) && Triple.getArch() == llvm::Triple::x86) { - OS << "x86_64"; - } else { - OS << Triple.getArchName(); - } - break; - } - OS << '-' << Triple.getVendorName() << '-' << - Triple.getOSAndEnvironmentName(); - OS.flush(); - return Result; -} - -bool ide::initInvocationByClangArguments(ArrayRef ArgList, - CompilerInvocation &Invok, - std::string &Error) { - llvm::IntrusiveRefCntPtr DiagOpts{ - new clang::DiagnosticOptions() - }; - - clang::TextDiagnosticBuffer DiagBuf; - llvm::IntrusiveRefCntPtr ClangDiags = - clang::CompilerInstance::createDiagnostics(DiagOpts.get(), - &DiagBuf, - /*ShouldOwnClient=*/false); - - // Clang expects this to be like an actual command line. So we need to pass in - // "clang" for argv[0]. - std::vector ClangArgList; - ClangArgList.push_back("clang"); - ClangArgList.insert(ClangArgList.end(), ArgList.begin(), ArgList.end()); - - // Create a new Clang compiler invocation. - std::unique_ptr ClangInvok = - clang::createInvocationFromCommandLine(ClangArgList, ClangDiags); - if (!ClangInvok || ClangDiags->hasErrorOccurred()) { - for (auto I = DiagBuf.err_begin(), E = DiagBuf.err_end(); I != E; ++I) { - Error += I->second; - Error += " "; - } - return true; - } - - auto &PPOpts = ClangInvok->getPreprocessorOpts(); - auto &HSOpts = ClangInvok->getHeaderSearchOpts(); - - Invok.setTargetTriple(adjustClangTriple(ClangInvok->getTargetOpts().Triple)); - if (!HSOpts.Sysroot.empty()) - Invok.setSDKPath(HSOpts.Sysroot); - if (!HSOpts.ModuleCachePath.empty()) - Invok.setClangModuleCachePath(HSOpts.ModuleCachePath); - - auto &CCArgs = Invok.getClangImporterOptions().ExtraArgs; - for (auto MacroEntry : PPOpts.Macros) { - std::string MacroFlag; - if (MacroEntry.second) - MacroFlag += "-U"; - else - MacroFlag += "-D"; - MacroFlag += MacroEntry.first; - CCArgs.push_back(MacroFlag); - } - - for (auto &Entry : HSOpts.UserEntries) { - switch (Entry.Group) { - case clang::frontend::Quoted: - CCArgs.push_back("-iquote"); - CCArgs.push_back(Entry.Path); - break; - case clang::frontend::IndexHeaderMap: - CCArgs.push_back("-index-header-map"); - LLVM_FALLTHROUGH; - case clang::frontend::Angled: { - std::string Flag; - if (Entry.IsFramework) - Flag += "-F"; - else - Flag += "-I"; - Flag += Entry.Path; - CCArgs.push_back(Flag); - break; - } - case clang::frontend::System: - if (Entry.IsFramework) - CCArgs.push_back("-iframework"); - else - CCArgs.push_back("-isystem"); - CCArgs.push_back(Entry.Path); - break; - case clang::frontend::ExternCSystem: - case clang::frontend::CSystem: - case clang::frontend::CXXSystem: - case clang::frontend::ObjCSystem: - case clang::frontend::ObjCXXSystem: - case clang::frontend::After: - break; - } - } - - if (!PPOpts.ImplicitPCHInclude.empty()) { - clang::FileSystemOptions FileSysOpts; - clang::FileManager FileMgr(FileSysOpts); - auto PCHContainerOperations = - std::make_shared(); - std::string HeaderFile = clang::ASTReader::getOriginalSourceFile( - PPOpts.ImplicitPCHInclude, FileMgr, - PCHContainerOperations->getRawReader(), *ClangDiags); - if (!HeaderFile.empty()) { - CCArgs.push_back("-include"); - CCArgs.push_back(std::move(HeaderFile)); - } - } - for (auto &Header : PPOpts.Includes) { - CCArgs.push_back("-include"); - CCArgs.push_back(Header); - } - - for (auto &Entry : HSOpts.ModulesIgnoreMacros) { - std::string Flag = "-fmodules-ignore-macro="; - Flag += Entry; - CCArgs.push_back(Flag); - } - - for (auto &Entry : HSOpts.VFSOverlayFiles) { - CCArgs.push_back("-ivfsoverlay"); - CCArgs.push_back(Entry); - } - - if (!ClangInvok->getLangOpts()->isCompilingModule()) { - CCArgs.push_back("-Xclang"); - llvm::SmallString<64> Str; - Str += "-fmodule-name="; - Str += ClangInvok->getLangOpts()->CurrentModule; - CCArgs.push_back(std::string(Str.str())); - } - - if (PPOpts.DetailedRecord) { - Invok.getClangImporterOptions().DetailedPreprocessingRecord = true; - } - - if (!ClangInvok->getFrontendOpts().Inputs.empty()) { - Invok.getFrontendOptions().ImplicitObjCHeaderPath = - ClangInvok->getFrontendOpts().Inputs[0].getFile().str(); - } - - return false; -} - template static void walkOverriddenClangDecls(const clang::NamedDecl *D, const FnTy &Fn){ SmallVector OverDecls; diff --git a/lib/IDETool/CMakeLists.txt b/lib/IDETool/CMakeLists.txt index 28ce561a7b26d..0dcd3560c2d44 100644 --- a/lib/IDETool/CMakeLists.txt +++ b/lib/IDETool/CMakeLists.txt @@ -1,6 +1,7 @@ add_swift_host_library(swiftIDETool STATIC CompileInstance.cpp + CompilerInvocation.cpp CompletionInstance.cpp DependencyChecking.cpp ) @@ -9,6 +10,8 @@ target_link_libraries(swiftIDETool PRIVATE swiftAST swiftDriver swiftFrontend - swiftFrontendTool) + swiftFrontendTool + clangAST + clangFrontend) set_swift_llvm_is_available(swiftIDETool) diff --git a/lib/IDETool/CompilerInvocation.cpp b/lib/IDETool/CompilerInvocation.cpp new file mode 100644 index 0000000000000..d4fa6e160a195 --- /dev/null +++ b/lib/IDETool/CompilerInvocation.cpp @@ -0,0 +1,394 @@ +//===--- CompilerInvocation.cpp - Compiler invocation utilities -----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/IDETool/CompilerInvocation.h" + +#include "swift/Driver/FrontendUtil.h" +#include "swift/Frontend/Frontend.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/ASTReader.h" + +using namespace swift; + +static void disableExpensiveSILOptions(SILOptions &Opts) { + // Disable the sanitizers. + Opts.Sanitizers = {}; + + // Disable PGO and code coverage. + Opts.GenerateProfile = false; + Opts.EmitProfileCoverageMapping = false; + Opts.UseProfile = ""; +} + +// Adjust the cc1 triple string we got from clang, to make sure it will be +// accepted when it goes through the swift clang importer. +static std::string adjustClangTriple(StringRef TripleStr) { + std::string Result; + llvm::raw_string_ostream OS(Result); + + llvm::Triple Triple(TripleStr); + switch (Triple.getSubArch()) { + case llvm::Triple::SubArchType::ARMSubArch_v7: + OS << "armv7"; break; + case llvm::Triple::SubArchType::ARMSubArch_v7s: + OS << "armv7s"; break; + case llvm::Triple::SubArchType::ARMSubArch_v7k: + OS << "armv7k"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6: + OS << "armv6"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6m: + OS << "armv6m"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6k: + OS << "armv6k"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6t2: + OS << "armv6t2"; break; + case llvm::Triple::SubArchType::ARMSubArch_v5: + OS << "armv5"; break; + case llvm::Triple::SubArchType::ARMSubArch_v5te: + OS << "armv5te"; break; + default: + // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386. + if ((Triple.getOS() == llvm::Triple::MacOSX || + Triple.getOS() == llvm::Triple::Darwin) && + Triple.getArch() == llvm::Triple::x86) { + OS << "x86_64"; + } else { + OS << Triple.getArchName(); + } + break; + } + OS << '-' << Triple.getVendorName() << '-' + << Triple.getOSAndEnvironmentName(); + OS.flush(); + return Result; +} + +static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( + FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile, + llvm::IntrusiveRefCntPtr FileSystem, + std::string &Error) { + assert(FileSystem); + + llvm::SmallString<128> PrimaryFile; + if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile)) + PrimaryFile = UnresolvedPrimaryFile; + + unsigned primaryCount = 0; + // FIXME: The frontend should be dealing with symlinks, maybe similar to + // clang's FileManager ? + FrontendInputsAndOutputs replacementInputsAndOutputs; + for (const InputFile &input : inputsAndOutputs.getAllInputs()) { + llvm::SmallString<128> newFilename; + if (auto err = FileSystem->getRealPath(input.getFileName(), newFilename)) + newFilename = input.getFileName(); + llvm::sys::path::native(newFilename); + bool newIsPrimary = input.isPrimary() || + (!PrimaryFile.empty() && PrimaryFile == newFilename); + if (newIsPrimary) { + ++primaryCount; + } + assert(primaryCount < 2 && "cannot handle multiple primaries"); + + replacementInputsAndOutputs.addInput( + InputFile(newFilename.str(), newIsPrimary, input.getBuffer())); + } + + if (PrimaryFile.empty() || primaryCount == 1) { + return replacementInputsAndOutputs; + } + + llvm::SmallString<64> Err; + llvm::raw_svector_ostream OS(Err); + OS << "'" << PrimaryFile << "' is not part of the input files"; + Error = std::string(OS.str()); + return replacementInputsAndOutputs; +} + +namespace { +class StreamDiagConsumer : public DiagnosticConsumer { + llvm::raw_ostream &OS; + +public: + StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {} + + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { + // FIXME: Print location info if available. + switch (Info.Kind) { + case DiagnosticKind::Error: + OS << "error: "; + break; + case DiagnosticKind::Warning: + OS << "warning: "; + break; + case DiagnosticKind::Note: + OS << "note: "; + break; + case DiagnosticKind::Remark: + OS << "remark: "; + break; + } + DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, + Info.FormatArgs); + } +}; +} // end anonymous namespace + +bool ide::initCompilerInvocation( + CompilerInvocation &Invocation, ArrayRef OrigArgs, + FrontendOptions::ActionType Action, DiagnosticEngine &Diags, + StringRef UnresolvedPrimaryFile, + llvm::IntrusiveRefCntPtr FileSystem, + const std::string &swiftExecutablePath, + const std::string &runtimeResourcePath, + const std::string &diagnosticDocumentationPath, time_t sessionTimestamp, + std::string &Error) { + SmallVector Args; + // Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at + // the top to allow overriding them with the passed in arguments. + Args.push_back("-resource-dir"); + Args.push_back(runtimeResourcePath.c_str()); + Args.push_back("-Xfrontend"); + Args.push_back("-diagnostic-documentation-path"); + Args.push_back("-Xfrontend"); + Args.push_back(diagnosticDocumentationPath.c_str()); + Args.append(OrigArgs.begin(), OrigArgs.end()); + + SmallString<32> ErrStr; + llvm::raw_svector_ostream ErrOS(ErrStr); + StreamDiagConsumer DiagConsumer(ErrOS); + Diags.addConsumer(DiagConsumer); + + bool InvocationCreationFailed = + driver::getSingleFrontendInvocationFromDriverArguments( + Args, Diags, + [&](ArrayRef FrontendArgs) { + return Invocation.parseArgs( + FrontendArgs, Diags, /*ConfigurationFileBuffers=*/nullptr, + /*workingDirectory=*/"", swiftExecutablePath); + }, + /*ForceNoOutputs=*/true); + + // Remove the StreamDiagConsumer as it's no longer needed. + Diags.removeConsumer(DiagConsumer); + + Error = std::string(ErrOS.str()); + if (InvocationCreationFailed) { + return true; + } + + std::string SymlinkResolveError; + Invocation.getFrontendOptions().InputsAndOutputs = + resolveSymbolicLinksInInputs( + Invocation.getFrontendOptions().InputsAndOutputs, + UnresolvedPrimaryFile, FileSystem, SymlinkResolveError); + + // SourceKit functionalities want to proceed even if there are missing inputs. + Invocation.getFrontendOptions() + .InputsAndOutputs.setShouldRecoverMissingInputs(); + + if (!SymlinkResolveError.empty()) { + // resolveSymbolicLinksInInputs fails if the unresolved primary file is not + // in the input files. We can't recover from that. + Error += SymlinkResolveError; + return true; + } + + ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions(); + ImporterOpts.DetailedPreprocessingRecord = true; + + assert(!Invocation.getModuleName().empty()); + + auto &LangOpts = Invocation.getLangOptions(); + LangOpts.AttachCommentsToDecls = true; + LangOpts.DiagnosticsEditorMode = true; + LangOpts.CollectParsedToken = true; + if (LangOpts.PlaygroundTransform) { + // The playground instrumenter changes the AST in ways that disrupt the + // SourceKit functionality. Since we don't need the instrumenter, and all we + // actually need is the playground semantics visible to the user, like + // silencing the "expression resolves to an unused l-value" error, disable + // it. + LangOpts.PlaygroundTransform = false; + } + + // Disable the index-store functionality for the sourcekitd requests. + auto &FrontendOpts = Invocation.getFrontendOptions(); + FrontendOpts.IndexStorePath.clear(); + ImporterOpts.IndexStorePath.clear(); + + FrontendOpts.RequestedAction = Action; + + // We don't care about LLVMArgs + FrontendOpts.LLVMArgs.clear(); + + // To save the time for module validation, consider the lifetime of ASTManager + // as a single build session. + // NOTE: Do this only if '-disable-modules-validate-system-headers' is *not* + // explicitly enabled. + auto &SearchPathOpts = Invocation.getSearchPathOptions(); + if (!SearchPathOpts.DisableModulesValidateSystemDependencies) { + // NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may + // cause unnecessary validations if they happens within one second + // from the SourceKit startup. + ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" + + std::to_string(sessionTimestamp - 1)); + ImporterOpts.ExtraArgs.push_back( + "-fmodules-validate-once-per-build-session"); + + SearchPathOpts.DisableModulesValidateSystemDependencies = true; + } + + // Disable expensive SIL options to reduce time spent in SILGen. + disableExpensiveSILOptions(Invocation.getSILOptions()); + + return false; +} + +bool ide::initInvocationByClangArguments(ArrayRef ArgList, + CompilerInvocation &Invok, + std::string &Error) { + llvm::IntrusiveRefCntPtr DiagOpts{ + new clang::DiagnosticOptions() + }; + + clang::TextDiagnosticBuffer DiagBuf; + llvm::IntrusiveRefCntPtr ClangDiags = + clang::CompilerInstance::createDiagnostics(DiagOpts.get(), &DiagBuf, + /*ShouldOwnClient=*/false); + + // Clang expects this to be like an actual command line. So we need to pass in + // "clang" for argv[0]. + std::vector ClangArgList; + ClangArgList.push_back("clang"); + ClangArgList.insert(ClangArgList.end(), ArgList.begin(), ArgList.end()); + + // Create a new Clang compiler invocation. + std::unique_ptr ClangInvok = + clang::createInvocationFromCommandLine(ClangArgList, ClangDiags); + if (!ClangInvok || ClangDiags->hasErrorOccurred()) { + for (auto I = DiagBuf.err_begin(), E = DiagBuf.err_end(); I != E; ++I) { + Error += I->second; + Error += " "; + } + return true; + } + + auto &PPOpts = ClangInvok->getPreprocessorOpts(); + auto &HSOpts = ClangInvok->getHeaderSearchOpts(); + + Invok.setTargetTriple(adjustClangTriple(ClangInvok->getTargetOpts().Triple)); + if (!HSOpts.Sysroot.empty()) + Invok.setSDKPath(HSOpts.Sysroot); + if (!HSOpts.ModuleCachePath.empty()) + Invok.setClangModuleCachePath(HSOpts.ModuleCachePath); + + auto &CCArgs = Invok.getClangImporterOptions().ExtraArgs; + for (auto MacroEntry : PPOpts.Macros) { + std::string MacroFlag; + if (MacroEntry.second) + MacroFlag += "-U"; + else + MacroFlag += "-D"; + MacroFlag += MacroEntry.first; + CCArgs.push_back(MacroFlag); + } + + for (auto &Entry : HSOpts.UserEntries) { + switch (Entry.Group) { + case clang::frontend::Quoted: + CCArgs.push_back("-iquote"); + CCArgs.push_back(Entry.Path); + break; + case clang::frontend::IndexHeaderMap: + CCArgs.push_back("-index-header-map"); + LLVM_FALLTHROUGH; + case clang::frontend::Angled: { + std::string Flag; + if (Entry.IsFramework) + Flag += "-F"; + else + Flag += "-I"; + Flag += Entry.Path; + CCArgs.push_back(Flag); + break; + } + case clang::frontend::System: + if (Entry.IsFramework) + CCArgs.push_back("-iframework"); + else + CCArgs.push_back("-isystem"); + CCArgs.push_back(Entry.Path); + break; + case clang::frontend::ExternCSystem: + case clang::frontend::CSystem: + case clang::frontend::CXXSystem: + case clang::frontend::ObjCSystem: + case clang::frontend::ObjCXXSystem: + case clang::frontend::After: + break; + } + } + + if (!PPOpts.ImplicitPCHInclude.empty()) { + clang::FileSystemOptions FileSysOpts; + clang::FileManager FileMgr(FileSysOpts); + auto PCHContainerOperations = + std::make_shared(); + std::string HeaderFile = clang::ASTReader::getOriginalSourceFile( + PPOpts.ImplicitPCHInclude, FileMgr, + PCHContainerOperations->getRawReader(), *ClangDiags); + if (!HeaderFile.empty()) { + CCArgs.push_back("-include"); + CCArgs.push_back(std::move(HeaderFile)); + } + } + for (auto &Header : PPOpts.Includes) { + CCArgs.push_back("-include"); + CCArgs.push_back(Header); + } + + for (auto &Entry : HSOpts.ModulesIgnoreMacros) { + std::string Flag = "-fmodules-ignore-macro="; + Flag += Entry; + CCArgs.push_back(Flag); + } + + for (auto &Entry : HSOpts.VFSOverlayFiles) { + CCArgs.push_back("-ivfsoverlay"); + CCArgs.push_back(Entry); + } + + if (!ClangInvok->getLangOpts()->isCompilingModule()) { + CCArgs.push_back("-Xclang"); + llvm::SmallString<64> Str; + Str += "-fmodule-name="; + Str += ClangInvok->getLangOpts()->CurrentModule; + CCArgs.push_back(std::string(Str.str())); + } + + if (PPOpts.DetailedRecord) { + Invok.getClangImporterOptions().DetailedPreprocessingRecord = true; + } + + if (!ClangInvok->getFrontendOpts().Inputs.empty()) { + Invok.getFrontendOptions().ImplicitObjCHeaderPath = + ClangInvok->getFrontendOpts().Inputs[0].getFile().str(); + } + + return false; +} diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 0460027a0ce32..d965a8520a0cd 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -24,9 +24,10 @@ #include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/IDETool/CompilerInvocation.h" +#include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Strings.h" #include "swift/Subsystems.h" -#include "swift/SILOptimizer/PassManager/Passes.h" // This is included only for createLazyResolver(). Move to different header ? #include "swift/Sema/IDETypeChecking.h" diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp index cc7aecb6ad4a6..ab8d85d62d43c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp @@ -21,7 +21,7 @@ #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/ModuleInterfacePrinting.h" #include "swift/IDE/SyntaxModel.h" -#include "swift/IDE/Utils.h" +#include "swift/IDETool/CompilerInvocation.h" #include "swift/Parse/ParseVersion.h" #include "swift/Strings.h" diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 55bf7074c4be5..35369d5a44512 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -46,6 +46,7 @@ #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/TypeContextInfo.h" #include "swift/IDE/Utils.h" +#include "swift/IDETool/CompilerInvocation.h" #include "swift/IDETool/CompletionInstance.h" #include "swift/Index/Index.h" #include "swift/Markup/Markup.h"