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