xref: /aosp_15_r20/external/angle/build/landmines.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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