@@ -263,17 +263,40 @@ def with_selective_generation(
263263 * ,
264264 generate_omitted_as_internal : bool ,
265265 public_methods : Set [str ],
266+ excluded_addresses : Set ["metadata.Address" ],
266267 ) -> "Proto" :
267268
268269 services = {}
269270 for k , v in self .services .items ():
270271 new_v = v .with_selective_generation (
271272 generate_omitted_as_internal = generate_omitted_as_internal ,
272- public_methods = public_methods )
273+ public_methods = public_methods ,
274+ excluded_addresses = excluded_addresses )
273275 if new_v :
274276 services [k ] = new_v
275277
276- return dataclasses .replace (self , services = services )
278+ # We only prune messages/enums from protos that are not dependencies.
279+ # Messages and enums are excluded only if they are reachable from some RPC
280+ # but NOT from any of the publicly allowed RPCs.
281+ all_messages = {
282+ k : v for k , v in self .all_messages .items () if v .ident not in excluded_addresses
283+ }
284+
285+ all_enums = {
286+ k : v for k , v in self .all_enums .items () if v .ident not in excluded_addresses
287+ }
288+
289+ # If the proto becomes empty after pruning, we return None to signal
290+ # that it should be excluded from generation.
291+ if not services and not all_messages and not all_enums :
292+ return None
293+
294+ return dataclasses .replace (
295+ self ,
296+ services = services ,
297+ all_messages = all_messages ,
298+ all_enums = all_enums ,
299+ )
277300
278301
279302@dataclasses .dataclass (frozen = True )
@@ -438,10 +461,58 @@ def disambiguate_keyword_sanitize_fname(
438461 k : v for k , v in api .all_protos .items () if k not in api .protos
439462 }
440463
464+ all_resource_messages = collections .ChainMap (
465+ * (proto .resource_messages for proto in api .all_protos .values ())
466+ )
467+
468+ # Calculate all reachable addresses (API-wide).
469+ # This includes all messages and enums reachable from ANY RPC
470+ # defined in any proto of the API.
471+ all_rpc_addresses : Set ["metadata.Address" ] = set ([])
472+ all_methods = set (api .all_methods .keys ())
473+ # Create a global map of services to support cross-proto lookup
474+ # for extended LROs.
475+ all_services : Dict [str , wrappers .Service ] = {}
476+ for p in api .all_protos .values ():
477+ for s in p .services .values ():
478+ all_services [s .meta .address .proto ] = s
479+ all_services [s .name ] = s
480+
481+ for proto in api .all_protos .values ():
482+ for service in proto .services .values ():
483+ service .add_to_address_allowlist (
484+ address_allowlist = all_rpc_addresses ,
485+ method_allowlist = all_methods ,
486+ resource_messages = all_resource_messages ,
487+ services_in_proto = all_services ,
488+ )
489+
490+ # Calculate publicly reachable addresses (API-wide).
491+ # This includes only types reachable from the allowlisted methods.
492+ public_rpc_addresses : Set ["metadata.Address" ] = set ([])
493+ for proto in api .all_protos .values ():
494+ for service in proto .services .values ():
495+ service .add_to_address_allowlist (
496+ address_allowlist = public_rpc_addresses ,
497+ method_allowlist = selective_gapic_methods ,
498+ resource_messages = all_resource_messages ,
499+ services_in_proto = all_services ,
500+ )
501+
502+ # Addresses to exclude: those that ARE reachable from SOME RPC but NOT from any PUBLIC RPC.
503+ # Types not attached to any RPC will not be in all_rpc_addresses and thus
504+ # will NOT be in excluded_addresses, meaning they are preserved.
505+ excluded_addresses = (
506+ all_rpc_addresses - public_rpc_addresses
507+ if not selective_gapic_settings .generate_omitted_as_internal
508+ else set ([])
509+ )
510+
441511 for name , proto in api .protos .items ():
442512 proto_to_generate = proto .with_selective_generation (
443513 generate_omitted_as_internal = selective_gapic_settings .generate_omitted_as_internal ,
444514 public_methods = selective_gapic_methods ,
515+ excluded_addresses = excluded_addresses ,
445516 )
446517 if proto_to_generate :
447518 new_all_protos [name ] = proto_to_generate
0 commit comments