diff --git a/mlir/cmake/modules/AddMLIR.cmake b/mlir/cmake/modules/AddMLIR.cmake --- a/mlir/cmake/modules/AddMLIR.cmake +++ b/mlir/cmake/modules/AddMLIR.cmake @@ -1,10 +1,36 @@ include(GNUInstallDirs) include(LLVMDistributionSupport) +# Clear out any pre-existing compile_commands file before processing. This +# allows for generating a clean compile_commands on each configure. +file(REMOVE ${CMAKE_BINARY_DIR}/tablegen_compile_commands.yml) + function(mlir_tablegen ofn) tablegen(MLIR ${ARGV}) set(TABLEGEN_OUTPUT ${TABLEGEN_OUTPUT} ${CMAKE_CURRENT_BINARY_DIR}/${ofn} PARENT_SCOPE) + + # Get the current set of include paths for this td file. + cmake_parse_arguments(ARG "" "" "DEPENDS;EXTRA_INCLUDES" ${ARGN}) + get_directory_property(tblgen_includes INCLUDE_DIRECTORIES) + list(APPEND tblgen_includes ${ARG_EXTRA_INCLUDES}) + # Filter out any empty include items. + list(REMOVE_ITEM tblgen_includes "") + + # Build the absolute path for the current input file. + if (IS_ABSOLUTE ${LLVM_TARGET_DEFINITIONS}) + set(LLVM_TARGET_DEFINITIONS_ABSOLUTE ${LLVM_TARGET_DEFINITIONS}) + else() + set(LLVM_TARGET_DEFINITIONS_ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/${LLVM_TARGET_DEFINITIONS}) + endif() + + # Append the includes used for this file to the tablegen_compile_commands + # file. + file(APPEND ${CMAKE_BINARY_DIR}/tablegen_compile_commands.yml + "--- !FileInfo:\n" + " filepath: \"${LLVM_TARGET_DEFINITIONS_ABSOLUTE}\"\n" + " includes: \"${CMAKE_CURRENT_SOURCE_DIR};${tblgen_includes}\"\n" + ) endfunction() # Clear out any pre-existing compile_commands file before processing. This diff --git a/mlir/lib/Tools/lsp-server-support/CMakeLists.txt b/mlir/lib/Tools/lsp-server-support/CMakeLists.txt --- a/mlir/lib/Tools/lsp-server-support/CMakeLists.txt +++ b/mlir/lib/Tools/lsp-server-support/CMakeLists.txt @@ -1,4 +1,5 @@ add_mlir_library(MLIRLspServerSupportLib + CompilationDatabase.cpp Logging.cpp Protocol.cpp SourceMgrUtils.cpp diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/CompilationDatabase.h b/mlir/lib/Tools/lsp-server-support/CompilationDatabase.h rename from mlir/lib/Tools/mlir-pdll-lsp-server/CompilationDatabase.h rename to mlir/lib/Tools/lsp-server-support/CompilationDatabase.h --- a/mlir/lib/Tools/mlir-pdll-lsp-server/CompilationDatabase.h +++ b/mlir/lib/Tools/lsp-server-support/CompilationDatabase.h @@ -1,13 +1,20 @@ -//===- CompilationDatabase.h - PDLL Compilation Database --------*- C++ -*-===// +//===- CompilationDatabase.h - LSP Compilation Database ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// This file contains a definition of a generic compilation database that can be +// used to provide information about the compilation of a given source file. It +// contains generic components, leaving more complex interpretation to the +// specific language servers that consume it. +// +//===----------------------------------------------------------------------===// -#ifndef LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_COMPILATIONDATABASE_H_ -#define LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_COMPILATIONDATABASE_H_ +#ifndef LIB_MLIR_TOOLS_LSPSERVERSUPPORT_COMPILATIONDATABASE_H_ +#define LIB_MLIR_TOOLS_LSPSERVERSUPPORT_COMPILATIONDATABASE_H_ #include "mlir/Support/LLVM.h" #include "llvm/ADT/StringMap.h" @@ -30,8 +37,10 @@ public: /// Compilation information for a specific file within the database. struct FileInfo { - /// The absolute path to the file. - std::string filename; + FileInfo() = default; + FileInfo(std::vector &&includeDirs) + : includeDirs(std::move(includeDirs)) {} + /// The include directories available for the file. std::vector includeDirs; }; @@ -40,9 +49,8 @@ /// descriptions of the database. CompilationDatabase(ArrayRef databases); - /// Get the compilation information for the provided file, or nullptr if the - /// database doesn't include information for `filename`. - const FileInfo *getFileInfo(StringRef filename) const; + /// Get the compilation information for the provided file. + const FileInfo &getFileInfo(StringRef filename) const; private: /// Load the given database file into this database. @@ -51,8 +59,12 @@ /// A map of filename to file information for each known file within the /// databases. llvm::StringMap files; + + /// A default file info that contains basic information for use by files that + /// weren't explicitly in the database. + FileInfo defaultFileInfo; }; } // namespace lsp } // namespace mlir -#endif // LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_COMPILATIONDATABASE_H_ +#endif // LIB_MLIR_TOOLS_LSPSERVERSUPPORT_COMPILATIONDATABASE_H_ diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/CompilationDatabase.cpp b/mlir/lib/Tools/lsp-server-support/CompilationDatabase.cpp rename from mlir/lib/Tools/mlir-pdll-lsp-server/CompilationDatabase.cpp rename to mlir/lib/Tools/lsp-server-support/CompilationDatabase.cpp --- a/mlir/lib/Tools/mlir-pdll-lsp-server/CompilationDatabase.cpp +++ b/mlir/lib/Tools/lsp-server-support/CompilationDatabase.cpp @@ -1,4 +1,4 @@ -//===- CompilationDatabase.cpp - PDLL Compilation Database ----------------===// +//===- CompilationDatabase.cpp - LSP Compilation Database -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,23 +10,37 @@ #include "../lsp-server-support/Logging.h" #include "../lsp-server-support/Protocol.h" #include "mlir/Support/FileUtilities.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/YAMLTraits.h" using namespace mlir; using namespace mlir::lsp; +//===----------------------------------------------------------------------===// +// YamlFileInfo +//===----------------------------------------------------------------------===// + +namespace { +struct YamlFileInfo { + /// The absolute path to the file. + std::string filename; + /// The include directories available for the file. + std::vector includeDirs; +}; +} // namespace + //===----------------------------------------------------------------------===// // CompilationDatabase //===----------------------------------------------------------------------===// -LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(CompilationDatabase::FileInfo) +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(YamlFileInfo) namespace llvm { namespace yaml { template <> -struct MappingTraits { - static void mapping(IO &io, CompilationDatabase::FileInfo &info) { +struct MappingTraits { + static void mapping(IO &io, YamlFileInfo &info) { // Parse the filename and normalize it to the form we will expect from // incoming URIs. io.mapRequired("filepath", info.filename); @@ -54,10 +68,10 @@ loadDatabase(filename); } -const CompilationDatabase::FileInfo * +const CompilationDatabase::FileInfo & CompilationDatabase::getFileInfo(StringRef filename) const { auto it = files.find(filename); - return it == files.end() ? nullptr : &it->second; + return it == files.end() ? defaultFileInfo : it->second; } void CompilationDatabase::loadDatabase(StringRef filename) { @@ -75,15 +89,30 @@ llvm::yaml::Input yaml(inputFile->getBuffer()); // Parse the yaml description and add any new files to the database. - std::vector parsedFiles; + std::vector parsedFiles; yaml >> parsedFiles; + + SetVector knownIncludes; for (auto &file : parsedFiles) { - auto it = files.try_emplace(file.filename, std::move(file)); + auto it = files.try_emplace(file.filename, std::move(file.includeDirs)); // If we encounter a duplicate file, log a warning and ignore it. if (!it.second) { - Logger::info("Duplicate .pdll file in compilation database: {0}", + Logger::info("Duplicate file in compilation database: {0}", file.filename); + continue; } + + // Track the includes for the file. + for (StringRef include : it.first->second.includeDirs) + knownIncludes.insert(include); } + + // Add all of the known includes to the default file info. We don't know any + // information about how to treat these files, but these may be project files + // that we just don't yet have information for. In these cases, providing some + // heuristic information provides a better user experience, and generally + // shouldn't lead to any negative side effects. + for (StringRef include : knownIncludes) + defaultFileInfo.includeDirs.push_back(include.str()); } diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/CMakeLists.txt b/mlir/lib/Tools/mlir-pdll-lsp-server/CMakeLists.txt --- a/mlir/lib/Tools/mlir-pdll-lsp-server/CMakeLists.txt +++ b/mlir/lib/Tools/mlir-pdll-lsp-server/CMakeLists.txt @@ -1,5 +1,4 @@ llvm_add_library(MLIRPdllLspServerLib - CompilationDatabase.cpp LSPServer.cpp PDLLServer.cpp MlirPdllLspServerMain.cpp diff --git a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp --- a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp +++ b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp @@ -8,9 +8,9 @@ #include "PDLLServer.h" +#include "../lsp-server-support/CompilationDatabase.h" #include "../lsp-server-support/Logging.h" #include "../lsp-server-support/Protocol.h" -#include "CompilationDatabase.h" #include "mlir/Tools/PDLL/AST/Context.h" #include "mlir/Tools/PDLL/AST/Nodes.h" #include "mlir/Tools/PDLL/AST/Types.h" @@ -1422,9 +1422,10 @@ void lsp::PDLLServer::addOrUpdateDocument( const URIForFile &uri, StringRef contents, int64_t version, std::vector &diagnostics) { + // Build the set of additional include directories. std::vector additionalIncludeDirs = impl->options.extraDirs; - if (auto *fileInfo = impl->compilationDatabase.getFileInfo(uri.file())) - llvm::append_range(additionalIncludeDirs, fileInfo->includeDirs); + const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file()); + llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs); impl->files[uri.file()] = std::make_unique( uri, contents, version, additionalIncludeDirs, diagnostics); diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenLspServerMain.cpp b/mlir/lib/Tools/tblgen-lsp-server/TableGenLspServerMain.cpp --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenLspServerMain.cpp +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenLspServerMain.cpp @@ -51,6 +51,13 @@ llvm::cl::desc("Pretty-print JSON output"), llvm::cl::init(false), }; + llvm::cl::list extraIncludeDirs( + "tablegen-extra-dir", llvm::cl::desc("Extra directory of include files"), + llvm::cl::value_desc("directory"), llvm::cl::Prefix); + llvm::cl::list compilationDatabases( + "tablegen-compilation-database", + llvm::cl::desc("Compilation YAML databases containing additional " + "compilation information for .td files")); llvm::cl::ParseCommandLineOptions(argc, argv, "TableGen LSP Language Server"); @@ -68,6 +75,7 @@ JSONTransport transport(stdin, llvm::outs(), inputStyle, prettyPrint); // Configure the servers and start the main language server. - TableGenServer server; + TableGenServer::Options options(compilationDatabases, extraIncludeDirs); + TableGenServer server(options); return runTableGenLSPServer(server, transport); } diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.h @@ -24,7 +24,20 @@ /// logic separate from the logic that involves LSP server/client communication. class TableGenServer { public: - TableGenServer(); + struct Options { + Options(const std::vector &compilationDatabases, + const std::vector &extraDirs) + : compilationDatabases(compilationDatabases), extraDirs(extraDirs) {} + + /// The filenames for databases containing compilation commands for TableGen + /// files passed to the server. + const std::vector &compilationDatabases; + + /// Additional list of include directories to search. + const std::vector &extraDirs; + }; + + TableGenServer(const Options &options); ~TableGenServer(); /// Add or update the document, with the provided `version`, at the given URI. diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp @@ -8,6 +8,7 @@ #include "TableGenServer.h" +#include "../lsp-server-support/CompilationDatabase.h" #include "../lsp-server-support/Logging.h" #include "../lsp-server-support/Protocol.h" #include "../lsp-server-support/SourceMgrUtils.h" @@ -95,7 +96,9 @@ class TableGenTextFile { public: TableGenTextFile(const lsp::URIForFile &uri, StringRef fileContents, - int64_t version, std::vector &diagnostics); + int64_t version, + const std::vector &extraIncludeDirs, + std::vector &diagnostics); /// Return the current version of this text file. int64_t getVersion() const { return version; } @@ -118,9 +121,10 @@ }; } // namespace -TableGenTextFile::TableGenTextFile(const lsp::URIForFile &uri, - StringRef fileContents, int64_t version, - std::vector &diagnostics) +TableGenTextFile::TableGenTextFile( + const lsp::URIForFile &uri, StringRef fileContents, int64_t version, + const std::vector &extraIncludeDirs, + std::vector &diagnostics) : contents(fileContents.str()), version(version) { auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file()); if (!memBuffer) { @@ -129,10 +133,11 @@ } // Build the set of include directories for this file. - // TODO: Setup external include directories. llvm::SmallString<32> uriDirectory(uri.file()); llvm::sys::path::remove_filename(uriDirectory); includeDirs.push_back(uriDirectory.str().str()); + includeDirs.insert(includeDirs.end(), extraIncludeDirs.begin(), + extraIncludeDirs.end()); sourceMgr.setIncludeDirs(includeDirs); sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc()); @@ -161,6 +166,16 @@ //===----------------------------------------------------------------------===// struct lsp::TableGenServer::Impl { + explicit Impl(const Options &options) + : options(options), compilationDatabase(options.compilationDatabases) {} + + /// TableGen LSP options. + const Options &options; + + /// The compilation database containing additional information for files + /// passed to the server. + lsp::CompilationDatabase compilationDatabase; + /// The files held by the server, mapped by their URI file name. llvm::StringMap> files; }; @@ -169,14 +184,20 @@ // TableGenServer //===----------------------------------------------------------------------===// -lsp::TableGenServer::TableGenServer() : impl(std::make_unique()) {} +lsp::TableGenServer::TableGenServer(const Options &options) + : impl(std::make_unique(options)) {} lsp::TableGenServer::~TableGenServer() = default; void lsp::TableGenServer::addOrUpdateDocument( const URIForFile &uri, StringRef contents, int64_t version, std::vector &diagnostics) { - impl->files[uri.file()] = - std::make_unique(uri, contents, version, diagnostics); + // Build the set of additional include directories. + std::vector additionalIncludeDirs = impl->options.extraDirs; + const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file()); + llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs); + + impl->files[uri.file()] = std::make_unique( + uri, contents, version, additionalIncludeDirs, diagnostics); } Optional lsp::TableGenServer::removeDocument(const URIForFile &uri) { diff --git a/mlir/test/tblgen-lsp-server/compilation_database.test b/mlir/test/tblgen-lsp-server/compilation_database.test new file mode 100644 --- /dev/null +++ b/mlir/test/tblgen-lsp-server/compilation_database.test @@ -0,0 +1,21 @@ +// RUN: echo -e '--- !FileInfo:\n filepath: "/foo.td"\n includes: "%/S;%/S/../../include"' > %t.yml +// RUN: tblgen-lsp-server -tablegen-compilation-database=%t.yml -lit-test < %s | FileCheck %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"tablegen","capabilities":{},"trace":"off"}} +// ----- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{ + "uri":"test:///foo.td", + "languageId":"tablegen", + "version":1, + "text":"include \"include/included.td\"" +}}} +// Check that we can properly process the includes without errors. +// CHECK: "method": "textDocument/publishDiagnostics", +// CHECK-NEXT: "params": { +// CHECK-NEXT: "diagnostics": [], +// CHECK-NEXT: "uri": "test:///foo.td", +// CHECK-NEXT: "version": 1 +// CHECK-NEXT: } +// ----- +{"jsonrpc":"2.0","id":7,"method":"shutdown"} +// ----- +{"jsonrpc":"2.0","method":"exit"} diff --git a/mlir/test/tblgen-lsp-server/include/included.td b/mlir/test/tblgen-lsp-server/include/included.td new file mode 100644 --- /dev/null +++ b/mlir/test/tblgen-lsp-server/include/included.td @@ -0,0 +1,3 @@ + +// This file is merely to test the processing of includes, it has +// no other purpose or contents. diff --git a/mlir/test/tblgen-lsp-server/lit.local.cfg b/mlir/test/tblgen-lsp-server/lit.local.cfg new file mode 100644 --- /dev/null +++ b/mlir/test/tblgen-lsp-server/lit.local.cfg @@ -0,0 +1 @@ +config.excludes = ['include'] diff --git a/mlir/utils/vscode/package.json b/mlir/utils/vscode/package.json --- a/mlir/utils/vscode/package.json +++ b/mlir/utils/vscode/package.json @@ -156,6 +156,11 @@ "type": "string", "description": "The file path of the tblgen-lsp-server executable." }, + "mlir.tablegen_compilation_databases": { + "scope": "resource", + "type": "array", + "description": "A list of `tablegen_compile_commands.yml` database files containing information about .td files processed by the server." + }, "mlir.onSettingsChanged": { "type": "string", "default": "prompt", diff --git a/mlir/utils/vscode/src/config.ts b/mlir/utils/vscode/src/config.ts --- a/mlir/utils/vscode/src/config.ts +++ b/mlir/utils/vscode/src/config.ts @@ -4,8 +4,10 @@ * Gets the config value `mlir.`, with an optional workspace folder. */ export function get(key: string, - workspaceFolder: vscode.WorkspaceFolder = null): T { - return vscode.workspace.getConfiguration('mlir', workspaceFolder).get(key); + workspaceFolder: vscode.WorkspaceFolder = null, + defaultValue: T = undefined): T { + return vscode.workspace.getConfiguration('mlir', workspaceFolder) + .get(key, defaultValue); } /** diff --git a/mlir/utils/vscode/src/mlirContext.ts b/mlir/utils/vscode/src/mlirContext.ts --- a/mlir/utils/vscode/src/mlirContext.ts +++ b/mlir/utils/vscode/src/mlirContext.ts @@ -90,23 +90,22 @@ } /** - * Prepare the server options for a PDLL server, e.g. populating any - * accessible compilation databases. + * Prepare a compilation database option for a server. */ - async preparePDLLServerOptions(workspaceFolder: vscode.WorkspaceFolder, - configsToWatch: string[], - pathsToWatch: string[], - additionalServerArgs: string[]) { + async prepareCompilationDatabaseServerOptions( + languageName: string, workspaceFolder: vscode.WorkspaceFolder, + configsToWatch: string[], pathsToWatch: string[], + additionalServerArgs: string[]) { // Process the compilation databases attached for the workspace folder. - let databases = - config.get('pdll_compilation_databases', workspaceFolder); + let databases = config.get( + `${languageName}_compilation_databases`, workspaceFolder, []); // If no databases were explicitly specified, default to a database in the // 'build' directory within the current workspace. if (databases.length === 0) { if (workspaceFolder) { databases.push(workspaceFolder.uri.fsPath + - '/build/pdll_compile_commands.yml'); + `/build/${languageName}_compile_commands.yml`); } // Otherwise, try to resolve each of the paths. @@ -116,14 +115,40 @@ } } - configsToWatch.push('pdll_compilation_databases'); + configsToWatch.push(`${languageName}_compilation_databases`); pathsToWatch.push(...databases); // Setup the compilation databases as additional arguments to pass to the // server. databases.filter(database => database !== ''); additionalServerArgs.push(...databases.map( - (database) => `--pdll-compilation-database=${database}`)); + (database) => `--${languageName}-compilation-database=${database}`)); + } + + /** + * Prepare the server options for a PDLL server, e.g. populating any + * accessible compilation databases. + */ + async preparePDLLServerOptions(workspaceFolder: vscode.WorkspaceFolder, + configsToWatch: string[], + pathsToWatch: string[], + additionalServerArgs: string[]) { + await this.prepareCompilationDatabaseServerOptions( + 'pdll', workspaceFolder, configsToWatch, pathsToWatch, + additionalServerArgs); + } + + /** + * Prepare the server options for a TableGen server, e.g. populating any + * accessible compilation databases. + */ + async prepareTableGenServerOptions(workspaceFolder: vscode.WorkspaceFolder, + configsToWatch: string[], + pathsToWatch: string[], + additionalServerArgs: string[]) { + await this.prepareCompilationDatabaseServerOptions( + 'tablegen', workspaceFolder, configsToWatch, pathsToWatch, + additionalServerArgs); } /** @@ -143,6 +168,10 @@ await this.preparePDLLServerOptions(workspaceFolder, configsToWatch, filepathsToWatch, additionalServerArgs); + } else if (languageName == 'tablegen') { + await this.prepareTableGenServerOptions(workspaceFolder, configsToWatch, + filepathsToWatch, + additionalServerArgs); } // Try to activate the language client.