master
Сергей Маринкевич 2 months ago
parent 4433568545
commit 785fca07f1

@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# include source includes # include source includes
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# find python interpreter # find python interpreter
find_package(Python3 COMPONENTS Interpreter REQUIRED) find_package(Python3 COMPONENTS Interpreter REQUIRED)
@ -16,38 +17,54 @@ file(MAKE_DIRECTORY ${GENERATED_DIR})
set(RPC_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_rpc.py) set(RPC_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_rpc.py)
set(RPC_TEMPLATES ${CMAKE_CURRENT_SOURCE_DIR}/tools/templates) set(RPC_TEMPLATES ${CMAKE_CURRENT_SOURCE_DIR}/tools/templates)
# inputs to parse # helper to generate RPC code for an annotated header/source pair
set(RPC_INPUTS function(autocode_annotated_file OUT_BASENAME HEADER SOURCE)
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.h set(out_proxy_h "${GENERATED_DIR}/${OUT_BASENAME}.proxy.h")
) set(out_proxy_cpp "${GENERATED_DIR}/${OUT_BASENAME}.proxy.cpp")
set(out_skeleton_h "${GENERATED_DIR}/${OUT_BASENAME}.skeleton.h")
set(out_skeleton_cpp "${GENERATED_DIR}/${OUT_BASENAME}.skeleton.cpp")
# command to run generator add_custom_command(
add_custom_command( OUTPUT
OUTPUT ${out_proxy_h}
${GENERATED_DIR}/MyService.proxy.h ${out_proxy_cpp}
${GENERATED_DIR}/MyService.proxy.cpp ${out_skeleton_h}
${GENERATED_DIR}/MyService.skeleton.h ${out_skeleton_cpp}
${GENERATED_DIR}/MyService.skeleton.cpp COMMAND ${CMAKE_COMMAND} -E echo "Generating RPC stubs for ${OUT_BASENAME}..."
COMMAND ${CMAKE_COMMAND} -E echo "Generating RPC stubs..." COMMAND ${Python3_EXECUTABLE} ${RPC_GENERATOR} --out-dir ${GENERATED_DIR}
COMMAND ${Python3_EXECUTABLE} ${RPC_GENERATOR} --out-dir ${GENERATED_DIR} --compile-commands ${CMAKE_BINARY_DIR}/compile_commands.json
--compile-commands ${CMAKE_BINARY_DIR}/compile_commands.json --templates ${RPC_TEMPLATES}
--templates ${RPC_TEMPLATES} --header ${HEADER}
${RPC_INPUTS} --source ${SOURCE}
DEPENDS ${RPC_GENERATOR} ${RPC_TEMPLATES} ${RPC_INPUTS} --out-base ${OUT_BASENAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${RPC_GENERATOR} ${RPC_TEMPLATES} ${HEADER} ${SOURCE}
COMMENT "Running RPC code generator" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM COMMENT "Running RPC code generator for ${OUT_BASENAME}"
) VERBATIM
)
# accumulate all generated files into a global property so we can
# have one umbrella target that depends on all of them
set_property(GLOBAL APPEND PROPERTY AUTOCODE_GENERATED_FILES
${out_proxy_h}
${out_proxy_cpp}
${out_skeleton_h}
${out_skeleton_cpp}
)
endfunction()
include_directories(${GENERATED_DIR}) include_directories(${GENERATED_DIR})
add_custom_target(rpc_generated DEPENDS # declare all annotated files here
${GENERATED_DIR}/MyService.proxy.h autocode_annotated_file(MyService
${GENERATED_DIR}/MyService.proxy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.h
${GENERATED_DIR}/MyService.skeleton.h ${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.cpp
${GENERATED_DIR}/MyService.skeleton.cpp
) )
# umbrella target that depends on all generated RPC files
get_property(_all_generated GLOBAL PROPERTY AUTOCODE_GENERATED_FILES)
add_custom_target(rpc_generated DEPENDS ${_all_generated})
# Server # Server
add_executable(server add_executable(server
src/server.cpp src/server.cpp

@ -20,17 +20,21 @@ project/
├── CMakeLists.txt ├── CMakeLists.txt
├── README.md ├── README.md
├── src ├── src
│   ├── client.cpp # пример клиента, использующего MyServiceProxy и IpcPipeChannel │   ├── client.cpp
│   ├── server.cpp # пример сервера, использующего MyServiceSkeleton и IpcPipeChannel │   ├── common
│   │   ├── ipc
│   │   │   ├── IpcChannel.h
│   │   │   ├── IpcMessage.h
│   │   │   └── IpcPipeChannel.h
│   │   ├── proxy
│   │   │   └── ProxyMarshaller.h
│   │   └── rpc
│   │   ├── rpc_export.h
│   │   ├── RpcInvoker.h
│   │   └── RpcSerializer.h
│   ├── MyService.cpp │   ├── MyService.cpp
│   ├── MyService.h │   ├── MyService.h
│   ├── rpc_export.h │   └── server.cpp
│   └── rpc
│   ├── IpcMessage.h # типизированное IPCсообщение (add<T>/get<T>)
│   ├── IpcPipeChannel.h# реализация RpcChannel поверх FIFO
│   ├── RpcChannel.h # абстрактный канал для RPC
│   ├── ProxyMarshaller.h # клиентское ядро RPC (call<Ret>(method, args...))
│   └── RpcInvoker.h # серверное ядро RPC (dispatch)
├── tools ├── tools
│ ├── generate_rpc.py │ ├── generate_rpc.py
│ └── templates │ └── templates

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "IpcChannel.h" #include <ipc/IpcChannel.h>
class ProxyMarshaller { class ProxyMarshaller {
public: public:

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "IpcMessage.h" #include <ipc/IpcMessage.h>
#include <functional> #include <functional>
#include <string> #include <string>

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <rpc_export.h> #include <rpc/rpc_export.h>
// annotate with clang attribute or via comment annotation recognized by libclang // annotate with clang attribute or via comment annotation recognized by libclang
// Use ANNOTATE attribute supported by clang: __attribute__((annotate("export"))) // Use ANNOTATE attribute supported by clang: __attribute__((annotate("export")))

@ -1,5 +1,5 @@
#include "MyService.proxy.h" #include "MyService.proxy.h"
#include "rpc/IpcPipeChannel.h" #include "ipc/IpcPipeChannel.h"
#include <sys/stat.h> #include <sys/stat.h>

@ -1,7 +1,7 @@
#include "MyService.h" #include "MyService.h"
#include "MyService.skeleton.h" #include "MyService.skeleton.h"
#include "rpc/IpcPipeChannel.h" #include "ipc/IpcPipeChannel.h"
#include <sys/stat.h> #include <sys/stat.h>

@ -73,11 +73,22 @@ def load_compile_flags(compile_commands_path, src_path):
if skip_next: if skip_next:
skip_next = False skip_next = False
continue continue
if tok.endswith('g++') or tok.endswith('clang++') or tok.endswith('clang') or tok.endswith('g++') or tok.endswith('cc'): # skip the compiler binary itself
if (
tok.endswith("g++")
or tok.endswith("clang++")
or tok.endswith("clang")
or tok.endswith("cc")
or tok.endswith("c++")
):
continue continue
if tok == "-c": if tok == "-c":
skip_next = True skip_next = True
continue continue
# drop output file flag: -o <file>
if tok == "-o":
skip_next = True
continue
filtered.append(tok) filtered.append(tok)
return filtered return filtered
return [] return []
@ -183,7 +194,13 @@ def main():
p = argparse.ArgumentParser() p = argparse.ArgumentParser()
p.add_argument("--out-dir", "-o", required=True) p.add_argument("--out-dir", "-o", required=True)
p.add_argument("--compile-commands", "-c", default=None) p.add_argument("--compile-commands", "-c", default=None)
p.add_argument("inputs", nargs="+") p.add_argument("--header", required=True, help="C++ header file to scan for exported RPC classes")
p.add_argument("--source", required=True, help="C++ source file to use for resolving compile flags (from compile_commands.json)")
p.add_argument(
"--out-base",
required=True,
help="Base name for generated files: <out-base>.proxy.[h|cpp], <out-base>.skeleton.[h|cpp]",
)
p.add_argument("--templates", "-t", default=os.path.join(os.path.dirname(__file__), "templates")) p.add_argument("--templates", "-t", default=os.path.join(os.path.dirname(__file__), "templates"))
args = p.parse_args() args = p.parse_args()
@ -199,28 +216,68 @@ def main():
except Exception as e: except Exception as e:
print("WARNING: cannot set libclang path:", e) print("WARNING: cannot set libclang path:", e)
# determine compile flags from the source file (which is what appears in compile_commands.json)
if not os.path.exists(args.header):
print("ERROR: header file not found:", args.header)
return 1
if not os.path.exists(args.source):
print("ERROR: source file not found:", args.source)
return 1
compile_args = load_compile_flags(args.compile_commands, args.source)
if not any(a.startswith("-x") for a in compile_args):
compile_args = ["-x", "c++", "-std=c++17"] + compile_args
print("Parsing", args.header, "with args:", compile_args)
index = Index.create() index = Index.create()
all_classes = [] classes = parse_file(index, args.header, compile_args)
for inp in args.inputs: if not classes:
if not os.path.exists(inp): print("No exported classes/methods found in header. Nothing to generate.")
print("WARN: input file not found:", inp)
continue
compile_args = load_compile_flags(args.compile_commands, inp)
# ensure -x c++ if missing
if not any(a.startswith("-x") for a in compile_args):
compile_args = ["-x", "c++", "-std=c++17"] + compile_args
print("Parsing", inp, "with args:", compile_args)
classes = parse_file(index, inp, compile_args)
all_classes.extend(classes)
if not all_classes:
print("No exported classes/methods found. Nothing to generate.")
return 0 return 0
# render templates # For this PoC we expect a single exported service per header.
render_templates(all_classes, out_dir, args.templates) # If there are multiple, refuse to guess which one should define the filenames.
print("Generated files for classes:", ", ".join(c.name for c in all_classes)) if len(classes) > 1:
print(
"ERROR: multiple exported classes found in header; "
"current generator expects exactly one when using --out-base."
)
for c in classes:
print(" -", c.name)
return 1
# render templates using the single discovered class but the userprovided base name
cls = classes[0]
env = Environment(
loader=FileSystemLoader(args.templates),
autoescape=False,
trim_blocks=True,
lstrip_blocks=True,
)
proxy_h = env.get_template("proxy.h.j2")
proxy_cpp = env.get_template("proxy.cpp.j2")
skeleton_h = env.get_template("skeleton.h.j2")
skeleton_cpp = env.get_template("skeleton.cpp.j2")
base = args.out_base
with open(f"{out_dir}/{base}.proxy.h", "w") as f:
f.write(proxy_h.render(cls=cls))
with open(f"{out_dir}/{base}.proxy.cpp", "w") as f:
f.write(proxy_cpp.render(cls=cls))
with open(f"{out_dir}/{base}.skeleton.h", "w") as f:
f.write(skeleton_h.render(cls=cls))
with open(f"{out_dir}/{base}.skeleton.cpp", "w") as f:
f.write(skeleton_cpp.render(cls=cls))
print("Generated files for class", cls.name, "into base", base)
return 0 return 0
if __name__ == "__main__": if __name__ == "__main__":

@ -1,5 +1,5 @@
#include "{{ cls.name }}.proxy.h" #include "{{ cls.name }}.proxy.h"
#include "rpc/ProxyMarshaller.h" #include "proxy/ProxyMarshaller.h"
class {{ cls.name }}Proxy::Impl { class {{ cls.name }}Proxy::Impl {
public: public:

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "rpc/IpcChannel.h" #include "ipc/IpcChannel.h"
class {{ cls.name }}Proxy { class {{ cls.name }}Proxy {
public: public:

Loading…
Cancel
Save