| //===-- ClangdXPCTestClient.cpp ----------------------------------*- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #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; |
| |
| static 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 std::string(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) { |
| llvm::errs() << "Failed to load framework from \'" << LibPath << "\'\n"; |
| 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; |
| } |