• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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