1#!/usr/bin/env python 2# Copyright 2014 the V8 project 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 script runs every build as a hook. If it detects that the build should 8be clobbered, it will touch the file <build_dir>/.landmine_triggered. The 9various build scripts will then check for the presence of this file and clobber 10accordingly. The script will also emit the reasons for the clobber to stdout. 11 12A landmine is tripped when a builder checks out a different revision, and the 13diff between the new landmines and the old ones is non-null. At this point, the 14build is clobbered. 15""" 16 17import difflib 18import logging 19import optparse 20import os 21import sys 22import subprocess 23import time 24 25import landmine_utils 26 27 28SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 29 30 31def get_target_build_dir(build_tool, target): 32 """ 33 Returns output directory absolute path dependent on build and targets. 34 Examples: 35 r'c:\b\build\slave\win\build\src\out\Release' 36 '/mnt/data/b/build/slave/linux/build/src/out/Debug' 37 '/b/build/slave/ios_rel_device/build/src/xcodebuild/Release-iphoneos' 38 39 Keep this function in sync with tools/build/scripts/slave/compile.py 40 """ 41 ret = None 42 if build_tool == 'xcode': 43 ret = os.path.join(SRC_DIR, 'xcodebuild', target) 44 elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios. 45 ret = os.path.join(SRC_DIR, 'out', target) 46 elif build_tool in ['msvs', 'vs', 'ib']: 47 ret = os.path.join(SRC_DIR, 'build', target) 48 else: 49 raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool) 50 return os.path.abspath(ret) 51 52 53def set_up_landmines(target, new_landmines): 54 """Does the work of setting, planting, and triggering landmines.""" 55 out_dir = get_target_build_dir(landmine_utils.builder(), target) 56 57 landmines_path = os.path.join(out_dir, '.landmines') 58 if not os.path.exists(out_dir): 59 return 60 61 if not os.path.exists(landmines_path): 62 print "Landmines tracker didn't exists." 63 64 # FIXME(machenbach): Clobber deletes the .landmines tracker. Difficult 65 # to know if we are right after a clobber or if it is first-time landmines 66 # deployment. Also, a landmine-triggered clobber right after a clobber is 67 # not possible. Different clobber methods for msvs, xcode and make all 68 # have different blacklists of files that are not deleted. 69 if os.path.exists(landmines_path): 70 triggered = os.path.join(out_dir, '.landmines_triggered') 71 with open(landmines_path, 'r') as f: 72 old_landmines = f.readlines() 73 if old_landmines != new_landmines: 74 old_date = time.ctime(os.stat(landmines_path).st_ctime) 75 diff = difflib.unified_diff(old_landmines, new_landmines, 76 fromfile='old_landmines', tofile='new_landmines', 77 fromfiledate=old_date, tofiledate=time.ctime(), n=0) 78 79 with open(triggered, 'w') as f: 80 f.writelines(diff) 81 print "Setting landmine: %s" % triggered 82 elif os.path.exists(triggered): 83 # Remove false triggered landmines. 84 os.remove(triggered) 85 print "Removing landmine: %s" % triggered 86 with open(landmines_path, 'w') as f: 87 f.writelines(new_landmines) 88 89 90def process_options(): 91 """Returns a list of landmine emitting scripts.""" 92 parser = optparse.OptionParser() 93 parser.add_option( 94 '-s', '--landmine-scripts', action='append', 95 default=[os.path.join(SRC_DIR, 'build', 'get_landmines.py')], 96 help='Path to the script which emits landmines to stdout. The target ' 97 'is passed to this script via option -t. Note that an extra ' 98 'script can be specified via an env var EXTRA_LANDMINES_SCRIPT.') 99 parser.add_option('-v', '--verbose', action='store_true', 100 default=('LANDMINES_VERBOSE' in os.environ), 101 help=('Emit some extra debugging information (default off). This option ' 102 'is also enabled by the presence of a LANDMINES_VERBOSE environment ' 103 'variable.')) 104 105 options, args = parser.parse_args() 106 107 if args: 108 parser.error('Unknown arguments %s' % args) 109 110 logging.basicConfig( 111 level=logging.DEBUG if options.verbose else logging.ERROR) 112 113 extra_script = os.environ.get('EXTRA_LANDMINES_SCRIPT') 114 if extra_script: 115 return options.landmine_scripts + [extra_script] 116 else: 117 return options.landmine_scripts 118 119 120def main(): 121 landmine_scripts = process_options() 122 123 if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'): 124 return 0 125 126 landmines = [] 127 for s in landmine_scripts: 128 proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE) 129 output, _ = proc.communicate() 130 landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()]) 131 132 for target in ('Debug', 'Release'): 133 set_up_landmines(target, landmines) 134 135 return 0 136 137 138if __name__ == '__main__': 139 sys.exit(main()) 140