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