xref: /aosp_15_r20/external/perfetto/tools/gen_amalgamated_python_tools (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2022 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 os
17*6dbdd20aSAndroid Build Coastguard Workerimport sys
18*6dbdd20aSAndroid Build Coastguard Workerimport logging
19*6dbdd20aSAndroid Build Coastguard Workerimport re
20*6dbdd20aSAndroid Build Coastguard Worker
21*6dbdd20aSAndroid Build Coastguard WorkerROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
22*6dbdd20aSAndroid Build Coastguard Worker
23*6dbdd20aSAndroid Build Coastguard WorkerAMALGAMATION_MAP = {
24*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/record_android_trace.py': 'tools/record_android_trace',
25*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/tracebox.py': 'tools/tracebox',
26*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/traceconv.py': 'tools/traceconv',
27*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/trace_processor.py': 'tools/trace_processor',
28*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/cpu_profile.py': 'tools/cpu_profile',
29*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/heap_profile.py': 'tools/heap_profile',
30*6dbdd20aSAndroid Build Coastguard Worker    'python/tools/install_test_reporter_app.py': 'tools/install_test_reporter_app',
31*6dbdd20aSAndroid Build Coastguard Worker}
32*6dbdd20aSAndroid Build Coastguard Worker
33*6dbdd20aSAndroid Build Coastguard Worker
34*6dbdd20aSAndroid Build Coastguard Workerdef amalgamate_file(fname, stack=None, done=None, in_progress=None):
35*6dbdd20aSAndroid Build Coastguard Worker  stack = [] if stack is None else stack
36*6dbdd20aSAndroid Build Coastguard Worker  done = set() if done is None else done
37*6dbdd20aSAndroid Build Coastguard Worker  in_progress = set() if in_progress is None else in_progress
38*6dbdd20aSAndroid Build Coastguard Worker  if fname in in_progress:
39*6dbdd20aSAndroid Build Coastguard Worker    cycle = ' > '.join(stack + [fname])
40*6dbdd20aSAndroid Build Coastguard Worker    logging.fatal('Cycle detected in %s', cycle)
41*6dbdd20aSAndroid Build Coastguard Worker    sys.exit(1)
42*6dbdd20aSAndroid Build Coastguard Worker  if fname in done:
43*6dbdd20aSAndroid Build Coastguard Worker    return []
44*6dbdd20aSAndroid Build Coastguard Worker  logging.debug('Processing %s', fname)
45*6dbdd20aSAndroid Build Coastguard Worker  done.add(fname)
46*6dbdd20aSAndroid Build Coastguard Worker  in_progress.add(fname)
47*6dbdd20aSAndroid Build Coastguard Worker  with open(fname, encoding='utf-8') as f:
48*6dbdd20aSAndroid Build Coastguard Worker    lines = f.readlines()
49*6dbdd20aSAndroid Build Coastguard Worker  outlines = []
50*6dbdd20aSAndroid Build Coastguard Worker  for line in lines:
51*6dbdd20aSAndroid Build Coastguard Worker    if line.startswith('from perfetto') or line.startswith('import perfetto'):
52*6dbdd20aSAndroid Build Coastguard Worker      if not re.match('from perfetto[.][.\w]+\s+import\s+[*]$', line):
53*6dbdd20aSAndroid Build Coastguard Worker        logging.fatal('Error in %s on line \"%s\"', fname, line.rstrip())
54*6dbdd20aSAndroid Build Coastguard Worker        logging.fatal('Only "from perfetto.foo import *" is supported in '
55*6dbdd20aSAndroid Build Coastguard Worker                      'sources that are used in //tools and get amalgamated')
56*6dbdd20aSAndroid Build Coastguard Worker        sys.exit(1)
57*6dbdd20aSAndroid Build Coastguard Worker      pkg = line.split()[1]
58*6dbdd20aSAndroid Build Coastguard Worker      fpath = os.path.join('python', pkg.replace('.', os.sep) + '.py')
59*6dbdd20aSAndroid Build Coastguard Worker      outlines.append('\n# ----- Amalgamator: begin of %s\n' % fpath)
60*6dbdd20aSAndroid Build Coastguard Worker      outlines += amalgamate_file(fpath, stack + [fname], done, in_progress)
61*6dbdd20aSAndroid Build Coastguard Worker      outlines.append('\n# ----- Amalgamator: end of %s\n' % fpath)
62*6dbdd20aSAndroid Build Coastguard Worker    elif '__file__' in line and not 'amalgamator:nocheck' in line:
63*6dbdd20aSAndroid Build Coastguard Worker      logging.fatal('__file__ is not allowed in sources that get amalgamated.'
64*6dbdd20aSAndroid Build Coastguard Worker                    'In %s on line \"%s\"', fname, line.rstrip())
65*6dbdd20aSAndroid Build Coastguard Worker      sys.exit(1)
66*6dbdd20aSAndroid Build Coastguard Worker
67*6dbdd20aSAndroid Build Coastguard Worker    else:
68*6dbdd20aSAndroid Build Coastguard Worker      outlines.append(line)
69*6dbdd20aSAndroid Build Coastguard Worker  in_progress.remove(fname)
70*6dbdd20aSAndroid Build Coastguard Worker  logging.debug('%s: %d lines', fname, len(outlines))
71*6dbdd20aSAndroid Build Coastguard Worker  return outlines
72*6dbdd20aSAndroid Build Coastguard Worker
73*6dbdd20aSAndroid Build Coastguard Worker
74*6dbdd20aSAndroid Build Coastguard Workerdef amalgamate(src, dst, check_only=False):
75*6dbdd20aSAndroid Build Coastguard Worker  lines = amalgamate_file(src)
76*6dbdd20aSAndroid Build Coastguard Worker  banner = '''
77*6dbdd20aSAndroid Build Coastguard Worker# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
78*6dbdd20aSAndroid Build Coastguard Worker# DO NOT EDIT. Auto-generated by %s
79*6dbdd20aSAndroid Build Coastguard Worker# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
80*6dbdd20aSAndroid Build Coastguard Worker'''
81*6dbdd20aSAndroid Build Coastguard Worker  lines.insert(lines.index('\n'), banner % os.path.relpath(__file__, ROOT_DIR))
82*6dbdd20aSAndroid Build Coastguard Worker  new_content = ''.join(lines)
83*6dbdd20aSAndroid Build Coastguard Worker
84*6dbdd20aSAndroid Build Coastguard Worker  if check_only:
85*6dbdd20aSAndroid Build Coastguard Worker    if not os.path.exists(dst):
86*6dbdd20aSAndroid Build Coastguard Worker      return False
87*6dbdd20aSAndroid Build Coastguard Worker    with open(dst, encoding='utf-8') as f:
88*6dbdd20aSAndroid Build Coastguard Worker      return f.read() == new_content
89*6dbdd20aSAndroid Build Coastguard Worker
90*6dbdd20aSAndroid Build Coastguard Worker  logging.info('Amalgamating %s -> %s', src, dst)
91*6dbdd20aSAndroid Build Coastguard Worker  with open(dst + '.tmp', 'w', encoding='utf-8') as f:
92*6dbdd20aSAndroid Build Coastguard Worker    f.write(new_content)
93*6dbdd20aSAndroid Build Coastguard Worker  os.chmod(dst + '.tmp', 0o755)
94*6dbdd20aSAndroid Build Coastguard Worker  os.rename(dst + '.tmp', dst)
95*6dbdd20aSAndroid Build Coastguard Worker  return True
96*6dbdd20aSAndroid Build Coastguard Worker
97*6dbdd20aSAndroid Build Coastguard Worker
98*6dbdd20aSAndroid Build Coastguard Workerdef main():
99*6dbdd20aSAndroid Build Coastguard Worker  check_only = '--check-only' in sys.argv
100*6dbdd20aSAndroid Build Coastguard Worker  logging.basicConfig(
101*6dbdd20aSAndroid Build Coastguard Worker    format='%(levelname)-8s: %(message)s',
102*6dbdd20aSAndroid Build Coastguard Worker    level=logging.DEBUG if '-v' in sys.argv else logging.INFO)
103*6dbdd20aSAndroid Build Coastguard Worker  os.chdir(ROOT_DIR)  # Make the execution cwd-independent.
104*6dbdd20aSAndroid Build Coastguard Worker  success = True
105*6dbdd20aSAndroid Build Coastguard Worker  for src, dst in AMALGAMATION_MAP.items():
106*6dbdd20aSAndroid Build Coastguard Worker    success = success and amalgamate(src, dst, check_only)
107*6dbdd20aSAndroid Build Coastguard Worker  return 0 if success else 1
108*6dbdd20aSAndroid Build Coastguard Worker
109*6dbdd20aSAndroid Build Coastguard Worker
110*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__':
111*6dbdd20aSAndroid Build Coastguard Worker  sys.exit(main())
112