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 16from __future__ import absolute_import 17from __future__ import division 18from __future__ import print_function 19import os 20import re 21import sys 22import argparse 23import tempfile 24import subprocess 25import hashlib 26import textwrap 27 28SOURCE_TARGET = { 29 'protos/perfetto/config/perfetto_config.proto': 30 'src/perfetto_cmd/perfetto_config.descriptor.h', 31} 32 33ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 34 35SCRIPT_PATH = 'tools/gen_binary_descriptors' 36 37 38def 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 45def 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 55def 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 70def 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 113namespace perfetto {{ 114 115constexpr 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 134def 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 157if __name__ == '__main__': 158 exit(main()) 159