def visit_file(self, file_proto, type_context, services, msgs, enums): # Freeze protos that have next major version candidates. typedb = utils.get_type_db() output_proto = copy.deepcopy(file_proto) existing_pkg_version_status = output_proto.options.Extensions[ status_pb2.file_status].package_version_status empty_file = len(services) == 0 and len(enums) == 0 and len(msgs) == 0 pkg_version_status_exempt = file_proto.name.startswith('envoy/annotations') or empty_file # It's a format error not to set package_version_status. if existing_pkg_version_status == status_pb2.UNKNOWN and not pkg_version_status_exempt: raise ProtoXformError('package_version_status must be set in %s' % file_proto.name) # Only update package_version_status for .active_or_frozen.proto, # migrate.version_upgrade_xform has taken care of next major version # candidates. if self._active_or_frozen and not pkg_version_status_exempt: # Freeze if this is an active package with a next major version. Preserve # frozen status otherwise. if self._freeze and typedb.next_version_protos.get(output_proto.name, None): target_pkg_version_status = status_pb2.FROZEN elif existing_pkg_version_status == status_pb2.FROZEN: target_pkg_version_status = status_pb2.FROZEN else: assert (existing_pkg_version_status == status_pb2.ACTIVE) target_pkg_version_status = status_pb2.ACTIVE output_proto.options.Extensions[ status_pb2.file_status].package_version_status = target_pkg_version_status return str(output_proto)
def version_upgrade_xform(n, envoy_internal_shadow, file_proto, params): """Transform a FileDescriptorProto from vN[alpha\d] to v(N+1). Args: n: version N to upgrade from. envoy_internal_shadow: generate a shadow for Envoy internal use containing deprecated fields. file_proto: vN[alpha\d] FileDescriptorProto message. params: plugin parameters. Returns: v(N+1) FileDescriptorProto message. """ # Load type database. if params['type_db_path']: utils.load_type_db(params['type_db_path']) typedb = utils.get_type_db() # If this isn't a proto in an upgraded package, return None. if file_proto.name not in typedb.next_version_protos or not typedb.next_version_protos[ file_proto.name]: return None # Otherwise, this .proto needs upgrading, do it. freeze = 'extra_args' in params and params['extra_args'] == 'freeze' existing_pkg_version_status = file_proto.options.Extensions[ status_pb2.file_status].package_version_status # Normally, we are generating the NEXT_MAJOR_VERSION_CANDIDATE. However, if # freezing and previously this was the active major version, the migrated # version is now the ACTIVE version. if freeze and existing_pkg_version_status == status_pb2.ACTIVE: package_version_status = status_pb2.ACTIVE else: package_version_status = status_pb2.NEXT_MAJOR_VERSION_CANDIDATE return traverse.traverse_file( file_proto, UpgradeVisitor(n, typedb, envoy_internal_shadow, package_version_status))
def format_header_from_file( source_code_info, file_proto, empty_file, requires_deprecation_annotation): """Format proto header. Args: source_code_info: SourceCodeInfo object. file_proto: FileDescriptorProto for file. empty_file: are there no message/enum/service defs in file? requires_deprecation_annotation: does the proto have the deprecated version annotation or disallowed annotation. Returns: Formatted proto header as a string. """ # Load the type database. typedb = utils.get_type_db() # Figure out type dependencies in this .proto. types = types_pb2.Types() text_format.Merge( traverse.traverse_file(file_proto, type_whisperer.TypeWhispererVisitor()), types) type_dependencies = sum([list(t.type_dependencies) for t in types.types.values()], []) for service in file_proto.service: for m in service.method: type_dependencies.extend([m.input_type[1:], m.output_type[1:]]) # Determine the envoy/ import paths from type deps. envoy_proto_paths = set( typedb.types[t].proto_path for t in type_dependencies if t.startswith('envoy.') and typedb.types[t].proto_path != file_proto.name) def camel_case(s): return ''.join(t.capitalize() for t in re.split('[\._]', s)) package_line = 'package %s;\n' % file_proto.package file_block = '\n'.join(['syntax = "proto3";\n', package_line]) options = descriptor_pb2.FileOptions() options.java_outer_classname = camel_case(os.path.basename(file_proto.name)) for msg in file_proto.message_type: if msg.name == options.java_outer_classname: # This is a workaround for Java outer class names that would otherwise # conflict with types defined within the same proto file, see # https://github.com/envoyproxy/envoy/pull/13378. # TODO: in next major version, make this consistent. options.java_outer_classname += "OuterClass" options.java_multiple_files = True options.java_package = 'io.envoyproxy.' + file_proto.package # This is a workaround for C#/Ruby namespace conflicts between packages and # objects, see https://github.com/envoyproxy/envoy/pull/3854. # TODO(htuch): remove once v3 fixes this naming issue in # https://github.com/envoyproxy/envoy/issues/8120. if file_proto.package in ['envoy.api.v2.listener', 'envoy.api.v2.cluster']: qualified_package = '.'.join(s.capitalize() for s in file_proto.package.split('.')) + 'NS' options.csharp_namespace = qualified_package options.ruby_package = qualified_package if file_proto.service: options.java_generic_services = True if file_proto.options.HasExtension(migrate_pb2.file_migrate): options.Extensions[migrate_pb2.file_migrate].CopyFrom( file_proto.options.Extensions[migrate_pb2.file_migrate]) if file_proto.options.HasExtension( status_pb2.file_status) and file_proto.package.endswith('alpha'): options.Extensions[status_pb2.file_status].CopyFrom( file_proto.options.Extensions[status_pb2.file_status]) frozen_proto = file_proto.options.HasExtension( status_pb2.file_status) and file_proto.options.Extensions[ status_pb2.file_status].package_version_status == status_pb2.FROZEN if not empty_file: options.Extensions[ status_pb2.file_status].package_version_status = file_proto.options.Extensions[ status_pb2.file_status].package_version_status options_block = format_options(options) requires_versioning_import = any( protoxform_options.get_versioning_annotation(m.options) for m in file_proto.message_type) envoy_imports = list(envoy_proto_paths) google_imports = [] infra_imports = [] misc_imports = [] public_imports = [] for idx, d in enumerate(file_proto.dependency): if idx in file_proto.public_dependency: public_imports.append(d) continue elif d.startswith('envoy/annotations') or d.startswith('udpa/annotations'): if d == 'envoy/annotations/deprecation.proto' and not frozen_proto: # Skip adding, as deprecation proto should be added if # import_deprecation_proto is True or the proto is frozen. continue infra_imports.append(d) elif d.startswith('envoy/'): # We ignore existing envoy/ imports, since these are computed explicitly # from type_dependencies. pass elif d.startswith('google/'): google_imports.append(d) elif d.startswith('validate/'): infra_imports.append(d) elif d in ['udpa/annotations/versioning.proto', 'udpa/annotations/status.proto']: # Skip, we decide to add this based on requires_versioning_import and options. pass else: misc_imports.append(d) if requires_deprecation_annotation: infra_imports.append('envoy/annotations/deprecation.proto') if options.HasExtension(status_pb2.file_status): infra_imports.append('udpa/annotations/status.proto') if requires_versioning_import: infra_imports.append('udpa/annotations/versioning.proto') def format_import_block(xs): if not xs: return '' return format_block('\n'.join(sorted('import "%s";' % x for x in set(xs) if x))) def format_public_import_block(xs): if not xs: return '' return format_block('\n'.join(sorted('import public "%s";' % x for x in xs))) import_block = '\n'.join( map(format_import_block, [envoy_imports, google_imports, misc_imports, infra_imports])) import_block += '\n' + format_public_import_block(public_imports) comment_block = format_comments(source_code_info.file_level_comments) return ''.join(map(format_block, [file_block, import_block, options_block, comment_block]))