| #!/usr/bin/env python3 | |
| # Copyright (C) 2018 The Android Open Source Project | |
| # | |
| # 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. | |
| import os | |
| import argparse | |
| import tempfile | |
| import subprocess | |
| import shutil | |
| SOURCE_TARGET = [('protos/perfetto/trace/perfetto_trace.proto', | |
| 'python/perfetto')] | |
| ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | |
| SCRIPT_PATH = 'tools/gen_python_protos' | |
| def find_protoc(): | |
| for root, _, files in os.walk(os.path.join(ROOT_DIR, 'out')): | |
| if 'protoc' in files: | |
| return os.path.join(root, 'protoc') | |
| return None | |
| def generate(source: str, target: str, protoc_path, check_only): | |
| # delete=False + manual unlink is required for Windows. Otherwise the temp | |
| # file is kept locked exclusively and unaccassible until it's destroyed. | |
| with tempfile.TemporaryDirectory() as directory: | |
| subprocess.check_call([ | |
| protoc_path, | |
| '--python_out={}'.format(directory), | |
| '--pyi_out={}'.format(directory), | |
| source, | |
| ], | |
| cwd=ROOT_DIR) | |
| py_path = source.replace('.proto', '_pb2.py') | |
| pyi_path = source.replace('.proto', '_pb2.pyi') | |
| with open(os.path.join(directory, py_path), 'rb') as f: | |
| py = f.read() | |
| with open(os.path.join(directory, pyi_path), 'rb') as f: | |
| pyi = f.read() | |
| shutil.rmtree(directory) | |
| if check_only: | |
| with open(os.path.join(target, py_path), 'rb') as f: | |
| old_py = f.read() | |
| with open(os.path.join(target, pyi_path), 'rb') as f: | |
| old_pyi = f.read() | |
| if (old_py != py): | |
| raise AssertionError('Target {} does not match', py_path) | |
| if (old_pyi != pyi): | |
| raise AssertionError('Target {} does not match', pyi_path) | |
| return | |
| with open(os.path.join(target, py_path), 'wb') as out: | |
| out.write(py) | |
| with open(os.path.join(target, pyi_path), 'wb') as out: | |
| out.write(pyi) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--check-only', action='store_true') | |
| parser.add_argument('--protoc') | |
| args = parser.parse_args() | |
| try: | |
| for source, target in SOURCE_TARGET: | |
| protoc = args.protoc or find_protoc() | |
| assert protoc, 'protoc not found specific (--protoc PROTOC_PATH)' | |
| assert os.path.exists(protoc), '{} does not exist'.format(protoc) | |
| if protoc is not args.protoc: | |
| print('Using protoc: {}'.format(protoc)) | |
| generate(source, target, protoc, args.check_only) | |
| except AssertionError as e: | |
| if not str(e): | |
| raise | |
| print('Error: {}'.format(e)) | |
| return 1 | |
| if __name__ == '__main__': | |
| exit(main()) |