1*635a8641SAndroid Build Coastguard Worker#!/usr/bin/env python 2*635a8641SAndroid Build Coastguard Worker# Copyright (c) 2012 The Chromium Authors. All rights reserved. 3*635a8641SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*635a8641SAndroid Build Coastguard Worker# found in the LICENSE file. 5*635a8641SAndroid Build Coastguard Worker 6*635a8641SAndroid Build Coastguard Worker""" 7*635a8641SAndroid Build Coastguard WorkerThis script runs every build as the first hook (See DEPS). If it detects that 8*635a8641SAndroid Build Coastguard Workerthe build should be clobbered, it will delete the contents of the build 9*635a8641SAndroid Build Coastguard Workerdirectory. 10*635a8641SAndroid Build Coastguard Worker 11*635a8641SAndroid Build Coastguard WorkerA landmine is tripped when a builder checks out a different revision, and the 12*635a8641SAndroid Build Coastguard Workerdiff between the new landmines and the old ones is non-null. At this point, the 13*635a8641SAndroid Build Coastguard Workerbuild is clobbered. 14*635a8641SAndroid Build Coastguard Worker 15*635a8641SAndroid Build Coastguard WorkerBefore adding or changing a landmine consider the consequences of doing so. 16*635a8641SAndroid Build Coastguard WorkerDoing so will wipe out every output directory on every Chrome developer's 17*635a8641SAndroid Build Coastguard Workermachine. This can be particularly problematic on Windows where the directory 18*635a8641SAndroid Build Coastguard Workerdeletion may well fail (locked files, command prompt in the directory, etc.), 19*635a8641SAndroid Build Coastguard Workerand generated .sln and .vcxproj files will be deleted. 20*635a8641SAndroid Build Coastguard Worker 21*635a8641SAndroid Build Coastguard WorkerThis output directory deletion will be repated when going back and forth across 22*635a8641SAndroid Build Coastguard Workerthe change that added the landmine, adding to the cost. There are usually less 23*635a8641SAndroid Build Coastguard Workertroublesome alternatives. 24*635a8641SAndroid Build Coastguard Worker""" 25*635a8641SAndroid Build Coastguard Worker 26*635a8641SAndroid Build Coastguard Workerimport difflib 27*635a8641SAndroid Build Coastguard Workerimport errno 28*635a8641SAndroid Build Coastguard Workerimport logging 29*635a8641SAndroid Build Coastguard Workerimport optparse 30*635a8641SAndroid Build Coastguard Workerimport os 31*635a8641SAndroid Build Coastguard Workerimport sys 32*635a8641SAndroid Build Coastguard Workerimport subprocess 33*635a8641SAndroid Build Coastguard Workerimport time 34*635a8641SAndroid Build Coastguard Worker 35*635a8641SAndroid Build Coastguard Workerimport clobber 36*635a8641SAndroid Build Coastguard Workerimport landmine_utils 37*635a8641SAndroid Build Coastguard Worker 38*635a8641SAndroid Build Coastguard Worker 39*635a8641SAndroid Build Coastguard Workerdef get_build_dir(src_dir): 40*635a8641SAndroid Build Coastguard Worker """ 41*635a8641SAndroid Build Coastguard Worker Returns output directory absolute path dependent on build and targets. 42*635a8641SAndroid Build Coastguard Worker Examples: 43*635a8641SAndroid Build Coastguard Worker r'c:\b\build\slave\win\build\src\out' 44*635a8641SAndroid Build Coastguard Worker '/mnt/data/b/build/slave/linux/build/src/out' 45*635a8641SAndroid Build Coastguard Worker '/b/build/slave/ios_rel_device/build/src/out' 46*635a8641SAndroid Build Coastguard Worker 47*635a8641SAndroid Build Coastguard Worker Keep this function in sync with tools/build/scripts/slave/compile.py 48*635a8641SAndroid Build Coastguard Worker """ 49*635a8641SAndroid Build Coastguard Worker if 'CHROMIUM_OUT_DIR' in os.environ: 50*635a8641SAndroid Build Coastguard Worker output_dir = os.environ.get('CHROMIUM_OUT_DIR').strip() 51*635a8641SAndroid Build Coastguard Worker if not output_dir: 52*635a8641SAndroid Build Coastguard Worker raise Error('CHROMIUM_OUT_DIR environment variable is set but blank!') 53*635a8641SAndroid Build Coastguard Worker else: 54*635a8641SAndroid Build Coastguard Worker output_dir = 'out' 55*635a8641SAndroid Build Coastguard Worker return os.path.abspath(os.path.join(src_dir, output_dir)) 56*635a8641SAndroid Build Coastguard Worker 57*635a8641SAndroid Build Coastguard Worker 58*635a8641SAndroid Build Coastguard Workerdef clobber_if_necessary(new_landmines, src_dir): 59*635a8641SAndroid Build Coastguard Worker """Does the work of setting, planting, and triggering landmines.""" 60*635a8641SAndroid Build Coastguard Worker out_dir = get_build_dir(src_dir) 61*635a8641SAndroid Build Coastguard Worker landmines_path = os.path.normpath(os.path.join(src_dir, '.landmines')) 62*635a8641SAndroid Build Coastguard Worker try: 63*635a8641SAndroid Build Coastguard Worker os.makedirs(out_dir) 64*635a8641SAndroid Build Coastguard Worker except OSError as e: 65*635a8641SAndroid Build Coastguard Worker if e.errno == errno.EEXIST: 66*635a8641SAndroid Build Coastguard Worker pass 67*635a8641SAndroid Build Coastguard Worker 68*635a8641SAndroid Build Coastguard Worker if os.path.exists(landmines_path): 69*635a8641SAndroid Build Coastguard Worker with open(landmines_path, 'r') as f: 70*635a8641SAndroid Build Coastguard Worker old_landmines = f.readlines() 71*635a8641SAndroid Build Coastguard Worker if old_landmines != new_landmines: 72*635a8641SAndroid Build Coastguard Worker old_date = time.ctime(os.stat(landmines_path).st_ctime) 73*635a8641SAndroid Build Coastguard Worker diff = difflib.unified_diff(old_landmines, new_landmines, 74*635a8641SAndroid Build Coastguard Worker fromfile='old_landmines', tofile='new_landmines', 75*635a8641SAndroid Build Coastguard Worker fromfiledate=old_date, tofiledate=time.ctime(), n=0) 76*635a8641SAndroid Build Coastguard Worker sys.stdout.write('Clobbering due to:\n') 77*635a8641SAndroid Build Coastguard Worker sys.stdout.writelines(diff) 78*635a8641SAndroid Build Coastguard Worker sys.stdout.flush() 79*635a8641SAndroid Build Coastguard Worker 80*635a8641SAndroid Build Coastguard Worker clobber.clobber(out_dir) 81*635a8641SAndroid Build Coastguard Worker 82*635a8641SAndroid Build Coastguard Worker # Save current set of landmines for next time. 83*635a8641SAndroid Build Coastguard Worker with open(landmines_path, 'w') as f: 84*635a8641SAndroid Build Coastguard Worker f.writelines(new_landmines) 85*635a8641SAndroid Build Coastguard Worker 86*635a8641SAndroid Build Coastguard Worker 87*635a8641SAndroid Build Coastguard Workerdef process_options(): 88*635a8641SAndroid Build Coastguard Worker """Returns an options object containing the configuration for this script.""" 89*635a8641SAndroid Build Coastguard Worker parser = optparse.OptionParser() 90*635a8641SAndroid Build Coastguard Worker parser.add_option( 91*635a8641SAndroid Build Coastguard Worker '-s', '--landmine-scripts', action='append', 92*635a8641SAndroid Build Coastguard Worker help='Path to the script which emits landmines to stdout. The target ' 93*635a8641SAndroid Build Coastguard Worker 'is passed to this script via option -t. Note that an extra ' 94*635a8641SAndroid Build Coastguard Worker 'script can be specified via an env var EXTRA_LANDMINES_SCRIPT.') 95*635a8641SAndroid Build Coastguard Worker parser.add_option('-d', '--src-dir', 96*635a8641SAndroid Build Coastguard Worker help='Path of the source root dir. Overrides the default location of the ' 97*635a8641SAndroid Build Coastguard Worker 'source root dir when calculating the build directory.') 98*635a8641SAndroid Build Coastguard Worker parser.add_option('-v', '--verbose', action='store_true', 99*635a8641SAndroid Build Coastguard Worker default=('LANDMINES_VERBOSE' in os.environ), 100*635a8641SAndroid Build Coastguard Worker help=('Emit some extra debugging information (default off). This option ' 101*635a8641SAndroid Build Coastguard Worker 'is also enabled by the presence of a LANDMINES_VERBOSE environment ' 102*635a8641SAndroid Build Coastguard Worker 'variable.')) 103*635a8641SAndroid Build Coastguard Worker 104*635a8641SAndroid Build Coastguard Worker options, args = parser.parse_args() 105*635a8641SAndroid Build Coastguard Worker 106*635a8641SAndroid Build Coastguard Worker if args: 107*635a8641SAndroid Build Coastguard Worker parser.error('Unknown arguments %s' % args) 108*635a8641SAndroid Build Coastguard Worker 109*635a8641SAndroid Build Coastguard Worker logging.basicConfig( 110*635a8641SAndroid Build Coastguard Worker level=logging.DEBUG if options.verbose else logging.ERROR) 111*635a8641SAndroid Build Coastguard Worker 112*635a8641SAndroid Build Coastguard Worker if options.src_dir: 113*635a8641SAndroid Build Coastguard Worker if not os.path.isdir(options.src_dir): 114*635a8641SAndroid Build Coastguard Worker parser.error('Cannot find source root dir at %s' % options.src_dir) 115*635a8641SAndroid Build Coastguard Worker logging.debug('Overriding source root dir. Using: %s', options.src_dir) 116*635a8641SAndroid Build Coastguard Worker else: 117*635a8641SAndroid Build Coastguard Worker options.src_dir = \ 118*635a8641SAndroid Build Coastguard Worker os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 119*635a8641SAndroid Build Coastguard Worker 120*635a8641SAndroid Build Coastguard Worker if not options.landmine_scripts: 121*635a8641SAndroid Build Coastguard Worker options.landmine_scripts = [os.path.join(options.src_dir, 'build', 122*635a8641SAndroid Build Coastguard Worker 'get_landmines.py')] 123*635a8641SAndroid Build Coastguard Worker 124*635a8641SAndroid Build Coastguard Worker extra_script = os.environ.get('EXTRA_LANDMINES_SCRIPT') 125*635a8641SAndroid Build Coastguard Worker if extra_script: 126*635a8641SAndroid Build Coastguard Worker options.landmine_scripts += [extra_script] 127*635a8641SAndroid Build Coastguard Worker 128*635a8641SAndroid Build Coastguard Worker return options 129*635a8641SAndroid Build Coastguard Worker 130*635a8641SAndroid Build Coastguard Worker 131*635a8641SAndroid Build Coastguard Workerdef main(): 132*635a8641SAndroid Build Coastguard Worker options = process_options() 133*635a8641SAndroid Build Coastguard Worker 134*635a8641SAndroid Build Coastguard Worker landmines = [] 135*635a8641SAndroid Build Coastguard Worker for s in options.landmine_scripts: 136*635a8641SAndroid Build Coastguard Worker proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE) 137*635a8641SAndroid Build Coastguard Worker output, _ = proc.communicate() 138*635a8641SAndroid Build Coastguard Worker landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()]) 139*635a8641SAndroid Build Coastguard Worker clobber_if_necessary(landmines, options.src_dir) 140*635a8641SAndroid Build Coastguard Worker 141*635a8641SAndroid Build Coastguard Worker return 0 142*635a8641SAndroid Build Coastguard Worker 143*635a8641SAndroid Build Coastguard Worker 144*635a8641SAndroid Build Coastguard Workerif __name__ == '__main__': 145*635a8641SAndroid Build Coastguard Worker sys.exit(main()) 146