1 #!/usr/bin/env python 2 # Copyright (C) 2018 The Android Open Source Project 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 from __future__ import absolute_import 17 from __future__ import division 18 from __future__ import print_function 19 import os 20 import re 21 import sys 22 import argparse 23 import tempfile 24 import subprocess 25 import hashlib 26 import textwrap 27 28 SOURCE_TARGET = { 29 'protos/perfetto/config/perfetto_config.proto': 30 'src/perfetto_cmd/perfetto_config.descriptor.h', 31 } 32 33 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 34 35 SCRIPT_PATH = 'tools/gen_binary_descriptors' 36 37 38 def hash_path(path): 39 hash = hashlib.sha1() 40 with open(os.path.join(ROOT_DIR, path)) as f: 41 hash.update(f.read()) 42 return hash.hexdigest() 43 44 45 def find_protoc(): 46 for root, dirs, files in os.walk(ROOT_DIR): 47 if 'protoc' in files: 48 return os.path.join(root, 'protoc') 49 for name in ('src', 'buildtools'): 50 if name in dirs: 51 dirs.remove(name) 52 return None 53 54 55 def check(source, target): 56 assert os.path.exists(os.path.join(ROOT_DIR, target)), \ 57 'Output file {} does not exist and so cannot be checked'.format(target) 58 59 with open(target, 'rb') as f: 60 s = f.read() 61 62 hashes = re.findall(r'// SHA1\((.*)\)\n// (.*)\n', s) 63 assert sorted([SCRIPT_PATH, source]) == sorted([key for key, _ in hashes]) 64 for path, expected_sha1 in hashes: 65 actual_sha1 = hash_path(os.path.join(ROOT_DIR, path)) 66 assert actual_sha1 == expected_sha1, \ 67 'In {} hash given for {} did not match'.format(target, path) 68 69 70 def generate(source, target, protoc_path): 71 _, source_name = os.path.split(source) 72 _, target_name = os.path.split(target) 73 assert source_name.replace('.proto', '.descriptor.h') == target_name 74 75 with tempfile.NamedTemporaryFile() as fdescriptor: 76 subprocess.check_call([ 77 protoc_path, 78 '--proto_path=protos', 79 '-o{}'.format(fdescriptor.name), 80 source, 81 ], cwd=ROOT_DIR) 82 83 s = fdescriptor.read() 84 proto_name = source_name[:-len('.proto')].title().replace("_", "") 85 constant_name = 'k' + proto_name + 'Descriptor' 86 binary = '{' + ', '.join('{0:#04x}'.format(ord(c)) for c in s) + '}' 87 binary = textwrap.fill(binary, 88 width=80, 89 initial_indent=' ', 90 subsequent_indent=' ') 91 include_guard = target.replace('/', '_').replace('.', '_').upper() + '_' 92 93 with open(os.path.join(ROOT_DIR, target), 'wb') as f: 94 f.write(""" 95 #ifndef {include_guard} 96 #define {include_guard} 97 98 #include <stddef.h> 99 #include <stdint.h> 100 101 #include <array> 102 103 // This file was autogenerated by tools/gen_binary_descriptors. Do not edit. 104 105 // SHA1({script_path}) 106 // {script_hash} 107 // SHA1({source_path}) 108 // {source_hash} 109 110 // This is the proto {proto_name} encoded as a ProtoFileDescriptor to allow 111 // for reflection without libprotobuf full/non-lite protos. 112 113 namespace perfetto {{ 114 115 constexpr std::array<uint8_t, {size}> {constant_name}{{ 116 {binary}}}; 117 118 }} // namespace perfetto 119 120 #endif // {include_guard} 121 """.format(**{ 122 'proto_name': proto_name, 123 'size': len(s), 124 'constant_name': constant_name, 125 'binary': binary, 126 'include_guard': include_guard, 127 'script_path': SCRIPT_PATH, 128 'script_hash': hash_path(__file__), 129 'source_path': source, 130 'source_hash': hash_path(os.path.join(source)), 131 })) 132 133 134 def main(): 135 parser = argparse.ArgumentParser() 136 parser.add_argument('--check-only', action='store_true') 137 parser.add_argument('--protoc') 138 args = parser.parse_args() 139 140 try: 141 for source, target in SOURCE_TARGET.iteritems(): 142 if args.check_only: 143 check(source, target) 144 else: 145 protoc = args.protoc or find_protoc() 146 assert protoc, 'protoc not found specific (--protoc PROTOC_PATH)' 147 assert os.path.exists(protoc), '{} does not exist'.format(protoc) 148 if protoc is not args.protoc: 149 print('Using protoc: {}'.format(protoc)) 150 generate(source, target, protoc) 151 except AssertionError as e: 152 if not str(e): 153 raise 154 print('Error: {}'.format(e)) 155 return 1 156 157 if __name__ == '__main__': 158 exit(main()) 159