xref: /aosp_15_r20/external/cronet/build/clobber.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6777b538SAndroid Build Coastguard Worker# Copyright 2015 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"""This script provides methods for clobbering build directories."""
7*6777b538SAndroid Build Coastguard Worker
8*6777b538SAndroid Build Coastguard Workerimport argparse
9*6777b538SAndroid Build Coastguard Workerimport os
10*6777b538SAndroid Build Coastguard Workerimport shutil
11*6777b538SAndroid Build Coastguard Workerimport subprocess
12*6777b538SAndroid Build Coastguard Workerimport sys
13*6777b538SAndroid Build Coastguard Worker
14*6777b538SAndroid Build Coastguard Worker
15*6777b538SAndroid Build Coastguard Workerdef extract_gn_build_commands(build_ninja_file):
16*6777b538SAndroid Build Coastguard Worker  """Extracts from a build.ninja the commands to run GN.
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker  The commands to run GN are the gn rule and build.ninja build step at the
19*6777b538SAndroid Build Coastguard Worker  top of the build.ninja file. We want to keep these when deleting GN builds
20*6777b538SAndroid Build Coastguard Worker  since we want to preserve the command-line flags to GN.
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Worker  On error, returns the empty string."""
23*6777b538SAndroid Build Coastguard Worker  result = ""
24*6777b538SAndroid Build Coastguard Worker  with open(build_ninja_file, 'r') as f:
25*6777b538SAndroid Build Coastguard Worker    # Reads until the first empty line after the "build build.ninja:" target.
26*6777b538SAndroid Build Coastguard Worker    # We assume everything before it necessary as well (eg the
27*6777b538SAndroid Build Coastguard Worker    # "ninja_required_version" line).
28*6777b538SAndroid Build Coastguard Worker    found_build_dot_ninja_target = False
29*6777b538SAndroid Build Coastguard Worker    for line in f.readlines():
30*6777b538SAndroid Build Coastguard Worker      result += line
31*6777b538SAndroid Build Coastguard Worker      if line.startswith('build build.ninja:'):
32*6777b538SAndroid Build Coastguard Worker        found_build_dot_ninja_target = True
33*6777b538SAndroid Build Coastguard Worker      if found_build_dot_ninja_target and line[0] == '\n':
34*6777b538SAndroid Build Coastguard Worker        return result
35*6777b538SAndroid Build Coastguard Worker  return ''  # We got to EOF and didn't find what we were looking for.
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker
38*6777b538SAndroid Build Coastguard Workerdef _rmtree(d):
39*6777b538SAndroid Build Coastguard Worker  # For unknown reasons (anti-virus?) rmtree of Chromium build directories
40*6777b538SAndroid Build Coastguard Worker  # often fails on Windows.
41*6777b538SAndroid Build Coastguard Worker  if sys.platform.startswith('win'):
42*6777b538SAndroid Build Coastguard Worker    subprocess.check_call(['rmdir', '/s', '/q', d], shell=True)
43*6777b538SAndroid Build Coastguard Worker  else:
44*6777b538SAndroid Build Coastguard Worker    shutil.rmtree(d)
45*6777b538SAndroid Build Coastguard Worker
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Workerdef _clean_dir(build_dir):
48*6777b538SAndroid Build Coastguard Worker  # Remove files/sub directories individually instead of recreating the build
49*6777b538SAndroid Build Coastguard Worker  # dir because it fails when the build dir is symlinked or mounted.
50*6777b538SAndroid Build Coastguard Worker  for e in os.scandir(build_dir):
51*6777b538SAndroid Build Coastguard Worker    if e.is_dir():
52*6777b538SAndroid Build Coastguard Worker      _rmtree(e.path)
53*6777b538SAndroid Build Coastguard Worker    else:
54*6777b538SAndroid Build Coastguard Worker      os.remove(e.path)
55*6777b538SAndroid Build Coastguard Worker
56*6777b538SAndroid Build Coastguard Worker
57*6777b538SAndroid Build Coastguard Workerdef delete_build_dir(build_dir):
58*6777b538SAndroid Build Coastguard Worker  # GN writes a build.ninja.d file. Note that not all GN builds have args.gn.
59*6777b538SAndroid Build Coastguard Worker  build_ninja_d_file = os.path.join(build_dir, 'build.ninja.d')
60*6777b538SAndroid Build Coastguard Worker  if not os.path.exists(build_ninja_d_file):
61*6777b538SAndroid Build Coastguard Worker    _clean_dir(build_dir)
62*6777b538SAndroid Build Coastguard Worker    return
63*6777b538SAndroid Build Coastguard Worker
64*6777b538SAndroid Build Coastguard Worker  # GN builds aren't automatically regenerated when you sync. To avoid
65*6777b538SAndroid Build Coastguard Worker  # messing with the GN workflow, erase everything but the args file, and
66*6777b538SAndroid Build Coastguard Worker  # write a dummy build.ninja file that will automatically rerun GN the next
67*6777b538SAndroid Build Coastguard Worker  # time Ninja is run.
68*6777b538SAndroid Build Coastguard Worker  build_ninja_file = os.path.join(build_dir, 'build.ninja')
69*6777b538SAndroid Build Coastguard Worker  build_commands = extract_gn_build_commands(build_ninja_file)
70*6777b538SAndroid Build Coastguard Worker
71*6777b538SAndroid Build Coastguard Worker  try:
72*6777b538SAndroid Build Coastguard Worker    gn_args_file = os.path.join(build_dir, 'args.gn')
73*6777b538SAndroid Build Coastguard Worker    with open(gn_args_file, 'r') as f:
74*6777b538SAndroid Build Coastguard Worker      args_contents = f.read()
75*6777b538SAndroid Build Coastguard Worker  except IOError:
76*6777b538SAndroid Build Coastguard Worker    args_contents = ''
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker  exception_during_rm = None
79*6777b538SAndroid Build Coastguard Worker  try:
80*6777b538SAndroid Build Coastguard Worker    # _clean_dir() may fail, such as when chrome.exe is running,
81*6777b538SAndroid Build Coastguard Worker    # and we still want to restore args.gn/build.ninja/build.ninja.d, so catch
82*6777b538SAndroid Build Coastguard Worker    # the exception and rethrow it later.
83*6777b538SAndroid Build Coastguard Worker    # We manually rm files inside the build dir rather than using "gn clean/gen"
84*6777b538SAndroid Build Coastguard Worker    # since we may not have run all necessary DEPS hooks yet at this point.
85*6777b538SAndroid Build Coastguard Worker    _clean_dir(build_dir)
86*6777b538SAndroid Build Coastguard Worker  except Exception as e:
87*6777b538SAndroid Build Coastguard Worker    exception_during_rm = e
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker  # Put back the args file (if any).
90*6777b538SAndroid Build Coastguard Worker  if args_contents != '':
91*6777b538SAndroid Build Coastguard Worker    with open(gn_args_file, 'w') as f:
92*6777b538SAndroid Build Coastguard Worker      f.write(args_contents)
93*6777b538SAndroid Build Coastguard Worker
94*6777b538SAndroid Build Coastguard Worker  # Write the build.ninja file sufficiently to regenerate itself.
95*6777b538SAndroid Build Coastguard Worker  with open(os.path.join(build_dir, 'build.ninja'), 'w') as f:
96*6777b538SAndroid Build Coastguard Worker    if build_commands != '':
97*6777b538SAndroid Build Coastguard Worker      f.write(build_commands)
98*6777b538SAndroid Build Coastguard Worker    else:
99*6777b538SAndroid Build Coastguard Worker      # Couldn't parse the build.ninja file, write a default thing.
100*6777b538SAndroid Build Coastguard Worker      f.write('''ninja_required_version = 1.7.2
101*6777b538SAndroid Build Coastguard Worker
102*6777b538SAndroid Build Coastguard Workerrule gn
103*6777b538SAndroid Build Coastguard Worker  command = gn -q gen //out/%s/
104*6777b538SAndroid Build Coastguard Worker  description = Regenerating ninja files
105*6777b538SAndroid Build Coastguard Worker
106*6777b538SAndroid Build Coastguard Workerbuild build.ninja: gn
107*6777b538SAndroid Build Coastguard Worker  generator = 1
108*6777b538SAndroid Build Coastguard Worker  depfile = build.ninja.d
109*6777b538SAndroid Build Coastguard Worker''' % (os.path.split(build_dir)[1]))
110*6777b538SAndroid Build Coastguard Worker
111*6777b538SAndroid Build Coastguard Worker  # Write a .d file for the build which references a nonexistant file. This
112*6777b538SAndroid Build Coastguard Worker  # will make Ninja always mark the build as dirty.
113*6777b538SAndroid Build Coastguard Worker  with open(build_ninja_d_file, 'w') as f:
114*6777b538SAndroid Build Coastguard Worker    f.write('build.ninja: nonexistant_file.gn\n')
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker  if exception_during_rm:
117*6777b538SAndroid Build Coastguard Worker    # Rethrow the exception we caught earlier.
118*6777b538SAndroid Build Coastguard Worker    raise exception_during_rm
119*6777b538SAndroid Build Coastguard Worker
120*6777b538SAndroid Build Coastguard Worker
121*6777b538SAndroid Build Coastguard Workerdef clobber(out_dir):
122*6777b538SAndroid Build Coastguard Worker  """Clobber contents of build directory.
123*6777b538SAndroid Build Coastguard Worker
124*6777b538SAndroid Build Coastguard Worker  Don't delete the directory itself: some checkouts have the build directory
125*6777b538SAndroid Build Coastguard Worker  mounted."""
126*6777b538SAndroid Build Coastguard Worker  for f in os.listdir(out_dir):
127*6777b538SAndroid Build Coastguard Worker    path = os.path.join(out_dir, f)
128*6777b538SAndroid Build Coastguard Worker    if os.path.isfile(path):
129*6777b538SAndroid Build Coastguard Worker      os.unlink(path)
130*6777b538SAndroid Build Coastguard Worker    elif os.path.isdir(path):
131*6777b538SAndroid Build Coastguard Worker      delete_build_dir(path)
132*6777b538SAndroid Build Coastguard Worker
133*6777b538SAndroid Build Coastguard Worker
134*6777b538SAndroid Build Coastguard Workerdef main():
135*6777b538SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
136*6777b538SAndroid Build Coastguard Worker  parser.add_argument('out_dir', help='The output directory to clobber')
137*6777b538SAndroid Build Coastguard Worker  args = parser.parse_args()
138*6777b538SAndroid Build Coastguard Worker  clobber(args.out_dir)
139*6777b538SAndroid Build Coastguard Worker  return 0
140*6777b538SAndroid Build Coastguard Worker
141*6777b538SAndroid Build Coastguard Worker
142*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__':
143*6777b538SAndroid Build Coastguard Worker  sys.exit(main())
144