123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- #!/usr/bin/env python
- # Copyright (c) 2012 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """
- This script runs every gclient runhooks as the first hook (See DEPS). If it
- detects that the build should be clobbered, it will delete the contents of the
- build directory.
- A landmine is tripped when a builder checks out a different revision, and the
- diff between the new landmines and the old ones is non-null. At this point, the
- build is clobbered.
- Before adding or changing a landmine consider the consequences of doing so.
- Doing so will wipe out every output directory on every Chrome developer's
- machine. This can be particularly problematic on Windows where the directory
- deletion may well fail (locked files, command prompt in the directory, etc.),
- and generated .sln and .vcxproj files will be deleted.
- This output directory deletion will be repeated when going back and forth across
- the change that added the landmine, adding to the cost. There are usually less
- troublesome alternatives.
- """
- import difflib
- import errno
- import logging
- import optparse
- import os
- import sys
- import subprocess
- import time
- import clobber
- import landmine_utils
- def get_build_dir(src_dir):
- r"""
- Returns the absolute path to the directory containing the build directories.
- Examples:
- 'C:\src\out'
- '/b/s/w/ir/cache/builder/src/out'
- """
- if 'CHROMIUM_OUT_DIR' in os.environ:
- output_dir = os.environ.get('CHROMIUM_OUT_DIR').strip()
- if not output_dir:
- raise Error('CHROMIUM_OUT_DIR environment variable is set but blank!')
- else:
- output_dir = 'out'
- return os.path.abspath(os.path.join(src_dir, output_dir))
- def clobber_if_necessary(new_landmines, src_dir, landmines_path):
- """Does the work of setting, planting, and triggering landmines."""
- out_dir = get_build_dir(src_dir)
- try:
- os.makedirs(out_dir)
- except OSError as e:
- if e.errno == errno.EEXIST:
- pass
- if os.path.exists(landmines_path):
- with open(landmines_path, 'r') as f:
- old_landmines = f.readlines()
- if old_landmines != new_landmines:
- old_date = time.ctime(os.stat(landmines_path).st_ctime)
- diff = difflib.unified_diff(old_landmines, new_landmines,
- fromfile='old_landmines', tofile='new_landmines',
- fromfiledate=old_date, tofiledate=time.ctime(), n=0)
- sys.stdout.write('Clobbering due to:\n')
- sys.stdout.writelines(diff)
- sys.stdout.flush()
- clobber.clobber(out_dir)
- # Save current set of landmines for next time.
- with open(landmines_path, 'w') as f:
- f.writelines(new_landmines)
- def process_options():
- """Returns an options object containing the configuration for this script."""
- parser = optparse.OptionParser()
- parser.add_option(
- '-s', '--landmine-scripts', action='append',
- help='Path to the script which emits landmines to stdout. The target '
- 'is passed to this script via option -t. Note that an extra '
- 'script can be specified via an env var EXTRA_LANDMINES_SCRIPT.')
- parser.add_option('-d', '--src-dir',
- help='Path of the source root dir. Overrides the default location of the '
- 'source root dir when calculating the build directory.')
- parser.add_option(
- '-l',
- '--landmines-path',
- help='Path to the landmines file to use (defaults to .landmines)')
- parser.add_option('-v', '--verbose', action='store_true',
- default=('LANDMINES_VERBOSE' in os.environ),
- help=('Emit some extra debugging information (default off). This option '
- 'is also enabled by the presence of a LANDMINES_VERBOSE environment '
- 'variable.'))
- options, args = parser.parse_args()
- if args:
- parser.error('Unknown arguments %s' % args)
- logging.basicConfig(
- level=logging.DEBUG if options.verbose else logging.ERROR)
- if options.src_dir:
- if not os.path.isdir(options.src_dir):
- parser.error('Cannot find source root dir at %s' % options.src_dir)
- logging.debug('Overriding source root dir. Using: %s', options.src_dir)
- else:
- options.src_dir = \
- os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
- if not options.landmine_scripts:
- options.landmine_scripts = [os.path.join(options.src_dir, 'build',
- 'get_landmines.py')]
- extra_script = os.environ.get('EXTRA_LANDMINES_SCRIPT')
- if extra_script:
- options.landmine_scripts += [extra_script]
- return options
- def main():
- options = process_options()
- landmines = []
- for s in options.landmine_scripts:
- proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE,
- universal_newlines=True)
- output, _ = proc.communicate()
- landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()])
- if options.landmines_path:
- landmines_path = options.landmines_path
- else:
- landmines_path = os.path.join(options.src_dir, '.landmines')
- clobber_if_necessary(landmines, options.src_dir,
- os.path.normpath(landmines_path))
- return 0
- if __name__ == '__main__':
- sys.exit(main())
|