| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <functional> |
| #include <memory> |
| #include <set> |
| #include <string> |
| |
| #include <google/protobuf/compiler/code_generator.h> |
| #include <google/protobuf/compiler/plugin.h> |
| #include <google/protobuf/descriptor.h> |
| #include <google/protobuf/descriptor.pb.h> |
| #include <google/protobuf/io/printer.h> |
| #include <google/protobuf/io/zero_copy_stream.h> |
| |
| #include "perfetto/ext/base/string_utils.h" |
| |
| namespace perfetto { |
| namespace ipc { |
| namespace { |
| |
| using google::protobuf::FileDescriptor; |
| using google::protobuf::MethodDescriptor; |
| using google::protobuf::ServiceDescriptor; |
| using google::protobuf::compiler::GeneratorContext; |
| using google::protobuf::io::Printer; |
| using google::protobuf::io::ZeroCopyOutputStream; |
| using perfetto::base::SplitString; |
| using perfetto::base::StripChars; |
| using perfetto::base::StripSuffix; |
| using perfetto::base::ToUpper; |
| |
| static const char kBanner[] = "// DO NOT EDIT. Autogenerated by Perfetto IPC\n"; |
| |
| static const char kHeaderSvcClass[] = R"( |
| class $c$ : public ::perfetto::ipc::Service { |
| private: |
| static ::perfetto::ipc::ServiceDescriptor* NewDescriptor(); |
| |
| public: |
| ~$c$() override; |
| |
| static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic(); |
| |
| // Service implementation. |
| const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override; |
| |
| // Methods from the .proto file |
| )"; |
| |
| static const char kHeaderProxyClass[] = R"( |
| class $c$Proxy : public ::perfetto::ipc::ServiceProxy { |
| public: |
| explicit $c$Proxy(::perfetto::ipc::ServiceProxy::EventListener*); |
| ~$c$Proxy() override; |
| |
| // ServiceProxy implementation. |
| const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override; |
| |
| // Methods from the .proto file |
| )"; |
| |
| static const char kCppClassDefinitions[] = R"( |
| const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptorStatic() { |
| static auto* instance = NewDescriptor(); |
| return *instance; |
| } |
| |
| // Host-side definitions. |
| $c$::~$c$() = default; |
| |
| const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptor() { |
| return GetDescriptorStatic(); |
| } |
| |
| // Client-side definitions. |
| $c$Proxy::$c$Proxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener) |
| : ::perfetto::ipc::ServiceProxy(event_listener) {} |
| |
| $c$Proxy::~$c$Proxy() = default; |
| |
| const ::perfetto::ipc::ServiceDescriptor& $c$Proxy::GetDescriptor() { |
| return $c$::GetDescriptorStatic(); |
| } |
| )"; |
| |
| static const char kCppMethodDescriptor[] = R"( |
| desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{ |
| "$m$", |
| &_IPC_Decoder<$i$>, |
| &_IPC_Decoder<$o$>, |
| &_IPC_Invoker<$c$, $i$, $o$, &$c$::$m$>}); |
| )"; |
| |
| static const char kCppMethod[] = R"( |
| void $c$Proxy::$m$(const $i$& request, Deferred$o$ reply, int fd) { |
| BeginInvoke("$m$", request, ::perfetto::ipc::DeferredBase(std::move(reply)), |
| fd); |
| } |
| )"; |
| |
| std::string StripName(const FileDescriptor& file) { |
| return StripSuffix(file.name(), ".proto"); |
| } |
| |
| std::string GetStubName(const FileDescriptor& file) { |
| return StripName(file) + ".ipc"; |
| } |
| |
| void ForEachMethod(const ServiceDescriptor& svc, |
| std::function<void(const MethodDescriptor&, |
| const std::string&, |
| const std::string&)> function) { |
| for (int i = 0; i < svc.method_count(); i++) { |
| const MethodDescriptor& method = *svc.method(i); |
| // TODO if the input or output type are in a different namespace we need to |
| // emit the ::fully::qualified::name. |
| std::string input_type = method.input_type()->name(); |
| std::string output_type = method.output_type()->name(); |
| function(method, input_type, output_type); |
| } |
| } |
| |
| class IPCGenerator : public ::google::protobuf::compiler::CodeGenerator { |
| public: |
| explicit IPCGenerator(); |
| ~IPCGenerator() override; |
| |
| // CodeGenerator implementation |
| bool Generate(const google::protobuf::FileDescriptor* file, |
| const std::string& options, |
| google::protobuf::compiler::GeneratorContext* context, |
| std::string* error) const override; |
| |
| void GenerateServiceCpp(const FileDescriptor& file, |
| const ServiceDescriptor& svc, |
| Printer* printer) const; |
| void GenerateServiceHeader(const FileDescriptor& file, |
| const ServiceDescriptor& svc, |
| Printer* printer) const; |
| |
| std::vector<std::string> GetNamespaces(const FileDescriptor& file) const { |
| std::string pkg = file.package() + wrapper_namespace_; |
| return SplitString(pkg, "."); |
| } |
| |
| mutable std::string wrapper_namespace_; |
| }; |
| |
| IPCGenerator::IPCGenerator() = default; |
| IPCGenerator::~IPCGenerator() = default; |
| |
| void IPCGenerator::GenerateServiceHeader(const FileDescriptor& file, |
| const ServiceDescriptor& svc, |
| Printer* printer) const { |
| printer->Print("\n"); |
| for (const std::string& ns : GetNamespaces(file)) |
| printer->Print("namespace $ns$ {\n", "ns", ns); |
| |
| // Generate the host-side declarations. |
| printer->Print(kHeaderSvcClass, "c", svc.name()); |
| std::set<std::string> types_seen; |
| ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method, |
| const std::string& input_type, |
| const std::string& output_type) { |
| if (types_seen.count(output_type) == 0) { |
| printer->Print(" using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n", |
| "o", output_type); |
| types_seen.insert(output_type); |
| } |
| printer->Print(" virtual void $m$(const $i$&, Deferred$o$) = 0;\n\n", "m", |
| method.name(), "i", input_type, "o", output_type); |
| }); |
| printer->Print("};\n\n"); |
| |
| // Generate the client-side declarations. |
| printer->Print(kHeaderProxyClass, "c", svc.name()); |
| types_seen.clear(); |
| ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method, |
| const std::string& input_type, |
| const std::string& output_type) { |
| if (types_seen.count(output_type) == 0) { |
| printer->Print(" using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n", |
| "o", output_type); |
| types_seen.insert(output_type); |
| } |
| printer->Print(" void $m$(const $i$&, Deferred$o$, int fd = -1);\n\n", "m", |
| method.name(), "i", input_type, "o", output_type); |
| }); |
| printer->Print("};\n\n"); |
| |
| for (const std::string& ns : GetNamespaces(file)) |
| printer->Print("} // namespace $ns$\n", "ns", ns); |
| |
| printer->Print("\n"); |
| } |
| |
| void IPCGenerator::GenerateServiceCpp(const FileDescriptor& file, |
| const ServiceDescriptor& svc, |
| Printer* printer) const { |
| printer->Print("\n"); |
| for (const std::string& ns : GetNamespaces(file)) |
| printer->Print("namespace $ns$ {\n", "ns", ns); |
| |
| printer->Print("::perfetto::ipc::ServiceDescriptor* $c$::NewDescriptor() {\n", |
| "c", svc.name()); |
| printer->Print(" auto* desc = new ::perfetto::ipc::ServiceDescriptor();\n"); |
| printer->Print(" desc->service_name = \"$c$\";\n", "c", svc.name()); |
| |
| ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method, |
| const std::string& input_type, |
| const std::string& output_type) { |
| printer->Print(kCppMethodDescriptor, "c", svc.name(), "i", input_type, "o", |
| output_type, "m", method.name()); |
| }); |
| |
| printer->Print(" desc->methods.shrink_to_fit();\n"); |
| printer->Print(" return desc;\n"); |
| printer->Print("}\n\n"); |
| |
| printer->Print(kCppClassDefinitions, "c", svc.name()); |
| |
| ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method, |
| const std::string& input_type, |
| const std::string& output_type) { |
| printer->Print(kCppMethod, "c", svc.name(), "m", method.name(), "i", |
| input_type, "o", output_type); |
| }); |
| |
| for (const std::string& ns : GetNamespaces(file)) |
| printer->Print("} // namespace $ns$\n", "ns", ns); |
| } |
| |
| bool IPCGenerator::Generate(const FileDescriptor* file, |
| const std::string& options, |
| GeneratorContext* context, |
| std::string* error) const { |
| for (const std::string& option : SplitString(options, ",")) { |
| std::vector<std::string> option_pair = SplitString(option, "="); |
| if (option_pair[0] == "wrapper_namespace") { |
| wrapper_namespace_ = |
| option_pair.size() == 2 ? "." + option_pair[1] : std::string(); |
| } else { |
| *error = "Unknown plugin option: " + option_pair[0]; |
| return false; |
| } |
| } |
| |
| if (file->options().cc_generic_services()) { |
| *error = "Please set \"cc_generic_service = false\"."; |
| return false; |
| } |
| |
| const std::unique_ptr<ZeroCopyOutputStream> h_fstream( |
| context->Open(GetStubName(*file) + ".h")); |
| const std::unique_ptr<ZeroCopyOutputStream> cc_fstream( |
| context->Open(GetStubName(*file) + ".cc")); |
| |
| // Variables are delimited by $. |
| Printer h_printer(h_fstream.get(), '$'); |
| Printer cc_printer(cc_fstream.get(), '$'); |
| |
| std::string guard = ToUpper(file->package() + "_" + file->name() + "_H_"); |
| guard = StripChars(guard, ".-/\\", '_'); |
| |
| h_printer.Print(kBanner); |
| h_printer.Print("#ifndef $guard$\n#define $guard$\n\n", "guard", guard); |
| h_printer.Print("#include \"perfetto/ext/ipc/deferred.h\"\n"); |
| h_printer.Print("#include \"perfetto/ext/ipc/service.h\"\n"); |
| h_printer.Print("#include \"perfetto/ext/ipc/service_descriptor.h\"\n"); |
| h_printer.Print("#include \"perfetto/ext/ipc/service_proxy.h\"\n\n"); |
| |
| // Add #include-s to .gen.h file for each .proto file imported, including the |
| // .proto file that defines services itself. |
| h_printer.Print("#include \"$h$\"\n", "h", StripName(*file) + ".gen.h"); |
| for (int i = 0; i < file->dependency_count(); ++i) { |
| const FileDescriptor* file_dep = file->dependency(i); |
| h_printer.Print("#include \"$h$\"\n", "h", StripName(*file_dep) + ".gen.h"); |
| } |
| |
| cc_printer.Print(kBanner); |
| cc_printer.Print("#include \"$h$\"\n", "h", GetStubName(*file) + ".h"); |
| cc_printer.Print("#include \"perfetto/ext/ipc/codegen_helpers.h\"\n\n"); |
| cc_printer.Print("#include <memory>\n"); |
| |
| for (int i = 0; i < file->service_count(); i++) { |
| const ServiceDescriptor* svc = file->service(i); |
| GenerateServiceHeader(*file, *svc, &h_printer); |
| GenerateServiceCpp(*file, *svc, &cc_printer); |
| } |
| |
| h_printer.Print("#endif // $guard$\n", "guard", guard); |
| |
| return true; |
| } |
| |
| } // namespace |
| } // namespace ipc |
| } // namespace perfetto |
| |
| int main(int argc, char* argv[]) { |
| ::perfetto::ipc::IPCGenerator generator; |
| return google::protobuf::compiler::PluginMain(argc, argv, &generator); |
| } |