[clang-tools-extra] r351280 - [clangd] XPC transport layer

Jan Korous via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 15 16:24:22 PST 2019


Author: jkorous
Date: Tue Jan 15 16:24:22 2019
New Revision: 351280

URL: http://llvm.org/viewvc/llvm-project?rev=351280&view=rev
Log:
[clangd] XPC transport layer

- New transport layer for macOS.
- XPC Framework
- Test client

Framework and client were written by Alex Lorenz.

Differential Revision: https://reviews.llvm.org/D54428

Added:
    clang-tools-extra/trunk/clangd/Features.inc.in
    clang-tools-extra/trunk/clangd/xpc/
    clang-tools-extra/trunk/clangd/xpc/CMakeLists.txt
    clang-tools-extra/trunk/clangd/xpc/Conversion.cpp
    clang-tools-extra/trunk/clangd/xpc/Conversion.h
    clang-tools-extra/trunk/clangd/xpc/README.txt
    clang-tools-extra/trunk/clangd/xpc/XPCTransport.cpp
    clang-tools-extra/trunk/clangd/xpc/cmake/
    clang-tools-extra/trunk/clangd/xpc/cmake/Info.plist.in
    clang-tools-extra/trunk/clangd/xpc/cmake/XPCServiceInfo.plist.in
    clang-tools-extra/trunk/clangd/xpc/cmake/modules/
    clang-tools-extra/trunk/clangd/xpc/cmake/modules/CreateClangdXPCFramework.cmake
    clang-tools-extra/trunk/clangd/xpc/framework/
    clang-tools-extra/trunk/clangd/xpc/framework/CMakeLists.txt
    clang-tools-extra/trunk/clangd/xpc/framework/ClangdXPC.cpp
    clang-tools-extra/trunk/clangd/xpc/test-client/
    clang-tools-extra/trunk/clangd/xpc/test-client/CMakeLists.txt
    clang-tools-extra/trunk/clangd/xpc/test-client/ClangdXPCTestClient.cpp
    clang-tools-extra/trunk/test/clangd/xpc/
    clang-tools-extra/trunk/test/clangd/xpc/initialize.test
    clang-tools-extra/trunk/unittests/clangd/xpc/
    clang-tools-extra/trunk/unittests/clangd/xpc/CMakeLists.txt
    clang-tools-extra/trunk/unittests/clangd/xpc/ConversionTests.cpp
Modified:
    clang-tools-extra/trunk/CMakeLists.txt
    clang-tools-extra/trunk/clangd/CMakeLists.txt
    clang-tools-extra/trunk/clangd/Transport.h
    clang-tools-extra/trunk/clangd/tool/CMakeLists.txt
    clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
    clang-tools-extra/trunk/test/CMakeLists.txt
    clang-tools-extra/trunk/test/lit.cfg
    clang-tools-extra/trunk/test/lit.site.cfg.in
    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt

Modified: clang-tools-extra/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/CMakeLists.txt?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -1,3 +1,8 @@
+option(CLANGD_BUILD_XPC "Build XPC Support For Clangd." OFF)
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+  set(CLANGD_BUILD_XPC ON CACHE BOOL "" FORCE)
+endif ()
+
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-reorder-fields)
 add_subdirectory(modularize)

Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -1,3 +1,11 @@
+# Configure the Features.inc file.
+llvm_canonicalize_cmake_booleans(
+  CLANGD_BUILD_XPC)
+configure_file(
+  ${CMAKE_CURRENT_SOURCE_DIR}/Features.inc.in
+  ${CMAKE_CURRENT_BINARY_DIR}/Features.inc
+)
+
 set(LLVM_LINK_COMPONENTS
   Support
   )
@@ -109,3 +117,6 @@ add_subdirectory(index/dex/dexp)
 if (LLVM_INCLUDE_BENCHMARKS)
   add_subdirectory(benchmarks)
 endif()
+if ( CLANGD_BUILD_XPC )
+  add_subdirectory(xpc)
+endif ()

Added: clang-tools-extra/trunk/clangd/Features.inc.in
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Features.inc.in?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/Features.inc.in (added)
+++ clang-tools-extra/trunk/clangd/Features.inc.in Tue Jan 15 16:24:22 2019
@@ -0,0 +1 @@
+#define CLANGD_BUILD_XPC @CLANGD_BUILD_XPC@

Modified: clang-tools-extra/trunk/clangd/Transport.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Transport.h?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Transport.h (original)
+++ clang-tools-extra/trunk/clangd/Transport.h Tue Jan 15 16:24:22 2019
@@ -86,6 +86,12 @@ newJSONTransport(std::FILE *In, llvm::ra
                  llvm::raw_ostream *InMirror, bool Pretty,
                  JSONStreamStyle = JSONStreamStyle::Standard);
 
+#ifdef CLANGD_BUILD_XPC
+// Returns a Transport for macOS based on XPC.
+// Clangd with this transport is meant to be run as bundled XPC service.
+std::unique_ptr<Transport> newXPCTransport();
+#endif
+
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/clangd/tool/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/CMakeLists.txt?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/tool/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/tool/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -1,4 +1,5 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
 
 add_clang_tool(clangd
   ClangdMain.cpp
@@ -8,6 +9,11 @@ set(LLVM_LINK_COMPONENTS
   support
   )
 
+set(CLANGD_XPC_LIBS "")
+if(CLANGD_BUILD_XPC)
+  list(APPEND CLANGD_XPC_LIBS "clangdXpcJsonConversions" "clangdXpcTransport")
+endif()
+
 target_link_libraries(clangd
   PRIVATE
   clangBasic
@@ -17,4 +23,5 @@ target_link_libraries(clangd
   clangSema
   clangTooling
   clangToolingCore
+  ${CLANGD_XPC_LIBS}
   )

Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
+++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Tue Jan 15 16:24:22 2019
@@ -7,6 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "Features.inc"
 #include "ClangdLSPServer.h"
 #include "Path.h"
 #include "Trace.h"
@@ -255,6 +256,11 @@ const char TestScheme::TestDir[] = "/cla
 } // namespace clangd
 } // namespace clang
 
+enum class ErrorResultCode : int {
+  NoShutdownRequest = 1,
+  CantRunAsXPCService = 2
+};
+
 int main(int argc, char *argv[]) {
   using namespace clang;
   using namespace clang::clangd;
@@ -408,14 +414,26 @@ int main(int argc, char *argv[]) {
   // Initialize and run ClangdLSPServer.
   // Change stdin to binary to not lose \r\n on windows.
   llvm::sys::ChangeStdinToBinary();
-  auto Transport = newJSONTransport(
-      stdin, llvm::outs(),
-      InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint,
-      InputStyle);
+
+  std::unique_ptr<Transport> TransportLayer;
+  if (getenv("CLANGD_AS_XPC_SERVICE")) {
+#ifdef CLANGD_BUILD_XPC
+    TransportLayer = newXPCTransport();
+#else
+    errs() << "This clangd binary wasn't built with XPC support.\n";
+    return ErrorResultCode::CantRunAsXPCService;
+#endif
+  } else {
+    TransportLayer = newJSONTransport(
+        stdin, llvm::outs(),
+        InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
+        PrettyPrint, InputStyle);
+  }
+
   ClangdLSPServer LSPServer(
-      *Transport, CCOpts, CompileCommandsDirPath,
+      *TransportLayer, CCOpts, CompileCommandsDirPath,
       /*UseDirBasedCDB=*/CompileArgsFrom == FilesystemCompileArgs, Opts);
-  constexpr int NoShutdownRequestErrorCode = 1;
   llvm::set_thread_name("clangd.main");
-  return LSPServer.run() ? 0 : NoShutdownRequestErrorCode;
+  return LSPServer.run() ? 0
+                         : static_cast<int>(ErrorResultCode::NoShutdownRequest);
 }

Added: clang-tools-extra/trunk/clangd/xpc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/CMakeLists.txt?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/clangd/xpc/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -0,0 +1,29 @@
+set(CLANGD_XPC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+set(CLANGD_XPC_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
+include(CreateClangdXPCFramework)
+
+add_subdirectory(framework)
+add_subdirectory(test-client)
+
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}/../
+)
+
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+# Needed by LLVM's CMake checks because this file defines multiple targets.
+set(LLVM_OPTIONAL_SOURCES Conversion.cpp XPCTransport.cpp)
+
+add_clang_library(clangdXpcJsonConversions
+  Conversion.cpp
+  )
+
+add_clang_library(clangdXpcTransport
+  XPCTransport.cpp
+  DEPENDS clangdXpcJsonConversions
+  LINK_LIBS clangdXpcJsonConversions
+  )

Added: clang-tools-extra/trunk/clangd/xpc/Conversion.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/Conversion.cpp?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/Conversion.cpp (added)
+++ clang-tools-extra/trunk/clangd/xpc/Conversion.cpp Tue Jan 15 16:24:22 2019
@@ -0,0 +1,40 @@
+//===--- Conversion.cpp - LSP data (de-)serialization through XPC - C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xpc/Conversion.h"
+#include "Logger.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+namespace clang {
+namespace clangd {
+
+xpc_object_t jsonToXpc(const json::Value &JSON) {
+  const char *const Key = "LSP";
+  xpc_object_t PayloadObj = xpc_string_create(llvm::to_string(JSON).c_str());
+  return xpc_dictionary_create(&Key, &PayloadObj, 1);
+}
+
+json::Value xpcToJson(const xpc_object_t &XPCObject) {
+  if (xpc_get_type(XPCObject) == XPC_TYPE_DICTIONARY) {
+    const char *const LSP = xpc_dictionary_get_string(XPCObject, "LSP");
+    auto Json = json::parse(llvm::StringRef(LSP));
+    if (Json)
+      return *Json;
+    else
+      elog("JSON parse error: {0}", toString(Json.takeError()));
+  }
+  return json::Value(nullptr);
+}
+
+} // namespace clangd
+} // namespace clang

Added: clang-tools-extra/trunk/clangd/xpc/Conversion.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/Conversion.h?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/Conversion.h (added)
+++ clang-tools-extra/trunk/clangd/xpc/Conversion.h Tue Jan 15 16:24:22 2019
@@ -0,0 +1,25 @@
+//===--- Conversion.h - LSP data (de-)serialization through XPC -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_XPC_XPCJSONCONVERSIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XPC_XPCJSONCONVERSIONS_H
+
+#include "llvm/Support/JSON.h"
+#include <xpc/xpc.h>
+
+namespace clang {
+namespace clangd {
+
+xpc_object_t jsonToXpc(const llvm::json::Value &JSON);
+llvm::json::Value xpcToJson(const xpc_object_t &XPCObject);
+
+} // namespace clangd
+} // namespace clang
+
+#endif

Added: clang-tools-extra/trunk/clangd/xpc/README.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/README.txt?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/README.txt (added)
+++ clang-tools-extra/trunk/clangd/xpc/README.txt Tue Jan 15 16:24:22 2019
@@ -0,0 +1,6 @@
+This directory contains:
+- the XPC transport layer (alternative transport layer to JSON-RPC)
+- XPC framework wrapper that wraps around Clangd to make it a valid XPC service
+- XPC test-client
+
+MacOS only. Feature is guarded by CLANGD_BUILD_XPC, including whole xpc/ dir.
\ No newline at end of file

Added: clang-tools-extra/trunk/clangd/xpc/XPCTransport.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/XPCTransport.cpp?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/XPCTransport.cpp (added)
+++ clang-tools-extra/trunk/clangd/xpc/XPCTransport.cpp Tue Jan 15 16:24:22 2019
@@ -0,0 +1,217 @@
+//===--- XPCTransport.cpp - sending and receiving LSP messages over XPC ---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "Conversion.h"
+#include "Logger.h"
+#include "Protocol.h" // For LSPError
+#include "Transport.h"
+#include "llvm/Support/Errno.h"
+
+#include <xpc/xpc.h>
+
+using namespace llvm;
+using namespace clang;
+using namespace clangd;
+
+namespace {
+
+json::Object encodeError(Error E) {
+  std::string Message;
+  ErrorCode Code = ErrorCode::UnknownErrorCode;
+  if (Error Unhandled =
+          handleErrors(std::move(E), [&](const LSPError &L) -> Error {
+            Message = L.Message;
+            Code = L.Code;
+            return Error::success();
+          }))
+    Message = toString(std::move(Unhandled));
+
+  return json::Object{
+      {"message", std::move(Message)},
+      {"code", int64_t(Code)},
+  };
+}
+
+Error decodeError(const json::Object &O) {
+  std::string Msg = O.getString("message").getValueOr("Unspecified error");
+  if (auto Code = O.getInteger("code"))
+    return make_error<LSPError>(std::move(Msg), ErrorCode(*Code));
+  return make_error<StringError>(std::move(Msg), inconvertibleErrorCode());
+}
+
+// C "closure" for XPCTransport::loop() method
+namespace xpcClosure {
+void connection_handler(xpc_connection_t clientConnection);
+}
+
+class XPCTransport : public Transport {
+public:
+  XPCTransport() {}
+
+  void notify(StringRef Method, json::Value Params) override {
+    sendMessage(json::Object{
+        {"jsonrpc", "2.0"},
+        {"method", Method},
+        {"params", std::move(Params)},
+    });
+  }
+  void call(StringRef Method, json::Value Params, json::Value ID) override {
+    sendMessage(json::Object{
+        {"jsonrpc", "2.0"},
+        {"id", std::move(ID)},
+        {"method", Method},
+        {"params", std::move(Params)},
+    });
+  }
+  void reply(json::Value ID, Expected<json::Value> Result) override {
+    if (Result) {
+      sendMessage(json::Object{
+          {"jsonrpc", "2.0"},
+          {"id", std::move(ID)},
+          {"result", std::move(*Result)},
+      });
+    } else {
+      sendMessage(json::Object{
+          {"jsonrpc", "2.0"},
+          {"id", std::move(ID)},
+          {"error", encodeError(Result.takeError())},
+      });
+    }
+  }
+
+  Error loop(MessageHandler &Handler) override;
+
+private:
+  // Needs access to handleMessage() and resetClientConnection()
+  friend void xpcClosure::connection_handler(xpc_connection_t clientConnection);
+
+  // Dispatches incoming message to Handler onNotify/onCall/onReply.
+  bool handleMessage(json::Value Message, MessageHandler &Handler);
+  void sendMessage(json::Value Message) {
+    xpc_object_t response = jsonToXpc(Message);
+    xpc_connection_send_message(clientConnection, response);
+    xpc_release(response);
+  }
+  void resetClientConnection(xpc_connection_t newClientConnection) {
+    clientConnection = newClientConnection;
+  }
+  xpc_connection_t clientConnection;
+};
+
+bool XPCTransport::handleMessage(json::Value Message, MessageHandler &Handler) {
+  // Message must be an object with "jsonrpc":"2.0".
+  auto *Object = Message.getAsObject();
+  if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0")) {
+    elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
+    return false;
+  }
+  // ID may be any JSON value. If absent, this is a notification.
+  Optional<json::Value> ID;
+  if (auto *I = Object->get("id"))
+    ID = std::move(*I);
+  auto Method = Object->getString("method");
+  if (!Method) { // This is a response.
+    if (!ID) {
+      elog("No method and no response ID: {0:2}", Message);
+      return false;
+    }
+    if (auto *Err = Object->getObject("error"))
+      return Handler.onReply(std::move(*ID), decodeError(*Err));
+    // Result should be given, use null if not.
+    json::Value Result = nullptr;
+    if (auto *R = Object->get("result"))
+      Result = std::move(*R);
+    return Handler.onReply(std::move(*ID), std::move(Result));
+  }
+  // Params should be given, use null if not.
+  json::Value Params = nullptr;
+  if (auto *P = Object->get("params"))
+    Params = std::move(*P);
+
+  if (ID)
+    return Handler.onCall(*Method, std::move(Params), std::move(*ID));
+  else
+    return Handler.onNotify(*Method, std::move(Params));
+}
+
+namespace xpcClosure {
+// "owner" of this "closure object" - necessary for propagating connection to
+// XPCTransport so it can send messages to the client.
+XPCTransport *TransportObject = nullptr;
+Transport::MessageHandler *HandlerPtr = nullptr;
+
+void connection_handler(xpc_connection_t clientConnection) {
+  xpc_connection_set_target_queue(clientConnection, dispatch_get_main_queue());
+
+  xpc_transaction_begin();
+
+  TransportObject->resetClientConnection(clientConnection);
+
+  xpc_connection_set_event_handler(clientConnection, ^(xpc_object_t message) {
+    if (message == XPC_ERROR_CONNECTION_INVALID) {
+      // connection is being terminated
+      log("Received XPC_ERROR_CONNECTION_INVALID message - returning from the "
+          "event_handler.");
+      return;
+    }
+
+    if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) {
+      log("Received XPC message of unknown type - returning from the "
+          "event_handler.");
+      return;
+    }
+
+    const json::Value Doc = xpcToJson(message);
+    if (Doc == json::Value(nullptr)) {
+      log("XPC message was converted to Null JSON message - returning from the "
+          "event_handler.");
+      return;
+    }
+
+    vlog("<<< {0}\n", Doc);
+
+    if (!TransportObject->handleMessage(std::move(Doc), *HandlerPtr)) {
+      log("Received exit notification - cancelling connection.");
+      xpc_connection_cancel(xpc_dictionary_get_remote_connection(message));
+      xpc_transaction_end();
+    }
+  });
+
+  xpc_connection_resume(clientConnection);
+}
+} // namespace xpcClosure
+
+Error XPCTransport::loop(MessageHandler &Handler) {
+  assert(xpcClosure::TransportObject == nullptr &&
+         "TransportObject has already been set.");
+  // This looks scary since lifetime of this (or any) XPCTransport object has
+  // to fully contain lifetime of any XPC connection. In practise any Transport
+  // object is destroyed only at the end of main() which is always after
+  // exit of xpc_main().
+  xpcClosure::TransportObject = this;
+
+  assert(xpcClosure::HandlerPtr == nullptr &&
+         "HandlerPtr has already been set.");
+  xpcClosure::HandlerPtr = &Handler;
+
+  xpc_main(xpcClosure::connection_handler);
+  // xpc_main doesn't ever return
+  return errorCodeToError(std::make_error_code(std::errc::io_error));
+}
+
+} // namespace
+
+namespace clang {
+namespace clangd {
+
+std::unique_ptr<Transport> newXPCTransport() {
+  return llvm::make_unique<XPCTransport>();
+}
+
+} // namespace clangd
+} // namespace clang

Added: clang-tools-extra/trunk/clangd/xpc/cmake/Info.plist.in
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/cmake/Info.plist.in?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/cmake/Info.plist.in (added)
+++ clang-tools-extra/trunk/clangd/xpc/cmake/Info.plist.in Tue Jan 15 16:24:22 2019
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>English</string>
+  <key>CFBundleExecutable</key>
+  <string>${CLANGD_XPC_FRAMEWORK_NAME}</string>
+  <key>CFBundleIconFile</key>
+  <string></string>
+  <key>CFBundleIdentifier</key>
+  <string>org.llvm.${CLANGD_XPC_FRAMEWORK_NAME}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${CLANGD_XPC_FRAMEWORK_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string></string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CSResourcesFileMapped</key>
+  <true/>
+</dict>
+</plist>

Added: clang-tools-extra/trunk/clangd/xpc/cmake/XPCServiceInfo.plist.in
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/cmake/XPCServiceInfo.plist.in?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/cmake/XPCServiceInfo.plist.in (added)
+++ clang-tools-extra/trunk/clangd/xpc/cmake/XPCServiceInfo.plist.in Tue Jan 15 16:24:22 2019
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleExecutable</key>
+  <string>${CLANGD_XPC_SERVICE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${CLANGD_XPC_SERVICE_BUNDLE_NAME}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${CLANGD_XPC_SERVICE_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>XPC!</string>
+  <key>CFBundleVersion</key>
+  <string></string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>XPCService</key>
+  <dict>
+    <key>ServiceType</key>
+    <string>Application</string>
+    <key>EnvironmentVariables</key>
+    <dict>
+        <key>CLANGD_AS_XPC_SERVICE</key>
+        <string>1</string>
+    </dict>
+  </dict>
+</dict>
+</plist>

Added: clang-tools-extra/trunk/clangd/xpc/cmake/modules/CreateClangdXPCFramework.cmake
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/cmake/modules/CreateClangdXPCFramework.cmake?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/cmake/modules/CreateClangdXPCFramework.cmake (added)
+++ clang-tools-extra/trunk/clangd/xpc/cmake/modules/CreateClangdXPCFramework.cmake Tue Jan 15 16:24:22 2019
@@ -0,0 +1,73 @@
+# Creates the ClangdXPC framework.
+macro(create_clangd_xpc_framework target name)
+  set(CLANGD_FRAMEWORK_LOCATION "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${name}.framework")
+  set(CLANGD_FRAMEWORK_OUT_LOCATION "${CLANGD_FRAMEWORK_LOCATION}/Versions/A")
+
+  # Create the framework info PLIST.
+  set(CLANGD_XPC_FRAMEWORK_NAME "${name}")
+  configure_file(
+    "${CLANGD_XPC_SOURCE_DIR}/cmake/Info.plist.in"
+    "${CLANGD_XPC_BINARY_DIR}/${name}.Info.plist")
+
+  set(CLANGD_XPC_SERVICE_NAME "clangd")
+  set(CLANGD_XPC_SERVICE_OUT_LOCATION
+      "${CLANGD_FRAMEWORK_OUT_LOCATION}/XPCServices/${CLANGD_XPC_SERVICE_NAME}.xpc/Contents")
+
+  # Create the XPC service info PLIST.
+  set(CLANGD_XPC_SERVICE_BUNDLE_NAME "org.llvm.${CLANGD_XPC_SERVICE_NAME}")
+  configure_file(
+    "${CLANGD_XPC_SOURCE_DIR}/cmake/XPCServiceInfo.plist.in"
+    "${CLANGD_XPC_BINARY_DIR}/${name}Service.Info.plist")
+
+  # Create the custom command
+  add_custom_command(OUTPUT ${CLANGD_FRAMEWORK_LOCATION}
+    # Copy the PLIST.
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${CLANGD_XPC_BINARY_DIR}/${name}.Info.plist"
+      "${CLANGD_FRAMEWORK_OUT_LOCATION}/Resources/Info.plist"
+
+    # Copy the framework binary.
+    COMMAND ${CMAKE_COMMAND} -E copy
+       "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${target}.dylib"
+       "${CLANGD_FRAMEWORK_OUT_LOCATION}/${name}"
+
+    # Copy the XPC Service PLIST.
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${CLANGD_XPC_BINARY_DIR}/${name}Service.Info.plist"
+      "${CLANGD_XPC_SERVICE_OUT_LOCATION}/Info.plist"
+
+    # Copy the Clangd binary.
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/clangd"
+      "${CLANGD_XPC_SERVICE_OUT_LOCATION}/MacOS/clangd"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink "A"
+     "${CLANGD_FRAMEWORK_LOCATION}/Versions/Current"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink
+     "Versions/Current/Resources"
+     "${CLANGD_FRAMEWORK_LOCATION}/Resources"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink
+     "Versions/Current/XPCServices"
+     "${CLANGD_FRAMEWORK_LOCATION}/XPCServices"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink
+     "Versions/Current/${name}"
+     "${CLANGD_FRAMEWORK_LOCATION}/${name}"
+
+    DEPENDS
+      "${CLANGD_XPC_BINARY_DIR}/${name}.Info.plist"
+      "${CLANGD_XPC_BINARY_DIR}/${name}Service.Info.plist"
+      clangd
+    COMMENT "Creating ClangdXPC framework"
+    VERBATIM
+  )
+
+  add_custom_target(
+    ClangdXPC
+    DEPENDS
+    ${target}
+    ${CLANGD_FRAMEWORK_LOCATION}
+  )
+endmacro(create_clangd_xpc_framework)

Added: clang-tools-extra/trunk/clangd/xpc/framework/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/framework/CMakeLists.txt?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/framework/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/clangd/xpc/framework/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -0,0 +1,9 @@
+
+set(SOURCES
+    ClangdXPC.cpp)
+add_clang_library(ClangdXPCLib SHARED
+  ${SOURCES}
+  DEPENDS
+  clangd
+)
+create_clangd_xpc_framework(ClangdXPCLib "ClangdXPC")

Added: clang-tools-extra/trunk/clangd/xpc/framework/ClangdXPC.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/framework/ClangdXPC.cpp?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/framework/ClangdXPC.cpp (added)
+++ clang-tools-extra/trunk/clangd/xpc/framework/ClangdXPC.cpp Tue Jan 15 16:24:22 2019
@@ -0,0 +1,5 @@
+
+/// Returns the bundle identifier of the Clangd XPC service.
+extern "C" const char *clangd_xpc_get_bundle_identifier() {
+  return "org.llvm.clangd";
+}

Added: clang-tools-extra/trunk/clangd/xpc/test-client/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/test-client/CMakeLists.txt?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/test-client/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/clangd/xpc/test-client/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -0,0 +1,26 @@
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../
+)
+
+add_clang_tool(
+  clangd-xpc-test-client
+  ClangdXPCTestClient.cpp
+
+  DEPENDS ClangdXPC
+)
+
+set(LLVM_LINK_COMPONENTS
+    support
+)
+
+target_link_libraries(clangd-xpc-test-client
+  PRIVATE
+  clangBasic
+  clangDaemon
+  clangFormat
+  clangFrontend
+  clangSema
+  clangTooling
+  clangToolingCore
+  clangdXpcJsonConversions
+)

Added: clang-tools-extra/trunk/clangd/xpc/test-client/ClangdXPCTestClient.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/xpc/test-client/ClangdXPCTestClient.cpp?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/xpc/test-client/ClangdXPCTestClient.cpp (added)
+++ clang-tools-extra/trunk/clangd/xpc/test-client/ClangdXPCTestClient.cpp Tue Jan 15 16:24:22 2019
@@ -0,0 +1,96 @@
+#include "xpc/Conversion.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string>
+#include <xpc/xpc.h>
+
+typedef const char *(*clangd_xpc_get_bundle_identifier_t)(void);
+
+using namespace llvm;
+using namespace clang;
+
+std::string getLibraryPath() {
+  Dl_info info;
+  if (dladdr((void *)(uintptr_t)getLibraryPath, &info) == 0)
+    llvm_unreachable("Call to dladdr() failed");
+  llvm::SmallString<128> LibClangPath;
+  LibClangPath = llvm::sys::path::parent_path(
+      llvm::sys::path::parent_path(info.dli_fname));
+  llvm::sys::path::append(LibClangPath, "lib", "ClangdXPC.framework",
+                          "ClangdXPC");
+  return LibClangPath.str();
+}
+
+static void dumpXPCObject(xpc_object_t Object, llvm::raw_ostream &OS) {
+  xpc_type_t Type = xpc_get_type(Object);
+  if (Type == XPC_TYPE_DICTIONARY) {
+    json::Value Json = clang::clangd::xpcToJson(Object);
+    OS << Json;
+  } else {
+    OS << "<UNKNOWN>";
+  }
+}
+
+int main(int argc, char *argv[]) {
+  // Open the ClangdXPC dylib in the framework.
+  std::string LibPath = getLibraryPath();
+  void *dlHandle = dlopen(LibPath.c_str(), RTLD_LOCAL | RTLD_FIRST);
+  if (!dlHandle)
+    return 1;
+
+  // Lookup the XPC service bundle name, and launch it.
+  clangd_xpc_get_bundle_identifier_t clangd_xpc_get_bundle_identifier =
+      (clangd_xpc_get_bundle_identifier_t)dlsym(
+          dlHandle, "clangd_xpc_get_bundle_identifier");
+  xpc_connection_t conn = xpc_connection_create(
+      clangd_xpc_get_bundle_identifier(), dispatch_get_main_queue());
+
+  // Dump the XPC events.
+  xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
+    if (event == XPC_ERROR_CONNECTION_INVALID) {
+      llvm::errs() << "Received XPC_ERROR_CONNECTION_INVALID.";
+      exit(EXIT_SUCCESS);
+    }
+    if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
+      llvm::errs() << "Received XPC_ERROR_CONNECTION_INTERRUPTED.";
+      exit(EXIT_SUCCESS);
+    }
+
+    dumpXPCObject(event, llvm::outs());
+    llvm::outs() << "\n";
+  });
+
+  xpc_connection_resume(conn);
+
+  // Read the input to determine the things to send to clangd.
+  llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Stdin =
+      llvm::MemoryBuffer::getSTDIN();
+  if (!Stdin) {
+    llvm::errs() << "Failed to get STDIN!\n";
+    return 1;
+  }
+  for (llvm::line_iterator It(**Stdin, /*SkipBlanks=*/true,
+                              /*CommentMarker=*/'#');
+       !It.is_at_eof(); ++It) {
+    StringRef Line = *It;
+    if (auto Request = json::parse(Line)) {
+      xpc_object_t Object = clangd::jsonToXpc(*Request);
+      xpc_connection_send_message(conn, Object);
+    } else {
+      llvm::errs() << llvm::Twine("JSON parse error: ")
+                   << llvm::toString(Request.takeError());
+      return 1;
+    }
+  }
+
+  dispatch_main();
+
+  // dispatch_main() doesn't return
+  return EXIT_FAILURE;
+}

Modified: clang-tools-extra/trunk/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/CMakeLists.txt?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/test/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -16,7 +16,8 @@ endif ()
 string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} CLANG_TOOLS_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR})
 
 llvm_canonicalize_cmake_booleans(
-  CLANG_ENABLE_STATIC_ANALYZER)
+  CLANG_ENABLE_STATIC_ANALYZER
+  CLANGD_BUILD_XPC_SUPPORT)
 
 configure_lit_site_cfg(
   ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
@@ -61,7 +62,11 @@ set(CLANG_TOOLS_TEST_DEPS
   clang-headers
 
   clang-tidy
-  )
+)
+
+if(CLANGD_BUILD_XPC_SUPPORT)
+  list(APPEND CLANG_TOOLS_TEST_DEPS clangd-xpc-test-client)
+endif()
 
 set(CLANGD_TEST_DEPS
   clangd

Added: clang-tools-extra/trunk/test/clangd/xpc/initialize.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/xpc/initialize.test?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/xpc/initialize.test (added)
+++ clang-tools-extra/trunk/test/clangd/xpc/initialize.test Tue Jan 15 16:24:22 2019
@@ -0,0 +1,10 @@
+# RUN: clangd-xpc-test-client < %s | FileCheck %s
+# REQUIRES: clangd-xpc-support
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"test:///workspace","capabilities":{},"trace":"off"}}
+# CHECK: {"id":0,"jsonrpc":"2.0","result":{"capabilities"
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+# CHECK: {"id":3,"jsonrpc":"2.0","result":null}
+
+{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/lit.cfg
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/lit.cfg?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/lit.cfg (original)
+++ clang-tools-extra/trunk/test/lit.cfg Tue Jan 15 16:24:22 2019
@@ -117,6 +117,10 @@ if not platform.system() in ['Windows']
 if platform.system() not in ['Windows']:
     config.available_features.add('ansi-escape-sequences')
 
+# XPC support for Clangd.
+if config.clangd_xpc_support:
+    config.available_features.add('clangd-xpc-support')
+
 if config.clang_staticanalyzer:
     config.available_features.add('static-analyzer')
 

Modified: clang-tools-extra/trunk/test/lit.site.cfg.in
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/lit.site.cfg.in?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/lit.site.cfg.in (original)
+++ clang-tools-extra/trunk/test/lit.site.cfg.in Tue Jan 15 16:24:22 2019
@@ -11,6 +11,7 @@ config.clang_libs_dir = "@SHLIBDIR@"
 config.python_executable = "@PYTHON_EXECUTABLE@"
 config.target_triple = "@TARGET_TRIPLE@"
 config.clang_staticanalyzer = @CLANG_ENABLE_STATIC_ANALYZER@
+config.clangd_xpc_support = @CLANGD_BUILD_XPC_SUPPORT@
 
 # Support substitution of the tools and libs dirs with user parameters. This is
 # used when we can't determine the tool dir at configuration time.

Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=351280&r1=351279&r2=351280&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -65,3 +65,7 @@ target_link_libraries(ClangdTests
   LLVMSupport
   LLVMTestingSupport
   )
+
+if (CLANGD_BUILD_XPC)
+  add_subdirectory(xpc)
+endif ()

Added: clang-tools-extra/trunk/unittests/clangd/xpc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/xpc/CMakeLists.txt?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/xpc/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/unittests/clangd/xpc/CMakeLists.txt Tue Jan 15 16:24:22 2019
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANGD_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clangd REALPATH)
+include_directories(
+  ${CLANGD_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangdXpcTests
+  ConversionTests.cpp
+  )
+
+target_link_libraries(ClangdXpcTests
+  PRIVATE
+  clangdXpcJsonConversions
+  clangDaemon
+  LLVMSupport
+  LLVMTestingSupport
+  )

Added: clang-tools-extra/trunk/unittests/clangd/xpc/ConversionTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/xpc/ConversionTests.cpp?rev=351280&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/xpc/ConversionTests.cpp (added)
+++ clang-tools-extra/trunk/unittests/clangd/xpc/ConversionTests.cpp Tue Jan 15 16:24:22 2019
@@ -0,0 +1,36 @@
+//===-- ConversionTests.cpp  --------------------------*- C++ -*-----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xpc/Conversion.h"
+#include "gtest/gtest.h"
+
+#include <limits>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using namespace llvm;
+
+TEST(JsonXpcConversionTest, JsonToXpcToJson) {
+
+  for (auto &testcase :
+       {json::Value(false), json::Value(3.14), json::Value(42),
+        json::Value(-100), json::Value("foo"), json::Value(""),
+        json::Value("123"), json::Value(" "),
+        json::Value{true, "foo", nullptr, 42},
+        json::Value(json::Object{
+            {"a", true}, {"b", "foo"}, {"c", nullptr}, {"d", 42}})}) {
+    EXPECT_TRUE(testcase == xpcToJson(jsonToXpc(testcase))) << testcase;
+  }
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang




More information about the cfe-commits mailing list