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