Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: build/landmines.py

Issue 955463002: Port chromium landmines script. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/landmine_utils.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 the V8 project authors. All rights reserved. 2 # Copyright 2014 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 This script runs every build as a hook. If it detects that the build should 7 This script runs every build as the first hook (See DEPS). If it detects that
8 be clobbered, it will touch the file <build_dir>/.landmine_triggered. The 8 the build should be clobbered, it will delete the contents of the build
9 various build scripts will then check for the presence of this file and clobber 9 directory.
10 accordingly. The script will also emit the reasons for the clobber to stdout.
11 10
12 A landmine is tripped when a builder checks out a different revision, and the 11 A landmine is tripped when a builder checks out a different revision, and the
13 diff between the new landmines and the old ones is non-null. At this point, the 12 diff between the new landmines and the old ones is non-null. At this point, the
14 build is clobbered. 13 build is clobbered.
15 """ 14 """
16 15
17 import difflib 16 import difflib
17 import errno
18 import gyp_environment
18 import logging 19 import logging
19 import optparse 20 import optparse
20 import os 21 import os
22 import re
23 import shutil
21 import sys 24 import sys
22 import subprocess 25 import subprocess
23 import time 26 import time
24 27
25 import landmine_utils 28 import landmine_utils
26 29
27 30
28 SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 31 SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
29 32
30 33
31 def get_target_build_dir(build_tool, target): 34 def get_build_dir(build_tool, is_iphone=False):
32 """ 35 """
33 Returns output directory absolute path dependent on build and targets. 36 Returns output directory absolute path dependent on build and targets.
34 Examples: 37 Examples:
35 r'c:\b\build\slave\win\build\src\out\Release' 38 r'c:\b\build\slave\win\build\src\out'
36 '/mnt/data/b/build/slave/linux/build/src/out/Debug' 39 '/mnt/data/b/build/slave/linux/build/src/out'
37 '/b/build/slave/ios_rel_device/build/src/xcodebuild/Release-iphoneos' 40 '/b/build/slave/ios_rel_device/build/src/xcodebuild'
38 41
39 Keep this function in sync with tools/build/scripts/slave/compile.py 42 Keep this function in sync with tools/build/scripts/slave/compile.py
40 """ 43 """
41 ret = None 44 ret = None
42 if build_tool == 'xcode': 45 if build_tool == 'xcode':
43 ret = os.path.join(SRC_DIR, 'xcodebuild', target) 46 ret = os.path.join(SRC_DIR, 'xcodebuild')
44 elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios. 47 elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios.
45 ret = os.path.join(SRC_DIR, 'out', target) 48 if 'CHROMIUM_OUT_DIR' in os.environ:
49 output_dir = os.environ.get('CHROMIUM_OUT_DIR').strip()
50 if not output_dir:
51 raise Error('CHROMIUM_OUT_DIR environment variable is set but blank!')
52 else:
53 output_dir = landmine_utils.gyp_generator_flags().get('output_dir', 'out')
54 ret = os.path.join(SRC_DIR, output_dir)
46 elif build_tool in ['msvs', 'vs', 'ib']: 55 elif build_tool in ['msvs', 'vs', 'ib']:
Michael Achenbach 2015/02/24 15:30:27 This is different to chromium's landmines script w
47 ret = os.path.join(SRC_DIR, 'build', target) 56 ret = os.path.join(SRC_DIR, 'build')
48 else: 57 else:
49 raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool) 58 raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool)
50 return os.path.abspath(ret) 59 return os.path.abspath(ret)
51 60
52 61
53 def set_up_landmines(target, new_landmines): 62 def extract_gn_build_commands(build_ninja_file):
54 """Does the work of setting, planting, and triggering landmines.""" 63 """Extracts from a build.ninja the commands to run GN.
55 out_dir = get_target_build_dir(landmine_utils.builder(), target)
56 64
57 landmines_path = os.path.join(out_dir, '.landmines') 65 The commands to run GN are the gn rule and build.ninja build step at the
58 if not os.path.exists(out_dir): 66 top of the build.ninja file. We want to keep these when deleting GN builds
67 since we want to preserve the command-line flags to GN.
68
69 On error, returns the empty string."""
70 result = ""
71 with open(build_ninja_file, 'r') as f:
72 # Read until the second blank line. The first thing GN writes to the file
73 # is the "rule gn" and the second is the section for "build build.ninja",
74 # separated by blank lines.
75 num_blank_lines = 0
76 while num_blank_lines < 2:
77 line = f.readline()
78 if len(line) == 0:
79 return '' # Unexpected EOF.
80 result += line
81 if line[0] == '\n':
82 num_blank_lines = num_blank_lines + 1
83 return result
84
85 def delete_build_dir(build_dir):
86 # GN writes a build.ninja.d file. Note that not all GN builds have args.gn.
87 build_ninja_d_file = os.path.join(build_dir, 'build.ninja.d')
88 if not os.path.exists(build_ninja_d_file):
89 shutil.rmtree(build_dir)
59 return 90 return
60 91
61 if not os.path.exists(landmines_path): 92 # GN builds aren't automatically regenerated when you sync. To avoid
62 print "Landmines tracker didn't exists." 93 # messing with the GN workflow, erase everything but the args file, and
94 # write a dummy build.ninja file that will automatically rerun GN the next
95 # time Ninja is run.
96 build_ninja_file = os.path.join(build_dir, 'build.ninja')
97 build_commands = extract_gn_build_commands(build_ninja_file)
63 98
64 # FIXME(machenbach): Clobber deletes the .landmines tracker. Difficult 99 try:
65 # to know if we are right after a clobber or if it is first-time landmines 100 gn_args_file = os.path.join(build_dir, 'args.gn')
66 # deployment. Also, a landmine-triggered clobber right after a clobber is 101 with open(gn_args_file, 'r') as f:
67 # not possible. Different clobber methods for msvs, xcode and make all 102 args_contents = f.read()
68 # have different blacklists of files that are not deleted. 103 except IOError:
104 args_contents = ''
105
106 shutil.rmtree(build_dir)
107
108 # Put back the args file (if any).
109 os.mkdir(build_dir)
110 if args_contents != '':
111 with open(gn_args_file, 'w') as f:
112 f.write(args_contents)
113
114 # Write the build.ninja file sufficiently to regenerate itself.
115 with open(os.path.join(build_dir, 'build.ninja'), 'w') as f:
116 if build_commands != '':
117 f.write(build_commands)
118 else:
119 # Couldn't parse the build.ninja file, write a default thing.
120 f.write('''rule gn
121 command = gn -q gen //out/%s/
122 description = Regenerating ninja files
123
124 build build.ninja: gn
125 generator = 1
126 depfile = build.ninja.d
127 ''' % (os.path.split(build_dir)[1]))
128
129 # Write a .d file for the build which references a nonexistant file. This
130 # will make Ninja always mark the build as dirty.
131 with open(build_ninja_d_file, 'w') as f:
132 f.write('build.ninja: nonexistant_file.gn\n')
133
134
135 def clobber_if_necessary(new_landmines):
136 """Does the work of setting, planting, and triggering landmines."""
137 out_dir = get_build_dir(landmine_utils.builder())
138 landmines_path = os.path.normpath(os.path.join(out_dir, '..', '.landmines'))
139 try:
140 os.makedirs(out_dir)
141 except OSError as e:
142 if e.errno == errno.EEXIST:
143 pass
144
69 if os.path.exists(landmines_path): 145 if os.path.exists(landmines_path):
70 triggered = os.path.join(out_dir, '.landmines_triggered')
71 with open(landmines_path, 'r') as f: 146 with open(landmines_path, 'r') as f:
72 old_landmines = f.readlines() 147 old_landmines = f.readlines()
73 if old_landmines != new_landmines: 148 if old_landmines != new_landmines:
74 old_date = time.ctime(os.stat(landmines_path).st_ctime) 149 old_date = time.ctime(os.stat(landmines_path).st_ctime)
75 diff = difflib.unified_diff(old_landmines, new_landmines, 150 diff = difflib.unified_diff(old_landmines, new_landmines,
76 fromfile='old_landmines', tofile='new_landmines', 151 fromfile='old_landmines', tofile='new_landmines',
77 fromfiledate=old_date, tofiledate=time.ctime(), n=0) 152 fromfiledate=old_date, tofiledate=time.ctime(), n=0)
153 sys.stdout.write('Clobbering due to:\n')
154 sys.stdout.writelines(diff)
78 155
79 with open(triggered, 'w') as f: 156 # Clobber contents of build directory but not directory itself: some
80 f.writelines(diff) 157 # checkouts have the build directory mounted.
81 print "Setting landmine: %s" % triggered 158 for f in os.listdir(out_dir):
82 elif os.path.exists(triggered): 159 path = os.path.join(out_dir, f)
83 # Remove false triggered landmines. 160 # Soft version of chromium's clobber. Only delete directories not files
84 os.remove(triggered) 161 # as e.g. on windows the output dir is the build dir that shares some
85 print "Removing landmine: %s" % triggered 162 # checked out files.
163 if os.path.isdir(path) and re.search(r"(?:[Rr]elease)|(?:[Dd]ebug)", f):
Michael Achenbach 2015/02/24 15:30:27 This is different to chromium's landmines script.
jochen (gone - plz use gerrit) 2015/02/24 15:45:22 what about optdebug for developer checkouts?
Michael Achenbach 2015/02/24 16:29:32 re.search should return true for that as well. It'
164 delete_build_dir(path)
165
166 # Save current set of landmines for next time.
86 with open(landmines_path, 'w') as f: 167 with open(landmines_path, 'w') as f:
87 f.writelines(new_landmines) 168 f.writelines(new_landmines)
88 169
89 170
90 def process_options(): 171 def process_options():
91 """Returns a list of landmine emitting scripts.""" 172 """Returns a list of landmine emitting scripts."""
92 parser = optparse.OptionParser() 173 parser = optparse.OptionParser()
93 parser.add_option( 174 parser.add_option(
94 '-s', '--landmine-scripts', action='append', 175 '-s', '--landmine-scripts', action='append',
95 default=[os.path.join(SRC_DIR, 'build', 'get_landmines.py')], 176 default=[os.path.join(SRC_DIR, 'build', 'get_landmines.py')],
(...skipping 20 matching lines...) Expand all
116 else: 197 else:
117 return options.landmine_scripts 198 return options.landmine_scripts
118 199
119 200
120 def main(): 201 def main():
121 landmine_scripts = process_options() 202 landmine_scripts = process_options()
122 203
123 if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'): 204 if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'):
124 return 0 205 return 0
125 206
207 gyp_environment.set_environment()
208
126 landmines = [] 209 landmines = []
127 for s in landmine_scripts: 210 for s in landmine_scripts:
128 proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE) 211 proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE)
129 output, _ = proc.communicate() 212 output, _ = proc.communicate()
130 landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()]) 213 landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()])
131 214 clobber_if_necessary(landmines)
132 for target in ('Debug', 'Release'):
133 set_up_landmines(target, landmines)
134 215
135 return 0 216 return 0
136 217
137 218
138 if __name__ == '__main__': 219 if __name__ == '__main__':
139 sys.exit(main()) 220 sys.exit(main())
OLDNEW
« no previous file with comments | « build/landmine_utils.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698