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