diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc index ab7d8697589..ffdeb8f6b07 100644 --- a/src/compiler/objective_c_generator.cc +++ b/src/compiler/objective_c_generator.cc @@ -212,37 +212,49 @@ void PrintMethodImplementations(Printer* printer, return output; } -::grpc::string GetHeader(const ServiceDescriptor* service) { +::grpc::string GetProtocol(const ServiceDescriptor* service) { ::grpc::string output; - { - // Scope the output stream so it closes and finalizes output to the string. - grpc::protobuf::io::StringOutputStream output_stream(&output); - Printer printer(&output_stream, '$'); - - map< ::grpc::string, ::grpc::string> vars = { - {"service_class", ServiceClassName(service)}}; - printer.Print(vars, "@protocol $service_class$ \n\n"); + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); - for (int i = 0; i < service->method_count(); i++) { - PrintMethodDeclarations(&printer, service->method(i)); - } - printer.Print("@end\n\n"); + map< ::grpc::string, ::grpc::string> vars = { + {"service_class", ServiceClassName(service)}}; - printer.Print( - "/**\n" - " * Basic service implementation, over gRPC, that only does\n" - " * marshalling and parsing.\n" - " */\n"); - printer.Print(vars, - "@interface $service_class$ :" - " GRPCProtoService<$service_class$>\n"); - printer.Print( - "- (instancetype)initWithHost:(NSString *)host" - " NS_DESIGNATED_INITIALIZER;\n"); - printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); - printer.Print("@end\n"); + printer.Print(vars, "@protocol $service_class$ \n\n"); + for (int i = 0; i < service->method_count(); i++) { + PrintMethodDeclarations(&printer, service->method(i)); } + printer.Print("@end\n\n"); + + return output; +} + +::grpc::string GetInterface(const ServiceDescriptor* service) { + ::grpc::string output; + + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); + + map< ::grpc::string, ::grpc::string> vars = { + {"service_class", ServiceClassName(service)}}; + + printer.Print(vars, + "/**\n" + " * Basic service implementation, over gRPC, that only does\n" + " * marshalling and parsing.\n" + " */\n"); + printer.Print(vars, + "@interface $service_class$ :" + " GRPCProtoService<$service_class$>\n"); + printer.Print( + "- (instancetype)initWithHost:(NSString *)host" + " NS_DESIGNATED_INITIALIZER;\n"); + printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); + printer.Print("@end\n"); + return output; } @@ -258,26 +270,32 @@ void PrintMethodImplementations(Printer* printer, {"service_class", ServiceClassName(service)}, {"package", service->file()->package()}}; - printer.Print(vars, "@implementation $service_class$\n\n"); + printer.Print(vars, + "@implementation $service_class$\n\n" + "// Designated initializer\n" + "- (instancetype)initWithHost:(NSString *)host {\n" + " self = [super initWithHost:host\n" + " packageName:@\"$package$\"\n" + " serviceName:@\"$service_name$\"];\n" + " return self;\n" + "}\n\n"); - printer.Print("// Designated initializer\n"); - printer.Print("- (instancetype)initWithHost:(NSString *)host {\n"); - printer.Print( - vars, - " return (self = [super initWithHost:host" - " packageName:@\"$package$\" serviceName:@\"$service_name$\"]);\n"); - printer.Print("}\n\n"); printer.Print( "// Override superclass initializer to disallow different" - " package and service names.\n"); - printer.Print("- (instancetype)initWithHost:(NSString *)host\n"); - printer.Print(" packageName:(NSString *)packageName\n"); - printer.Print(" serviceName:(NSString *)serviceName {\n"); - printer.Print(" return [self initWithHost:host];\n"); - printer.Print("}\n\n"); - printer.Print("+ (instancetype)serviceWithHost:(NSString *)host {\n"); - printer.Print(" return [[self alloc] initWithHost:host];\n"); - printer.Print("}\n\n\n"); + " package and service names.\n" + "- (instancetype)initWithHost:(NSString *)host\n" + " packageName:(NSString *)packageName\n" + " serviceName:(NSString *)serviceName {\n" + " return [self initWithHost:host];\n" + "}\n\n"); + + printer.Print( + "#pragma mark - Class Methods\n\n" + "+ (instancetype)serviceWithHost:(NSString *)host {\n" + " return [[self alloc] initWithHost:host];\n" + "}\n\n"); + + printer.Print("#pragma mark - Method Implementations\n\n"); for (int i = 0; i < service->method_count(); i++) { PrintMethodImplementations(&printer, service->method(i)); diff --git a/src/compiler/objective_c_generator.h b/src/compiler/objective_c_generator.h index d3aed76c4f4..eb1c7ff005a 100644 --- a/src/compiler/objective_c_generator.h +++ b/src/compiler/objective_c_generator.h @@ -31,9 +31,13 @@ using ::grpc::string; // Returns forward declaration of classes in the generated header file. string GetAllMessageClasses(const FileDescriptor* file); -// Returns the content to be included in the "global_scope" insertion point of -// the generated header file. -string GetHeader(const ServiceDescriptor* service); +// Returns the content to be included defining the @protocol segment at the +// insertion point of the generated implementation file. +string GetProtocol(const ServiceDescriptor* service); + +// Returns the content to be included defining the @interface segment at the +// insertion point of the generated implementation file. +string GetInterface(const ServiceDescriptor* service); // Returns the content to be included in the "global_scope" insertion point of // the generated implementation file. diff --git a/src/compiler/objective_c_generator_helpers.h b/src/compiler/objective_c_generator_helpers.h index 4004e6aef8f..a284da97f4b 100644 --- a/src/compiler/objective_c_generator_helpers.h +++ b/src/compiler/objective_c_generator_helpers.h @@ -40,5 +40,45 @@ inline string ServiceClassName(const ServiceDescriptor* service) { string prefix = file->options().objc_class_prefix(); return prefix + service->name(); } + +inline ::grpc::string LocalImport(const ::grpc::string& import) { + return ::grpc::string("#import \"" + import + "\"\n"); +} + +inline ::grpc::string SystemImport(const ::grpc::string& import) { + return ::grpc::string("#import <" + import + ">\n"); +} + +inline ::grpc::string PreprocConditional(::grpc::string symbol, bool invert) { + return invert ? "!defined(" + symbol + ") || !" + symbol + : "defined(" + symbol + ") && " + symbol; +} + +inline ::grpc::string PreprocIf(const ::grpc::string& symbol, + const ::grpc::string& if_true) { + return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" + + if_true + "#endif\n"); +} + +inline ::grpc::string PreprocIfNot(const ::grpc::string& symbol, + const ::grpc::string& if_true) { + return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" + + if_true + "#endif\n"); +} + +inline ::grpc::string PreprocIfElse(const ::grpc::string& symbol, + const ::grpc::string& if_true, + const ::grpc::string& if_false) { + return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" + + if_true + "#else\n" + if_false + "#endif\n"); +} + +inline ::grpc::string PreprocIfNotElse(const ::grpc::string& symbol, + const ::grpc::string& if_true, + const ::grpc::string& if_false) { + return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" + + if_true + "#else\n" + if_false + "#endif\n"); +} + } // namespace grpc_objective_c_generator #endif // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H diff --git a/src/compiler/objective_c_plugin.cc b/src/compiler/objective_c_plugin.cc index d5d488e84d4..76703d79cd8 100644 --- a/src/compiler/objective_c_plugin.cc +++ b/src/compiler/objective_c_plugin.cc @@ -29,12 +29,42 @@ using ::google::protobuf::compiler::objectivec:: IsProtobufLibraryBundledProtoFile; using ::google::protobuf::compiler::objectivec::ProtobufLibraryFrameworkName; +using ::grpc_objective_c_generator::LocalImport; +using ::grpc_objective_c_generator::PreprocIfElse; +using ::grpc_objective_c_generator::PreprocIfNot; +using ::grpc_objective_c_generator::SystemImport; + +namespace { + +inline ::grpc::string ImportProtoHeaders( + const grpc::protobuf::FileDescriptor* dep, const char* indent) { + ::grpc::string header = grpc_objective_c_generator::MessageHeaderName(dep); + + if (!IsProtobufLibraryBundledProtoFile(dep)) { + return indent + LocalImport(header); + } + + ::grpc::string base_name = header; + grpc_generator::StripPrefix(&base_name, "google/protobuf/"); + // create the import code snippet + ::grpc::string framework_header = + ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name; + + static const ::grpc::string kFrameworkImportsCondition = + "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS"; + return PreprocIfElse(kFrameworkImportsCondition, + indent + SystemImport(framework_header), + indent + LocalImport(header)); +} + +} // namespace class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { public: ObjectiveCGrpcGenerator() {} virtual ~ObjectiveCGrpcGenerator() {} + public: virtual bool Generate(const grpc::protobuf::FileDescriptor* file, const ::grpc::string& parameter, grpc::protobuf::compiler::GeneratorContext* context, @@ -44,97 +74,68 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { return true; } + static const ::grpc::string kNonNullBegin = "NS_ASSUME_NONNULL_BEGIN\n"; + static const ::grpc::string kNonNullEnd = "NS_ASSUME_NONNULL_END\n"; + static const ::grpc::string kProtocolOnly = "GPB_GRPC_PROTOCOL_ONLY"; + static const ::grpc::string kForwardDeclare = + "GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO"; + ::grpc::string file_name = google::protobuf::compiler::objectivec::FilePath(file); - ::grpc::string prefix = file->options().objc_class_prefix(); { // Generate .pbrpc.h - ::grpc::string imports = - ::grpc::string("#if !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO\n") + - "#import \"" + file_name + - ".pbobjc.h\"\n" - "#endif\n\n" - "#import \n" - "#import \n" - "#import \n" - "#import \n"; - - ::grpc::string proto_imports; - proto_imports += "#if GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO\n" + - grpc_objective_c_generator::GetAllMessageClasses(file) + - "#else\n"; + ::grpc::string imports = LocalImport(file_name + ".pbobjc.h"); + + ::grpc::string system_imports = SystemImport("ProtoRPC/ProtoService.h") + + SystemImport("ProtoRPC/ProtoRPC.h") + + SystemImport("RxLibrary/GRXWriteable.h") + + SystemImport("RxLibrary/GRXWriter.h"); + + ::grpc::string forward_declarations = "@class GRPCProtoCall;\n\n"; + + ::grpc::string class_declarations = + grpc_objective_c_generator::GetAllMessageClasses(file); + + ::grpc::string class_imports; for (int i = 0; i < file->dependency_count(); i++) { - ::grpc::string header = - grpc_objective_c_generator::MessageHeaderName(file->dependency(i)); - const grpc::protobuf::FileDescriptor* dependency = file->dependency(i); - if (IsProtobufLibraryBundledProtoFile(dependency)) { - ::grpc::string base_name = header; - grpc_generator::StripPrefix(&base_name, "google/protobuf/"); - // create the import code snippet - proto_imports += - " #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS\n" - " #import <" + - ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name + - ">\n" - " #else\n" - " #import \"" + - header + - "\"\n" - " #endif\n"; - } else { - proto_imports += ::grpc::string(" #import \"") + header + "\"\n"; - } + class_imports += ImportProtoHeaders(file->dependency(i), " "); } - proto_imports += "#endif\n"; - ::grpc::string declarations; + ::grpc::string protocols; for (int i = 0; i < file->service_count(); i++) { const grpc::protobuf::ServiceDescriptor* service = file->service(i); - declarations += grpc_objective_c_generator::GetHeader(service); + protocols += grpc_objective_c_generator::GetProtocol(service); } - static const ::grpc::string kNonNullBegin = - "\nNS_ASSUME_NONNULL_BEGIN\n\n"; - static const ::grpc::string kNonNullEnd = "\nNS_ASSUME_NONNULL_END\n"; + ::grpc::string interfaces; + for (int i = 0; i < file->service_count(); i++) { + const grpc::protobuf::ServiceDescriptor* service = file->service(i); + interfaces += grpc_objective_c_generator::GetInterface(service); + } Write(context, file_name + ".pbrpc.h", - imports + '\n' + proto_imports + '\n' + kNonNullBegin + - declarations + kNonNullEnd); + PreprocIfNot(kForwardDeclare, imports) + "\n" + + PreprocIfNot(kProtocolOnly, system_imports) + "\n" + + PreprocIfElse(kForwardDeclare, class_declarations, + class_imports) + + "\n" + forward_declarations + "\n" + kNonNullBegin + "\n" + + protocols + "\n" + PreprocIfNot(kProtocolOnly, interfaces) + + "\n" + kNonNullEnd + "\n"); } { // Generate .pbrpc.m - ::grpc::string imports = ::grpc::string("#import \"") + file_name + - ".pbrpc.h\"\n" - "#import \"" + - file_name + - ".pbobjc.h\"\n\n" - "#import \n" - "#import \n"; + ::grpc::string imports = LocalImport(file_name + ".pbrpc.h") + + LocalImport(file_name + ".pbobjc.h") + + SystemImport("ProtoRPC/ProtoRPC.h") + + SystemImport("RxLibrary/GRXWriter+Immediate.h"); + + ::grpc::string class_imports; for (int i = 0; i < file->dependency_count(); i++) { - ::grpc::string header = - grpc_objective_c_generator::MessageHeaderName(file->dependency(i)); - const grpc::protobuf::FileDescriptor* dependency = file->dependency(i); - if (IsProtobufLibraryBundledProtoFile(dependency)) { - ::grpc::string base_name = header; - grpc_generator::StripPrefix(&base_name, "google/protobuf/"); - // create the import code snippet - imports += - "#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS\n" - " #import <" + - ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name + - ">\n" - "#else\n" - " #import \"" + - header + - "\"\n" - "#endif\n"; - } else { - imports += ::grpc::string("#import \"") + header + "\"\n"; - } + class_imports += ImportProtoHeaders(file->dependency(i), ""); } ::grpc::string definitions; @@ -143,7 +144,9 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { definitions += grpc_objective_c_generator::GetSource(service); } - Write(context, file_name + ".pbrpc.m", imports + '\n' + definitions); + Write(context, file_name + ".pbrpc.m", + PreprocIfNot(kProtocolOnly, + imports + "\n" + class_imports + "\n" + definitions)); } return true;