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