xref: /aosp_15_r20/external/perfetto/tools/gen_c_protos (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker#
4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker#
8*6dbdd20aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker#
10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker
16*6dbdd20aSAndroid Build Coastguard Workerimport argparse
17*6dbdd20aSAndroid Build Coastguard Workerimport filecmp
18*6dbdd20aSAndroid Build Coastguard Workerimport os
19*6dbdd20aSAndroid Build Coastguard Workerimport pathlib
20*6dbdd20aSAndroid Build Coastguard Workerimport shutil
21*6dbdd20aSAndroid Build Coastguard Workerimport subprocess
22*6dbdd20aSAndroid Build Coastguard Workerimport sys
23*6dbdd20aSAndroid Build Coastguard Workerimport tempfile
24*6dbdd20aSAndroid Build Coastguard Worker
25*6dbdd20aSAndroid Build Coastguard WorkerSOURCE_FILES = [
26*6dbdd20aSAndroid Build Coastguard Worker  {
27*6dbdd20aSAndroid Build Coastguard Worker    'files': [
28*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/common/builtin_clock.proto',
29*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/common/data_source_descriptor.proto',
30*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/config/data_source_config.proto',
31*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/config/trace_config.proto',
32*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/config/track_event/track_event_config.proto',
33*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/android/android_track_event.proto',
34*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/clock_snapshot.proto',
35*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/interned_data/interned_data.proto',
36*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/test_event.proto',
37*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/trace.proto',
38*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/trace_packet.proto',
39*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/track_event/counter_descriptor.proto',
40*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/track_event/debug_annotation.proto',
41*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/track_event/track_descriptor.proto',
42*6dbdd20aSAndroid Build Coastguard Worker      'protos/perfetto/trace/track_event/track_event.proto',
43*6dbdd20aSAndroid Build Coastguard Worker    ],
44*6dbdd20aSAndroid Build Coastguard Worker    'guard_strip_prefix': 'PROTOS_PERFETTO_',
45*6dbdd20aSAndroid Build Coastguard Worker    'guard_add_prefix':'INCLUDE_PERFETTO_PUBLIC_PROTOS_',
46*6dbdd20aSAndroid Build Coastguard Worker    'path_strip_prefix': 'protos/perfetto',
47*6dbdd20aSAndroid Build Coastguard Worker    'path_add_prefix': 'perfetto/public/protos',
48*6dbdd20aSAndroid Build Coastguard Worker    'include_prefix': 'include/',
49*6dbdd20aSAndroid Build Coastguard Worker  },
50*6dbdd20aSAndroid Build Coastguard Worker  {
51*6dbdd20aSAndroid Build Coastguard Worker    'files': [
52*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/extensions.proto',
53*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/library.proto',
54*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/library_internals/galaxies.proto',
55*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/other_package/test_messages.proto',
56*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/subpackage/test_messages.proto',
57*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/test_messages.proto',
58*6dbdd20aSAndroid Build Coastguard Worker      'src/protozero/test/example_proto/upper_import.proto',
59*6dbdd20aSAndroid Build Coastguard Worker    ],
60*6dbdd20aSAndroid Build Coastguard Worker    'guard_strip_prefix': 'SRC_PROTOZERO_TEST_EXAMPLE_PROTO_',
61*6dbdd20aSAndroid Build Coastguard Worker    'guard_add_prefix':'SRC_SHARED_LIB_TEST_PROTOS_',
62*6dbdd20aSAndroid Build Coastguard Worker    'path_strip_prefix': 'src/protozero/test/example_proto',
63*6dbdd20aSAndroid Build Coastguard Worker    'path_add_prefix': 'src/shared_lib/test/protos',
64*6dbdd20aSAndroid Build Coastguard Worker  },
65*6dbdd20aSAndroid Build Coastguard Worker]
66*6dbdd20aSAndroid Build Coastguard Worker
67*6dbdd20aSAndroid Build Coastguard WorkerROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
68*6dbdd20aSAndroid Build Coastguard WorkerIS_WIN = sys.platform.startswith('win')
69*6dbdd20aSAndroid Build Coastguard Worker
70*6dbdd20aSAndroid Build Coastguard WorkerSCRIPT_PATH = 'tools/gen_c_protos'
71*6dbdd20aSAndroid Build Coastguard Worker
72*6dbdd20aSAndroid Build Coastguard Worker
73*6dbdd20aSAndroid Build Coastguard Workerdef protozero_c_plugin_path(out_directory):
74*6dbdd20aSAndroid Build Coastguard Worker  path = os.path.join(out_directory,
75*6dbdd20aSAndroid Build Coastguard Worker                      'protozero_c_plugin') + ('.exe' if IS_WIN else '')
76*6dbdd20aSAndroid Build Coastguard Worker  assert os.path.isfile(path)
77*6dbdd20aSAndroid Build Coastguard Worker  return path
78*6dbdd20aSAndroid Build Coastguard Worker
79*6dbdd20aSAndroid Build Coastguard Worker
80*6dbdd20aSAndroid Build Coastguard Workerdef protoc_path(out_directory):
81*6dbdd20aSAndroid Build Coastguard Worker  path = os.path.join(out_directory, 'protoc') + ('.exe' if IS_WIN else '')
82*6dbdd20aSAndroid Build Coastguard Worker  assert os.path.isfile(path)
83*6dbdd20aSAndroid Build Coastguard Worker  return path
84*6dbdd20aSAndroid Build Coastguard Worker
85*6dbdd20aSAndroid Build Coastguard Worker
86*6dbdd20aSAndroid Build Coastguard Workerdef call(cmd, *args):
87*6dbdd20aSAndroid Build Coastguard Worker  path = os.path.join('tools', cmd)
88*6dbdd20aSAndroid Build Coastguard Worker  command = ['python3', path] + list(args)
89*6dbdd20aSAndroid Build Coastguard Worker  print('Running', ' '.join(command))
90*6dbdd20aSAndroid Build Coastguard Worker  try:
91*6dbdd20aSAndroid Build Coastguard Worker    subprocess.check_call(command, cwd=ROOT_DIR)
92*6dbdd20aSAndroid Build Coastguard Worker  except subprocess.CalledProcessError as e:
93*6dbdd20aSAndroid Build Coastguard Worker    assert False, 'Command: {} failed'.format(' '.join(command))
94*6dbdd20aSAndroid Build Coastguard Worker
95*6dbdd20aSAndroid Build Coastguard Worker
96*6dbdd20aSAndroid Build Coastguard Worker# Reformats filename
97*6dbdd20aSAndroid Build Coastguard Workerdef clang_format(filename):
98*6dbdd20aSAndroid Build Coastguard Worker  path = os.path.join(ROOT_DIR, 'third_party', 'clang-format',
99*6dbdd20aSAndroid Build Coastguard Worker                      'clang-format') + ('.exe' if IS_WIN else '')
100*6dbdd20aSAndroid Build Coastguard Worker  assert os.path.isfile(
101*6dbdd20aSAndroid Build Coastguard Worker      path), "clang-format not found. Run tools/install-build-deps"
102*6dbdd20aSAndroid Build Coastguard Worker  subprocess.check_call([
103*6dbdd20aSAndroid Build Coastguard Worker      path, '--style=file:{}'.format(os.path.join(ROOT_DIR, '.clang-format')),
104*6dbdd20aSAndroid Build Coastguard Worker      '-i', filename
105*6dbdd20aSAndroid Build Coastguard Worker  ],
106*6dbdd20aSAndroid Build Coastguard Worker                        cwd=ROOT_DIR)
107*6dbdd20aSAndroid Build Coastguard Worker
108*6dbdd20aSAndroid Build Coastguard Worker
109*6dbdd20aSAndroid Build Coastguard Worker# Transforms filename extension like the ProtoZero C plugin
110*6dbdd20aSAndroid Build Coastguard Workerdef transform_extension(filename):
111*6dbdd20aSAndroid Build Coastguard Worker  old_suffix = ".proto"
112*6dbdd20aSAndroid Build Coastguard Worker  new_suffix = ".pzc.h"
113*6dbdd20aSAndroid Build Coastguard Worker  if filename.endswith(old_suffix):
114*6dbdd20aSAndroid Build Coastguard Worker    return filename[:-len(old_suffix)] + new_suffix
115*6dbdd20aSAndroid Build Coastguard Worker  return filename
116*6dbdd20aSAndroid Build Coastguard Worker
117*6dbdd20aSAndroid Build Coastguard Worker
118*6dbdd20aSAndroid Build Coastguard Workerdef generate(source, outdir, protoc_path, protozero_c_plugin_path, guard_strip_prefix, guard_add_prefix, path_strip_prefix, path_add_prefix):
119*6dbdd20aSAndroid Build Coastguard Worker  options = {
120*6dbdd20aSAndroid Build Coastguard Worker      'guard_strip_prefix': guard_strip_prefix,
121*6dbdd20aSAndroid Build Coastguard Worker      'guard_add_prefix': guard_add_prefix,
122*6dbdd20aSAndroid Build Coastguard Worker      'path_strip_prefix': path_strip_prefix,
123*6dbdd20aSAndroid Build Coastguard Worker      'path_add_prefix': path_add_prefix,
124*6dbdd20aSAndroid Build Coastguard Worker      'invoker': SCRIPT_PATH,
125*6dbdd20aSAndroid Build Coastguard Worker  }
126*6dbdd20aSAndroid Build Coastguard Worker  serialized_options = ','.join(
127*6dbdd20aSAndroid Build Coastguard Worker      ['{}={}'.format(name, value) for name, value in options.items()])
128*6dbdd20aSAndroid Build Coastguard Worker  subprocess.check_call([
129*6dbdd20aSAndroid Build Coastguard Worker      protoc_path,
130*6dbdd20aSAndroid Build Coastguard Worker      '--proto_path=.',
131*6dbdd20aSAndroid Build Coastguard Worker      '--plugin=protoc-gen-plugin={}'.format(protozero_c_plugin_path),
132*6dbdd20aSAndroid Build Coastguard Worker      '--plugin_out={}:{}'.format(serialized_options, outdir),
133*6dbdd20aSAndroid Build Coastguard Worker      source,
134*6dbdd20aSAndroid Build Coastguard Worker  ],
135*6dbdd20aSAndroid Build Coastguard Worker                        cwd=ROOT_DIR)
136*6dbdd20aSAndroid Build Coastguard Worker
137*6dbdd20aSAndroid Build Coastguard Worker
138*6dbdd20aSAndroid Build Coastguard Worker# Given filename, the path of a header generated by the ProtoZero C plugin,
139*6dbdd20aSAndroid Build Coastguard Worker# returns the path where the header should go in the public include directory.
140*6dbdd20aSAndroid Build Coastguard Worker# Example
141*6dbdd20aSAndroid Build Coastguard Worker#
142*6dbdd20aSAndroid Build Coastguard Worker# include_path_for("protos/perfetto/trace/trace.pzc.h") ==
143*6dbdd20aSAndroid Build Coastguard Worker# "include/perfetto/public/protos/trace/trace.pzc.h"
144*6dbdd20aSAndroid Build Coastguard Workerdef include_path_for(filename):
145*6dbdd20aSAndroid Build Coastguard Worker  return os.path.join('include', 'perfetto', 'public', 'protos',
146*6dbdd20aSAndroid Build Coastguard Worker                      *pathlib.Path(transform_extension(filename)).parts[2:])
147*6dbdd20aSAndroid Build Coastguard Worker
148*6dbdd20aSAndroid Build Coastguard Worker
149*6dbdd20aSAndroid Build Coastguard Workerdef main():
150*6dbdd20aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
151*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--check-only', action='store_true')
152*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('OUT')
153*6dbdd20aSAndroid Build Coastguard Worker  args = parser.parse_args()
154*6dbdd20aSAndroid Build Coastguard Worker  out = args.OUT
155*6dbdd20aSAndroid Build Coastguard Worker
156*6dbdd20aSAndroid Build Coastguard Worker  call('ninja', '-C', out, 'protoc', 'protozero_c_plugin')
157*6dbdd20aSAndroid Build Coastguard Worker
158*6dbdd20aSAndroid Build Coastguard Worker  try:
159*6dbdd20aSAndroid Build Coastguard Worker    with tempfile.TemporaryDirectory() as tmpdirname:
160*6dbdd20aSAndroid Build Coastguard Worker      for sources in SOURCE_FILES:
161*6dbdd20aSAndroid Build Coastguard Worker        for source in sources['files']:
162*6dbdd20aSAndroid Build Coastguard Worker          generate(source, tmpdirname, protoc_path(out), protozero_c_plugin_path(out),
163*6dbdd20aSAndroid Build Coastguard Worker                   guard_strip_prefix=sources['guard_strip_prefix'],
164*6dbdd20aSAndroid Build Coastguard Worker                   guard_add_prefix=sources['guard_add_prefix'],
165*6dbdd20aSAndroid Build Coastguard Worker                   path_strip_prefix=sources['path_strip_prefix'],
166*6dbdd20aSAndroid Build Coastguard Worker                   path_add_prefix=sources['path_add_prefix'],
167*6dbdd20aSAndroid Build Coastguard Worker                   )
168*6dbdd20aSAndroid Build Coastguard Worker
169*6dbdd20aSAndroid Build Coastguard Worker          tmpfilename = os.path.join(tmpdirname, transform_extension(source))
170*6dbdd20aSAndroid Build Coastguard Worker          clang_format(tmpfilename)
171*6dbdd20aSAndroid Build Coastguard Worker          if source.startswith(sources['path_strip_prefix']):
172*6dbdd20aSAndroid Build Coastguard Worker            targetfilename = source[len(sources['path_strip_prefix']):]
173*6dbdd20aSAndroid Build Coastguard Worker          else:
174*6dbdd20aSAndroid Build Coastguard Worker            targetfilename = source
175*6dbdd20aSAndroid Build Coastguard Worker
176*6dbdd20aSAndroid Build Coastguard Worker          targetfilename = sources['path_add_prefix'] + targetfilename
177*6dbdd20aSAndroid Build Coastguard Worker
178*6dbdd20aSAndroid Build Coastguard Worker          if 'include_prefix' in sources:
179*6dbdd20aSAndroid Build Coastguard Worker            targetfilename = os.path.join(sources['include_prefix'], targetfilename)
180*6dbdd20aSAndroid Build Coastguard Worker          targetfilename = transform_extension(targetfilename)
181*6dbdd20aSAndroid Build Coastguard Worker
182*6dbdd20aSAndroid Build Coastguard Worker          if args.check_only:
183*6dbdd20aSAndroid Build Coastguard Worker            if not filecmp.cmp(tmpfilename, targetfilename):
184*6dbdd20aSAndroid Build Coastguard Worker              raise AssertionError('Target {} does not match', targetfilename)
185*6dbdd20aSAndroid Build Coastguard Worker          else:
186*6dbdd20aSAndroid Build Coastguard Worker            os.makedirs(os.path.dirname(targetfilename), exist_ok=True)
187*6dbdd20aSAndroid Build Coastguard Worker            shutil.copyfile(tmpfilename, targetfilename)
188*6dbdd20aSAndroid Build Coastguard Worker
189*6dbdd20aSAndroid Build Coastguard Worker  except AssertionError as e:
190*6dbdd20aSAndroid Build Coastguard Worker    if not str(e):
191*6dbdd20aSAndroid Build Coastguard Worker      raise
192*6dbdd20aSAndroid Build Coastguard Worker    print('Error: {}'.format(e))
193*6dbdd20aSAndroid Build Coastguard Worker    return 1
194*6dbdd20aSAndroid Build Coastguard Worker
195*6dbdd20aSAndroid Build Coastguard Worker
196*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__':
197*6dbdd20aSAndroid Build Coastguard Worker  exit(main())
198