//===-- 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 #include #include #include 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 << ""; } } 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> 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; }