Revert 215785 "[telemetry] Add a webdriver backend with support ..."
[chromium-blink-merge.git] / build / landmines.py
blobc09ffb887d8508f3cfe91cff7b186528e7d7c5ee
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """
7 This file holds a list of reasons why a particular build needs to be clobbered
8 (or a list of 'landmines').
10 This script runs every build as a hook. If it detects that the build should
11 be clobbered, it will touch the file <build_dir>/.landmine_triggered. The
12 various build scripts will then check for the presence of this file and clobber
13 accordingly. The script will also emit the reasons for the clobber to stdout.
15 A landmine is tripped when a builder checks out a different revision, and the
16 diff between the new landmines and the old ones is non-null. At this point, the
17 build is clobbered.
18 """
20 import difflib
21 import functools
22 import gyp_helper
23 import logging
24 import optparse
25 import os
26 import shlex
27 import sys
28 import time
30 SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
32 def memoize(default=None):
33 """This decorator caches the return value of a parameterless pure function"""
34 def memoizer(func):
35 val = []
36 @functools.wraps(func)
37 def inner():
38 if not val:
39 ret = func()
40 val.append(ret if ret is not None else default)
41 if logging.getLogger().isEnabledFor(logging.INFO):
42 print '%s -> %r' % (func.__name__, val[0])
43 return val[0]
44 return inner
45 return memoizer
48 @memoize()
49 def IsWindows():
50 return sys.platform in ['win32', 'cygwin']
53 @memoize()
54 def IsLinux():
55 return sys.platform.startswith('linux')
58 @memoize()
59 def IsMac():
60 return sys.platform == 'darwin'
63 @memoize()
64 def gyp_defines():
65 """Parses and returns GYP_DEFINES env var as a dictionary."""
66 return dict(arg.split('=', 1)
67 for arg in shlex.split(os.environ.get('GYP_DEFINES', '')))
69 @memoize()
70 def gyp_msvs_version():
71 return os.environ.get('GYP_MSVS_VERSION', '')
73 @memoize()
74 def distributor():
75 """
76 Returns a string which is the distributed build engine in use (if any).
77 Possible values: 'goma', 'ib', ''
78 """
79 if 'goma' in gyp_defines():
80 return 'goma'
81 elif IsWindows():
82 if 'CHROME_HEADLESS' in os.environ:
83 return 'ib' # use (win and !goma and headless) as approximation of ib
86 @memoize()
87 def platform():
88 """
89 Returns a string representing the platform this build is targetted for.
90 Possible values: 'win', 'mac', 'linux', 'ios', 'android'
91 """
92 if 'OS' in gyp_defines():
93 if 'android' in gyp_defines()['OS']:
94 return 'android'
95 else:
96 return gyp_defines()['OS']
97 elif IsWindows():
98 return 'win'
99 elif IsLinux():
100 return 'linux'
101 else:
102 return 'mac'
105 @memoize()
106 def builder():
108 Returns a string representing the build engine (not compiler) to use.
109 Possible values: 'make', 'ninja', 'xcode', 'msvs', 'scons'
111 if 'GYP_GENERATORS' in os.environ:
112 # for simplicity, only support the first explicit generator
113 generator = os.environ['GYP_GENERATORS'].split(',')[0]
114 if generator.endswith('-android'):
115 return generator.split('-')[0]
116 elif generator.endswith('-ninja'):
117 return 'ninja'
118 else:
119 return generator
120 else:
121 if platform() == 'android':
122 # Good enough for now? Do any android bots use make?
123 return 'ninja'
124 elif platform() == 'ios':
125 return 'xcode'
126 elif IsWindows():
127 return 'msvs'
128 elif IsLinux():
129 return 'ninja'
130 elif IsMac():
131 return 'xcode'
132 else:
133 assert False, 'Don\'t know what builder we\'re using!'
136 def get_landmines(target):
138 ALL LANDMINES ARE DEFINED HERE.
139 target is 'Release' or 'Debug'
141 landmines = []
142 add = lambda item: landmines.append(item + '\n')
144 if (distributor() == 'goma' and platform() == 'win32' and
145 builder() == 'ninja'):
146 add('Need to clobber winja goma due to backend cwd cache fix.')
147 if platform() == 'android':
148 add('Clobber: Resources removed in r195014 require clobber.')
149 if platform() == 'win' and builder() == 'ninja':
150 add('Compile on cc_unittests fails due to symbols removed in r185063.')
151 if platform() == 'linux' and builder() == 'ninja':
152 add('Builders switching from make to ninja will clobber on this.')
153 if platform() == 'mac':
154 add('Switching from bundle to unbundled dylib (issue 14743002).')
155 if (platform() == 'win' and builder() == 'ninja' and
156 gyp_msvs_version() == '2012' and
157 gyp_defines().get('target_arch') == 'x64' and
158 gyp_defines().get('dcheck_always_on') == '1'):
159 add("Switched win x64 trybots from VS2010 to VS2012.")
160 add('Need to clobber everything due to an IDL change in r154579 (blink)')
162 return landmines
165 def get_target_build_dir(build_tool, target, is_iphone=False):
167 Returns output directory absolute path dependent on build and targets.
168 Examples:
169 r'c:\b\build\slave\win\build\src\out\Release'
170 '/mnt/data/b/build/slave/linux/build/src/out/Debug'
171 '/b/build/slave/ios_rel_device/build/src/xcodebuild/Release-iphoneos'
173 Keep this function in sync with tools/build/scripts/slave/compile.py
175 ret = None
176 if build_tool == 'xcode':
177 ret = os.path.join(SRC_DIR, 'xcodebuild',
178 target + ('-iphoneos' if is_iphone else ''))
179 elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios.
180 ret = os.path.join(SRC_DIR, 'out', target)
181 elif build_tool in ['msvs', 'vs', 'ib']:
182 ret = os.path.join(SRC_DIR, 'build', target)
183 elif build_tool == 'scons':
184 ret = os.path.join(SRC_DIR, 'sconsbuild', target)
185 else:
186 raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool)
187 return os.path.abspath(ret)
190 def set_up_landmines(target):
191 """Does the work of setting, planting, and triggering landmines."""
192 out_dir = get_target_build_dir(builder(), target, platform() == 'ios')
194 landmines_path = os.path.join(out_dir, '.landmines')
195 if not os.path.exists(out_dir):
196 os.makedirs(out_dir)
198 new_landmines = get_landmines(target)
200 if not os.path.exists(landmines_path):
201 with open(landmines_path, 'w') as f:
202 f.writelines(new_landmines)
203 else:
204 triggered = os.path.join(out_dir, '.landmines_triggered')
205 with open(landmines_path, 'r') as f:
206 old_landmines = f.readlines()
207 if old_landmines != new_landmines:
208 old_date = time.ctime(os.stat(landmines_path).st_ctime)
209 diff = difflib.unified_diff(old_landmines, new_landmines,
210 fromfile='old_landmines', tofile='new_landmines',
211 fromfiledate=old_date, tofiledate=time.ctime(), n=0)
213 with open(triggered, 'w') as f:
214 f.writelines(diff)
215 elif os.path.exists(triggered):
216 # Remove false triggered landmines.
217 os.remove(triggered)
220 def main():
221 parser = optparse.OptionParser()
222 parser.add_option('-v', '--verbose', action='store_true',
223 default=('LANDMINES_VERBOSE' in os.environ),
224 help=('Emit some extra debugging information (default off). This option '
225 'is also enabled by the presence of a LANDMINES_VERBOSE environment '
226 'variable.'))
227 options, args = parser.parse_args()
229 if args:
230 parser.error('Unknown arguments %s' % args)
232 logging.basicConfig(
233 level=logging.DEBUG if options.verbose else logging.ERROR)
235 gyp_helper.apply_chromium_gyp_env()
237 for target in ('Debug', 'Release', 'Debug_x64', 'Release_x64'):
238 set_up_landmines(target)
240 return 0
243 if __name__ == '__main__':
244 sys.exit(main())