Flutter Engine
The Flutter Engine
merge_and_upload_debug_symbols.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2#
3# Copyright 2013 The Flutter Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7""" Merges the debug symbols and uploads them to cipd.
8"""
9
10import argparse
11import collections
12import json
13import os
14import platform
15import re
16import shutil
17import subprocess
18import sys
19import tarfile
20import tempfile
21
22# Path to the engine root checkout. This is used to calculate absolute
23# paths if relative ones are passed to the script.
24BUILD_ROOT_DIR = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..', '..', '..'))
25
26
27def IsLinux():
28 return platform.system() == 'Linux'
29
30
31# out_dir here is of the format "/b/s/w/ir/k/recipe_cleanup/tmpIbWDdp"
32# we need to place the cipd definition in this directory.
33def GetPackagingDir(out_dir):
34 return os.path.abspath(out_dir)
35
36
37def CreateCIPDDefinition(target_arch, out_dir, symbol_dirs):
38 dir_name = os.path.basename(os.path.normpath(out_dir))
39 pkg_def = """
40package: flutter/fuchsia-debug-symbols-%s
41description: Flutter and Dart runner debug symbols for Fuchsia. Target architecture %s.
42install_mode: copy
43data:
44""" % (target_arch, target_arch)
45 for symbol_dir in symbol_dirs:
46 symbol_dir_name = os.path.basename(os.path.normpath(symbol_dir))
47 data = '\n - dir: %s' % (symbol_dir_name)
48 pkg_def = pkg_def + data
49 return pkg_def
50
51
52# CIPD CLI needs the definition and data directory to be relative to each other.
53def WriteCIPDDefinition(target_arch, out_dir, symbol_dirs):
54 _packaging_dir = GetPackagingDir(out_dir)
55 yaml_file = os.path.join(_packaging_dir, 'debug_symbols.cipd.yaml')
56 with open(yaml_file, 'w') as f:
57 cipd_def = CreateCIPDDefinition(target_arch, out_dir, symbol_dirs)
58 f.write(cipd_def)
59 return yaml_file
60
61
62def CheckCIPDPackageExists(package_name, tag):
63 '''Check to see if the current package/tag combo has been published'''
64 command = [
65 'cipd',
66 'search',
67 package_name,
68 '-tag',
69 tag,
70 ]
71 stdout = subprocess.check_output(command)
72 stdout = stdout if isinstance(stdout, str) else stdout.decode('UTF-8')
73 match = re.search(r'No matching instances\.', stdout)
74 if match:
75 return False
76 else:
77 return True
78
79
80def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch):
81 _packaging_dir = GetPackagingDir(out_dir)
82 tag = 'git_revision:%s' % engine_version
83 package_name = 'flutter/fuchsia-debug-symbols-%s' % target_arch
84 already_exists = CheckCIPDPackageExists(package_name, tag)
85 if already_exists:
86 print('CIPD package %s tag %s already exists!' % (package_name, tag))
87
88 if upload and IsLinux() and not already_exists:
89 command = [
90 'cipd',
91 'create',
92 '-pkg-def',
93 cipd_yaml,
94 '-ref',
95 'latest',
96 '-tag',
97 tag,
98 '-verification-timeout',
99 '10m0s',
100 ]
101 else:
102 command = [
103 'cipd', 'pkg-build', '-pkg-def', cipd_yaml, '-out',
104 os.path.join(_packaging_dir, 'fuchsia-debug-symbols-%s.cipd' % target_arch)
105 ]
106
107 # Retry up to three times. We've seen CIPD fail on verification in some
108 # instances. Normally verification takes slightly more than 1 minute when
109 # it succeeds.
110 num_tries = 3
111 for tries in range(num_tries):
112 try:
113 subprocess.check_call(command, cwd=_packaging_dir)
114 break
115 except subprocess.CalledProcessError as error:
116 print('Failed %s times.\nError was: %s' % (tries + 1, error))
117 if tries == num_tries - 1:
118 raise
119
120
121# Recursively hardlinks contents from one directory to another,
122# skipping over collisions.
123def HardlinkContents(dirA, dirB):
124 internal_symbol_dirs = []
125 for src_dir, _, filenames in os.walk(dirA):
126 for filename in filenames:
127 # if a file contains 'dbg_success' in its name, it is a stamp file.
128 # An example of this would be
129 # '._dart_jit_runner_dbg_symbols_unstripped_dbg_success' these
130 # are generated by GN and have to be ignored.
131 if 'dbg_success' in filename:
132 continue
133 src = os.path.join(src_dir, filename)
134 dest_dir = os.path.join(dirB, os.path.relpath(src_dir, dirA))
135 try:
136 os.makedirs(dest_dir)
137 internal_symbol_dirs.append(dest_dir)
138 except:
139 pass
140 dest = os.path.join(dest_dir, filename)
141 if os.path.exists(dest):
142 # The last two path components provide a content address for a .build-id entry.
143 tokens = os.path.split(dest)
144 name = os.path.join(tokens[-2], tokens[-1])
145 print('%s already exists in destination; skipping linking' % name)
146 continue
147 os.link(src, dest)
148 return internal_symbol_dirs
149
150
152 results = []
153 for directory in dirs:
154 if os.path.isabs(directory):
155 results.append(directory)
156 else:
157 results.append(os.path.join(BUILD_ROOT_DIR, directory))
158 return results
159
160
161def main():
162 parser = argparse.ArgumentParser()
163
164 parser.add_argument(
165 '--symbol-dirs',
166 required=True,
167 nargs='+',
168 help='Space separated list of directories that contain the debug symbols.'
169 )
170 parser.add_argument(
171 '--out-dir',
172 action='store',
173 dest='out_dir',
174 default=tempfile.mkdtemp(),
175 help=(
176 'Output directory where the executables will be placed defaults to an '
177 'empty temp directory'
178 )
179 )
180 parser.add_argument('--target-arch', type=str, choices=['x64', 'arm64'], required=True)
181 parser.add_argument('--engine-version', required=True, help='Specifies the flutter engine SHA.')
182
183 parser.add_argument('--upload', default=False, action='store_true')
184
185 args = parser.parse_args()
186
187 symbol_dirs = CalculateAbsoluteDirs(args.symbol_dirs)
188 for symbol_dir in symbol_dirs:
189 assert os.path.exists(symbol_dir) and os.path.isdir(symbol_dir)
190
191 out_dir = args.out_dir
192
193 if os.path.exists(out_dir):
194 print('Directory: %s is not empty, deleting it.' % out_dir)
195 shutil.rmtree(out_dir)
196 os.makedirs(out_dir)
197
198 internal_symbol_dirs = []
199 for symbol_dir in symbol_dirs:
200 internal_symbol_dirs += HardlinkContents(symbol_dir, out_dir)
201
202 # make these unique
203 internal_symbol_dirs = list(set(internal_symbol_dirs))
204
205 arch = args.target_arch
206 cipd_def = WriteCIPDDefinition(arch, out_dir, internal_symbol_dirs)
207
208 # Set revision to HEAD if empty and remove upload. This is to support
209 # presubmit workflows. An empty engine_version means this script is running
210 # on presubmit.
211 should_upload = args.upload
212 engine_version = args.engine_version
213 if not engine_version:
214 engine_version = 'HEAD'
215 should_upload = False
216
217 ProcessCIPDPackage(should_upload, cipd_def, engine_version, out_dir, arch)
218 return 0
219
220
221if __name__ == '__main__':
222 sys.exit(main())
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition: switches.h:76
Definition: main.py:1
def WriteCIPDDefinition(target_arch, out_dir, symbol_dirs)
def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch)
def CreateCIPDDefinition(target_arch, out_dir, symbol_dirs)
def print(*args, **kwargs)
Definition: run_tests.py:49