9#include <capnp/schema.capnp.h>
10#include <capnp/schema.h>
11#include <capnp/schema-parser.h>
17#include <initializer_list>
21#include <kj/filesystem.h>
23#include <kj/exception.h>
33#define PROXY_BIN "mpgen"
34#define PROXY_DECL "mp/proxy.h"
35#define PROXY_TYPES "mp/proxy-types.h"
46template <
typename Reader>
57template <
typename Reader>
69template <
typename Reader>
81static void ForEachMethod(
const capnp::InterfaceSchema&
interface,
const std::function<
void(
const capnp::InterfaceSchema&
interface,
const capnp::InterfaceSchema::Method)>& callback)
86 for (
const auto method :
interface.getMethods()) {
103 template <
typename Value>
109 operator std::string()
const {
return m_os.str(); }
113static std::string
Cap(kj::StringPtr str)
115 std::string result = str;
116 if (!result.empty() &&
'a' <= result[0] && result[0] <=
'z') result[0] -=
'a' -
'A';
122 return !(type.isVoid() || type.isBool() || type.isInt8() || type.isInt16() || type.isInt32() || type.isInt64() ||
123 type.isUInt8() || type.isUInt16() || type.isUInt32() || type.isUInt64() || type.isFloat32() ||
124 type.isFloat64() || type.isEnum());
157 field.param_is_set =
true;
161 field.result_is_set =
true;
164 if (!param &&
field_name == kj::StringPtr{
"result"}) {
214 std::ostringstream out;
216 if (!
field.param_is_set) {
218 }
else if (
field.result_is_set) {
219 out <<
"FIELD_IN | FIELD_OUT";
223 if (
field.optional) out <<
" | FIELD_OPTIONAL";
224 if (
field.requested) out <<
" | FIELD_REQUESTED";
247 const kj::ReadableDirectory&
src_dir,
248 const std::vector<kj::Own<const kj::ReadableDirectory>>&
import_dirs)
255 throw std::runtime_error(
"src_prefix is not src_file prefix");
265 throw std::runtime_error(
"include_prefix is not src_file prefix");
274 std::vector<std::string>
args;
276 args.emplace_back(
"compile");
277 args.emplace_back(
"--src-prefix=");
280 args.emplace_back(
"--import-path=");
290 const capnp::SchemaParser
parser;
299 cpp_server <<
"// IWYU pragma: no_include <kj/memory.h>\n";
300 cpp_server <<
"// IWYU pragma: no_include <memory>\n";
304 cpp_server <<
"#include <capnp/generated-header-support.h>\n";
318 cpp_client <<
"// IWYU pragma: no_include <kj/memory.h>\n";
319 cpp_client <<
"// IWYU pragma: no_include <memory>\n";
324 cpp_client <<
"#include <capnp/capability.h>\n";
326 cpp_client <<
"#include <capnp/generated-header-support.h>\n";
342 cpp_types <<
"// IWYU pragma: no_include \"mp/proxy.h\"\n";
343 cpp_types <<
"// IWYU pragma: no_include \"mp/proxy-io.h\"\n";
351 std::ranges::transform(guard, guard.begin(), [](
unsigned char c) ->
unsigned char {
352 if (
'0' <= c && c <=
'9') return c;
353 if (
'A' <= c && c <=
'Z') return c;
354 if (
'a' <= c && c <=
'z') return c -
'a' +
'A';
360 inl <<
"#ifndef " << guard <<
"_PROXY_TYPES_H\n";
361 inl <<
"#define " << guard <<
"_PROXY_TYPES_H\n\n";
362 inl <<
"// IWYU pragma: no_include \"mp/proxy.h\"\n";
363 inl <<
"#include <mp/proxy.h> // IWYU pragma: keep\n";
364 inl <<
"#include <" <<
include_path <<
".proxy.h> // IWYU pragma: keep\n";
367 inl <<
"#include \"" <<
annotation.getValue().getText() <<
"\" // IWYU pragma: export\n";
370 inl <<
"namespace mp {\n";
374 h <<
"#ifndef " << guard <<
"_PROXY_H\n";
375 h <<
"#define " << guard <<
"_PROXY_H\n\n";
376 h <<
"#include <" <<
include_path <<
".h> // IWYU pragma: keep\n";
379 h <<
"#include \"" <<
annotation.getValue().getText() <<
"\" // IWYU pragma: export\n";
383 h <<
"#if defined(__GNUC__)\n";
384 h <<
"#pragma GCC diagnostic push\n";
385 h <<
"#if !defined(__has_warning)\n";
386 h <<
"#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
387 h <<
"#elif __has_warning(\"-Wsuggest-override\")\n";
388 h <<
"#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
391 h <<
"namespace mp {\n";
405 std::ostringstream dec;
416 accessors <<
" template<typename S> static auto get(S&& s) -> decltype(s.get" <<
cap <<
"()) { return s.get" <<
cap <<
"(); }\n";
417 accessors <<
" template<typename S> static bool has(S&& s) { return s.has" <<
cap <<
"(); }\n";
418 accessors <<
" template<typename S, typename A> static void set(S&& s, A&& a) { s.set" <<
cap
419 <<
"(std::forward<A>(a)); }\n";
420 accessors <<
" template<typename S, typename... A> static decltype(auto) init(S&& s, A&&... a) { return s.init"
421 <<
cap <<
"(std::forward<A>(a)...); }\n";
422 accessors <<
" template<typename S> static bool getWant(S&& s) { return s.getWant" <<
cap <<
"(); }\n";
423 accessors <<
" template<typename S> static void setWant(S&& s) { s.setWant" <<
cap <<
"(true); }\n";
424 accessors <<
" template<typename S> static bool getHas(S&& s) { return s.getHas" <<
cap <<
"(); }\n";
425 accessors <<
" template<typename S> static void setHas(S&& s) { s.setHas" <<
cap <<
"(true); }\n";
435 if (
node.getProto().isStruct()) {
448 for (
const auto param :
node.getProto().getParameters()) {
456 dec <<
"typename " << param.getName();
470 dec <<
" using Accessors = std::tuple<";
473 if (
field.skip)
continue;
475 dec <<
Cap(
field.param.getProto().getName()) <<
"Accessor";
479 dec <<
" static constexpr size_t fields = " << i <<
";\n";
483 inl <<
"template<>\n";
490 if (
field.skip)
continue;
494 inl <<
" static decltype(auto) get(std::integral_constant<size_t, " << i <<
">) { return "
498 inl <<
" static constexpr size_t fields = " << i <<
";\n";
506 std::ostringstream
client;
511 client <<
" using ProxyClientCustom::ProxyClientCustom;\n";
512 client <<
" ~ProxyClient();\n";
514 std::ostringstream server;
518 server <<
"public:\n";
519 server <<
" using ProxyServerCustom::ProxyServerCustom;\n";
520 server <<
" ~ProxyServer();\n";
527 const kj::StringPtr
method_name = method.getProto().getName();
537 for (
const auto schema_field : method.getParamType().getFields()) {
540 for (
const auto schema_field : method.getResultType().getFields()) {
560 if (
field.skip)
continue;
567 for (
int i = 0; i <
field.args; ++i) {
584 if (
field.exception.size()) {
599 if (
field.exception.size()) {
601 }
else if (
field.retval) {
631 <<
"Context call_context) override;\n";
635 <<
"Context call_context) {\n"
636 " return serverInvoke(*this, call_context, "
649 dec <<
"\n" <<
client.str() <<
"\n" << server.str() <<
"\n";
658 dec <<
" using Client = ProxyClient<Message>;\n";
659 dec <<
" using Server = ProxyServer<Message>;\n";
664 <<
">::~ProxyClient() { clientDestroy(*this); " <<
client_destroy.str() <<
" }\n";
666 <<
">::~ProxyServer() { serverDestroy(*this); }\n";
684 inl <<
"} // namespace mp\n";
687 h <<
"} // namespace mp\n";
688 h <<
"#if defined(__GNUC__)\n";
689 h <<
"#pragma GCC diagnostic pop\n";
697 KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
699 std::cerr <<
"Usage: " <<
PROXY_BIN <<
" SRC_PREFIX INCLUDE_PREFIX SRC_FILE [IMPORT_PATH...]\n";
703 std::vector<kj::Own<const kj::ReadableDirectory>>
import_dirs;
704 auto fs = kj::newDiskFilesystem();
705 auto cwd = fs->getCurrentPath();
706 kj::Own<const kj::ReadableDirectory>
src_dir;
710 throw std::runtime_error(std::string(
"Failed to open src_prefix prefix directory: ") +
argv[1]);
712 for (
int i = 4; i <
argc; ++i) {
717 throw std::runtime_error(std::string(
"Failed to open import directory: ") +
argv[i]);
730 std::cerr <<
"mpgen error: " << kj::str(*exception).cStr() <<
'\n';
kj::ArrayPtr< const char > CharSlice
constexpr uint64_t EXCEPTION_ANNOTATION_ID
constexpr uint64_t NAME_ANNOTATION_ID
static OutputStream & operator<<(OutputStream &os, const Array &array)
static void ForEachMethod(const capnp::InterfaceSchema &interface, const std::function< void(const capnp::InterfaceSchema &interface, const capnp::InterfaceSchema::Method)> &callback)
constexpr uint64_t SKIP_ANNOTATION_ID
constexpr uint64_t WRAP_ANNOTATION_ID
constexpr uint64_t INCLUDE_ANNOTATION_ID
static void Generate(kj::StringPtr src_prefix, kj::StringPtr include_prefix, kj::StringPtr src_file, const std::vector< kj::StringPtr > &import_paths, const kj::ReadableDirectory &src_dir, const std::vector< kj::Own< const kj::ReadableDirectory > > &import_dirs)
static bool GetAnnotationInt32(const Reader &reader, uint64_t id, int32_t *result)
static bool BoxedType(const ::capnp::Type &type)
constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID
constexpr uint64_t NAMESPACE_ANNOTATION_ID
static bool AnnotationExists(const Reader &reader, uint64_t id)
static std::string AccessorType(kj::StringPtr base_name, const Field &field)
static std::string Cap(kj::StringPtr str)
static bool GetAnnotationText(const Reader &reader, uint64_t id, kj::StringPtr *result)
constexpr uint64_t COUNT_ANNOTATION_ID
std::unique_ptr< ProxyClient< messages::FooInterface > > client
ProcessId StartProcess(const std::vector< std::string > &args)
Start a process and return its process id.
int WaitProcess(ProcessId pid)
Wait for a process to exit and return its exit code.
::capnp::StructSchema::Field result
::capnp::StructSchema::Field param
std::vector< Field > fields
std::map< kj::StringPtr, int > field_idx
void addField(const ::capnp::StructSchema::Field &schema_field, bool param, bool result)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.