2015-06-22 15:41:49 +03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# Copyright 2015 The Chromium Authors. All rights reserved.
|
|
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
|
|
# found in the LICENSE file.
|
|
|
|
|
|
|
|
"""This script provides methods for clobbering build directories."""
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import shutil
|
2016-03-19 03:30:51 +03:00
|
|
|
import subprocess
|
2015-06-22 15:41:49 +03:00
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
def extract_gn_build_commands(build_ninja_file):
|
|
|
|
"""Extracts from a build.ninja the commands to run GN.
|
|
|
|
|
|
|
|
The commands to run GN are the gn rule and build.ninja build step at the
|
|
|
|
top of the build.ninja file. We want to keep these when deleting GN builds
|
|
|
|
since we want to preserve the command-line flags to GN.
|
|
|
|
|
|
|
|
On error, returns the empty string."""
|
|
|
|
result = ""
|
|
|
|
with open(build_ninja_file, 'r') as f:
|
|
|
|
# Read until the second blank line. The first thing GN writes to the file
|
|
|
|
# is the "rule gn" and the second is the section for "build build.ninja",
|
|
|
|
# separated by blank lines.
|
|
|
|
num_blank_lines = 0
|
|
|
|
while num_blank_lines < 2:
|
|
|
|
line = f.readline()
|
|
|
|
if len(line) == 0:
|
|
|
|
return '' # Unexpected EOF.
|
|
|
|
result += line
|
|
|
|
if line[0] == '\n':
|
|
|
|
num_blank_lines = num_blank_lines + 1
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2016-03-19 03:30:51 +03:00
|
|
|
def delete_dir(build_dir):
|
2017-01-19 20:42:58 +03:00
|
|
|
if os.path.islink(build_dir):
|
|
|
|
return
|
2016-03-19 03:30:51 +03:00
|
|
|
# For unknown reasons (anti-virus?) rmtree of Chromium build directories
|
|
|
|
# often fails on Windows.
|
|
|
|
if sys.platform.startswith('win'):
|
|
|
|
subprocess.check_call(['rmdir', '/s', '/q', build_dir], shell=True)
|
|
|
|
else:
|
|
|
|
shutil.rmtree(build_dir)
|
|
|
|
|
|
|
|
|
2015-06-22 15:41:49 +03:00
|
|
|
def delete_build_dir(build_dir):
|
|
|
|
# GN writes a build.ninja.d file. Note that not all GN builds have args.gn.
|
|
|
|
build_ninja_d_file = os.path.join(build_dir, 'build.ninja.d')
|
|
|
|
if not os.path.exists(build_ninja_d_file):
|
2016-03-19 03:30:51 +03:00
|
|
|
delete_dir(build_dir)
|
2015-06-22 15:41:49 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
# GN builds aren't automatically regenerated when you sync. To avoid
|
|
|
|
# messing with the GN workflow, erase everything but the args file, and
|
|
|
|
# write a dummy build.ninja file that will automatically rerun GN the next
|
|
|
|
# time Ninja is run.
|
|
|
|
build_ninja_file = os.path.join(build_dir, 'build.ninja')
|
|
|
|
build_commands = extract_gn_build_commands(build_ninja_file)
|
|
|
|
|
|
|
|
try:
|
|
|
|
gn_args_file = os.path.join(build_dir, 'args.gn')
|
|
|
|
with open(gn_args_file, 'r') as f:
|
|
|
|
args_contents = f.read()
|
|
|
|
except IOError:
|
|
|
|
args_contents = ''
|
|
|
|
|
2016-05-21 01:00:46 +03:00
|
|
|
e = None
|
|
|
|
try:
|
|
|
|
# delete_dir and os.mkdir() may fail, such as when chrome.exe is running,
|
|
|
|
# and we still want to restore args.gn/build.ninja/build.ninja.d, so catch
|
|
|
|
# the exception and rethrow it later.
|
|
|
|
delete_dir(build_dir)
|
|
|
|
os.mkdir(build_dir)
|
|
|
|
except Exception as e:
|
|
|
|
pass
|
2015-06-22 15:41:49 +03:00
|
|
|
|
|
|
|
# Put back the args file (if any).
|
|
|
|
if args_contents != '':
|
|
|
|
with open(gn_args_file, 'w') as f:
|
|
|
|
f.write(args_contents)
|
|
|
|
|
|
|
|
# Write the build.ninja file sufficiently to regenerate itself.
|
|
|
|
with open(os.path.join(build_dir, 'build.ninja'), 'w') as f:
|
|
|
|
if build_commands != '':
|
|
|
|
f.write(build_commands)
|
|
|
|
else:
|
|
|
|
# Couldn't parse the build.ninja file, write a default thing.
|
|
|
|
f.write('''rule gn
|
|
|
|
command = gn -q gen //out/%s/
|
|
|
|
description = Regenerating ninja files
|
|
|
|
|
|
|
|
build build.ninja: gn
|
|
|
|
generator = 1
|
|
|
|
depfile = build.ninja.d
|
|
|
|
''' % (os.path.split(build_dir)[1]))
|
|
|
|
|
|
|
|
# Write a .d file for the build which references a nonexistant file. This
|
|
|
|
# will make Ninja always mark the build as dirty.
|
|
|
|
with open(build_ninja_d_file, 'w') as f:
|
|
|
|
f.write('build.ninja: nonexistant_file.gn\n')
|
|
|
|
|
2016-05-21 01:00:46 +03:00
|
|
|
if e:
|
|
|
|
# Rethrow the exception we caught earlier.
|
|
|
|
raise e
|
2015-06-22 15:41:49 +03:00
|
|
|
|
|
|
|
def clobber(out_dir):
|
|
|
|
"""Clobber contents of build directory.
|
|
|
|
|
|
|
|
Don't delete the directory itself: some checkouts have the build directory
|
|
|
|
mounted."""
|
|
|
|
for f in os.listdir(out_dir):
|
|
|
|
path = os.path.join(out_dir, f)
|
|
|
|
if os.path.isfile(path):
|
|
|
|
os.unlink(path)
|
|
|
|
elif os.path.isdir(path):
|
|
|
|
delete_build_dir(path)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument('out_dir', help='The output directory to clobber')
|
|
|
|
args = parser.parse_args()
|
|
|
|
clobber(args.out_dir)
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main())
|