xref: /aosp_15_r20/external/libchrome/build/landmines.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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