1 /* 2 * 3 * Copyright 2015 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 #include <cctype> 20 #include <map> 21 #include <sstream> 22 #include <vector> 23 24 #include "src/compiler/config.h" 25 #include "src/compiler/csharp_generator.h" 26 #include "src/compiler/csharp_generator_helpers.h" 27 28 using google::protobuf::compiler::csharp::GetClassName; 29 using google::protobuf::compiler::csharp::GetFileNamespace; 30 using google::protobuf::compiler::csharp::GetReflectionClassName; 31 using grpc::protobuf::Descriptor; 32 using grpc::protobuf::FileDescriptor; 33 using grpc::protobuf::MethodDescriptor; 34 using grpc::protobuf::ServiceDescriptor; 35 using grpc::protobuf::io::Printer; 36 using grpc::protobuf::io::StringOutputStream; 37 using grpc_generator::GetMethodType; 38 using grpc_generator::METHODTYPE_BIDI_STREAMING; 39 using grpc_generator::METHODTYPE_CLIENT_STREAMING; 40 using grpc_generator::METHODTYPE_NO_STREAMING; 41 using grpc_generator::METHODTYPE_SERVER_STREAMING; 42 using grpc_generator::MethodType; 43 using grpc_generator::StringReplace; 44 using std::map; 45 using std::vector; 46 47 namespace grpc_csharp_generator { 48 namespace { 49 50 // This function is a massaged version of 51 // https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc 52 // Currently, we cannot easily reuse the functionality as 53 // google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header. 54 // TODO(jtattermusch): reuse the functionality from google/protobuf. 55 bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, 56 grpc::protobuf::SourceLocation location) { 57 grpc::string comments = location.leading_comments.empty() 58 ? location.trailing_comments 59 : location.leading_comments; 60 if (comments.empty()) { 61 return false; 62 } 63 // XML escaping... no need for apostrophes etc as the whole text is going to 64 // be a child 65 // node of a summary element, not part of an attribute. 66 comments = grpc_generator::StringReplace(comments, "&", "&", true); 67 comments = grpc_generator::StringReplace(comments, "<", "<", true); 68 69 std::vector<grpc::string> lines; 70 grpc_generator::Split(comments, '\n', &lines); 71 // TODO: We really should work out which part to put in the summary and which 72 // to put in the remarks... 73 // but that needs to be part of a bigger effort to understand the markdown 74 // better anyway. 75 printer->Print("/// <summary>\n"); 76 bool last_was_empty = false; 77 // We squash multiple blank lines down to one, and remove any trailing blank 78 // lines. We need 79 // to preserve the blank lines themselves, as this is relevant in the 80 // markdown. 81 // Note that we can't remove leading or trailing whitespace as *that's* 82 // relevant in markdown too. 83 // (We don't skip "just whitespace" lines, either.) 84 for (std::vector<grpc::string>::iterator it = lines.begin(); 85 it != lines.end(); ++it) { 86 grpc::string line = *it; 87 if (line.empty()) { 88 last_was_empty = true; 89 } else { 90 if (last_was_empty) { 91 printer->Print("///\n"); 92 } 93 last_was_empty = false; 94 printer->Print("///$line$\n", "line", *it); 95 } 96 } 97 printer->Print("/// </summary>\n"); 98 return true; 99 } 100 101 template <typename DescriptorType> 102 bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer, 103 const DescriptorType* descriptor) { 104 grpc::protobuf::SourceLocation location; 105 if (!descriptor->GetSourceLocation(&location)) { 106 return false; 107 } 108 return GenerateDocCommentBodyImpl(printer, location); 109 } 110 111 void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer* printer, 112 const MethodDescriptor* method) { 113 if (GenerateDocCommentBody(printer, method)) { 114 if (method->client_streaming()) { 115 printer->Print( 116 "/// <param name=\"requestStream\">Used for reading requests from " 117 "the client.</param>\n"); 118 } else { 119 printer->Print( 120 "/// <param name=\"request\">The request received from the " 121 "client.</param>\n"); 122 } 123 if (method->server_streaming()) { 124 printer->Print( 125 "/// <param name=\"responseStream\">Used for sending responses back " 126 "to the client.</param>\n"); 127 } 128 printer->Print( 129 "/// <param name=\"context\">The context of the server-side call " 130 "handler being invoked.</param>\n"); 131 if (method->server_streaming()) { 132 printer->Print( 133 "/// <returns>A task indicating completion of the " 134 "handler.</returns>\n"); 135 } else { 136 printer->Print( 137 "/// <returns>The response to send back to the client (wrapped by a " 138 "task).</returns>\n"); 139 } 140 } 141 } 142 143 void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer* printer, 144 const MethodDescriptor* method, 145 bool is_sync, bool use_call_options) { 146 if (GenerateDocCommentBody(printer, method)) { 147 if (!method->client_streaming()) { 148 printer->Print( 149 "/// <param name=\"request\">The request to send to the " 150 "server.</param>\n"); 151 } 152 if (!use_call_options) { 153 printer->Print( 154 "/// <param name=\"headers\">The initial metadata to send with the " 155 "call. This parameter is optional.</param>\n"); 156 printer->Print( 157 "/// <param name=\"deadline\">An optional deadline for the call. The " 158 "call will be cancelled if deadline is hit.</param>\n"); 159 printer->Print( 160 "/// <param name=\"cancellationToken\">An optional token for " 161 "canceling the call.</param>\n"); 162 } else { 163 printer->Print( 164 "/// <param name=\"options\">The options for the call.</param>\n"); 165 } 166 if (is_sync) { 167 printer->Print( 168 "/// <returns>The response received from the server.</returns>\n"); 169 } else { 170 printer->Print("/// <returns>The call object.</returns>\n"); 171 } 172 } 173 } 174 175 std::string GetServiceClassName(const ServiceDescriptor* service) { 176 return service->name(); 177 } 178 179 std::string GetClientClassName(const ServiceDescriptor* service) { 180 return service->name() + "Client"; 181 } 182 183 std::string GetServerClassName(const ServiceDescriptor* service) { 184 return service->name() + "Base"; 185 } 186 187 std::string GetCSharpMethodType(MethodType method_type) { 188 switch (method_type) { 189 case METHODTYPE_NO_STREAMING: 190 return "grpc::MethodType.Unary"; 191 case METHODTYPE_CLIENT_STREAMING: 192 return "grpc::MethodType.ClientStreaming"; 193 case METHODTYPE_SERVER_STREAMING: 194 return "grpc::MethodType.ServerStreaming"; 195 case METHODTYPE_BIDI_STREAMING: 196 return "grpc::MethodType.DuplexStreaming"; 197 } 198 GOOGLE_LOG(FATAL) << "Can't get here."; 199 return ""; 200 } 201 202 std::string GetServiceNameFieldName() { return "__ServiceName"; } 203 204 std::string GetMarshallerFieldName(const Descriptor* message) { 205 return "__Marshaller_" + 206 grpc_generator::StringReplace(message->full_name(), ".", "_", true); 207 } 208 209 std::string GetMethodFieldName(const MethodDescriptor* method) { 210 return "__Method_" + method->name(); 211 } 212 213 std::string GetMethodRequestParamMaybe(const MethodDescriptor* method, 214 bool invocation_param = false) { 215 if (method->client_streaming()) { 216 return ""; 217 } 218 if (invocation_param) { 219 return "request, "; 220 } 221 return GetClassName(method->input_type()) + " request, "; 222 } 223 224 std::string GetAccessLevel(bool internal_access) { 225 return internal_access ? "internal" : "public"; 226 } 227 228 std::string GetMethodReturnTypeClient(const MethodDescriptor* method) { 229 switch (GetMethodType(method)) { 230 case METHODTYPE_NO_STREAMING: 231 return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) + 232 ">"; 233 case METHODTYPE_CLIENT_STREAMING: 234 return "grpc::AsyncClientStreamingCall<" + 235 GetClassName(method->input_type()) + ", " + 236 GetClassName(method->output_type()) + ">"; 237 case METHODTYPE_SERVER_STREAMING: 238 return "grpc::AsyncServerStreamingCall<" + 239 GetClassName(method->output_type()) + ">"; 240 case METHODTYPE_BIDI_STREAMING: 241 return "grpc::AsyncDuplexStreamingCall<" + 242 GetClassName(method->input_type()) + ", " + 243 GetClassName(method->output_type()) + ">"; 244 } 245 GOOGLE_LOG(FATAL) << "Can't get here."; 246 return ""; 247 } 248 249 std::string GetMethodRequestParamServer(const MethodDescriptor* method) { 250 switch (GetMethodType(method)) { 251 case METHODTYPE_NO_STREAMING: 252 case METHODTYPE_SERVER_STREAMING: 253 return GetClassName(method->input_type()) + " request"; 254 case METHODTYPE_CLIENT_STREAMING: 255 case METHODTYPE_BIDI_STREAMING: 256 return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) + 257 "> requestStream"; 258 } 259 GOOGLE_LOG(FATAL) << "Can't get here."; 260 return ""; 261 } 262 263 std::string GetMethodReturnTypeServer(const MethodDescriptor* method) { 264 switch (GetMethodType(method)) { 265 case METHODTYPE_NO_STREAMING: 266 case METHODTYPE_CLIENT_STREAMING: 267 return "global::System.Threading.Tasks.Task<" + 268 GetClassName(method->output_type()) + ">"; 269 case METHODTYPE_SERVER_STREAMING: 270 case METHODTYPE_BIDI_STREAMING: 271 return "global::System.Threading.Tasks.Task"; 272 } 273 GOOGLE_LOG(FATAL) << "Can't get here."; 274 return ""; 275 } 276 277 std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) { 278 switch (GetMethodType(method)) { 279 case METHODTYPE_NO_STREAMING: 280 case METHODTYPE_CLIENT_STREAMING: 281 return ""; 282 case METHODTYPE_SERVER_STREAMING: 283 case METHODTYPE_BIDI_STREAMING: 284 return ", grpc::IServerStreamWriter<" + 285 GetClassName(method->output_type()) + "> responseStream"; 286 } 287 GOOGLE_LOG(FATAL) << "Can't get here."; 288 return ""; 289 } 290 291 // Gets vector of all messages used as input or output types. 292 std::vector<const Descriptor*> GetUsedMessages( 293 const ServiceDescriptor* service) { 294 std::set<const Descriptor*> descriptor_set; 295 std::vector<const Descriptor*> 296 result; // vector is to maintain stable ordering 297 for (int i = 0; i < service->method_count(); i++) { 298 const MethodDescriptor* method = service->method(i); 299 if (descriptor_set.find(method->input_type()) == descriptor_set.end()) { 300 descriptor_set.insert(method->input_type()); 301 result.push_back(method->input_type()); 302 } 303 if (descriptor_set.find(method->output_type()) == descriptor_set.end()) { 304 descriptor_set.insert(method->output_type()); 305 result.push_back(method->output_type()); 306 } 307 } 308 return result; 309 } 310 311 void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) { 312 std::vector<const Descriptor*> used_messages = GetUsedMessages(service); 313 for (size_t i = 0; i < used_messages.size(); i++) { 314 const Descriptor* message = used_messages[i]; 315 out->Print( 316 "static readonly grpc::Marshaller<$type$> $fieldname$ = " 317 "grpc::Marshallers.Create((arg) => " 318 "global::Google.Protobuf.MessageExtensions.ToByteArray(arg), " 319 "$type$.Parser.ParseFrom);\n", 320 "fieldname", GetMarshallerFieldName(message), "type", 321 GetClassName(message)); 322 } 323 out->Print("\n"); 324 } 325 326 void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) { 327 out->Print( 328 "static readonly grpc::Method<$request$, $response$> $fieldname$ = new " 329 "grpc::Method<$request$, $response$>(\n", 330 "fieldname", GetMethodFieldName(method), "request", 331 GetClassName(method->input_type()), "response", 332 GetClassName(method->output_type())); 333 out->Indent(); 334 out->Indent(); 335 out->Print("$methodtype$,\n", "methodtype", 336 GetCSharpMethodType(GetMethodType(method))); 337 out->Print("$servicenamefield$,\n", "servicenamefield", 338 GetServiceNameFieldName()); 339 out->Print("\"$methodname$\",\n", "methodname", method->name()); 340 out->Print("$requestmarshaller$,\n", "requestmarshaller", 341 GetMarshallerFieldName(method->input_type())); 342 out->Print("$responsemarshaller$);\n", "responsemarshaller", 343 GetMarshallerFieldName(method->output_type())); 344 out->Print("\n"); 345 out->Outdent(); 346 out->Outdent(); 347 } 348 349 void GenerateServiceDescriptorProperty(Printer* out, 350 const ServiceDescriptor* service) { 351 std::ostringstream index; 352 index << service->index(); 353 out->Print("/// <summary>Service descriptor</summary>\n"); 354 out->Print( 355 "public static global::Google.Protobuf.Reflection.ServiceDescriptor " 356 "Descriptor\n"); 357 out->Print("{\n"); 358 out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n", 359 "umbrella", GetReflectionClassName(service->file()), "index", 360 index.str()); 361 out->Print("}\n"); 362 out->Print("\n"); 363 } 364 365 void GenerateServerClass(Printer* out, const ServiceDescriptor* service) { 366 out->Print( 367 "/// <summary>Base class for server-side implementations of " 368 "$servicename$</summary>\n", 369 "servicename", GetServiceClassName(service)); 370 out->Print("public abstract partial class $name$\n", "name", 371 GetServerClassName(service)); 372 out->Print("{\n"); 373 out->Indent(); 374 for (int i = 0; i < service->method_count(); i++) { 375 const MethodDescriptor* method = service->method(i); 376 GenerateDocCommentServerMethod(out, method); 377 out->Print( 378 "public virtual $returntype$ " 379 "$methodname$($request$$response_stream_maybe$, " 380 "grpc::ServerCallContext context)\n", 381 "methodname", method->name(), "returntype", 382 GetMethodReturnTypeServer(method), "request", 383 GetMethodRequestParamServer(method), "response_stream_maybe", 384 GetMethodResponseStreamMaybe(method)); 385 out->Print("{\n"); 386 out->Indent(); 387 out->Print( 388 "throw new grpc::RpcException(" 389 "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n"); 390 out->Outdent(); 391 out->Print("}\n\n"); 392 } 393 out->Outdent(); 394 out->Print("}\n"); 395 out->Print("\n"); 396 } 397 398 void GenerateClientStub(Printer* out, const ServiceDescriptor* service) { 399 out->Print("/// <summary>Client for $servicename$</summary>\n", "servicename", 400 GetServiceClassName(service)); 401 out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name", 402 GetClientClassName(service)); 403 out->Print("{\n"); 404 out->Indent(); 405 406 // constructors 407 out->Print( 408 "/// <summary>Creates a new client for $servicename$</summary>\n" 409 "/// <param name=\"channel\">The channel to use to make remote " 410 "calls.</param>\n", 411 "servicename", GetServiceClassName(service)); 412 out->Print("public $name$(grpc::Channel channel) : base(channel)\n", "name", 413 GetClientClassName(service)); 414 out->Print("{\n"); 415 out->Print("}\n"); 416 out->Print( 417 "/// <summary>Creates a new client for $servicename$ that uses a custom " 418 "<c>CallInvoker</c>.</summary>\n" 419 "/// <param name=\"callInvoker\">The callInvoker to use to make remote " 420 "calls.</param>\n", 421 "servicename", GetServiceClassName(service)); 422 out->Print( 423 "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n", 424 "name", GetClientClassName(service)); 425 out->Print("{\n"); 426 out->Print("}\n"); 427 out->Print( 428 "/// <summary>Protected parameterless constructor to allow creation" 429 " of test doubles.</summary>\n"); 430 out->Print("protected $name$() : base()\n", "name", 431 GetClientClassName(service)); 432 out->Print("{\n"); 433 out->Print("}\n"); 434 out->Print( 435 "/// <summary>Protected constructor to allow creation of configured " 436 "clients.</summary>\n" 437 "/// <param name=\"configuration\">The client configuration.</param>\n"); 438 out->Print( 439 "protected $name$(ClientBaseConfiguration configuration)" 440 " : base(configuration)\n", 441 "name", GetClientClassName(service)); 442 out->Print("{\n"); 443 out->Print("}\n\n"); 444 445 for (int i = 0; i < service->method_count(); i++) { 446 const MethodDescriptor* method = service->method(i); 447 MethodType method_type = GetMethodType(method); 448 449 if (method_type == METHODTYPE_NO_STREAMING) { 450 // unary calls have an extra synchronous stub method 451 GenerateDocCommentClientMethod(out, method, true, false); 452 out->Print( 453 "public virtual $response$ $methodname$($request$ request, " 454 "grpc::Metadata " 455 "headers = null, global::System.DateTime? deadline = null, " 456 "global::System.Threading.CancellationToken " 457 "cancellationToken = " 458 "default(global::System.Threading.CancellationToken))\n", 459 "methodname", method->name(), "request", 460 GetClassName(method->input_type()), "response", 461 GetClassName(method->output_type())); 462 out->Print("{\n"); 463 out->Indent(); 464 out->Print( 465 "return $methodname$(request, new grpc::CallOptions(headers, " 466 "deadline, " 467 "cancellationToken));\n", 468 "methodname", method->name()); 469 out->Outdent(); 470 out->Print("}\n"); 471 472 // overload taking CallOptions as a param 473 GenerateDocCommentClientMethod(out, method, true, true); 474 out->Print( 475 "public virtual $response$ $methodname$($request$ request, " 476 "grpc::CallOptions options)\n", 477 "methodname", method->name(), "request", 478 GetClassName(method->input_type()), "response", 479 GetClassName(method->output_type())); 480 out->Print("{\n"); 481 out->Indent(); 482 out->Print( 483 "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, " 484 "request);\n", 485 "methodfield", GetMethodFieldName(method)); 486 out->Outdent(); 487 out->Print("}\n"); 488 } 489 490 std::string method_name = method->name(); 491 if (method_type == METHODTYPE_NO_STREAMING) { 492 method_name += "Async"; // prevent name clash with synchronous method. 493 } 494 GenerateDocCommentClientMethod(out, method, false, false); 495 out->Print( 496 "public virtual $returntype$ " 497 "$methodname$($request_maybe$grpc::Metadata " 498 "headers = null, global::System.DateTime? deadline = null, " 499 "global::System.Threading.CancellationToken " 500 "cancellationToken = " 501 "default(global::System.Threading.CancellationToken))\n", 502 "methodname", method_name, "request_maybe", 503 GetMethodRequestParamMaybe(method), "returntype", 504 GetMethodReturnTypeClient(method)); 505 out->Print("{\n"); 506 out->Indent(); 507 508 out->Print( 509 "return $methodname$($request_maybe$new grpc::CallOptions(headers, " 510 "deadline, " 511 "cancellationToken));\n", 512 "methodname", method_name, "request_maybe", 513 GetMethodRequestParamMaybe(method, true)); 514 out->Outdent(); 515 out->Print("}\n"); 516 517 // overload taking CallOptions as a param 518 GenerateDocCommentClientMethod(out, method, false, true); 519 out->Print( 520 "public virtual $returntype$ " 521 "$methodname$($request_maybe$grpc::CallOptions " 522 "options)\n", 523 "methodname", method_name, "request_maybe", 524 GetMethodRequestParamMaybe(method), "returntype", 525 GetMethodReturnTypeClient(method)); 526 out->Print("{\n"); 527 out->Indent(); 528 switch (GetMethodType(method)) { 529 case METHODTYPE_NO_STREAMING: 530 out->Print( 531 "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, " 532 "request);\n", 533 "methodfield", GetMethodFieldName(method)); 534 break; 535 case METHODTYPE_CLIENT_STREAMING: 536 out->Print( 537 "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, " 538 "options);\n", 539 "methodfield", GetMethodFieldName(method)); 540 break; 541 case METHODTYPE_SERVER_STREAMING: 542 out->Print( 543 "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, " 544 "options, request);\n", 545 "methodfield", GetMethodFieldName(method)); 546 break; 547 case METHODTYPE_BIDI_STREAMING: 548 out->Print( 549 "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, " 550 "options);\n", 551 "methodfield", GetMethodFieldName(method)); 552 break; 553 default: 554 GOOGLE_LOG(FATAL) << "Can't get here."; 555 } 556 out->Outdent(); 557 out->Print("}\n"); 558 } 559 560 // override NewInstance method 561 out->Print( 562 "/// <summary>Creates a new instance of client from given " 563 "<c>ClientBaseConfiguration</c>.</summary>\n"); 564 out->Print( 565 "protected override $name$ NewInstance(ClientBaseConfiguration " 566 "configuration)\n", 567 "name", GetClientClassName(service)); 568 out->Print("{\n"); 569 out->Indent(); 570 out->Print("return new $name$(configuration);\n", "name", 571 GetClientClassName(service)); 572 out->Outdent(); 573 out->Print("}\n"); 574 575 out->Outdent(); 576 out->Print("}\n"); 577 out->Print("\n"); 578 } 579 580 void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) { 581 out->Print( 582 "/// <summary>Creates service definition that can be registered with a " 583 "server</summary>\n"); 584 out->Print( 585 "/// <param name=\"serviceImpl\">An object implementing the server-side" 586 " handling logic.</param>\n"); 587 out->Print( 588 "public static grpc::ServerServiceDefinition BindService($implclass$ " 589 "serviceImpl)\n", 590 "implclass", GetServerClassName(service)); 591 out->Print("{\n"); 592 out->Indent(); 593 594 out->Print("return grpc::ServerServiceDefinition.CreateBuilder()"); 595 out->Indent(); 596 out->Indent(); 597 for (int i = 0; i < service->method_count(); i++) { 598 const MethodDescriptor* method = service->method(i); 599 out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)", 600 "methodfield", GetMethodFieldName(method), "methodname", 601 method->name()); 602 } 603 out->Print(".Build();\n"); 604 out->Outdent(); 605 out->Outdent(); 606 607 out->Outdent(); 608 out->Print("}\n"); 609 out->Print("\n"); 610 } 611 612 void GenerateService(Printer* out, const ServiceDescriptor* service, 613 bool generate_client, bool generate_server, 614 bool internal_access) { 615 GenerateDocCommentBody(out, service); 616 out->Print("$access_level$ static partial class $classname$\n", 617 "access_level", GetAccessLevel(internal_access), "classname", 618 GetServiceClassName(service)); 619 out->Print("{\n"); 620 out->Indent(); 621 out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n", 622 "servicenamefield", GetServiceNameFieldName(), "servicename", 623 service->full_name()); 624 out->Print("\n"); 625 626 GenerateMarshallerFields(out, service); 627 for (int i = 0; i < service->method_count(); i++) { 628 GenerateStaticMethodField(out, service->method(i)); 629 } 630 GenerateServiceDescriptorProperty(out, service); 631 632 if (generate_server) { 633 GenerateServerClass(out, service); 634 } 635 if (generate_client) { 636 GenerateClientStub(out, service); 637 } 638 if (generate_server) { 639 GenerateBindServiceMethod(out, service); 640 } 641 642 out->Outdent(); 643 out->Print("}\n"); 644 } 645 646 } // anonymous namespace 647 648 grpc::string GetServices(const FileDescriptor* file, bool generate_client, 649 bool generate_server, bool internal_access) { 650 grpc::string output; 651 { 652 // Scope the output stream so it closes and finalizes output to the string. 653 654 StringOutputStream output_stream(&output); 655 Printer out(&output_stream, '$'); 656 657 // Don't write out any output if there no services, to avoid empty service 658 // files being generated for proto files that don't declare any. 659 if (file->service_count() == 0) { 660 return output; 661 } 662 663 // Write out a file header. 664 out.Print("// <auto-generated>\n"); 665 out.Print( 666 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); 667 out.Print("// source: $filename$\n", "filename", file->name()); 668 out.Print("// </auto-generated>\n"); 669 670 // use C++ style as there are no file-level XML comments in .NET 671 grpc::string leading_comments = GetCsharpComments(file, true); 672 if (!leading_comments.empty()) { 673 out.Print("// Original file comments:\n"); 674 out.PrintRaw(leading_comments.c_str()); 675 } 676 677 out.Print("#pragma warning disable 0414, 1591\n"); 678 679 out.Print("#region Designer generated code\n"); 680 out.Print("\n"); 681 out.Print("using grpc = global::Grpc.Core;\n"); 682 out.Print("\n"); 683 684 grpc::string file_namespace = GetFileNamespace(file); 685 if (file_namespace != "") { 686 out.Print("namespace $namespace$ {\n", "namespace", file_namespace); 687 out.Indent(); 688 } 689 for (int i = 0; i < file->service_count(); i++) { 690 GenerateService(&out, file->service(i), generate_client, generate_server, 691 internal_access); 692 } 693 if (file_namespace != "") { 694 out.Outdent(); 695 out.Print("}\n"); 696 } 697 out.Print("#endregion\n"); 698 } 699 return output; 700 } 701 702 } // namespace grpc_csharp_generator 703