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