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

Side by Side Diff: scripts/slave/recipe_modules/v8/api.py

Issue 2127573003: V8: Get build environment from MB (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: V8: Get build environment from MB Created 4 years, 5 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 | Annotate | Revision Log
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import argparse 5 import argparse
6 import datetime 6 import datetime
7 import difflib 7 import difflib
8 import random 8 import random
9 import re 9 import re
10 import urllib 10 import urllib
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 # Default failure retry. 111 # Default failure retry.
112 self.rerun_failures_count = 2 112 self.rerun_failures_count = 2
113 113
114 # If tests are run, this value will be set to their total duration. 114 # If tests are run, this value will be set to their total duration.
115 self.test_duration_sec = 0 115 self.test_duration_sec = 0
116 116
117 # Allow overriding the isolate hashes during bisection with the ones that 117 # Allow overriding the isolate hashes during bisection with the ones that
118 # correspond to the build of a bisect step. 118 # correspond to the build of a bisect step.
119 self._isolated_tests_override = None 119 self._isolated_tests_override = None
120 120
121 # This is inferred from the run_mb step or from the parent bot. If mb is
122 # run multiple times, it is overwritten. It contains either gyp or gn
123 # properties.
124 self.build_environment = self.m.properties.get(
125 'parent_build_environment', {})
126
121 @property 127 @property
122 def isolated_tests(self): 128 def isolated_tests(self):
123 # During bisection, the isolated hashes will be updated with hashes that 129 # During bisection, the isolated hashes will be updated with hashes that
124 # correspond to the bisect step. 130 # correspond to the bisect step.
125 # TODO(machenbach): Remove pragma as soon as rerun is implemented for 131 # TODO(machenbach): Remove pragma as soon as rerun is implemented for
126 # swarming. 132 # swarming.
127 if self._isolated_tests_override is not None: # pragma: no cover 133 if self._isolated_tests_override is not None: # pragma: no cover
128 return self._isolated_tests_override 134 return self._isolated_tests_override
129 return self.m.isolate.isolated_tests 135 return self.m.isolate.isolated_tests
130 136
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 build_dir=gn_build_dir, 301 build_dir=gn_build_dir,
296 ok_ret='any', 302 ok_ret='any',
297 ) 303 )
298 self.m.python( 304 self.m.python(
299 'compare build flags (fyi)', 305 'compare build flags (fyi)',
300 self.m.path['checkout'].join('tools', 'gyp_flag_compare.py'), 306 self.m.path['checkout'].join('tools', 'gyp_flag_compare.py'),
301 [gn_build_dir, gyp_build_dir, 'all', 'all'], 307 [gn_build_dir, gyp_build_dir, 'all', 'all'],
302 ok_ret='any', 308 ok_ret='any',
303 ) 309 )
304 310
305 @property
306 def build_environment(self):
307 if self.m.properties.get('parent_build_environment'):
308 return self.m.properties['parent_build_environment']
309 if self.bot_type == 'tester':
310 return None
311 build_environment = dict(
312 (k, v) for (k, v) in self.m.chromium.c.gyp_env.as_jsonish().iteritems()
313 if k.startswith('GYP') and v is not None
314 )
315 build_environment.update(self.c.gyp_env.as_jsonish())
316 if 'GYP_DEFINES' in build_environment:
Michael Achenbach 2016/07/05 14:36:48 This part moves to the update_build_environment me
317 # Filter out gomadir.
318 build_environment['GYP_DEFINES'] = ' '.join(
319 d for d in build_environment['GYP_DEFINES'].split()
320 if not d.startswith('gomadir')
321 )
322 return build_environment
323
324 def setup_mips_toolchain(self): 311 def setup_mips_toolchain(self):
325 mips_dir = self.m.path['slave_build'].join(MIPS_DIR, 'bin') 312 mips_dir = self.m.path['slave_build'].join(MIPS_DIR, 'bin')
326 if not self.m.path.exists(mips_dir): 313 if not self.m.path.exists(mips_dir):
327 self.m.gsutil.download_url( 314 self.m.gsutil.download_url(
328 'gs://chromium-v8/%s' % MIPS_TOOLCHAIN, 315 'gs://chromium-v8/%s' % MIPS_TOOLCHAIN,
329 self.m.path['slave_build'], 316 self.m.path['slave_build'],
330 name='bootstrapping mips toolchain') 317 name='bootstrapping mips toolchain')
331 self.m.step('unzipping', 318 self.m.step('unzipping',
332 ['tar', 'xf', MIPS_TOOLCHAIN], 319 ['tar', 'xf', MIPS_TOOLCHAIN],
333 cwd=self.m.path['slave_build']) 320 cwd=self.m.path['slave_build'])
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 if tests_to_isolate: 385 if tests_to_isolate:
399 self.m.isolate.isolate_tests( 386 self.m.isolate.isolate_tests(
400 self.m.chromium.output_dir, 387 self.m.chromium.output_dir,
401 targets=sorted(list(set(tests_to_isolate))), 388 targets=sorted(list(set(tests_to_isolate))),
402 verbose=True, 389 verbose=True,
403 set_swarm_hashes=False, 390 set_swarm_hashes=False,
404 ) 391 )
405 if self.should_upload_build: 392 if self.should_upload_build:
406 self.upload_isolated_json() 393 self.upload_isolated_json()
407 394
408 def _compare_gyp_defines(self, mb_output):
409 """Compare infra gyp flags with client gyp flags.
410 395
411 Returns the difference as a list of strings or an empty list if there is 396 def _gyp_defines_to_dict(self, gyp_defines):
Michael Achenbach 2016/07/05 14:36:48 This is just extracted one level up. No code chang
412 none. 397 # Example input: "foo=1 bar='buz bing'". We assume there's no '=' in the
398 # value part.
399 result = []
400 for x in gyp_defines.split():
401 kv = x.split('=', 1)
402 if len(kv) == 1:
403 # No '=' in x. It's part of a quoted string containing a space.
404 # Append it to the last value.
405 result[-1][1] += (' ' + kv[0])
406 else:
407 result.append(kv)
408 return dict(tuple(kv) for kv in result)
409
410 def _infer_build_environment(self, mb_output):
411 """Scans for gyp or gn properties in mb output.
412
413 Returns: dict, where key is e.g. 'GYP_DEFINES' or 'gn_args'.
413 """ 414 """
414 415 result = {}
415 def gyp_defines_to_dict(gyp_defines):
416 # Example input: "foo=1 bar='buz bing'". We assume there's no '=' in the
417 # value part.
418 result = []
419 for x in gyp_defines.split():
420 kv = x.split('=', 1)
421 if len(kv) == 1:
422 # No '=' in x. It's part of a quoted string containing a space.
423 # Append it to the last value.
424 result[-1][1] += (' ' + kv[0])
425 else:
426 result.append(kv)
427 return dict(tuple(kv) for kv in result)
428
429 infra_flags = gyp_defines_to_dict(
430 self.m.chromium.c.gyp_env.as_jsonish()['GYP_DEFINES'])
431
432 # Get the client's gyp flags from MB's output. Group 1 captures with posix, 416 # Get the client's gyp flags from MB's output. Group 1 captures with posix,
433 # group 2 with windows output semantics. 417 # group 2 with windows output semantics.
434 # 418 #
435 # Posix: 419 # Posix:
436 # GYP_DEFINES='foo=1 path=a/b/c' 420 # GYP_DEFINES='foo=1 path=a/b/c'
437 # 421 #
438 # Windows: 422 # Windows:
439 # set GYP_DEFINES=foo=1 path='a/b/c' 423 # set GYP_DEFINES=foo=1 path='a/b/c'
440 match = re.search( 424 # TODO(machenbach): Remove the gyp case after gyp is deprecated.
441 '^(?:set )?GYP_DEFINES=(?:(?:\'(.*)\')|(?:(.*)))$', mb_output, re.M) 425 for match in re.finditer(
426 '^(?:set )?GYP_([^=]*)=(?:(?:\'(.*)\')|(?:(.*)))$', mb_output, re.M):
Michael Achenbach 2016/07/05 14:36:48 Regexp is unchanged except for GYP_([^=]*). This a
427 # Yield the property name (e.g. GYP_DEFINES) and the value. Either the
428 # windows or the posix group matches.
429 result['GYP_' + match.group(1)] = match.group(2) or match.group(3)
442 430
443 # This won't match in the gn case. 431 # Check if the output looks like gn. Space-join all gn args, except
432 # goma_dir.
433 # TODO(machenbach): Instead of scanning the output, we could also read
434 # the gn.args file that was written.
435 match = re.search(r'Writing """\\?\s*(.*)""" to ', mb_output, re.S)
Michael Achenbach 2016/07/05 14:36:48 This matches e.g.: https://build.chromium.org/p/cl
444 if match: 436 if match:
445 client_flags = gyp_defines_to_dict(match.group(1) or match.group(2)) 437 result['gn_args'] = ' '.join(
438 l for l in match.group(1).strip().splitlines()
439 if not l.startswith('goma_dir'))
440
441 return result
442
443 def _compare_gyp_defines(self, mb_output):
Michael Achenbach 2016/07/05 14:36:48 This method will go away soon.
444 """Compare infra gyp flags with client gyp flags.
445
446 Returns the difference as a list of strings or an empty list if there is
447 none.
448 """
449 infra_flags = self._gyp_defines_to_dict(
450 self.m.chromium.c.gyp_env.as_jsonish()['GYP_DEFINES'])
451
452 props = self._infer_build_environment(mb_output)
453 if 'GYP_DEFINES' in props:
454 client_flags = self._gyp_defines_to_dict(props['GYP_DEFINES'])
446 455
447 # Tweak both dictionaries for known differences. 456 # Tweak both dictionaries for known differences.
448 if infra_flags.get('target_arch') == infra_flags.get('v8_target_arch'): 457 if infra_flags.get('target_arch') == infra_flags.get('v8_target_arch'):
449 # We drop the default case target_arch==v8_target_arch in MB and 458 # We drop the default case target_arch==v8_target_arch in MB and
450 # only specify target_arch. 459 # only specify target_arch.
451 infra_flags.pop('v8_target_arch') # pragma: no cover 460 infra_flags.pop('v8_target_arch') # pragma: no cover
452 461
453 if 'jsfunfuzz' in infra_flags: 462 if 'jsfunfuzz' in infra_flags:
454 # This is for runhooks only. Not used in MB. 463 # This is for runhooks only. Not used in MB.
455 infra_flags.pop('jsfunfuzz') # pragma: no cover 464 infra_flags.pop('jsfunfuzz') # pragma: no cover
456 465
457 if not 'component' in infra_flags and 'component' in client_flags: 466 if not 'component' in infra_flags and 'component' in client_flags:
458 # We make this explicit with MB but used the default without. Only 467 # We make this explicit with MB but used the default without. Only
459 # compare if we specified it explicitly in the infrastructure. 468 # compare if we specified it explicitly in the infrastructure.
460 client_flags.pop('component') # pragma: no cover 469 client_flags.pop('component') # pragma: no cover
461 470
462 if infra_flags != client_flags: 471 if infra_flags != client_flags:
463 to_str = lambda x: sorted('%s: %s' % kv for kv in x.iteritems()) 472 to_str = lambda x: sorted('%s: %s' % kv for kv in x.iteritems())
464 return list(difflib.ndiff(to_str(infra_flags), to_str(client_flags))) 473 return list(difflib.ndiff(to_str(infra_flags), to_str(client_flags)))
465 return [] # pragma: no cover 474 return [] # pragma: no cover
466 475
476 def _update_build_environment(self, mb_output):
477 build_environment = self._infer_build_environment(mb_output)
478 if 'GYP_DEFINES' in build_environment:
479 # Filter out gomadir.
480 build_environment['GYP_DEFINES'] = ' '.join(
481 d for d in build_environment['GYP_DEFINES'].split()
482 if not d.startswith('gomadir')
483 )
484 self.build_environment = build_environment
485
467 def compile(self, **kwargs): 486 def compile(self, **kwargs):
468 if self.m.chromium.c.project_generator.tool == 'mb': 487 if self.m.chromium.c.project_generator.tool == 'mb':
469 use_goma = (self.m.chromium.c.compile_py.compiler and 488 use_goma = (self.m.chromium.c.compile_py.compiler and
470 'goma' in self.m.chromium.c.compile_py.compiler) 489 'goma' in self.m.chromium.c.compile_py.compiler)
471 def step_test_data(): 490 def step_test_data():
472 # Fake gyp flags. In the expectations, the flag comparison will 491 # Fake gyp flags. In the expectations, the flag comparison will
473 # complain a lot because the fake data is different. 492 # complain a lot because the fake data is different.
474 return self.m.raw_io.test_api.stream_output( 493 return self.m.raw_io.test_api.stream_output(
475 'some line\n' 494 'some line\n'
476 'GYP_DEFINES=\'target_arch=x64 cool_flag=a=1\'\n' 495 'GYP_DEFINES=\'target_arch=x64 cool_flag=a=1\'\n'
477 'moar\n' 496 'moar\n'
478 ) 497 )
479 self.m.chromium.run_mb( 498 self.m.chromium.run_mb(
480 self.m.properties['mastername'], 499 self.m.properties['mastername'],
481 self.m.properties['buildername'], 500 self.m.properties['buildername'],
482 use_goma=use_goma, 501 use_goma=use_goma,
483 mb_config_path=self.m.path['checkout'].join( 502 mb_config_path=self.m.path['checkout'].join(
484 'infra', 'mb', 'mb_config.pyl'), 503 'infra', 'mb', 'mb_config.pyl'),
485 gyp_script=self.m.path.join('gypfiles', 'gyp_v8'), 504 gyp_script=self.m.path.join('gypfiles', 'gyp_v8'),
486 # TODO(machenbach): Remove the comparison after the mb switch and 505 # TODO(machenbach): Remove the comparison after the mb switch and
487 # once all gyp flags have been verified. 506 # once all gyp flags have been verified.
488 stdout=self.m.raw_io.output(), 507 stdout=self.m.raw_io.output(),
489 step_test_data=step_test_data, 508 step_test_data=step_test_data,
490 ) 509 )
491 # Log captured output. 510 # Log captured output.
492 self.m.step.active_result.presentation.logs['stdout'] = ( 511 self.m.step.active_result.presentation.logs['stdout'] = (
493 self.m.step.active_result.stdout.splitlines()) 512 self.m.step.active_result.stdout.splitlines())
494 513
514 # Update the build environment dictionary, which is printed to the
515 # user on test failures for easier build reproduction.
516 self._update_build_environment(self.m.step.active_result.stdout)
517
495 # Compare infra gyp flags with client gyp flags. 518 # Compare infra gyp flags with client gyp flags.
496 diff = self._compare_gyp_defines(self.m.step.active_result.stdout) 519 diff = self._compare_gyp_defines(self.m.step.active_result.stdout)
497 520
498 if diff: 521 if diff:
499 self.m.step.active_result.presentation.logs['diff'] = diff 522 self.m.step.active_result.presentation.logs['diff'] = diff
500 self.m.step.active_result.presentation.status = self.m.step.WARNING 523 self.m.step.active_result.presentation.status = self.m.step.WARNING
501 524
502 self.peek_gn() 525 self.peek_gn()
503 self.m.chromium.compile(**kwargs) 526 self.m.chromium.compile(**kwargs)
504 self.isolate_tests() 527 self.isolate_tests()
(...skipping 755 matching lines...) Expand 10 before | Expand all | Expand 10 after
1260 def report_culprits(self, culprit_range): 1283 def report_culprits(self, culprit_range):
1261 assert culprit_range 1284 assert culprit_range
1262 if len(culprit_range) > 1: 1285 if len(culprit_range) > 1:
1263 text = 'Suspecting multiple commits' 1286 text = 'Suspecting multiple commits'
1264 else: 1287 else:
1265 text = 'Suspecting %s' % culprit_range[0][:8] 1288 text = 'Suspecting %s' % culprit_range[0][:8]
1266 1289
1267 step_result = self.m.step(text, cmd=None) 1290 step_result = self.m.step(text, cmd=None)
1268 for culprit in culprit_range: 1291 for culprit in culprit_range:
1269 step_result.presentation.links[culprit[:8]] = COMMIT_TEMPLATE % culprit 1292 step_result.presentation.links[culprit[:8]] = COMMIT_TEMPLATE % culprit
OLDNEW
« no previous file with comments | « no previous file | scripts/slave/recipes/v8.py » ('j') | scripts/slave/recipes/v8.expected/full_client_v8_V8_Linux64___builder.json » ('J')

Powered by Google App Engine
This is Rietveld 408576698