#!/usr/bin/env python # 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. from __future__ import absolute_import from __future__ import division from __future__ import print_function import os import re import sys import argparse import tempfile import subprocess import hashlib import textwrap SOURCE_TARGET = { 'protos/perfetto/config/perfetto_config.proto': 'src/perfetto_cmd/perfetto_config.descriptor.h', } ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) SCRIPT_PATH = 'tools/gen_binary_descriptors' def hash_path(path): hash = hashlib.sha1() with open(os.path.join(ROOT_DIR, path)) as f: hash.update(f.read()) return hash.hexdigest() def find_protoc(): for root, dirs, files in os.walk(ROOT_DIR): if 'protoc' in files: return os.path.join(root, 'protoc') for name in ('src', 'buildtools'): if name in dirs: dirs.remove(name) return None def check(source, target): assert os.path.exists(os.path.join(ROOT_DIR, target)), \ 'Output file {} does not exist and so cannot be checked'.format(target) with open(target, 'rb') as f: s = f.read() hashes = re.findall(r'// SHA1\((.*)\)\n// (.*)\n', s) assert sorted([SCRIPT_PATH, source]) == sorted([key for key, _ in hashes]) for path, expected_sha1 in hashes: actual_sha1 = hash_path(os.path.join(ROOT_DIR, path)) assert actual_sha1 == expected_sha1, \ 'In {} hash given for {} did not match'.format(target, path) def generate(source, target, protoc_path): _, source_name = os.path.split(source) _, target_name = os.path.split(target) assert source_name.replace('.proto', '.descriptor.h') == target_name with tempfile.NamedTemporaryFile() as fdescriptor: subprocess.check_call([ protoc_path, '--proto_path=protos', '-o{}'.format(fdescriptor.name), source, ], cwd=ROOT_DIR) s = fdescriptor.read() proto_name = source_name[:-len('.proto')].title().replace("_", "") constant_name = 'k' + proto_name + 'Descriptor' binary = '{' + ', '.join('{0:#04x}'.format(ord(c)) for c in s) + '}' binary = textwrap.fill(binary, width=80, initial_indent=' ', subsequent_indent=' ') include_guard = target.replace('/', '_').replace('.', '_').upper() + '_' with open(os.path.join(ROOT_DIR, target), 'wb') as f: f.write(""" #ifndef {include_guard} #define {include_guard} #include #include #include // This file was autogenerated by tools/gen_binary_descriptors. Do not edit. // SHA1({script_path}) // {script_hash} // SHA1({source_path}) // {source_hash} // This is the proto {proto_name} encoded as a ProtoFileDescriptor to allow // for reflection without libprotobuf full/non-lite protos. namespace perfetto {{ constexpr std::array {constant_name}{{ {binary}}}; }} // namespace perfetto #endif // {include_guard} """.format(**{ 'proto_name': proto_name, 'size': len(s), 'constant_name': constant_name, 'binary': binary, 'include_guard': include_guard, 'script_path': SCRIPT_PATH, 'script_hash': hash_path(__file__), 'source_path': source, 'source_hash': hash_path(os.path.join(source)), })) 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.iteritems(): if args.check_only: check(source, target) else: 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) except AssertionError as e: if not str(e): raise print('Error: {}'.format(e)) return 1 if __name__ == '__main__': exit(main())