xref: /aosp_15_r20/external/cronet/build/landmines.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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