| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium 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 import json | |
| 7 import optparse | 6 import optparse |
| 8 import os | 7 import os |
| 9 import subprocess | 8 import subprocess |
| 10 import sys | 9 import sys |
| 11 | 10 |
| 12 BUILD_ROOT = os.path.dirname(os.path.dirname(os.path.dirname( | 11 BUILD_ROOT = os.path.dirname(os.path.dirname(os.path.dirname( |
| 13 os.path.abspath(__file__)))) | 12 os.path.abspath(__file__)))) |
| 14 sys.path.append(os.path.join(BUILD_ROOT, 'scripts')) | 13 sys.path.append(os.path.join(BUILD_ROOT, 'scripts')) |
| 15 sys.path.append(os.path.join(BUILD_ROOT, 'third_party')) | 14 sys.path.append(os.path.join(BUILD_ROOT, 'third_party')) |
| 16 | 15 |
| 17 from common import annotator | 16 from common import annotator |
| 18 from common import chromium_utils | 17 from common import chromium_utils |
| 19 from common import master_cfg_utils | |
| 20 from slave import recipe_universe | 18 from slave import recipe_universe |
| 21 | 19 |
| 22 from recipe_engine import main as recipe_main | 20 from recipe_engine import main as recipe_main |
| 23 | 21 |
| 24 | 22 |
| 25 def get_recipe_properties(factory_properties, build_properties, | 23 def get_recipe_properties(factory_properties, build_properties): |
| 26 master_overrides_slave): | |
| 27 """Constructs the recipe's properties from buildbot's properties. | 24 """Constructs the recipe's properties from buildbot's properties. |
| 28 | 25 |
| 29 This retrieves the current factory properties from the master_config | 26 This merges factory_properties and build_properties. Furthermore, it |
| 30 in the slave's checkout (the factory properties handed to us from the | 27 tries to reconstruct the 'recipe' property from builders.pyl if it isn't |
| 31 master might be out of date), and merges in the build properties. | 28 already there, and in that case merges in properties form builders.pyl. |
| 29 """ |
| 30 properties = factory_properties.copy() |
| 31 properties.update(build_properties) |
| 32 | 32 |
| 33 Using the values from the checkout allows us to do things like change | 33 # Try to reconstruct the recipe from builders.pyl if not given. |
| 34 the recipe and other factory properties for a builder without needing | 34 if 'recipe' not in properties: |
| 35 a master restart. | 35 mastername = properties['mastername'] |
| 36 """ | 36 buildername = properties['buildername'] |
| 37 master_properties = factory_properties.copy() | |
| 38 master_properties.update(build_properties) | |
| 39 | 37 |
| 40 mastername = master_properties['mastername'] | 38 master_path = chromium_utils.MasterPath(mastername) |
| 41 buildername = master_properties['buildername'] | 39 builders_file = os.path.join(master_path, 'builders.pyl') |
| 42 if mastername and buildername: | 40 if os.path.isfile(builders_file): |
| 43 slave_properties = get_factory_properties_from_disk(mastername, buildername) | 41 builders = chromium_utils.ReadBuildersFile(builders_file) |
| 44 else: | 42 assert buildername in builders['builders'], ( |
| 45 slave_properties = {} | 43 'buildername %s is not listed in %s' % (buildername, builders_file)) |
| 44 builder = builders['builders'][buildername] |
| 46 | 45 |
| 47 properties = master_properties.copy() | 46 # Update properties with builders.pyl data. |
| 48 conflicting_properties = {} | 47 properties['recipe'] = builder['recipe'] |
| 49 for name in slave_properties: | 48 properties.update(builder.get('properties', {})) |
| 50 if master_properties.get(name) != slave_properties[name]: | 49 else: |
| 51 conflicting_properties[name] = (master_properties.get(name), | 50 raise LookupError('Cannot find recipe for %s on %s' % |
| 52 slave_properties[name]) | 51 (build_properties['buildername'], |
| 53 | 52 build_properties['mastername'])) |
| 54 if conflicting_properties: | |
| 55 print >> sys.stderr, ( | |
| 56 'The following build properties differ between master and slave:') | |
| 57 for name, (master_value, slave_value) in conflicting_properties.items(): | |
| 58 print >> sys.stderr, ' "%s": master: "%s", slave: "%s"' % ( | |
| 59 name, | |
| 60 "<unset>" if (master_value is None) else master_value, | |
| 61 slave_value) | |
| 62 print >> sys.stderr, ("Using the values from the %s." % | |
| 63 ("master" if master_overrides_slave else "slave")) | |
| 64 | |
| 65 if not master_overrides_slave: | |
| 66 for name, (_, slave_value) in conflicting_properties.items(): | |
| 67 properties[name] = slave_value | |
| 68 | |
| 69 return properties | 53 return properties |
| 70 | 54 |
| 71 | 55 |
| 72 def get_factory_properties_from_disk(mastername, buildername): | |
| 73 master_list = master_cfg_utils.GetMasters() | |
| 74 master_path = None | |
| 75 for name, path in master_list: | |
| 76 if name == mastername: | |
| 77 master_path = path | |
| 78 | |
| 79 if not master_path: | |
| 80 raise LookupError('master "%s" not found.' % mastername) | |
| 81 | |
| 82 script_path = os.path.join(BUILD_ROOT, 'scripts', 'tools', | |
| 83 'dump_master_cfg.py') | |
| 84 dump_cmd = [sys.executable, | |
| 85 script_path, | |
| 86 master_path, '-'] | |
| 87 proc = subprocess.Popen(dump_cmd, cwd=BUILD_ROOT, stdout=subprocess.PIPE) | |
| 88 out, _ = proc.communicate() | |
| 89 exit_code = proc.returncode | |
| 90 | |
| 91 if exit_code: | |
| 92 raise LookupError('Failed to get the master config; dump_master_cfg %s' | |
| 93 'returned %d):\n%s\n'% ( | |
| 94 mastername, exit_code, out)) | |
| 95 | |
| 96 config = json.loads(out) | |
| 97 | |
| 98 # Now extract just the factory properties for the requested builder | |
| 99 # from the master config. | |
| 100 props = {} | |
| 101 for builder_dict in config['builders']: | |
| 102 if builder_dict['name'] == buildername: | |
| 103 factory_properties = builder_dict['factory']['properties'] | |
| 104 for name, (value, _) in factory_properties.items(): | |
| 105 props[name] = value | |
| 106 | |
| 107 if not props: | |
| 108 raise LookupError('builder "%s" not found on in master "%s"' % | |
| 109 (buildername, mastername)) | |
| 110 | |
| 111 if 'recipe' not in props: | |
| 112 raise LookupError('Cannot find recipe for %s on %s' % | |
| 113 (buildername, mastername)) | |
| 114 | |
| 115 return props | |
| 116 | |
| 117 | |
| 118 def get_args(argv): | 56 def get_args(argv): |
| 119 """Process command-line arguments.""" | 57 """Process command-line arguments.""" |
| 120 | 58 |
| 121 parser = optparse.OptionParser( | 59 parser = optparse.OptionParser( |
| 122 description='Entry point for annotated builds.') | 60 description='Entry point for annotated builds.') |
| 123 parser.add_option('--build-properties', | 61 parser.add_option('--build-properties', |
| 124 action='callback', callback=chromium_utils.convert_json, | 62 action='callback', callback=chromium_utils.convert_json, |
| 125 type='string', default={}, | 63 type='string', default={}, |
| 126 help='build properties in JSON format') | 64 help='build properties in JSON format') |
| 127 parser.add_option('--factory-properties', | 65 parser.add_option('--factory-properties', |
| 128 action='callback', callback=chromium_utils.convert_json, | 66 action='callback', callback=chromium_utils.convert_json, |
| 129 type='string', default={}, | 67 type='string', default={}, |
| 130 help='factory properties in JSON format') | 68 help='factory properties in JSON format') |
| 131 parser.add_option('--build-properties-gz', | 69 parser.add_option('--build-properties-gz', |
| 132 action='callback', callback=chromium_utils.convert_gz_json, | 70 action='callback', callback=chromium_utils.convert_gz_json, |
| 133 type='string', default={}, dest='build_properties', | 71 type='string', default={}, dest='build_properties', |
| 134 help='build properties in b64 gz JSON format') | 72 help='build properties in b64 gz JSON format') |
| 135 parser.add_option('--factory-properties-gz', | 73 parser.add_option('--factory-properties-gz', |
| 136 action='callback', callback=chromium_utils.convert_gz_json, | 74 action='callback', callback=chromium_utils.convert_gz_json, |
| 137 type='string', default={}, dest='factory_properties', | 75 type='string', default={}, dest='factory_properties', |
| 138 help='factory properties in b64 gz JSON format') | 76 help='factory properties in b64 gz JSON format') |
| 139 parser.add_option('--keep-stdin', action='store_true', default=False, | 77 parser.add_option('--keep-stdin', action='store_true', default=False, |
| 140 help='don\'t close stdin when running recipe steps') | 78 help='don\'t close stdin when running recipe steps') |
| 141 parser.add_option('--master-overrides-slave', action='store_true', | |
| 142 help='use the property values given on the command line ' | |
| 143 'from the master, not the ones looked up on the slave') | |
| 144 return parser.parse_args(argv) | 79 return parser.parse_args(argv) |
| 145 | 80 |
| 146 | 81 |
| 147 def update_scripts(): | 82 def update_scripts(): |
| 148 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): | 83 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): |
| 149 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') | 84 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') |
| 150 return False | 85 return False |
| 151 | 86 |
| 152 stream = annotator.StructuredAnnotationStream() | 87 stream = annotator.StructuredAnnotationStream() |
| 153 | 88 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 171 # After running update scripts, set PYTHONIOENCODING=UTF-8 for the real | 106 # After running update scripts, set PYTHONIOENCODING=UTF-8 for the real |
| 172 # annotated_run. | 107 # annotated_run. |
| 173 os.environ['PYTHONIOENCODING'] = 'UTF-8' | 108 os.environ['PYTHONIOENCODING'] = 'UTF-8' |
| 174 | 109 |
| 175 return True | 110 return True |
| 176 | 111 |
| 177 | 112 |
| 178 def main(argv): | 113 def main(argv): |
| 179 opts, _ = get_args(argv) | 114 opts, _ = get_args(argv) |
| 180 properties = get_recipe_properties( | 115 properties = get_recipe_properties( |
| 181 opts.factory_properties, opts.build_properties, | 116 opts.factory_properties, opts.build_properties) |
| 182 opts.master_overrides_slave) | |
| 183 stream = annotator.StructuredAnnotationStream() | 117 stream = annotator.StructuredAnnotationStream() |
| 184 ret = recipe_main.run_steps(properties, stream, | 118 ret = recipe_main.run_steps(properties, stream, |
| 185 universe=recipe_universe.get_universe()) | 119 universe=recipe_universe.get_universe()) |
| 186 return ret.status_code | 120 return ret.status_code |
| 187 | 121 |
| 188 | 122 |
| 189 def shell_main(argv): | 123 def shell_main(argv): |
| 190 if update_scripts(): | 124 if update_scripts(): |
| 191 return subprocess.call([sys.executable] + argv) | 125 return subprocess.call([sys.executable] + argv) |
| 192 else: | 126 else: |
| 193 return main(argv) | 127 return main(argv) |
| 194 | 128 |
| 195 if __name__ == '__main__': | 129 if __name__ == '__main__': |
| 196 sys.exit(shell_main(sys.argv)) | 130 sys.exit(shell_main(sys.argv)) |
| OLD | NEW |