1 | //===-- ClangdXPCTestClient.cpp ----------------------------------*- C++-*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "xpc/Conversion.h" |
10 | #include "clang/Basic/LLVM.h" |
11 | #include "llvm/ADT/SmallString.h" |
12 | #include "llvm/Support/LineIterator.h" |
13 | #include "llvm/Support/MemoryBuffer.h" |
14 | #include "llvm/Support/Path.h" |
15 | #include "llvm/Support/raw_ostream.h" |
16 | #include <dlfcn.h> |
17 | #include <stdio.h> |
18 | #include <string> |
19 | #include <xpc/xpc.h> |
20 | |
21 | typedef const char *(*clangd_xpc_get_bundle_identifier_t)(void); |
22 | |
23 | using namespace llvm; |
24 | using namespace clang; |
25 | |
26 | static std::string getLibraryPath() { |
27 | Dl_info info; |
28 | if (dladdr(address: (void *)(uintptr_t)getLibraryPath, info: &info) == 0) |
29 | llvm_unreachable("Call to dladdr() failed" ); |
30 | llvm::SmallString<128> LibClangPath; |
31 | LibClangPath = llvm::sys::path::parent_path( |
32 | path: llvm::sys::path::parent_path(path: info.dli_fname)); |
33 | llvm::sys::path::append(LibClangPath, "lib" , "ClangdXPC.framework" , |
34 | "ClangdXPC" ); |
35 | return std::string(LibClangPath.str()); |
36 | } |
37 | |
38 | static void dumpXPCObject(xpc_object_t Object, llvm::raw_ostream &OS) { |
39 | xpc_type_t Type = xpc_get_type(Object); |
40 | if (Type == XPC_TYPE_DICTIONARY) { |
41 | json::Value Json = clang::clangd::xpcToJson(Object); |
42 | OS << Json; |
43 | } else { |
44 | OS << "<UNKNOWN>" ; |
45 | } |
46 | } |
47 | |
48 | int main(int argc, char *argv[]) { |
49 | // Open the ClangdXPC dylib in the framework. |
50 | std::string LibPath = getLibraryPath(); |
51 | void *dlHandle = dlopen(LibPath.c_str(), RTLD_LOCAL | RTLD_FIRST); |
52 | if (!dlHandle) { |
53 | llvm::errs() << "Failed to load framework from \'" << LibPath << "\'\n" ; |
54 | return 1; |
55 | } |
56 | |
57 | // Lookup the XPC service bundle name, and launch it. |
58 | clangd_xpc_get_bundle_identifier_t clangd_xpc_get_bundle_identifier = |
59 | (clangd_xpc_get_bundle_identifier_t)dlsym( |
60 | handle: dlHandle, name: "clangd_xpc_get_bundle_identifier" ); |
61 | xpc_connection_t conn = xpc_connection_create( |
62 | clangd_xpc_get_bundle_identifier(), dispatch_get_main_queue()); |
63 | |
64 | // Dump the XPC events. |
65 | xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { |
66 | if (event == XPC_ERROR_CONNECTION_INVALID) { |
67 | llvm::errs() << "Received XPC_ERROR_CONNECTION_INVALID." ; |
68 | exit(EXIT_SUCCESS); |
69 | } |
70 | if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { |
71 | llvm::errs() << "Received XPC_ERROR_CONNECTION_INTERRUPTED." ; |
72 | exit(EXIT_SUCCESS); |
73 | } |
74 | |
75 | dumpXPCObject(event, llvm::outs()); |
76 | llvm::outs() << "\n" ; |
77 | }); |
78 | |
79 | xpc_connection_resume(conn); |
80 | |
81 | // Read the input to determine the things to send to clangd. |
82 | llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Stdin = |
83 | llvm::MemoryBuffer::getSTDIN(); |
84 | if (!Stdin) { |
85 | llvm::errs() << "Failed to get STDIN!\n" ; |
86 | return 1; |
87 | } |
88 | for (llvm::line_iterator It(**Stdin, /*SkipBlanks=*/true, |
89 | /*CommentMarker=*/'#'); |
90 | !It.is_at_eof(); ++It) { |
91 | StringRef Line = *It; |
92 | if (auto Request = json::parse(JSON: Line)) { |
93 | xpc_object_t Object = clangd::jsonToXpc(*Request); |
94 | xpc_connection_send_message(conn, Object); |
95 | } else { |
96 | llvm::errs() << llvm::Twine("JSON parse error: " ) |
97 | << llvm::toString(E: Request.takeError()); |
98 | return 1; |
99 | } |
100 | } |
101 | |
102 | dispatch_main(); |
103 | |
104 | // dispatch_main() doesn't return |
105 | return EXIT_FAILURE; |
106 | } |
107 | |