read from and generate grpc service metadata (#991)

diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md
index 5c5fe95..012572d 100644
--- a/protoc_plugin/CHANGELOG.md
+++ b/protoc_plugin/CHANGELOG.md
@@ -1,7 +1,38 @@
-## 22.1.1-wip
+## 22.2.0
 
+* Read `default_host` and `oauth_scopes` options from gRPC service definitions
+  and write that information to the generated gRPC clients.
 * Generate dartdocs for grpc services ([#973]).
 
+We now parse and generate code cooresponding to the proto options
+`google.api.default_host` and `google.api.oauth_scopes`:
+
+```proto
+service Firestore {
+  option (google.api.default_host) = "firestore.googleapis.com";
+  option (google.api.oauth_scopes) =
+      "https://www.googleapis.com/auth/cloud-platform,"
+      "https://www.googleapis.com/auth/datastore";
+
+  ...
+```
+
+Will generate as:
+
+```dart
+class FirestoreClient extends $grpc.Client {
+  /// The hostname for this service.
+  static const $core.String defaultHost = 'firestore.googleapis.com';
+
+  /// OAuth scopes needed for the client.
+  static const $core.List<$core.String> oauthScopes = [
+    'https://www.googleapis.com/auth/cloud-platform',
+    'https://www.googleapis.com/auth/datastore',
+  ];
+
+  ...
+```
+
 [#973]: https://github.com/google/protobuf.dart/issues/973
 
 ## 22.1.0
diff --git a/protoc_plugin/Makefile b/protoc_plugin/Makefile
index 6c04fa5..9b68b43 100644
--- a/protoc_plugin/Makefile
+++ b/protoc_plugin/Makefile
@@ -77,7 +77,7 @@
 TEST_PROTO_SRCS=$(foreach proto, $(TEST_PROTO_LIST), \
   $(TEST_PROTO_SRC_DIR)/$(proto).proto)
 
-PREGENERATED_SRCS=protos/descriptor.proto protos/plugin.proto protos/dart_options.proto
+PREGENERATED_SRCS=protos/descriptor.proto protos/client.proto protos/plugin.proto protos/dart_options.proto
 
 $(TEST_PROTO_LIBS): $(PLUGIN_SRC) $(TEST_PROTO_SRCS)
 	mkdir -p $(TEST_PROTO_DIR)
@@ -104,6 +104,7 @@
 
 update-pregenerated: $(PLUGIN_PATH) $(PREGENERATED_SRCS)
 	protoc --dart_out=lib/src/generated -Iprotos --plugin=protoc-gen-dart=$(realpath $(PLUGIN_PATH)) $(PREGENERATED_SRCS)
+	rm lib/src/generated/client.pb{json,server}.dart
 	rm lib/src/generated/descriptor.pb{json,server}.dart
 	rm lib/src/generated/dart_options.pb{enum,json,server}.dart
 	rm lib/src/generated/plugin.pb{json,server}.dart
diff --git a/protoc_plugin/lib/protoc.dart b/protoc_plugin/lib/protoc.dart
index e6d175b..8020405 100644
--- a/protoc_plugin/lib/protoc.dart
+++ b/protoc_plugin/lib/protoc.dart
@@ -7,6 +7,7 @@
 import 'mixins.dart';
 import 'names.dart';
 import 'src/code_generator.dart';
+import 'src/generated/client.pb.dart';
 import 'src/generated/dart_options.pb.dart';
 import 'src/generated/descriptor.pb.dart';
 import 'src/generated/plugin.pb.dart';
diff --git a/protoc_plugin/lib/src/code_generator.dart b/protoc_plugin/lib/src/code_generator.dart
index dd4c19f..45cfe55 100644
--- a/protoc_plugin/lib/src/code_generator.dart
+++ b/protoc_plugin/lib/src/code_generator.dart
@@ -11,6 +11,7 @@
 
 import '../names.dart' show lowerCaseFirstLetter;
 import '../protoc.dart' show FileGenerator;
+import 'generated/client.pb.dart';
 import 'generated/dart_options.pb.dart';
 import 'generated/plugin.pb.dart';
 import 'linker.dart';
@@ -87,57 +88,57 @@
   /// for details), and [config] can be used to override where
   /// generated files are created and how imports between generated files are
   /// constructed (see [OutputConfiguration] for details).
-  void generate(
-      {Map<String, SingleOptionParser>? optionParsers,
-      OutputConfiguration config = const DefaultOutputConfiguration()}) {
+  Future<void> generate({
+    Map<String, SingleOptionParser>? optionParsers,
+    OutputConfiguration config = const DefaultOutputConfiguration(),
+  }) async {
     final extensions = ExtensionRegistry();
+
     Dart_options.registerAllExtensions(extensions);
+    Client.registerAllExtensions(extensions);
 
-    _streamIn
-        .fold(
-            BytesBuilder(), (BytesBuilder builder, data) => builder..add(data))
-        .then((builder) => builder.takeBytes())
-        .then((List<int> bytes) {
-      // Suppress CodedBufferReader builtin size limitation when reading
-      // the request, as protobuf definitions can be larger than default
-      // limit of 64Mb.
-      final reader = CodedBufferReader(bytes, sizeLimit: bytes.length);
-      final request = CodeGeneratorRequest();
-      request.mergeFromCodedBufferReader(reader, extensions);
-      reader.checkLastTagWas(0);
+    final builder = await _streamIn.fold(
+      BytesBuilder(),
+      (builder, data) => builder..add(data),
+    );
+    final bytes = builder.takeBytes();
+    // Suppress CodedBufferReader builtin size limitation when reading the
+    // request; protobuf definitions can be larger than default limit of 64Mb.
+    final reader = CodedBufferReader(bytes, sizeLimit: bytes.length);
+    final request = CodeGeneratorRequest();
+    request.mergeFromCodedBufferReader(reader, extensions);
+    reader.checkLastTagWas(0);
 
-      request.protoFile.sortBy((desc) => desc.name);
+    request.protoFile.sortBy((desc) => desc.name);
 
-      final response = CodeGeneratorResponse();
+    final response = CodeGeneratorResponse();
 
-      // Parse the options in the request. Return the errors if any.
-      final options = parseGenerationOptions(request, response, optionParsers);
-      if (options == null) {
-        _streamOut.add(response.writeToBuffer());
-        return;
-      }
-
-      // Create a syntax tree for each .proto file given to us.
-      // (We may import it even if we don't generate the .pb.dart file.)
-      final generators = <FileGenerator>[];
-      for (final file in request.protoFile) {
-        generators.add(FileGenerator(file, options));
-      }
-
-      // Collect field types and importable files.
-      link(options, generators);
-
-      // Generate the .pb.dart file if requested.
-      for (final gen in generators) {
-        final name = gen.descriptor.name;
-        if (request.fileToGenerate.contains(name)) {
-          response.file.addAll(gen.generateFiles(config));
-        }
-      }
-      response.supportedFeatures =
-          Int64(CodeGeneratorResponse_Feature.FEATURE_PROTO3_OPTIONAL.value);
-
+    // Parse the options in the request. Return the errors if any.
+    final options = parseGenerationOptions(request, response, optionParsers);
+    if (options == null) {
       _streamOut.add(response.writeToBuffer());
-    });
+      return;
+    }
+
+    // Create a syntax tree for each .proto file given to us.
+    // (We may import it even if we don't generate the .pb.dart file.)
+    final generators = <FileGenerator>[];
+    for (final file in request.protoFile) {
+      generators.add(FileGenerator(file, options));
+    }
+
+    // Collect field types and importable files.
+    link(options, generators);
+
+    // Generate the .pb.dart file if requested.
+    for (final gen in generators) {
+      final name = gen.descriptor.name;
+      if (request.fileToGenerate.contains(name)) {
+        response.file.addAll(gen.generateFiles(config));
+      }
+    }
+    response.supportedFeatures =
+        Int64(CodeGeneratorResponse_Feature.FEATURE_PROTO3_OPTIONAL.value);
+    _streamOut.add(response.writeToBuffer());
   }
 }
diff --git a/protoc_plugin/lib/src/generated/client.pb.dart b/protoc_plugin/lib/src/generated/client.pb.dart
new file mode 100644
index 0000000..ccb157d
--- /dev/null
+++ b/protoc_plugin/lib/src/generated/client.pb.dart
@@ -0,0 +1,1263 @@
+//
+//  Generated code. Do not modify.
+//  source: client.proto
+//
+// @dart = 3.3
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'client.pbenum.dart';
+
+export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
+
+export 'client.pbenum.dart';
+
+/// Required information for every language.
+class CommonLanguageSettings extends $pb.GeneratedMessage {
+  factory CommonLanguageSettings({
+    @$core.Deprecated('This field is deprecated.')
+    $core.String? referenceDocsUri,
+    $core.Iterable<ClientLibraryDestination>? destinations,
+    SelectiveGapicGeneration? selectiveGapicGeneration,
+  }) {
+    final $result = create();
+    if (referenceDocsUri != null) {
+      // ignore: deprecated_member_use_from_same_package
+      $result.referenceDocsUri = referenceDocsUri;
+    }
+    if (destinations != null) {
+      $result.destinations.addAll(destinations);
+    }
+    if (selectiveGapicGeneration != null) {
+      $result.selectiveGapicGeneration = selectiveGapicGeneration;
+    }
+    return $result;
+  }
+  CommonLanguageSettings._() : super();
+  factory CommonLanguageSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory CommonLanguageSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'CommonLanguageSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOS(1, _omitFieldNames ? '' : 'referenceDocsUri')
+    ..pc<ClientLibraryDestination>(
+        2, _omitFieldNames ? '' : 'destinations', $pb.PbFieldType.KE,
+        valueOf: ClientLibraryDestination.valueOf,
+        enumValues: ClientLibraryDestination.values,
+        defaultEnumValue:
+            ClientLibraryDestination.CLIENT_LIBRARY_DESTINATION_UNSPECIFIED)
+    ..aOM<SelectiveGapicGeneration>(
+        3, _omitFieldNames ? '' : 'selectiveGapicGeneration',
+        subBuilder: SelectiveGapicGeneration.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  CommonLanguageSettings clone() =>
+      CommonLanguageSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  CommonLanguageSettings copyWith(
+          void Function(CommonLanguageSettings) updates) =>
+      super.copyWith((message) => updates(message as CommonLanguageSettings))
+          as CommonLanguageSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static CommonLanguageSettings create() => CommonLanguageSettings._();
+  CommonLanguageSettings createEmptyInstance() => create();
+  static $pb.PbList<CommonLanguageSettings> createRepeated() =>
+      $pb.PbList<CommonLanguageSettings>();
+  @$core.pragma('dart2js:noInline')
+  static CommonLanguageSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<CommonLanguageSettings>(create);
+  static CommonLanguageSettings? _defaultInstance;
+
+  /// Link to automatically generated reference documentation.  Example:
+  /// https://cloud.google.com/nodejs/docs/reference/asset/latest
+  @$core.Deprecated('This field is deprecated.')
+  @$pb.TagNumber(1)
+  $core.String get referenceDocsUri => $_getSZ(0);
+  @$core.Deprecated('This field is deprecated.')
+  @$pb.TagNumber(1)
+  set referenceDocsUri($core.String v) {
+    $_setString(0, v);
+  }
+
+  @$core.Deprecated('This field is deprecated.')
+  @$pb.TagNumber(1)
+  $core.bool hasReferenceDocsUri() => $_has(0);
+  @$core.Deprecated('This field is deprecated.')
+  @$pb.TagNumber(1)
+  void clearReferenceDocsUri() => $_clearField(1);
+
+  /// The destination where API teams want this client library to be published.
+  @$pb.TagNumber(2)
+  $pb.PbList<ClientLibraryDestination> get destinations => $_getList(1);
+
+  /// Configuration for which RPCs should be generated in the GAPIC client.
+  @$pb.TagNumber(3)
+  SelectiveGapicGeneration get selectiveGapicGeneration => $_getN(2);
+  @$pb.TagNumber(3)
+  set selectiveGapicGeneration(SelectiveGapicGeneration v) {
+    $_setField(3, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasSelectiveGapicGeneration() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearSelectiveGapicGeneration() => $_clearField(3);
+  @$pb.TagNumber(3)
+  SelectiveGapicGeneration ensureSelectiveGapicGeneration() => $_ensure(2);
+}
+
+/// This message configures the settings for publishing [Google Cloud Client
+/// libraries](https://cloud.google.com/apis/docs/cloud-client-libraries)
+/// generated from the service config.
+class Publishing extends $pb.GeneratedMessage {
+  factory Publishing({
+    $core.String? newIssueUri,
+    $core.String? documentationUri,
+    $core.String? apiShortName,
+    $core.String? githubLabel,
+    $core.Iterable<$core.String>? codeownerGithubTeams,
+    $core.String? docTagPrefix,
+    ClientLibraryOrganization? organization,
+    $core.String? protoReferenceDocumentationUri,
+    $core.String? restReferenceDocumentationUri,
+  }) {
+    final $result = create();
+    if (newIssueUri != null) {
+      $result.newIssueUri = newIssueUri;
+    }
+    if (documentationUri != null) {
+      $result.documentationUri = documentationUri;
+    }
+    if (apiShortName != null) {
+      $result.apiShortName = apiShortName;
+    }
+    if (githubLabel != null) {
+      $result.githubLabel = githubLabel;
+    }
+    if (codeownerGithubTeams != null) {
+      $result.codeownerGithubTeams.addAll(codeownerGithubTeams);
+    }
+    if (docTagPrefix != null) {
+      $result.docTagPrefix = docTagPrefix;
+    }
+    if (organization != null) {
+      $result.organization = organization;
+    }
+    if (protoReferenceDocumentationUri != null) {
+      $result.protoReferenceDocumentationUri = protoReferenceDocumentationUri;
+    }
+    if (restReferenceDocumentationUri != null) {
+      $result.restReferenceDocumentationUri = restReferenceDocumentationUri;
+    }
+    return $result;
+  }
+  Publishing._() : super();
+  factory Publishing.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Publishing.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Publishing',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOS(101, _omitFieldNames ? '' : 'newIssueUri')
+    ..aOS(102, _omitFieldNames ? '' : 'documentationUri')
+    ..aOS(103, _omitFieldNames ? '' : 'apiShortName')
+    ..aOS(104, _omitFieldNames ? '' : 'githubLabel')
+    ..pPS(105, _omitFieldNames ? '' : 'codeownerGithubTeams')
+    ..aOS(106, _omitFieldNames ? '' : 'docTagPrefix')
+    ..e<ClientLibraryOrganization>(
+        107, _omitFieldNames ? '' : 'organization', $pb.PbFieldType.OE,
+        defaultOrMaker:
+            ClientLibraryOrganization.CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED,
+        valueOf: ClientLibraryOrganization.valueOf,
+        enumValues: ClientLibraryOrganization.values)
+    ..aOS(110, _omitFieldNames ? '' : 'protoReferenceDocumentationUri')
+    ..aOS(111, _omitFieldNames ? '' : 'restReferenceDocumentationUri')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Publishing clone() => Publishing()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Publishing copyWith(void Function(Publishing) updates) =>
+      super.copyWith((message) => updates(message as Publishing)) as Publishing;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Publishing create() => Publishing._();
+  Publishing createEmptyInstance() => create();
+  static $pb.PbList<Publishing> createRepeated() => $pb.PbList<Publishing>();
+  @$core.pragma('dart2js:noInline')
+  static Publishing getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<Publishing>(create);
+  static Publishing? _defaultInstance;
+
+  /// Link to a *public* URI where users can report issues.  Example:
+  /// https://issuetracker.google.com/issues/new?component=190865&template=1161103
+  @$pb.TagNumber(101)
+  $core.String get newIssueUri => $_getSZ(0);
+  @$pb.TagNumber(101)
+  set newIssueUri($core.String v) {
+    $_setString(0, v);
+  }
+
+  @$pb.TagNumber(101)
+  $core.bool hasNewIssueUri() => $_has(0);
+  @$pb.TagNumber(101)
+  void clearNewIssueUri() => $_clearField(101);
+
+  /// Link to product home page.  Example:
+  /// https://cloud.google.com/asset-inventory/docs/overview
+  @$pb.TagNumber(102)
+  $core.String get documentationUri => $_getSZ(1);
+  @$pb.TagNumber(102)
+  set documentationUri($core.String v) {
+    $_setString(1, v);
+  }
+
+  @$pb.TagNumber(102)
+  $core.bool hasDocumentationUri() => $_has(1);
+  @$pb.TagNumber(102)
+  void clearDocumentationUri() => $_clearField(102);
+
+  /// Used as a tracking tag when collecting data about the APIs developer
+  /// relations artifacts like docs, packages delivered to package managers,
+  /// etc.  Example: "speech".
+  @$pb.TagNumber(103)
+  $core.String get apiShortName => $_getSZ(2);
+  @$pb.TagNumber(103)
+  set apiShortName($core.String v) {
+    $_setString(2, v);
+  }
+
+  @$pb.TagNumber(103)
+  $core.bool hasApiShortName() => $_has(2);
+  @$pb.TagNumber(103)
+  void clearApiShortName() => $_clearField(103);
+
+  /// GitHub label to apply to issues and pull requests opened for this API.
+  @$pb.TagNumber(104)
+  $core.String get githubLabel => $_getSZ(3);
+  @$pb.TagNumber(104)
+  set githubLabel($core.String v) {
+    $_setString(3, v);
+  }
+
+  @$pb.TagNumber(104)
+  $core.bool hasGithubLabel() => $_has(3);
+  @$pb.TagNumber(104)
+  void clearGithubLabel() => $_clearField(104);
+
+  /// GitHub teams to be added to CODEOWNERS in the directory in GitHub
+  /// containing source code for the client libraries for this API.
+  @$pb.TagNumber(105)
+  $pb.PbList<$core.String> get codeownerGithubTeams => $_getList(4);
+
+  /// A prefix used in sample code when demarking regions to be included in
+  /// documentation.
+  @$pb.TagNumber(106)
+  $core.String get docTagPrefix => $_getSZ(5);
+  @$pb.TagNumber(106)
+  set docTagPrefix($core.String v) {
+    $_setString(5, v);
+  }
+
+  @$pb.TagNumber(106)
+  $core.bool hasDocTagPrefix() => $_has(5);
+  @$pb.TagNumber(106)
+  void clearDocTagPrefix() => $_clearField(106);
+
+  /// For whom the client library is being published.
+  @$pb.TagNumber(107)
+  ClientLibraryOrganization get organization => $_getN(6);
+  @$pb.TagNumber(107)
+  set organization(ClientLibraryOrganization v) {
+    $_setField(107, v);
+  }
+
+  @$pb.TagNumber(107)
+  $core.bool hasOrganization() => $_has(6);
+  @$pb.TagNumber(107)
+  void clearOrganization() => $_clearField(107);
+
+  /// Optional link to proto reference documentation.  Example:
+  /// https://cloud.google.com/pubsub/lite/docs/reference/rpc
+  @$pb.TagNumber(110)
+  $core.String get protoReferenceDocumentationUri => $_getSZ(7);
+  @$pb.TagNumber(110)
+  set protoReferenceDocumentationUri($core.String v) {
+    $_setString(7, v);
+  }
+
+  @$pb.TagNumber(110)
+  $core.bool hasProtoReferenceDocumentationUri() => $_has(7);
+  @$pb.TagNumber(110)
+  void clearProtoReferenceDocumentationUri() => $_clearField(110);
+
+  /// Optional link to REST reference documentation.  Example:
+  /// https://cloud.google.com/pubsub/lite/docs/reference/rest
+  @$pb.TagNumber(111)
+  $core.String get restReferenceDocumentationUri => $_getSZ(8);
+  @$pb.TagNumber(111)
+  set restReferenceDocumentationUri($core.String v) {
+    $_setString(8, v);
+  }
+
+  @$pb.TagNumber(111)
+  $core.bool hasRestReferenceDocumentationUri() => $_has(8);
+  @$pb.TagNumber(111)
+  void clearRestReferenceDocumentationUri() => $_clearField(111);
+}
+
+/// Settings for Java client libraries.
+class JavaSettings extends $pb.GeneratedMessage {
+  factory JavaSettings({
+    $core.String? libraryPackage,
+    $core.Iterable<$core.MapEntry<$core.String, $core.String>>?
+        serviceClassNames,
+    CommonLanguageSettings? common,
+  }) {
+    final $result = create();
+    if (libraryPackage != null) {
+      $result.libraryPackage = libraryPackage;
+    }
+    if (serviceClassNames != null) {
+      $result.serviceClassNames.addEntries(serviceClassNames);
+    }
+    if (common != null) {
+      $result.common = common;
+    }
+    return $result;
+  }
+  JavaSettings._() : super();
+  factory JavaSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory JavaSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'JavaSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOS(1, _omitFieldNames ? '' : 'libraryPackage')
+    ..m<$core.String, $core.String>(
+        2, _omitFieldNames ? '' : 'serviceClassNames',
+        entryClassName: 'JavaSettings.ServiceClassNamesEntry',
+        keyFieldType: $pb.PbFieldType.OS,
+        valueFieldType: $pb.PbFieldType.OS,
+        packageName: const $pb.PackageName('google.api'))
+    ..aOM<CommonLanguageSettings>(3, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  JavaSettings clone() => JavaSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  JavaSettings copyWith(void Function(JavaSettings) updates) =>
+      super.copyWith((message) => updates(message as JavaSettings))
+          as JavaSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static JavaSettings create() => JavaSettings._();
+  JavaSettings createEmptyInstance() => create();
+  static $pb.PbList<JavaSettings> createRepeated() =>
+      $pb.PbList<JavaSettings>();
+  @$core.pragma('dart2js:noInline')
+  static JavaSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<JavaSettings>(create);
+  static JavaSettings? _defaultInstance;
+
+  /// The package name to use in Java. Clobbers the java_package option
+  /// set in the protobuf. This should be used **only** by APIs
+  /// who have already set the language_settings.java.package_name" field
+  /// in gapic.yaml. API teams should use the protobuf java_package option
+  /// where possible.
+  ///
+  /// Example of a YAML configuration::
+  ///
+  ///  publishing:
+  ///    java_settings:
+  ///      library_package: com.google.cloud.pubsub.v1
+  @$pb.TagNumber(1)
+  $core.String get libraryPackage => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set libraryPackage($core.String v) {
+    $_setString(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasLibraryPackage() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearLibraryPackage() => $_clearField(1);
+
+  /// Configure the Java class name to use instead of the service's for its
+  /// corresponding generated GAPIC client. Keys are fully-qualified
+  /// service names as they appear in the protobuf (including the full
+  /// the language_settings.java.interface_names" field in gapic.yaml. API
+  /// teams should otherwise use the service name as it appears in the
+  /// protobuf.
+  ///
+  /// Example of a YAML configuration::
+  ///
+  ///  publishing:
+  ///    java_settings:
+  ///      service_class_names:
+  ///        - google.pubsub.v1.Publisher: TopicAdmin
+  ///        - google.pubsub.v1.Subscriber: SubscriptionAdmin
+  @$pb.TagNumber(2)
+  $pb.PbMap<$core.String, $core.String> get serviceClassNames => $_getMap(1);
+
+  /// Some settings.
+  @$pb.TagNumber(3)
+  CommonLanguageSettings get common => $_getN(2);
+  @$pb.TagNumber(3)
+  set common(CommonLanguageSettings v) {
+    $_setField(3, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasCommon() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearCommon() => $_clearField(3);
+  @$pb.TagNumber(3)
+  CommonLanguageSettings ensureCommon() => $_ensure(2);
+}
+
+/// Settings for C++ client libraries.
+class CppSettings extends $pb.GeneratedMessage {
+  factory CppSettings({
+    CommonLanguageSettings? common,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    return $result;
+  }
+  CppSettings._() : super();
+  factory CppSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory CppSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'CppSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  CppSettings clone() => CppSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  CppSettings copyWith(void Function(CppSettings) updates) =>
+      super.copyWith((message) => updates(message as CppSettings))
+          as CppSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static CppSettings create() => CppSettings._();
+  CppSettings createEmptyInstance() => create();
+  static $pb.PbList<CppSettings> createRepeated() => $pb.PbList<CppSettings>();
+  @$core.pragma('dart2js:noInline')
+  static CppSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<CppSettings>(create);
+  static CppSettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+}
+
+/// Settings for Php client libraries.
+class PhpSettings extends $pb.GeneratedMessage {
+  factory PhpSettings({
+    CommonLanguageSettings? common,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    return $result;
+  }
+  PhpSettings._() : super();
+  factory PhpSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory PhpSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'PhpSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  PhpSettings clone() => PhpSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  PhpSettings copyWith(void Function(PhpSettings) updates) =>
+      super.copyWith((message) => updates(message as PhpSettings))
+          as PhpSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static PhpSettings create() => PhpSettings._();
+  PhpSettings createEmptyInstance() => create();
+  static $pb.PbList<PhpSettings> createRepeated() => $pb.PbList<PhpSettings>();
+  @$core.pragma('dart2js:noInline')
+  static PhpSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<PhpSettings>(create);
+  static PhpSettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+}
+
+/// Experimental features to be included during client library generation.
+/// These fields will be deprecated once the feature graduates and is enabled
+/// by default.
+class PythonSettings_ExperimentalFeatures extends $pb.GeneratedMessage {
+  factory PythonSettings_ExperimentalFeatures({
+    $core.bool? restAsyncIoEnabled,
+    $core.bool? protobufPythonicTypesEnabled,
+    $core.bool? unversionedPackageDisabled,
+  }) {
+    final $result = create();
+    if (restAsyncIoEnabled != null) {
+      $result.restAsyncIoEnabled = restAsyncIoEnabled;
+    }
+    if (protobufPythonicTypesEnabled != null) {
+      $result.protobufPythonicTypesEnabled = protobufPythonicTypesEnabled;
+    }
+    if (unversionedPackageDisabled != null) {
+      $result.unversionedPackageDisabled = unversionedPackageDisabled;
+    }
+    return $result;
+  }
+  PythonSettings_ExperimentalFeatures._() : super();
+  factory PythonSettings_ExperimentalFeatures.fromBuffer(
+          $core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory PythonSettings_ExperimentalFeatures.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'PythonSettings.ExperimentalFeatures',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOB(1, _omitFieldNames ? '' : 'restAsyncIoEnabled')
+    ..aOB(2, _omitFieldNames ? '' : 'protobufPythonicTypesEnabled')
+    ..aOB(3, _omitFieldNames ? '' : 'unversionedPackageDisabled')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  PythonSettings_ExperimentalFeatures clone() =>
+      PythonSettings_ExperimentalFeatures()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  PythonSettings_ExperimentalFeatures copyWith(
+          void Function(PythonSettings_ExperimentalFeatures) updates) =>
+      super.copyWith((message) =>
+              updates(message as PythonSettings_ExperimentalFeatures))
+          as PythonSettings_ExperimentalFeatures;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static PythonSettings_ExperimentalFeatures create() =>
+      PythonSettings_ExperimentalFeatures._();
+  PythonSettings_ExperimentalFeatures createEmptyInstance() => create();
+  static $pb.PbList<PythonSettings_ExperimentalFeatures> createRepeated() =>
+      $pb.PbList<PythonSettings_ExperimentalFeatures>();
+  @$core.pragma('dart2js:noInline')
+  static PythonSettings_ExperimentalFeatures getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
+          PythonSettings_ExperimentalFeatures>(create);
+  static PythonSettings_ExperimentalFeatures? _defaultInstance;
+
+  /// Enables generation of asynchronous REST clients if `rest` transport is
+  /// enabled. By default, asynchronous REST clients will not be generated.
+  /// This feature will be enabled by default 1 month after launching the
+  /// feature in preview packages.
+  @$pb.TagNumber(1)
+  $core.bool get restAsyncIoEnabled => $_getBF(0);
+  @$pb.TagNumber(1)
+  set restAsyncIoEnabled($core.bool v) {
+    $_setBool(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasRestAsyncIoEnabled() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearRestAsyncIoEnabled() => $_clearField(1);
+
+  /// Enables generation of protobuf code using new types that are more
+  /// Pythonic which are included in `protobuf>=5.29.x`. This feature will be
+  /// enabled by default 1 month after launching the feature in preview
+  /// packages.
+  @$pb.TagNumber(2)
+  $core.bool get protobufPythonicTypesEnabled => $_getBF(1);
+  @$pb.TagNumber(2)
+  set protobufPythonicTypesEnabled($core.bool v) {
+    $_setBool(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasProtobufPythonicTypesEnabled() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearProtobufPythonicTypesEnabled() => $_clearField(2);
+
+  /// Disables generation of an unversioned Python package for this client
+  /// library. This means that the module names will need to be versioned in
+  /// import statements. For example `import google.cloud.library_v2` instead
+  /// of `import google.cloud.library`.
+  @$pb.TagNumber(3)
+  $core.bool get unversionedPackageDisabled => $_getBF(2);
+  @$pb.TagNumber(3)
+  set unversionedPackageDisabled($core.bool v) {
+    $_setBool(2, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasUnversionedPackageDisabled() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearUnversionedPackageDisabled() => $_clearField(3);
+}
+
+/// Settings for Python client libraries.
+class PythonSettings extends $pb.GeneratedMessage {
+  factory PythonSettings({
+    CommonLanguageSettings? common,
+    PythonSettings_ExperimentalFeatures? experimentalFeatures,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    if (experimentalFeatures != null) {
+      $result.experimentalFeatures = experimentalFeatures;
+    }
+    return $result;
+  }
+  PythonSettings._() : super();
+  factory PythonSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory PythonSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'PythonSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..aOM<PythonSettings_ExperimentalFeatures>(
+        2, _omitFieldNames ? '' : 'experimentalFeatures',
+        subBuilder: PythonSettings_ExperimentalFeatures.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  PythonSettings clone() => PythonSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  PythonSettings copyWith(void Function(PythonSettings) updates) =>
+      super.copyWith((message) => updates(message as PythonSettings))
+          as PythonSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static PythonSettings create() => PythonSettings._();
+  PythonSettings createEmptyInstance() => create();
+  static $pb.PbList<PythonSettings> createRepeated() =>
+      $pb.PbList<PythonSettings>();
+  @$core.pragma('dart2js:noInline')
+  static PythonSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<PythonSettings>(create);
+  static PythonSettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+
+  /// Experimental features to be included during client library generation.
+  @$pb.TagNumber(2)
+  PythonSettings_ExperimentalFeatures get experimentalFeatures => $_getN(1);
+  @$pb.TagNumber(2)
+  set experimentalFeatures(PythonSettings_ExperimentalFeatures v) {
+    $_setField(2, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasExperimentalFeatures() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearExperimentalFeatures() => $_clearField(2);
+  @$pb.TagNumber(2)
+  PythonSettings_ExperimentalFeatures ensureExperimentalFeatures() =>
+      $_ensure(1);
+}
+
+/// Settings for Node client libraries.
+class NodeSettings extends $pb.GeneratedMessage {
+  factory NodeSettings({
+    CommonLanguageSettings? common,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    return $result;
+  }
+  NodeSettings._() : super();
+  factory NodeSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory NodeSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'NodeSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  NodeSettings clone() => NodeSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  NodeSettings copyWith(void Function(NodeSettings) updates) =>
+      super.copyWith((message) => updates(message as NodeSettings))
+          as NodeSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static NodeSettings create() => NodeSettings._();
+  NodeSettings createEmptyInstance() => create();
+  static $pb.PbList<NodeSettings> createRepeated() =>
+      $pb.PbList<NodeSettings>();
+  @$core.pragma('dart2js:noInline')
+  static NodeSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<NodeSettings>(create);
+  static NodeSettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+}
+
+/// Settings for Dotnet client libraries.
+class DotnetSettings extends $pb.GeneratedMessage {
+  factory DotnetSettings({
+    CommonLanguageSettings? common,
+    $core.Iterable<$core.MapEntry<$core.String, $core.String>>? renamedServices,
+    $core.Iterable<$core.MapEntry<$core.String, $core.String>>?
+        renamedResources,
+    $core.Iterable<$core.String>? ignoredResources,
+    $core.Iterable<$core.String>? forcedNamespaceAliases,
+    $core.Iterable<$core.String>? handwrittenSignatures,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    if (renamedServices != null) {
+      $result.renamedServices.addEntries(renamedServices);
+    }
+    if (renamedResources != null) {
+      $result.renamedResources.addEntries(renamedResources);
+    }
+    if (ignoredResources != null) {
+      $result.ignoredResources.addAll(ignoredResources);
+    }
+    if (forcedNamespaceAliases != null) {
+      $result.forcedNamespaceAliases.addAll(forcedNamespaceAliases);
+    }
+    if (handwrittenSignatures != null) {
+      $result.handwrittenSignatures.addAll(handwrittenSignatures);
+    }
+    return $result;
+  }
+  DotnetSettings._() : super();
+  factory DotnetSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory DotnetSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'DotnetSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..m<$core.String, $core.String>(2, _omitFieldNames ? '' : 'renamedServices',
+        entryClassName: 'DotnetSettings.RenamedServicesEntry',
+        keyFieldType: $pb.PbFieldType.OS,
+        valueFieldType: $pb.PbFieldType.OS,
+        packageName: const $pb.PackageName('google.api'))
+    ..m<$core.String, $core.String>(
+        3, _omitFieldNames ? '' : 'renamedResources',
+        entryClassName: 'DotnetSettings.RenamedResourcesEntry',
+        keyFieldType: $pb.PbFieldType.OS,
+        valueFieldType: $pb.PbFieldType.OS,
+        packageName: const $pb.PackageName('google.api'))
+    ..pPS(4, _omitFieldNames ? '' : 'ignoredResources')
+    ..pPS(5, _omitFieldNames ? '' : 'forcedNamespaceAliases')
+    ..pPS(6, _omitFieldNames ? '' : 'handwrittenSignatures')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  DotnetSettings clone() => DotnetSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  DotnetSettings copyWith(void Function(DotnetSettings) updates) =>
+      super.copyWith((message) => updates(message as DotnetSettings))
+          as DotnetSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static DotnetSettings create() => DotnetSettings._();
+  DotnetSettings createEmptyInstance() => create();
+  static $pb.PbList<DotnetSettings> createRepeated() =>
+      $pb.PbList<DotnetSettings>();
+  @$core.pragma('dart2js:noInline')
+  static DotnetSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<DotnetSettings>(create);
+  static DotnetSettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+
+  /// Map from original service names to renamed versions.
+  /// This is used when the default generated types
+  /// would cause a naming conflict. (Neither name is
+  /// fully-qualified.)
+  /// Example: Subscriber to SubscriberServiceApi.
+  @$pb.TagNumber(2)
+  $pb.PbMap<$core.String, $core.String> get renamedServices => $_getMap(1);
+
+  /// Map from full resource types to the effective short name
+  /// for the resource. This is used when otherwise resource
+  /// named from different services would cause naming collisions.
+  /// Example entry:
+  /// "datalabeling.googleapis.com/Dataset": "DataLabelingDataset"
+  @$pb.TagNumber(3)
+  $pb.PbMap<$core.String, $core.String> get renamedResources => $_getMap(2);
+
+  /// List of full resource types to ignore during generation.
+  /// This is typically used for API-specific Location resources,
+  /// which should be handled by the generator as if they were actually
+  /// the common Location resources.
+  /// Example entry: "documentai.googleapis.com/Location"
+  @$pb.TagNumber(4)
+  $pb.PbList<$core.String> get ignoredResources => $_getList(3);
+
+  /// Namespaces which must be aliased in snippets due to
+  /// a known (but non-generator-predictable) naming collision
+  @$pb.TagNumber(5)
+  $pb.PbList<$core.String> get forcedNamespaceAliases => $_getList(4);
+
+  /// Method signatures (in the form "service.method(signature)")
+  /// which are provided separately, so shouldn't be generated.
+  /// Snippets *calling* these methods are still generated, however.
+  @$pb.TagNumber(6)
+  $pb.PbList<$core.String> get handwrittenSignatures => $_getList(5);
+}
+
+/// Settings for Ruby client libraries.
+class RubySettings extends $pb.GeneratedMessage {
+  factory RubySettings({
+    CommonLanguageSettings? common,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    return $result;
+  }
+  RubySettings._() : super();
+  factory RubySettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory RubySettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'RubySettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  RubySettings clone() => RubySettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  RubySettings copyWith(void Function(RubySettings) updates) =>
+      super.copyWith((message) => updates(message as RubySettings))
+          as RubySettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static RubySettings create() => RubySettings._();
+  RubySettings createEmptyInstance() => create();
+  static $pb.PbList<RubySettings> createRepeated() =>
+      $pb.PbList<RubySettings>();
+  @$core.pragma('dart2js:noInline')
+  static RubySettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<RubySettings>(create);
+  static RubySettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+}
+
+/// Settings for Go client libraries.
+class GoSettings extends $pb.GeneratedMessage {
+  factory GoSettings({
+    CommonLanguageSettings? common,
+    $core.Iterable<$core.MapEntry<$core.String, $core.String>>? renamedServices,
+  }) {
+    final $result = create();
+    if (common != null) {
+      $result.common = common;
+    }
+    if (renamedServices != null) {
+      $result.renamedServices.addEntries(renamedServices);
+    }
+    return $result;
+  }
+  GoSettings._() : super();
+  factory GoSettings.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory GoSettings.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'GoSettings',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..aOM<CommonLanguageSettings>(1, _omitFieldNames ? '' : 'common',
+        subBuilder: CommonLanguageSettings.create)
+    ..m<$core.String, $core.String>(2, _omitFieldNames ? '' : 'renamedServices',
+        entryClassName: 'GoSettings.RenamedServicesEntry',
+        keyFieldType: $pb.PbFieldType.OS,
+        valueFieldType: $pb.PbFieldType.OS,
+        packageName: const $pb.PackageName('google.api'))
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  GoSettings clone() => GoSettings()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  GoSettings copyWith(void Function(GoSettings) updates) =>
+      super.copyWith((message) => updates(message as GoSettings)) as GoSettings;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static GoSettings create() => GoSettings._();
+  GoSettings createEmptyInstance() => create();
+  static $pb.PbList<GoSettings> createRepeated() => $pb.PbList<GoSettings>();
+  @$core.pragma('dart2js:noInline')
+  static GoSettings getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<GoSettings>(create);
+  static GoSettings? _defaultInstance;
+
+  /// Some settings.
+  @$pb.TagNumber(1)
+  CommonLanguageSettings get common => $_getN(0);
+  @$pb.TagNumber(1)
+  set common(CommonLanguageSettings v) {
+    $_setField(1, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasCommon() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCommon() => $_clearField(1);
+  @$pb.TagNumber(1)
+  CommonLanguageSettings ensureCommon() => $_ensure(0);
+
+  /// Map of service names to renamed services. Keys are the package relative
+  /// service names and values are the name to be used for the service client
+  /// and call options.
+  ///
+  /// publishing:
+  ///   go_settings:
+  ///     renamed_services:
+  ///       Publisher: TopicAdmin
+  @$pb.TagNumber(2)
+  $pb.PbMap<$core.String, $core.String> get renamedServices => $_getMap(1);
+}
+
+/// This message is used to configure the generation of a subset of the RPCs in
+/// a service for client libraries.
+class SelectiveGapicGeneration extends $pb.GeneratedMessage {
+  factory SelectiveGapicGeneration({
+    $core.Iterable<$core.String>? methods,
+    $core.bool? generateOmittedAsInternal,
+  }) {
+    final $result = create();
+    if (methods != null) {
+      $result.methods.addAll(methods);
+    }
+    if (generateOmittedAsInternal != null) {
+      $result.generateOmittedAsInternal = generateOmittedAsInternal;
+    }
+    return $result;
+  }
+  SelectiveGapicGeneration._() : super();
+  factory SelectiveGapicGeneration.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory SelectiveGapicGeneration.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'SelectiveGapicGeneration',
+      package: const $pb.PackageName(_omitMessageNames ? '' : 'google.api'),
+      createEmptyInstance: create)
+    ..pPS(1, _omitFieldNames ? '' : 'methods')
+    ..aOB(2, _omitFieldNames ? '' : 'generateOmittedAsInternal')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  SelectiveGapicGeneration clone() =>
+      SelectiveGapicGeneration()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  SelectiveGapicGeneration copyWith(
+          void Function(SelectiveGapicGeneration) updates) =>
+      super.copyWith((message) => updates(message as SelectiveGapicGeneration))
+          as SelectiveGapicGeneration;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static SelectiveGapicGeneration create() => SelectiveGapicGeneration._();
+  SelectiveGapicGeneration createEmptyInstance() => create();
+  static $pb.PbList<SelectiveGapicGeneration> createRepeated() =>
+      $pb.PbList<SelectiveGapicGeneration>();
+  @$core.pragma('dart2js:noInline')
+  static SelectiveGapicGeneration getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<SelectiveGapicGeneration>(create);
+  static SelectiveGapicGeneration? _defaultInstance;
+
+  /// An allowlist of the fully qualified names of RPCs that should be included
+  /// on public client surfaces.
+  @$pb.TagNumber(1)
+  $pb.PbList<$core.String> get methods => $_getList(0);
+
+  /// Setting this to true indicates to the client generators that methods
+  /// that would be excluded from the generation should instead be generated
+  /// in a way that indicates these methods should not be consumed by
+  /// end users. How this is expressed is up to individual language
+  /// implementations to decide. Some examples may be: added annotations,
+  /// obfuscated identifiers, or other language idiomatic patterns.
+  @$pb.TagNumber(2)
+  $core.bool get generateOmittedAsInternal => $_getBF(1);
+  @$pb.TagNumber(2)
+  set generateOmittedAsInternal($core.bool v) {
+    $_setBool(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasGenerateOmittedAsInternal() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearGenerateOmittedAsInternal() => $_clearField(2);
+}
+
+class Client {
+  static final methodSignature = $pb.Extension<$core.String>.repeated(
+      _omitMessageNames ? '' : 'google.protobuf.MethodOptions',
+      _omitFieldNames ? '' : 'methodSignature',
+      1051,
+      $pb.PbFieldType.PS,
+      check: $pb.getCheckFunction($pb.PbFieldType.PS));
+  static final defaultHost = $pb.Extension<$core.String>(
+      _omitMessageNames ? '' : 'google.protobuf.ServiceOptions',
+      _omitFieldNames ? '' : 'defaultHost',
+      1049,
+      $pb.PbFieldType.OS);
+  static final oauthScopes = $pb.Extension<$core.String>(
+      _omitMessageNames ? '' : 'google.protobuf.ServiceOptions',
+      _omitFieldNames ? '' : 'oauthScopes',
+      1050,
+      $pb.PbFieldType.OS);
+  static final apiVersion = $pb.Extension<$core.String>(
+      _omitMessageNames ? '' : 'google.protobuf.ServiceOptions',
+      _omitFieldNames ? '' : 'apiVersion',
+      525000001,
+      $pb.PbFieldType.OS);
+  static void registerAllExtensions($pb.ExtensionRegistry registry) {
+    registry.add(methodSignature);
+    registry.add(defaultHost);
+    registry.add(oauthScopes);
+    registry.add(apiVersion);
+  }
+}
+
+const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
+const _omitMessageNames =
+    $core.bool.fromEnvironment('protobuf.omit_message_names');
diff --git a/protoc_plugin/lib/src/generated/client.pbenum.dart b/protoc_plugin/lib/src/generated/client.pbenum.dart
new file mode 100644
index 0000000..bc568da
--- /dev/null
+++ b/protoc_plugin/lib/src/generated/client.pbenum.dart
@@ -0,0 +1,103 @@
+//
+//  Generated code. Do not modify.
+//  source: client.proto
+//
+// @dart = 3.3
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+/// The organization for which the client libraries are being published.
+/// Affects the url where generated docs are published, etc.
+class ClientLibraryOrganization extends $pb.ProtobufEnum {
+  /// Not useful.
+  static const ClientLibraryOrganization
+      CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED = ClientLibraryOrganization._(
+          0, _omitEnumNames ? '' : 'CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED');
+
+  /// Google Cloud Platform Org.
+  static const ClientLibraryOrganization CLOUD =
+      ClientLibraryOrganization._(1, _omitEnumNames ? '' : 'CLOUD');
+
+  /// Ads (Advertising) Org.
+  static const ClientLibraryOrganization ADS =
+      ClientLibraryOrganization._(2, _omitEnumNames ? '' : 'ADS');
+
+  /// Photos Org.
+  static const ClientLibraryOrganization PHOTOS =
+      ClientLibraryOrganization._(3, _omitEnumNames ? '' : 'PHOTOS');
+
+  /// Street View Org.
+  static const ClientLibraryOrganization STREET_VIEW =
+      ClientLibraryOrganization._(4, _omitEnumNames ? '' : 'STREET_VIEW');
+
+  /// Shopping Org.
+  static const ClientLibraryOrganization SHOPPING =
+      ClientLibraryOrganization._(5, _omitEnumNames ? '' : 'SHOPPING');
+
+  /// Geo Org.
+  static const ClientLibraryOrganization GEO =
+      ClientLibraryOrganization._(6, _omitEnumNames ? '' : 'GEO');
+
+  /// Generative AI - https://developers.generativeai.google
+  static const ClientLibraryOrganization GENERATIVE_AI =
+      ClientLibraryOrganization._(7, _omitEnumNames ? '' : 'GENERATIVE_AI');
+
+  static const $core.List<ClientLibraryOrganization> values =
+      <ClientLibraryOrganization>[
+    CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED,
+    CLOUD,
+    ADS,
+    PHOTOS,
+    STREET_VIEW,
+    SHOPPING,
+    GEO,
+    GENERATIVE_AI,
+  ];
+
+  static final $core.List<ClientLibraryOrganization?> _byValue =
+      $pb.ProtobufEnum.$_initByValueList(values, 7);
+  static ClientLibraryOrganization? valueOf($core.int value) =>
+      value < 0 || value >= _byValue.length ? null : _byValue[value];
+
+  const ClientLibraryOrganization._(super.v, super.n);
+}
+
+/// To where should client libraries be published?
+class ClientLibraryDestination extends $pb.ProtobufEnum {
+  /// Client libraries will neither be generated nor published to package
+  /// managers.
+  static const ClientLibraryDestination CLIENT_LIBRARY_DESTINATION_UNSPECIFIED =
+      ClientLibraryDestination._(
+          0, _omitEnumNames ? '' : 'CLIENT_LIBRARY_DESTINATION_UNSPECIFIED');
+
+  /// Generate the client library in a repo under github.com/googleapis,
+  /// but don't publish it to package managers.
+  static const ClientLibraryDestination GITHUB =
+      ClientLibraryDestination._(10, _omitEnumNames ? '' : 'GITHUB');
+
+  /// Publish the library to package managers like nuget.org and npmjs.com.
+  static const ClientLibraryDestination PACKAGE_MANAGER =
+      ClientLibraryDestination._(20, _omitEnumNames ? '' : 'PACKAGE_MANAGER');
+
+  static const $core.List<ClientLibraryDestination> values =
+      <ClientLibraryDestination>[
+    CLIENT_LIBRARY_DESTINATION_UNSPECIFIED,
+    GITHUB,
+    PACKAGE_MANAGER,
+  ];
+
+  static final $core.Map<$core.int, ClientLibraryDestination> _byValue =
+      $pb.ProtobufEnum.initByValue(values);
+  static ClientLibraryDestination? valueOf($core.int value) => _byValue[value];
+
+  const ClientLibraryDestination._(super.v, super.n);
+}
+
+const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');
diff --git a/protoc_plugin/lib/src/grpc_generator.dart b/protoc_plugin/lib/src/grpc_generator.dart
index 85ad5e9..b7b3784 100644
--- a/protoc_plugin/lib/src/grpc_generator.dart
+++ b/protoc_plugin/lib/src/grpc_generator.dart
@@ -124,6 +124,26 @@
     }
     out.println("@$protobufImportPrefix.GrpcServiceName('$_fullServiceName')");
     out.addBlock('class $_clientClassname extends $_client {', '}', () {
+      // Look for and generate default_host info.
+      final defaultHost = _descriptor.options.defaultHost;
+      if (defaultHost != null) {
+        out.println('/// The hostname for this service.');
+        out.println("static const $_string defaultHost = '$defaultHost';");
+        out.println();
+      }
+
+      // Look for and generate oauth_scopes info.
+      final oauthScopes = _descriptor.options.oauthScopes;
+      if (oauthScopes != null) {
+        out.println('/// OAuth scopes needed for the client.');
+        out.println('static const $_list<$_string> oauthScopes = [');
+        for (final scope in oauthScopes.split(',')) {
+          out.println("  '$scope',");
+        }
+        out.println('];');
+        out.println();
+      }
+
       for (final method in _methods) {
         method.generateClientMethodDescriptor(out);
       }
@@ -161,6 +181,8 @@
   static final String _callOptions = '$grpcImportPrefix.CallOptions';
   static final String _client = '$grpcImportPrefix.Client';
   static final String _service = '$grpcImportPrefix.Service';
+  static final String _list = '$coreImportPrefix.List';
+  static final String _string = '$coreImportPrefix.String';
 }
 
 class _GrpcMethod {
@@ -318,3 +340,9 @@
   static final String _responseFuture = '$grpcImportPrefix.ResponseFuture';
   static final String _responseStream = '$grpcImportPrefix.ResponseStream';
 }
+
+extension on ServiceOptions {
+  String? get defaultHost => getExtension(Client.defaultHost) as String?;
+
+  String? get oauthScopes => getExtension(Client.oauthScopes) as String?;
+}
diff --git a/protoc_plugin/protos/client.proto b/protoc_plugin/protos/client.proto
new file mode 100644
index 0000000..0991b2d
--- /dev/null
+++ b/protoc_plugin/protos/client.proto
@@ -0,0 +1,369 @@
+// Copyright 2025 Google LLC
+//
+// 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.
+
+syntax = "proto3";
+
+package google.api;
+
+import "descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "ClientProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+  // A definition of a client library method signature.
+  //
+  // In client libraries, each proto RPC corresponds to one or more methods
+  // which the end user is able to call, and calls the underlying RPC.
+  // Normally, this method receives a single argument (a struct or instance
+  // corresponding to the RPC request object). Defining this field will
+  // add one or more overloads providing flattened or simpler method signatures
+  // in some languages.
+  //
+  // The fields on the method signature are provided as a comma-separated
+  // string.
+  //
+  // For example, the proto RPC and annotation:
+  //
+  //   rpc CreateSubscription(CreateSubscriptionRequest)
+  //       returns (Subscription) {
+  //     option (google.api.method_signature) = "name,topic";
+  //   }
+  //
+  // Would add the following Java overload (in addition to the method accepting
+  // the request object):
+  //
+  //   public final Subscription createSubscription(String name, String topic)
+  //
+  // The following backwards-compatibility guidelines apply:
+  //
+  //   * Adding this annotation to an unannotated method is backwards
+  //     compatible.
+  //   * Adding this annotation to a method which already has existing
+  //     method signature annotations is backwards compatible if and only if
+  //     the new method signature annotation is last in the sequence.
+  //   * Modifying or removing an existing method signature annotation is
+  //     a breaking change.
+  //   * Re-ordering existing method signature annotations is a breaking
+  //     change.
+  repeated string method_signature = 1051;
+}
+
+extend google.protobuf.ServiceOptions {
+  // The hostname for this service.
+  // This should be specified with no prefix or protocol.
+  //
+  // Example:
+  //
+  //   service Foo {
+  //     option (google.api.default_host) = "foo.googleapi.com";
+  //     ...
+  //   }
+  string default_host = 1049;
+
+  // OAuth scopes needed for the client.
+  //
+  // Example:
+  //
+  //   service Foo {
+  //     option (google.api.oauth_scopes) = \
+  //       "https://www.googleapis.com/auth/cloud-platform";
+  //     ...
+  //   }
+  //
+  // If there is more than one scope, use a comma-separated string:
+  //
+  // Example:
+  //
+  //   service Foo {
+  //     option (google.api.oauth_scopes) = \
+  //       "https://www.googleapis.com/auth/cloud-platform,"
+  //       "https://www.googleapis.com/auth/monitoring";
+  //     ...
+  //   }
+  string oauth_scopes = 1050;
+
+  // The API version of this service, which should be sent by version-aware
+  // clients to the service. This allows services to abide by the schema and
+  // behavior of the service at the time this API version was deployed.
+  // The format of the API version must be treated as opaque by clients.
+  // Services may use a format with an apparent structure, but clients must
+  // not rely on this to determine components within an API version, or attempt
+  // to construct other valid API versions. Note that this is for upcoming
+  // functionality and may not be implemented for all services.
+  //
+  // Example:
+  //
+  //   service Foo {
+  //     option (google.api.api_version) = "v1_20230821_preview";
+  //   }
+  string api_version = 525000001;
+}
+
+// Required information for every language.
+message CommonLanguageSettings {
+  // Link to automatically generated reference documentation.  Example:
+  // https://cloud.google.com/nodejs/docs/reference/asset/latest
+  string reference_docs_uri = 1 [deprecated = true];
+
+  // The destination where API teams want this client library to be published.
+  repeated ClientLibraryDestination destinations = 2;
+
+  // Configuration for which RPCs should be generated in the GAPIC client.
+  SelectiveGapicGeneration selective_gapic_generation = 3;
+}
+
+// This message configures the settings for publishing [Google Cloud Client
+// libraries](https://cloud.google.com/apis/docs/cloud-client-libraries)
+// generated from the service config.
+message Publishing {
+  // Link to a *public* URI where users can report issues.  Example:
+  // https://issuetracker.google.com/issues/new?component=190865&template=1161103
+  string new_issue_uri = 101;
+
+  // Link to product home page.  Example:
+  // https://cloud.google.com/asset-inventory/docs/overview
+  string documentation_uri = 102;
+
+  // Used as a tracking tag when collecting data about the APIs developer
+  // relations artifacts like docs, packages delivered to package managers,
+  // etc.  Example: "speech".
+  string api_short_name = 103;
+
+  // GitHub label to apply to issues and pull requests opened for this API.
+  string github_label = 104;
+
+  // GitHub teams to be added to CODEOWNERS in the directory in GitHub
+  // containing source code for the client libraries for this API.
+  repeated string codeowner_github_teams = 105;
+
+  // A prefix used in sample code when demarking regions to be included in
+  // documentation.
+  string doc_tag_prefix = 106;
+
+  // For whom the client library is being published.
+  ClientLibraryOrganization organization = 107;
+
+  // Optional link to proto reference documentation.  Example:
+  // https://cloud.google.com/pubsub/lite/docs/reference/rpc
+  string proto_reference_documentation_uri = 110;
+
+  // Optional link to REST reference documentation.  Example:
+  // https://cloud.google.com/pubsub/lite/docs/reference/rest
+  string rest_reference_documentation_uri = 111;
+}
+
+// Settings for Java client libraries.
+message JavaSettings {
+  // The package name to use in Java. Clobbers the java_package option
+  // set in the protobuf. This should be used **only** by APIs
+  // who have already set the language_settings.java.package_name" field
+  // in gapic.yaml. API teams should use the protobuf java_package option
+  // where possible.
+  //
+  // Example of a YAML configuration::
+  //
+  //  publishing:
+  //    java_settings:
+  //      library_package: com.google.cloud.pubsub.v1
+  string library_package = 1;
+
+  // Configure the Java class name to use instead of the service's for its
+  // corresponding generated GAPIC client. Keys are fully-qualified
+  // service names as they appear in the protobuf (including the full
+  // the language_settings.java.interface_names" field in gapic.yaml. API
+  // teams should otherwise use the service name as it appears in the
+  // protobuf.
+  //
+  // Example of a YAML configuration::
+  //
+  //  publishing:
+  //    java_settings:
+  //      service_class_names:
+  //        - google.pubsub.v1.Publisher: TopicAdmin
+  //        - google.pubsub.v1.Subscriber: SubscriptionAdmin
+  map<string, string> service_class_names = 2;
+
+  // Some settings.
+  CommonLanguageSettings common = 3;
+}
+
+// Settings for C++ client libraries.
+message CppSettings {
+  // Some settings.
+  CommonLanguageSettings common = 1;
+}
+
+// Settings for Php client libraries.
+message PhpSettings {
+  // Some settings.
+  CommonLanguageSettings common = 1;
+}
+
+// Settings for Python client libraries.
+message PythonSettings {
+  // Experimental features to be included during client library generation.
+  // These fields will be deprecated once the feature graduates and is enabled
+  // by default.
+  message ExperimentalFeatures {
+    // Enables generation of asynchronous REST clients if `rest` transport is
+    // enabled. By default, asynchronous REST clients will not be generated.
+    // This feature will be enabled by default 1 month after launching the
+    // feature in preview packages.
+    bool rest_async_io_enabled = 1;
+
+    // Enables generation of protobuf code using new types that are more
+    // Pythonic which are included in `protobuf>=5.29.x`. This feature will be
+    // enabled by default 1 month after launching the feature in preview
+    // packages.
+    bool protobuf_pythonic_types_enabled = 2;
+
+    // Disables generation of an unversioned Python package for this client
+    // library. This means that the module names will need to be versioned in
+    // import statements. For example `import google.cloud.library_v2` instead
+    // of `import google.cloud.library`.
+    bool unversioned_package_disabled = 3;
+  }
+
+  // Some settings.
+  CommonLanguageSettings common = 1;
+
+  // Experimental features to be included during client library generation.
+  ExperimentalFeatures experimental_features = 2;
+}
+
+// Settings for Node client libraries.
+message NodeSettings {
+  // Some settings.
+  CommonLanguageSettings common = 1;
+}
+
+// Settings for Dotnet client libraries.
+message DotnetSettings {
+  // Some settings.
+  CommonLanguageSettings common = 1;
+
+  // Map from original service names to renamed versions.
+  // This is used when the default generated types
+  // would cause a naming conflict. (Neither name is
+  // fully-qualified.)
+  // Example: Subscriber to SubscriberServiceApi.
+  map<string, string> renamed_services = 2;
+
+  // Map from full resource types to the effective short name
+  // for the resource. This is used when otherwise resource
+  // named from different services would cause naming collisions.
+  // Example entry:
+  // "datalabeling.googleapis.com/Dataset": "DataLabelingDataset"
+  map<string, string> renamed_resources = 3;
+
+  // List of full resource types to ignore during generation.
+  // This is typically used for API-specific Location resources,
+  // which should be handled by the generator as if they were actually
+  // the common Location resources.
+  // Example entry: "documentai.googleapis.com/Location"
+  repeated string ignored_resources = 4;
+
+  // Namespaces which must be aliased in snippets due to
+  // a known (but non-generator-predictable) naming collision
+  repeated string forced_namespace_aliases = 5;
+
+  // Method signatures (in the form "service.method(signature)")
+  // which are provided separately, so shouldn't be generated.
+  // Snippets *calling* these methods are still generated, however.
+  repeated string handwritten_signatures = 6;
+}
+
+// Settings for Ruby client libraries.
+message RubySettings {
+  // Some settings.
+  CommonLanguageSettings common = 1;
+}
+
+// Settings for Go client libraries.
+message GoSettings {
+  // Some settings.
+  CommonLanguageSettings common = 1;
+
+  // Map of service names to renamed services. Keys are the package relative
+  // service names and values are the name to be used for the service client
+  // and call options.
+  //
+  // publishing:
+  //   go_settings:
+  //     renamed_services:
+  //       Publisher: TopicAdmin
+  map<string, string> renamed_services = 2;
+}
+
+// The organization for which the client libraries are being published.
+// Affects the url where generated docs are published, etc.
+enum ClientLibraryOrganization {
+  // Not useful.
+  CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED = 0;
+
+  // Google Cloud Platform Org.
+  CLOUD = 1;
+
+  // Ads (Advertising) Org.
+  ADS = 2;
+
+  // Photos Org.
+  PHOTOS = 3;
+
+  // Street View Org.
+  STREET_VIEW = 4;
+
+  // Shopping Org.
+  SHOPPING = 5;
+
+  // Geo Org.
+  GEO = 6;
+
+  // Generative AI - https://developers.generativeai.google
+  GENERATIVE_AI = 7;
+}
+
+// To where should client libraries be published?
+enum ClientLibraryDestination {
+  // Client libraries will neither be generated nor published to package
+  // managers.
+  CLIENT_LIBRARY_DESTINATION_UNSPECIFIED = 0;
+
+  // Generate the client library in a repo under github.com/googleapis,
+  // but don't publish it to package managers.
+  GITHUB = 10;
+
+  // Publish the library to package managers like nuget.org and npmjs.com.
+  PACKAGE_MANAGER = 20;
+}
+
+// This message is used to configure the generation of a subset of the RPCs in
+// a service for client libraries.
+message SelectiveGapicGeneration {
+  // An allowlist of the fully qualified names of RPCs that should be included
+  // on public client surfaces.
+  repeated string methods = 1;
+
+  // Setting this to true indicates to the client generators that methods
+  // that would be excluded from the generation should instead be generated
+  // in a way that indicates these methods should not be consumed by
+  // end users. How this is expressed is up to individual language
+  // implementations to decide. Some examples may be: added annotations,
+  // obfuscated identifiers, or other language idiomatic patterns.
+  bool generate_omitted_as_internal = 2;
+}
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index c1c12a2..a1070de 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protoc_plugin
-version: 22.1.1-wip
+version: 22.2.0
 description: A protobuf protoc compiler plugin used to generate Dart code.
 repository: https://github.com/google/protobuf.dart/tree/master/protoc_plugin
 
diff --git a/protoc_plugin/test/file_generator_test.dart b/protoc_plugin/test/file_generator_test.dart
index f248e8a..26a297d 100644
--- a/protoc_plugin/test/file_generator_test.dart
+++ b/protoc_plugin/test/file_generator_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:protoc_plugin/indenting_writer.dart';
 import 'package:protoc_plugin/protoc.dart';
+import 'package:protoc_plugin/src/generated/client.pb.dart';
 import 'package:protoc_plugin/src/generated/descriptor.pb.dart';
 import 'package:protoc_plugin/src/generated/plugin.pb.dart';
 import 'package:protoc_plugin/src/linker.dart';
@@ -328,8 +329,17 @@
       ..clientStreaming = false
       ..serverStreaming = false;
 
+    final serviceOptions = ServiceOptions();
+    serviceOptions.setExtension(Client.defaultHost, 'www.example.com');
+    serviceOptions.setExtension(
+      Client.oauthScopes,
+      'https://www.googleapis.com/auth/cloud-platform,'
+      'https://www.googleapis.com/auth/datastore',
+    );
+
     final sd = ServiceDescriptorProto()
       ..name = 'Test'
+      ..options = serviceOptions
       ..method.addAll([
         unary,
         clientStreaming,
diff --git a/protoc_plugin/test/goldens/grpc_service.pbgrpc b/protoc_plugin/test/goldens/grpc_service.pbgrpc
index 2d6d5a2..eb99c20 100644
--- a/protoc_plugin/test/goldens/grpc_service.pbgrpc
+++ b/protoc_plugin/test/goldens/grpc_service.pbgrpc
@@ -21,6 +21,15 @@
 
 @$pb.GrpcServiceName('Test')
 class TestClient extends $grpc.Client {
+  /// The hostname for this service.
+  static const $core.String defaultHost = 'www.example.com';
+
+  /// OAuth scopes needed for the client.
+  static const $core.List<$core.String> oauthScopes = [
+    'https://www.googleapis.com/auth/cloud-platform',
+    'https://www.googleapis.com/auth/datastore',
+  ];
+
   static final _$unary = $grpc.ClientMethod<$0.Input, $0.Output>(
       '/Test/Unary',
       ($0.Input value) => value.writeToBuffer(),