1#!/usr/bin/env python3 2# Copyright (C) 2020 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""" Compiles the stress_test configs protos and bundles in a .h C++ array. 16 17This scripts takes all the configs in /test/stress_test/configs, compiles them 18with protoc and generates a C++ header which contains the configs' names and 19proto-encoded bytes. 20 21This is invoked by the build system and is used by the stress_test runner. The 22goal is making the stress_test binary hermetic and not depend on the repo. 23""" 24 25from __future__ import absolute_import 26from __future__ import division 27from __future__ import print_function 28import os 29import sys 30import argparse 31import shutil 32import subprocess 33 34CUR_DIR = os.path.dirname(os.path.realpath(__file__)) 35ROOT_DIR = os.path.dirname(os.path.dirname(CUR_DIR)) 36CONFIGS_DIR = os.path.join(CUR_DIR, 'configs') 37 38 39def find_protoc(): 40 for root, _, files in os.walk(os.path.join(ROOT_DIR, 'out')): 41 if 'protoc' in files: 42 return os.path.join(root, 'protoc') 43 return None 44 45 46def main(): 47 parser = argparse.ArgumentParser() 48 parser.add_argument('--protoc') 49 parser.add_argument('--out', required=True) 50 parser.add_argument('cfgfiles', nargs='+') 51 args = parser.parse_args() 52 53 protoc = args.protoc or find_protoc() 54 assert protoc, 'protoc not found, pass --protoc /path/to/protoc' 55 assert os.path.exists(protoc), '{} does not exist'.format(protoc) 56 if protoc is not args.protoc: 57 print('Using protoc: {}'.format(protoc)) 58 59 blobs = {} 60 for cfg_path in args.cfgfiles: 61 cfg_path = cfg_path.replace('\\', '/') 62 cfg_name = os.path.splitext(cfg_path)[0].split('/')[-1] 63 with open(cfg_path, 'r') as in_file: 64 compiled_proto = subprocess.check_output([ 65 protoc, 66 '--encode=perfetto.protos.StressTestConfig', 67 '--proto_path=' + ROOT_DIR, 68 os.path.join(ROOT_DIR, 'protos', 'perfetto', 'config', 69 'stress_test_config.proto'), 70 ], 71 stdin=in_file) 72 blobs[cfg_name] = bytearray(compiled_proto) 73 74 # Write the C++ header file 75 fout = open(args.out, 'wb') 76 include_guard = args.out.replace('/', '_').replace('.', '_').upper() + '_' 77 fout.write(""" 78#ifndef {include_guard} 79#define {include_guard} 80 81#include <stddef.h> 82#include <stdint.h> 83 84// This file was autogenerated by ${gen_script}. Do not edit. 85 86namespace perfetto {{ 87namespace {{ 88 89struct StressTestConfigBlob {{ 90 const char* name; 91 const uint8_t* data; 92 size_t size; 93}};\n\n""".format( 94 gen_script=__file__, 95 include_guard=include_guard, 96 ).encode()) 97 98 configs_arr = '\nconst StressTestConfigBlob kStressTestConfigs[] = {\n' 99 for cfg_name, blob in blobs.items(): 100 arr_str = ','.join(str(b) for b in blob) 101 line = 'const uint8_t _config_%s[]{%s};\n' % (cfg_name, arr_str) 102 fout.write(line.encode()) 103 configs_arr += ' {{"{n}", _config_{n}, sizeof(_config_{n})}},\n'.format( 104 n=cfg_name) 105 configs_arr += '};\n' 106 fout.write(configs_arr.encode()) 107 fout.write(""" 108} // namespace 109} // namespace perfetto 110#endif\n""".encode()) 111 fout.close() 112 113 114if __name__ == '__main__': 115 exit(main()) 116