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

@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# include source includes
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# find python interpreter
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_TEMPLATES ${CMAKE_CURRENT_SOURCE_DIR}/tools/templates)
# inputs to parse
set(RPC_INPUTS
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.h
)
# helper to generate RPC code for an annotated header/source pair
function(autocode_annotated_file OUT_BASENAME HEADER SOURCE)
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(
OUTPUT
${GENERATED_DIR}/MyService.proxy.h
${GENERATED_DIR}/MyService.proxy.cpp
${GENERATED_DIR}/MyService.skeleton.h
${GENERATED_DIR}/MyService.skeleton.cpp
COMMAND ${CMAKE_COMMAND} -E echo "Generating RPC stubs..."
${out_proxy_h}
${out_proxy_cpp}
${out_skeleton_h}
${out_skeleton_cpp}
COMMAND ${CMAKE_COMMAND} -E echo "Generating RPC stubs for ${OUT_BASENAME}..."
COMMAND ${Python3_EXECUTABLE} ${RPC_GENERATOR} --out-dir ${GENERATED_DIR}
--compile-commands ${CMAKE_BINARY_DIR}/compile_commands.json
--templates ${RPC_TEMPLATES}
${RPC_INPUTS}
DEPENDS ${RPC_GENERATOR} ${RPC_TEMPLATES} ${RPC_INPUTS}
--header ${HEADER}
--source ${SOURCE}
--out-base ${OUT_BASENAME}
DEPENDS ${RPC_GENERATOR} ${RPC_TEMPLATES} ${HEADER} ${SOURCE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running RPC code generator"
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})
add_custom_target(rpc_generated DEPENDS
${GENERATED_DIR}/MyService.proxy.h
${GENERATED_DIR}/MyService.proxy.cpp
${GENERATED_DIR}/MyService.skeleton.h
${GENERATED_DIR}/MyService.skeleton.cpp
# declare all annotated files here
autocode_annotated_file(MyService
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.h
${CMAKE_CURRENT_SOURCE_DIR}/src/MyService.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
add_executable(server
src/server.cpp

@ -20,17 +20,21 @@ project/
├── CMakeLists.txt
├── README.md
├── src
│   ├── client.cpp # пример клиента, использующего MyServiceProxy и IpcPipeChannel
│   ├── server.cpp # пример сервера, использующего MyServiceSkeleton и IpcPipeChannel
│   ├── client.cpp
│   ├── common
│   │   ├── ipc
│   │   │   ├── IpcChannel.h
│   │   │   ├── IpcMessage.h
│   │   │   └── IpcPipeChannel.h
│   │   ├── proxy
│   │   │   └── ProxyMarshaller.h
│   │   └── rpc
│   │   ├── rpc_export.h
│   │   ├── RpcInvoker.h
│   │   └── RpcSerializer.h
│   ├── MyService.cpp
│   ├── MyService.h
│   ├── rpc_export.h
│   └── 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)
│   └── server.cpp
├── tools
│ ├── generate_rpc.py
│ └── templates

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

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

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

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

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

@ -73,11 +73,22 @@ def load_compile_flags(compile_commands_path, src_path):
if skip_next:
skip_next = False
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
if tok == "-c":
skip_next = True
continue
# drop output file flag: -o <file>
if tok == "-o":
skip_next = True
continue
filtered.append(tok)
return filtered
return []
@ -183,7 +194,13 @@ def main():
p = argparse.ArgumentParser()
p.add_argument("--out-dir", "-o", required=True)
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"))
args = p.parse_args()
@ -199,28 +216,68 @@ def main():
except Exception as e:
print("WARNING: cannot set libclang path:", e)
index = Index.create()
all_classes = []
# 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
for inp in args.inputs:
if not os.path.exists(inp):
print("WARN: input file not found:", inp)
continue
compile_args = load_compile_flags(args.compile_commands, inp)
# ensure -x c++ if missing
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", 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.")
print("Parsing", args.header, "with args:", compile_args)
index = Index.create()
classes = parse_file(index, args.header, compile_args)
if not classes:
print("No exported classes/methods found in header. Nothing to generate.")
return 0
# render templates
render_templates(all_classes, out_dir, args.templates)
print("Generated files for classes:", ", ".join(c.name for c in all_classes))
# For this PoC we expect a single exported service per header.
# If there are multiple, refuse to guess which one should define the filenames.
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
if __name__ == "__main__":

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

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

Loading…
Cancel
Save