mirror of https://github.com/grpc/grpc.git
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
5.8 KiB
145 lines
5.8 KiB
#! /usr/bin/env python3 |
|
# Copyright 2021 The gRPC Authors |
|
# |
|
# 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. |
|
"""Builds the content of xds-protos package""" |
|
|
|
import os |
|
|
|
from grpc_tools import protoc |
|
import pkg_resources |
|
|
|
# We might not want to compile all the protos |
|
EXCLUDE_PROTO_PACKAGES_LIST = [ |
|
# Requires extra dependency to Prometheus protos |
|
'envoy/service/metrics/v2', |
|
'envoy/service/metrics/v3', |
|
'envoy/service/metrics/v4alpha', |
|
] |
|
|
|
# Compute the pathes |
|
WORK_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
GRPC_ROOT = os.path.abspath(os.path.join(WORK_DIR, '..', '..', '..', '..')) |
|
XDS_PROTO_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'envoy-api') |
|
UDPA_PROTO_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'udpa') |
|
GOOGLEAPIS_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'googleapis') |
|
VALIDATE_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'protoc-gen-validate') |
|
OPENCENSUS_PROTO_ROOT = os.path.join(GRPC_ROOT, 'third_party', |
|
'opencensus-proto', 'src') |
|
OPENTELEMETRY_PROTO_ROOT = os.path.join(GRPC_ROOT, 'third_party', |
|
'opentelemetry') |
|
WELL_KNOWN_PROTOS_INCLUDE = pkg_resources.resource_filename( |
|
'grpc_tools', '_proto') |
|
OUTPUT_PATH = WORK_DIR |
|
|
|
# Prepare the test file generation |
|
TEST_FILE_NAME = 'generated_file_import_test.py' |
|
TEST_IMPORTS = [] |
|
|
|
# The pkgutil-style namespace packaging __init__.py |
|
PKGUTIL_STYLE_INIT = "__path__ = __import__('pkgutil').extend_path(__path__, __name__)\n" |
|
NAMESPACE_PACKAGES = ["google"] |
|
|
|
|
|
def add_test_import(proto_package_path: str, |
|
file_name: str, |
|
service: bool = False): |
|
TEST_IMPORTS.append("from %s import %s\n" % (proto_package_path.replace( |
|
'/', '.'), file_name.replace('.proto', '_pb2'))) |
|
if service: |
|
TEST_IMPORTS.append("from %s import %s\n" % (proto_package_path.replace( |
|
'/', '.'), file_name.replace('.proto', '_pb2_grpc'))) |
|
|
|
|
|
# Prepare Protoc command |
|
COMPILE_PROTO_ONLY = [ |
|
'grpc_tools.protoc', |
|
'--proto_path={}'.format(XDS_PROTO_ROOT), |
|
'--proto_path={}'.format(UDPA_PROTO_ROOT), |
|
'--proto_path={}'.format(GOOGLEAPIS_ROOT), |
|
'--proto_path={}'.format(VALIDATE_ROOT), |
|
'--proto_path={}'.format(WELL_KNOWN_PROTOS_INCLUDE), |
|
'--proto_path={}'.format(OPENCENSUS_PROTO_ROOT), |
|
'--proto_path={}'.format(OPENTELEMETRY_PROTO_ROOT), |
|
'--python_out={}'.format(OUTPUT_PATH), |
|
] |
|
COMPILE_BOTH = COMPILE_PROTO_ONLY + ['--grpc_python_out={}'.format(OUTPUT_PATH)] |
|
|
|
|
|
def has_grpc_service(proto_package_path: str) -> bool: |
|
return proto_package_path.startswith('envoy/service') |
|
|
|
|
|
def compile_protos(proto_root: str, sub_dir: str = '.') -> None: |
|
for root, _, files in os.walk(os.path.join(proto_root, sub_dir)): |
|
proto_package_path = os.path.relpath(root, proto_root) |
|
if proto_package_path in EXCLUDE_PROTO_PACKAGES_LIST: |
|
print(f'Skipping package {proto_package_path}') |
|
continue |
|
for file_name in files: |
|
if file_name.endswith('.proto'): |
|
# Compile proto |
|
if has_grpc_service(proto_package_path): |
|
return_code = protoc.main(COMPILE_BOTH + |
|
[os.path.join(root, file_name)]) |
|
add_test_import(proto_package_path, file_name, service=True) |
|
else: |
|
return_code = protoc.main(COMPILE_PROTO_ONLY + |
|
[os.path.join(root, file_name)]) |
|
add_test_import(proto_package_path, |
|
file_name, |
|
service=False) |
|
if return_code != 0: |
|
raise Exception('error: {} failed'.format(COMPILE_BOTH)) |
|
|
|
|
|
def create_init_file(path: str, package_path: str = "") -> None: |
|
with open(os.path.join(path, "__init__.py"), 'w') as f: |
|
# Apply the pkgutil-style namespace packaging, which is compatible for 2 |
|
# and 3. Here is the full table of namespace compatibility: |
|
# https://github.com/pypa/sample-namespace-packages/blob/master/table.md |
|
if package_path in NAMESPACE_PACKAGES: |
|
f.write(PKGUTIL_STYLE_INIT) |
|
|
|
|
|
def main(): |
|
# Compile xDS protos |
|
compile_protos(XDS_PROTO_ROOT) |
|
compile_protos(UDPA_PROTO_ROOT) |
|
# We don't want to compile the entire GCP surface API, just the essential ones |
|
compile_protos(GOOGLEAPIS_ROOT, os.path.join('google', 'api')) |
|
compile_protos(GOOGLEAPIS_ROOT, os.path.join('google', 'rpc')) |
|
compile_protos(GOOGLEAPIS_ROOT, os.path.join('google', 'longrunning')) |
|
compile_protos(GOOGLEAPIS_ROOT, os.path.join('google', 'logging')) |
|
compile_protos(GOOGLEAPIS_ROOT, os.path.join('google', 'type')) |
|
compile_protos(VALIDATE_ROOT, 'validate') |
|
compile_protos(OPENCENSUS_PROTO_ROOT) |
|
compile_protos(OPENTELEMETRY_PROTO_ROOT) |
|
|
|
# Generate __init__.py files for all modules |
|
create_init_file(WORK_DIR) |
|
for proto_root_module in [ |
|
'envoy', 'google', 'opencensus', 'udpa', 'validate', 'xds', |
|
'opentelemetry' |
|
]: |
|
for root, _, _ in os.walk(os.path.join(WORK_DIR, proto_root_module)): |
|
package_path = os.path.relpath(root, WORK_DIR) |
|
create_init_file(root, package_path) |
|
|
|
# Generate test file |
|
with open(os.path.join(WORK_DIR, TEST_FILE_NAME), 'w') as f: |
|
f.writelines(TEST_IMPORTS) |
|
|
|
|
|
if __name__ == "__main__": |
|
main()
|
|
|