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

Side by Side Diff: build/landmines.py

Issue 1848553003: [gn] Move build to gypfiles (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Preserve old state for the dance Created 4 years, 7 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') | build/mac/asan.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
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
4 # found in the LICENSE file.
5
6 """
7 This script runs every build as the first hook (See DEPS). If it detects that
8 the build should be clobbered, it will delete the contents of the build
9 directory.
10
11 A landmine is tripped when a builder checks out a different revision, and the
12 diff between the new landmines and the old ones is non-null. At this point, the
13 build is clobbered.
14 """
15
16 import difflib
17 import errno
18 import gyp_environment
19 import logging
20 import optparse
21 import os
22 import re
23 import shutil
24 import sys
25 import subprocess
26 import time
27
28 import landmine_utils
29
30
31 SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
32
33
34 def get_build_dir(build_tool, is_iphone=False):
35 """
36 Returns output directory absolute path dependent on build and targets.
37 Examples:
38 r'c:\b\build\slave\win\build\src\out'
39 '/mnt/data/b/build/slave/linux/build/src/out'
40 '/b/build/slave/ios_rel_device/build/src/xcodebuild'
41
42 Keep this function in sync with tools/build/scripts/slave/compile.py
43 """
44 ret = None
45 if build_tool == 'xcode':
46 ret = os.path.join(SRC_DIR, 'xcodebuild')
47 elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios.
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)
55 elif build_tool in ['msvs', 'vs', 'ib']:
56 ret = os.path.join(SRC_DIR, 'build')
57 else:
58 raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool)
59 return os.path.abspath(ret)
60
61
62 def extract_gn_build_commands(build_ninja_file):
63 """Extracts from a build.ninja the commands to run GN.
64
65 The commands to run GN are the gn rule and build.ninja build step at the
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)
90 return
91
92 # GN builds aren't automatically regenerated when you sync. To avoid
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)
98
99 try:
100 gn_args_file = os.path.join(build_dir, 'args.gn')
101 with open(gn_args_file, 'r') as f:
102 args_contents = f.read()
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 needs_clobber(landmines_path, new_landmines):
136 if os.path.exists(landmines_path):
137 with open(landmines_path, 'r') as f:
138 old_landmines = f.readlines()
139 if old_landmines != new_landmines:
140 old_date = time.ctime(os.stat(landmines_path).st_ctime)
141 diff = difflib.unified_diff(old_landmines, new_landmines,
142 fromfile='old_landmines', tofile='new_landmines',
143 fromfiledate=old_date, tofiledate=time.ctime(), n=0)
144 sys.stdout.write('Clobbering due to:\n')
145 sys.stdout.writelines(diff)
146 return True
147 else:
148 sys.stdout.write('Clobbering due to missing landmines file.\n')
149 return True
150 return False
151
152
153 def clobber_if_necessary(new_landmines):
154 """Does the work of setting, planting, and triggering landmines."""
155 out_dir = get_build_dir(landmine_utils.builder())
156 landmines_path = os.path.normpath(os.path.join(out_dir, '..', '.landmines'))
157 try:
158 os.makedirs(out_dir)
159 except OSError as e:
160 if e.errno == errno.EEXIST:
161 pass
162
163 if needs_clobber(landmines_path, new_landmines):
164 # Clobber contents of build directory but not directory itself: some
165 # checkouts have the build directory mounted.
166 for f in os.listdir(out_dir):
167 path = os.path.join(out_dir, f)
168 if os.path.basename(out_dir) == 'build':
169 # Only delete build directories and files for MSVS builds as the folder
170 # shares some checked out files and directories.
171 if (os.path.isdir(path) and
172 re.search(r'(?:[Rr]elease)|(?:[Dd]ebug)', f)):
173 delete_build_dir(path)
174 elif (os.path.isfile(path) and
175 (path.endswith('.sln') or
176 path.endswith('.vcxproj') or
177 path.endswith('.vcxproj.user'))):
178 os.unlink(path)
179 else:
180 if os.path.isfile(path):
181 os.unlink(path)
182 elif os.path.isdir(path):
183 delete_build_dir(path)
184 if os.path.basename(out_dir) == 'xcodebuild':
185 # Xcodebuild puts an additional project file structure into build,
186 # while the output folder is xcodebuild.
187 project_dir = os.path.join(SRC_DIR, 'build', 'all.xcodeproj')
188 if os.path.exists(project_dir) and os.path.isdir(project_dir):
189 delete_build_dir(project_dir)
190
191 # Save current set of landmines for next time.
192 with open(landmines_path, 'w') as f:
193 f.writelines(new_landmines)
194
195
196 def process_options():
197 """Returns a list of landmine emitting scripts."""
198 parser = optparse.OptionParser()
199 parser.add_option(
200 '-s', '--landmine-scripts', action='append',
201 default=[os.path.join(SRC_DIR, 'build', 'get_landmines.py')],
202 help='Path to the script which emits landmines to stdout. The target '
203 'is passed to this script via option -t. Note that an extra '
204 'script can be specified via an env var EXTRA_LANDMINES_SCRIPT.')
205 parser.add_option('-v', '--verbose', action='store_true',
206 default=('LANDMINES_VERBOSE' in os.environ),
207 help=('Emit some extra debugging information (default off). This option '
208 'is also enabled by the presence of a LANDMINES_VERBOSE environment '
209 'variable.'))
210
211 options, args = parser.parse_args()
212
213 if args:
214 parser.error('Unknown arguments %s' % args)
215
216 logging.basicConfig(
217 level=logging.DEBUG if options.verbose else logging.ERROR)
218
219 extra_script = os.environ.get('EXTRA_LANDMINES_SCRIPT')
220 if extra_script:
221 return options.landmine_scripts + [extra_script]
222 else:
223 return options.landmine_scripts
224
225
226 def main():
227 landmine_scripts = process_options()
228
229 if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'):
230 return 0
231
232 gyp_environment.set_environment()
233
234 landmines = []
235 for s in landmine_scripts:
236 proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE)
237 output, _ = proc.communicate()
238 landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()])
239 clobber_if_necessary(landmines)
240
241 return 0
242
243
244 if __name__ == '__main__':
245 sys.exit(main())
OLDNEW
« no previous file with comments | « build/landmine_utils.py ('k') | build/mac/asan.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698