xref: /aosp_15_r20/external/libchrome/build/clobber.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker#!/usr/bin/env python
2*635a8641SAndroid Build Coastguard Worker# Copyright 2015 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"""This script provides methods for clobbering build directories."""
7*635a8641SAndroid Build Coastguard Worker
8*635a8641SAndroid Build Coastguard Workerimport argparse
9*635a8641SAndroid Build Coastguard Workerimport os
10*635a8641SAndroid Build Coastguard Workerimport shutil
11*635a8641SAndroid Build Coastguard Workerimport subprocess
12*635a8641SAndroid Build Coastguard Workerimport sys
13*635a8641SAndroid Build Coastguard Worker
14*635a8641SAndroid Build Coastguard Worker
15*635a8641SAndroid Build Coastguard Workerdef extract_gn_build_commands(build_ninja_file):
16*635a8641SAndroid Build Coastguard Worker  """Extracts from a build.ninja the commands to run GN.
17*635a8641SAndroid Build Coastguard Worker
18*635a8641SAndroid Build Coastguard Worker  The commands to run GN are the gn rule and build.ninja build step at the
19*635a8641SAndroid Build Coastguard Worker  top of the build.ninja file. We want to keep these when deleting GN builds
20*635a8641SAndroid Build Coastguard Worker  since we want to preserve the command-line flags to GN.
21*635a8641SAndroid Build Coastguard Worker
22*635a8641SAndroid Build Coastguard Worker  On error, returns the empty string."""
23*635a8641SAndroid Build Coastguard Worker  result = ""
24*635a8641SAndroid Build Coastguard Worker  with open(build_ninja_file, 'r') as f:
25*635a8641SAndroid Build Coastguard Worker    # Read until the third blank line. The first thing GN writes to the file
26*635a8641SAndroid Build Coastguard Worker    # is "ninja_required_version = x.y.z", then the "rule gn" and the third
27*635a8641SAndroid Build Coastguard Worker    # is the section for "build build.ninja", separated by blank lines.
28*635a8641SAndroid Build Coastguard Worker    num_blank_lines = 0
29*635a8641SAndroid Build Coastguard Worker    while num_blank_lines < 3:
30*635a8641SAndroid Build Coastguard Worker      line = f.readline()
31*635a8641SAndroid Build Coastguard Worker      if len(line) == 0:
32*635a8641SAndroid Build Coastguard Worker        return ''  # Unexpected EOF.
33*635a8641SAndroid Build Coastguard Worker      result += line
34*635a8641SAndroid Build Coastguard Worker      if line[0] == '\n':
35*635a8641SAndroid Build Coastguard Worker        num_blank_lines = num_blank_lines + 1
36*635a8641SAndroid Build Coastguard Worker  return result
37*635a8641SAndroid Build Coastguard Worker
38*635a8641SAndroid Build Coastguard Worker
39*635a8641SAndroid Build Coastguard Workerdef delete_dir(build_dir):
40*635a8641SAndroid Build Coastguard Worker  if os.path.islink(build_dir):
41*635a8641SAndroid Build Coastguard Worker    return
42*635a8641SAndroid Build Coastguard Worker  # For unknown reasons (anti-virus?) rmtree of Chromium build directories
43*635a8641SAndroid Build Coastguard Worker  # often fails on Windows.
44*635a8641SAndroid Build Coastguard Worker  if sys.platform.startswith('win'):
45*635a8641SAndroid Build Coastguard Worker    subprocess.check_call(['rmdir', '/s', '/q', build_dir], shell=True)
46*635a8641SAndroid Build Coastguard Worker  else:
47*635a8641SAndroid Build Coastguard Worker    shutil.rmtree(build_dir)
48*635a8641SAndroid Build Coastguard Worker
49*635a8641SAndroid Build Coastguard Worker
50*635a8641SAndroid Build Coastguard Workerdef delete_build_dir(build_dir):
51*635a8641SAndroid Build Coastguard Worker  # GN writes a build.ninja.d file. Note that not all GN builds have args.gn.
52*635a8641SAndroid Build Coastguard Worker  build_ninja_d_file = os.path.join(build_dir, 'build.ninja.d')
53*635a8641SAndroid Build Coastguard Worker  if not os.path.exists(build_ninja_d_file):
54*635a8641SAndroid Build Coastguard Worker    delete_dir(build_dir)
55*635a8641SAndroid Build Coastguard Worker    return
56*635a8641SAndroid Build Coastguard Worker
57*635a8641SAndroid Build Coastguard Worker  # GN builds aren't automatically regenerated when you sync. To avoid
58*635a8641SAndroid Build Coastguard Worker  # messing with the GN workflow, erase everything but the args file, and
59*635a8641SAndroid Build Coastguard Worker  # write a dummy build.ninja file that will automatically rerun GN the next
60*635a8641SAndroid Build Coastguard Worker  # time Ninja is run.
61*635a8641SAndroid Build Coastguard Worker  build_ninja_file = os.path.join(build_dir, 'build.ninja')
62*635a8641SAndroid Build Coastguard Worker  build_commands = extract_gn_build_commands(build_ninja_file)
63*635a8641SAndroid Build Coastguard Worker
64*635a8641SAndroid Build Coastguard Worker  try:
65*635a8641SAndroid Build Coastguard Worker    gn_args_file = os.path.join(build_dir, 'args.gn')
66*635a8641SAndroid Build Coastguard Worker    with open(gn_args_file, 'r') as f:
67*635a8641SAndroid Build Coastguard Worker      args_contents = f.read()
68*635a8641SAndroid Build Coastguard Worker  except IOError:
69*635a8641SAndroid Build Coastguard Worker    args_contents = ''
70*635a8641SAndroid Build Coastguard Worker
71*635a8641SAndroid Build Coastguard Worker  e = None
72*635a8641SAndroid Build Coastguard Worker  try:
73*635a8641SAndroid Build Coastguard Worker    # delete_dir and os.mkdir() may fail, such as when chrome.exe is running,
74*635a8641SAndroid Build Coastguard Worker    # and we still want to restore args.gn/build.ninja/build.ninja.d, so catch
75*635a8641SAndroid Build Coastguard Worker    # the exception and rethrow it later.
76*635a8641SAndroid Build Coastguard Worker    delete_dir(build_dir)
77*635a8641SAndroid Build Coastguard Worker    os.mkdir(build_dir)
78*635a8641SAndroid Build Coastguard Worker  except Exception as e:
79*635a8641SAndroid Build Coastguard Worker    pass
80*635a8641SAndroid Build Coastguard Worker
81*635a8641SAndroid Build Coastguard Worker  # Put back the args file (if any).
82*635a8641SAndroid Build Coastguard Worker  if args_contents != '':
83*635a8641SAndroid Build Coastguard Worker    with open(gn_args_file, 'w') as f:
84*635a8641SAndroid Build Coastguard Worker      f.write(args_contents)
85*635a8641SAndroid Build Coastguard Worker
86*635a8641SAndroid Build Coastguard Worker  # Write the build.ninja file sufficiently to regenerate itself.
87*635a8641SAndroid Build Coastguard Worker  with open(os.path.join(build_dir, 'build.ninja'), 'w') as f:
88*635a8641SAndroid Build Coastguard Worker    if build_commands != '':
89*635a8641SAndroid Build Coastguard Worker      f.write(build_commands)
90*635a8641SAndroid Build Coastguard Worker    else:
91*635a8641SAndroid Build Coastguard Worker      # Couldn't parse the build.ninja file, write a default thing.
92*635a8641SAndroid Build Coastguard Worker      f.write('''rule gn
93*635a8641SAndroid Build Coastguard Workercommand = gn -q gen //out/%s/
94*635a8641SAndroid Build Coastguard Workerdescription = Regenerating ninja files
95*635a8641SAndroid Build Coastguard Worker
96*635a8641SAndroid Build Coastguard Workerbuild build.ninja: gn
97*635a8641SAndroid Build Coastguard Workergenerator = 1
98*635a8641SAndroid Build Coastguard Workerdepfile = build.ninja.d
99*635a8641SAndroid Build Coastguard Worker''' % (os.path.split(build_dir)[1]))
100*635a8641SAndroid Build Coastguard Worker
101*635a8641SAndroid Build Coastguard Worker  # Write a .d file for the build which references a nonexistant file. This
102*635a8641SAndroid Build Coastguard Worker  # will make Ninja always mark the build as dirty.
103*635a8641SAndroid Build Coastguard Worker  with open(build_ninja_d_file, 'w') as f:
104*635a8641SAndroid Build Coastguard Worker    f.write('build.ninja: nonexistant_file.gn\n')
105*635a8641SAndroid Build Coastguard Worker
106*635a8641SAndroid Build Coastguard Worker  if e:
107*635a8641SAndroid Build Coastguard Worker    # Rethrow the exception we caught earlier.
108*635a8641SAndroid Build Coastguard Worker    raise e
109*635a8641SAndroid Build Coastguard Worker
110*635a8641SAndroid Build Coastguard Workerdef clobber(out_dir):
111*635a8641SAndroid Build Coastguard Worker  """Clobber contents of build directory.
112*635a8641SAndroid Build Coastguard Worker
113*635a8641SAndroid Build Coastguard Worker  Don't delete the directory itself: some checkouts have the build directory
114*635a8641SAndroid Build Coastguard Worker  mounted."""
115*635a8641SAndroid Build Coastguard Worker  for f in os.listdir(out_dir):
116*635a8641SAndroid Build Coastguard Worker    path = os.path.join(out_dir, f)
117*635a8641SAndroid Build Coastguard Worker    if os.path.isfile(path):
118*635a8641SAndroid Build Coastguard Worker      os.unlink(path)
119*635a8641SAndroid Build Coastguard Worker    elif os.path.isdir(path):
120*635a8641SAndroid Build Coastguard Worker      delete_build_dir(path)
121*635a8641SAndroid Build Coastguard Worker
122*635a8641SAndroid Build Coastguard Worker
123*635a8641SAndroid Build Coastguard Workerdef main():
124*635a8641SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
125*635a8641SAndroid Build Coastguard Worker  parser.add_argument('out_dir', help='The output directory to clobber')
126*635a8641SAndroid Build Coastguard Worker  args = parser.parse_args()
127*635a8641SAndroid Build Coastguard Worker  clobber(args.out_dir)
128*635a8641SAndroid Build Coastguard Worker  return 0
129*635a8641SAndroid Build Coastguard Worker
130*635a8641SAndroid Build Coastguard Worker
131*635a8641SAndroid Build Coastguard Workerif __name__ == '__main__':
132*635a8641SAndroid Build Coastguard Worker  sys.exit(main())
133