blob: 40177eadd7affdce00bab46e38fd9696f55ed25e [file] [log] [blame]
[email protected]abab8d72012-11-14 04:59:481#!/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.
5
6"""
7This file holds a list of reasons why a particular build needs to be clobbered
8(or a list of 'landmines').
9
10This script runs every build as a hook. If it detects that the build should
11be clobbered, it will touch the file <build_dir>/.landmine_triggered. The
12various build scripts will then check for the presence of this file and clobber
13accordingly. The script will also emit the reasons for the clobber to stdout.
14
15A landmine is tripped when a builder checks out a different revision, and the
16diff between the new landmines and the old ones is non-null. At this point, the
17build is clobbered.
18"""
19
20import difflib
21import functools
22import gyp_helper
[email protected]c45227b2012-11-15 02:53:0323import logging
24import optparse
[email protected]abab8d72012-11-14 04:59:4825import os
26import shlex
27import sys
28import time
29
30SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
31
32def 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)
[email protected]c45227b2012-11-15 02:53:0341 if logging.getLogger().isEnabledFor(logging.INFO):
42 print '%s -> %r' % (func.__name__, val[0])
[email protected]abab8d72012-11-14 04:59:4843 return val[0]
44 return inner
45 return memoizer
46
47
48@memoize()
49def IsWindows():
[email protected]ddca2f72013-05-11 19:13:2150 return sys.platform in ['win32', 'cygwin']
[email protected]abab8d72012-11-14 04:59:4851
52
53@memoize()
54def IsLinux():
55 return sys.platform.startswith('linux')
56
57
58@memoize()
59def IsMac():
[email protected]ddca2f72013-05-11 19:13:2160 return sys.platform == 'darwin'
[email protected]abab8d72012-11-14 04:59:4861
62
63@memoize()
64def 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', '')))
68
69
70@memoize()
71def distributor():
72 """
73 Returns a string which is the distributed build engine in use (if any).
74 Possible values: 'goma', 'ib', ''
75 """
76 if 'goma' in gyp_defines():
77 return 'goma'
78 elif IsWindows():
79 if 'CHROME_HEADLESS' in os.environ:
80 return 'ib' # use (win and !goma and headless) as approximation of ib
81
82
83@memoize()
84def platform():
85 """
86 Returns a string representing the platform this build is targetted for.
87 Possible values: 'win', 'mac', 'linux', 'ios', 'android'
88 """
89 if 'OS' in gyp_defines():
90 if 'android' in gyp_defines()['OS']:
91 return 'android'
92 else:
93 return gyp_defines()['OS']
94 elif IsWindows():
95 return 'win'
96 elif IsLinux():
97 return 'linux'
98 else:
99 return 'mac'
100
101
102@memoize()
103def builder():
104 """
105 Returns a string representing the build engine (not compiler) to use.
106 Possible values: 'make', 'ninja', 'xcode', 'msvs', 'scons'
107 """
108 if 'GYP_GENERATORS' in os.environ:
109 # for simplicity, only support the first explicit generator
110 generator = os.environ['GYP_GENERATORS'].split(',')[0]
111 if generator.endswith('-android'):
112 return generator.split('-')[0]
[email protected]df4226d2013-04-30 20:39:16113 elif generator.endswith('-ninja'):
114 return 'ninja'
[email protected]abab8d72012-11-14 04:59:48115 else:
116 return generator
117 else:
118 if platform() == 'android':
119 # Good enough for now? Do any android bots use make?
120 return 'ninja'
121 elif platform() == 'ios':
122 return 'xcode'
123 elif IsWindows():
124 return 'msvs'
125 elif IsLinux():
[email protected]ddca2f72013-05-11 19:13:21126 return 'ninja'
[email protected]abab8d72012-11-14 04:59:48127 elif IsMac():
128 return 'xcode'
129 else:
130 assert False, 'Don\'t know what builder we\'re using!'
131
132
133def get_landmines(target):
134 """
135 ALL LANDMINES ARE DEFINED HERE.
136 target is 'Release' or 'Debug'
137 """
138 landmines = []
139 add = lambda item: landmines.append(item + '\n')
140
141 if (distributor() == 'goma' and platform() == 'win32' and
142 builder() == 'ninja'):
143 add('Need to clobber winja goma due to backend cwd cache fix.')
[email protected]a3078d82012-12-01 01:59:36144 if platform() == 'android':
[email protected]491657e2013-04-19 01:55:24145 add('Clobber: Resources removed in r195014 require clobber.')
[email protected]95795dd92013-03-04 19:17:42146 if platform() == 'win' and builder() == 'ninja':
147 add('Compile on cc_unittests fails due to symbols removed in r185063.')
[email protected]4ae6e842013-03-09 19:40:31148 if platform() == 'linux' and builder() == 'ninja':
149 add('Builders switching from make to ninja will clobber on this.')
[email protected]19a904b2013-05-03 22:12:29150 if platform() == 'mac':
151 add('Switching from bundle to unbundled dylib (issue 14743002).')
[email protected]abab8d72012-11-14 04:59:48152
153 return landmines
154
155
156def get_target_build_dir(build_tool, target, is_iphone=False):
157 """
158 Returns output directory absolute path dependent on build and targets.
159 Examples:
160 r'c:\b\build\slave\win\build\src\out\Release'
161 '/mnt/data/b/build/slave/linux/build/src/out/Debug'
162 '/b/build/slave/ios_rel_device/build/src/xcodebuild/Release-iphoneos'
163
164 Keep this function in sync with tools/build/scripts/slave/compile.py
165 """
166 ret = None
167 if build_tool == 'xcode':
168 ret = os.path.join(SRC_DIR, 'xcodebuild',
169 target + ('-iphoneos' if is_iphone else ''))
[email protected]ddca2f72013-05-11 19:13:21170 elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios.
[email protected]abab8d72012-11-14 04:59:48171 ret = os.path.join(SRC_DIR, 'out', target)
[email protected]d074d472012-11-15 01:52:07172 elif build_tool in ['msvs', 'vs', 'ib']:
[email protected]abab8d72012-11-14 04:59:48173 ret = os.path.join(SRC_DIR, 'build', target)
174 elif build_tool == 'scons':
175 ret = os.path.join(SRC_DIR, 'sconsbuild', target)
176 else:
[email protected]68d80692013-01-17 23:50:02177 raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool)
[email protected]abab8d72012-11-14 04:59:48178 return os.path.abspath(ret)
179
180
[email protected]c45227b2012-11-15 02:53:03181def set_up_landmines(target):
182 """Does the work of setting, planting, and triggering landmines."""
183 out_dir = get_target_build_dir(builder(), target, platform() == 'ios')
184
185 landmines_path = os.path.join(out_dir, '.landmines')
186 if not os.path.exists(out_dir):
187 os.makedirs(out_dir)
188
189 new_landmines = get_landmines(target)
190
191 if not os.path.exists(landmines_path):
192 with open(landmines_path, 'w') as f:
193 f.writelines(new_landmines)
194 else:
195 triggered = os.path.join(out_dir, '.landmines_triggered')
196 with open(landmines_path, 'r') as f:
197 old_landmines = f.readlines()
198 if old_landmines != new_landmines:
199 old_date = time.ctime(os.stat(landmines_path).st_ctime)
200 diff = difflib.unified_diff(old_landmines, new_landmines,
201 fromfile='old_landmines', tofile='new_landmines',
202 fromfiledate=old_date, tofiledate=time.ctime(), n=0)
203
204 with open(triggered, 'w') as f:
205 f.writelines(diff)
206 elif os.path.exists(triggered):
207 # Remove false triggered landmines.
208 os.remove(triggered)
209
210
211def main():
212 parser = optparse.OptionParser()
213 parser.add_option('-v', '--verbose', action='store_true',
214 default=('LANDMINES_VERBOSE' in os.environ),
215 help=('Emit some extra debugging information (default off). This option '
216 'is also enabled by the presence of a LANDMINES_VERBOSE environment '
217 'variable.'))
218 options, args = parser.parse_args()
219
220 if args:
221 parser.error('Unknown arguments %s' % args)
222
223 logging.basicConfig(
224 level=logging.DEBUG if options.verbose else logging.ERROR)
[email protected]abab8d72012-11-14 04:59:48225
226 gyp_helper.apply_chromium_gyp_env()
227
[email protected]31e8a852013-03-29 21:17:44228 for target in ('Debug', 'Release', 'Debug_x64', 'Release_x64'):
[email protected]c45227b2012-11-15 02:53:03229 set_up_landmines(target)
[email protected]abab8d72012-11-14 04:59:48230
231 return 0
232
233
234if __name__ == '__main__':
[email protected]c45227b2012-11-15 02:53:03235 sys.exit(main())