Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 cgi | 5 import cgi |
| 6 import re | 6 import re |
| 7 | 7 |
| 8 from recipe_engine import recipe_api | 8 from recipe_engine import recipe_api |
| 9 | 9 |
| 10 | 10 |
| 11 class Config(object): | |
| 12 _NONE = object() | |
| 13 | |
| 14 def __init__(self, name, *layers): | |
| 15 self.name = name | |
| 16 self.layers = layers | |
| 17 | |
| 18 def get(self, key, d=None): | |
| 19 for l in self.layers: | |
| 20 v = l.get(key, self._NONE) | |
| 21 if v is not self._NONE: | |
| 22 return v | |
| 23 return d | |
| 24 | |
| 25 def __getitem__(self, key): | |
| 26 v = self.get(key, self._NONE) | |
| 27 if v is not self._NONE: | |
| 28 return v | |
| 29 raise KeyError('Invalid configuration key: %s' % (key,)) | |
| 30 | |
| 31 def dict(self): | |
| 32 all_keys = set() | |
| 33 for l in self.layers: | |
| 34 all_keys.update(l.iterkeys()) | |
| 35 return {k: self.get(k) for k in all_keys} | |
|
iannucci
2015/09/29 19:17:30
hm... this seems potentially much less efficient t
dnj
2015/09/29 19:28:40
Done.
| |
| 36 | |
| 37 | |
| 11 class ChromiteApi(recipe_api.RecipeApi): | 38 class ChromiteApi(recipe_api.RecipeApi): |
| 12 chromite_url = 'https://chromium.googlesource.com/chromiumos/chromite.git' | 39 chromite_url = 'https://chromium.googlesource.com/chromiumos/chromite.git' |
| 13 manifest_url = 'https://chromium.googlesource.com/chromiumos/manifest.git' | 40 manifest_url = 'https://chromium.googlesource.com/chromiumos/manifest.git' |
| 14 repo_url = 'https://chromium.googlesource.com/external/repo.git' | 41 repo_url = 'https://chromium.googlesource.com/external/repo.git' |
| 15 chromite_subpath = 'chromite' | 42 |
| 43 _chromite_subpath = 'chromite' | |
| 16 | 44 |
| 17 # The number of Gitiles attempts to make before giving up. | 45 # The number of Gitiles attempts to make before giving up. |
| 18 _GITILES_ATTEMPTS = 10 | 46 _GITILES_ATTEMPTS = 10 |
| 19 | 47 |
| 20 _MANIFEST_CMD_RE = re.compile(r'Automatic:\s+Start\s+([^\s]+)\s+([^\s]+)') | 48 _MANIFEST_CMD_RE = re.compile(r'Automatic:\s+Start\s+([^\s]+)\s+([^\s]+)') |
| 21 _BUILD_ID_RE = re.compile(r'CrOS-Build-Id: (.+)') | 49 _BUILD_ID_RE = re.compile(r'CrOS-Build-Id: (.+)') |
| 22 | 50 |
| 51 _cached_config = None | |
| 52 | |
| 53 @property | |
| 54 def chromite_path(self): | |
| 55 v = self.m.path.c.dynamic_paths.get('chromite') | |
| 56 if v: | |
| 57 return v | |
| 58 return self.m.path['slave_build'].join(self._chromite_subpath) | |
| 59 | |
| 60 def _set_chromite_path(self, path): | |
| 61 self.m.path.c.dynamic_paths['chromite'] = path | |
| 62 | |
| 23 def get_config_defaults(self): | 63 def get_config_defaults(self): |
| 24 defaults = { | 64 defaults = { |
| 25 'CBB_CONFIG': self.m.properties.get('cbb_config'), | 65 'CBB_CONFIG': self.m.properties.get('cbb_config'), |
| 26 'CBB_BRANCH': self.m.properties.get('cbb_branch'), | 66 'CBB_BRANCH': self.m.properties.get('cbb_branch'), |
| 27 'CBB_DEBUG': self.m.properties.get('cbb_debug') is not None, | 67 'CBB_DEBUG': self.m.properties.get('cbb_debug') is not None, |
| 28 'CBB_CLOBBER': 'clobber' in self.m.properties, | 68 'CBB_CLOBBER': 'clobber' in self.m.properties, |
| 29 } | 69 } |
| 30 if 'buildnumber' in self.m.properties: | 70 if 'buildnumber' in self.m.properties: |
| 31 defaults['CBB_BUILD_NUMBER'] = int(self.m.properties['buildnumber']) | 71 defaults['CBB_BUILD_NUMBER'] = int(self.m.properties['buildnumber']) |
| 32 return defaults | 72 return defaults |
| 33 | 73 |
| 74 def _load_config_dump(self): | |
| 75 if not self._cached_config: | |
| 76 config_path = self.m.path.join(self.chromite_path, | |
| 77 'cbuildbot', 'config_dump.json') | |
| 78 step_result = self.m.json.read('read chromite config', config_path, | |
| 79 add_json_log=False) | |
| 80 self._cached_config = step_result.json.output | |
|
iannucci
2015/09/29 19:17:30
ugh, really? json.read should be changed to return
dnj
2015/09/29 19:28:40
Acknowledged.
| |
| 81 return self._cached_config | |
| 82 | |
| 83 def load_config(self, name): | |
| 84 c = self._load_config_dump() | |
| 85 conf = c.get(name) | |
| 86 if conf is None: | |
| 87 return None | |
| 88 | |
| 89 layers = [conf] | |
| 90 template = conf.get('_template') | |
| 91 if template: | |
| 92 layers.append(c['_templates'][template]) | |
| 93 default = c.get('_default') | |
| 94 if default: | |
| 95 layers.append(default) | |
| 96 | |
| 97 config = Config(name, *layers) | |
| 98 | |
| 99 presentation_dict = {name: config.dict()} | |
| 100 self.m.step.active_result.presentation.logs['config'] = [ | |
| 101 self.m.json.dumps(presentation_dict, indent=2), | |
| 102 ] | |
| 103 return config | |
| 104 | |
| 34 def check_repository(self, repo_type_key, value): | 105 def check_repository(self, repo_type_key, value): |
| 35 """Scans through registered repositories for a specified value. | 106 """Scans through registered repositories for a specified value. |
| 36 | 107 |
| 37 Args: | 108 Args: |
| 38 repo_type_key (str): The key in the 'repositories' config to scan through. | 109 repo_type_key (str): The key in the 'repositories' config to scan through. |
| 39 value (str): The value to scan for. | 110 value (str): The value to scan for. |
| 40 Returns (bool): True if the value was found. | 111 Returns (bool): True if the value was found. |
| 41 """ | 112 """ |
| 42 def remove_tail(v, tail): | 113 def remove_tail(v, tail): |
| 43 if v.endswith(tail): | 114 if v.endswith(tail): |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 83 # Build ID? | 154 # Build ID? |
| 84 match = self._BUILD_ID_RE.match(line) | 155 match = self._BUILD_ID_RE.match(line) |
| 85 if match: | 156 if match: |
| 86 self.c.cbb.build_id = match.group(1) | 157 self.c.cbb.build_id = match.group(1) |
| 87 loaded.append('Build ID: %s' % (self.c.cbb.build_id,)) | 158 loaded.append('Build ID: %s' % (self.c.cbb.build_id,)) |
| 88 continue | 159 continue |
| 89 if loaded: | 160 if loaded: |
| 90 loaded.insert(0, '') | 161 loaded.insert(0, '') |
| 91 result.presentation.step_text += '<br/>'.join(loaded) | 162 result.presentation.step_text += '<br/>'.join(loaded) |
| 92 | 163 |
| 93 @property | |
| 94 def default_chromite_path(self): | |
| 95 """Returns: (Path) The default Chromite checkout path.""" | |
| 96 return self.m.path['slave_build'].join(self.chromite_subpath) | |
| 97 | |
| 98 def gclient_config(self): | 164 def gclient_config(self): |
| 99 """Generate a 'gclient' configuration to check out Chromite. | 165 """Generate a 'gclient' configuration to check out Chromite. |
| 100 | 166 |
| 101 Return: (config) A 'gclient' recipe module configuration. | 167 Return: (config) A 'gclient' recipe module configuration. |
| 102 """ | 168 """ |
| 103 cfg = self.m.gclient.make_config() | 169 cfg = self.m.gclient.make_config() |
| 104 soln = cfg.solutions.add() | 170 soln = cfg.solutions.add() |
| 105 soln.name = 'chromite' | 171 soln.name = 'chromite' |
| 106 soln.url = self.chromite_url | 172 soln.url = self.chromite_url |
| 107 # Set the revision using 'bot_update' remote branch:revision notation. | 173 # Set the revision using 'bot_update' remote branch:revision notation. |
| 108 # Omitting the revision uses HEAD. | 174 # Omitting the revision uses HEAD. |
| 109 soln.revision = '%s:' % (self.c.chromite_branch,) | 175 soln.revision = '%s:' % (self.c.chromite_branch,) |
| 110 return cfg | 176 return cfg |
| 111 | 177 |
| 112 def checkout(self, manifest_url=None, repo_url=None): | 178 def checkout(self, manifest_url=None, repo_url=None): |
| 113 manifest_url = manifest_url or self.manifest_url | 179 manifest_url = manifest_url or self.manifest_url |
| 114 repo_url = repo_url or self.repo_url | 180 repo_url = repo_url or self.repo_url |
| 115 | 181 |
| 116 self.m.repo.init(manifest_url, '--repo-url', repo_url) | 182 self.m.repo.init(manifest_url, '--repo-url', repo_url) |
| 117 self.m.repo.sync() | 183 self.m.repo.sync() |
| 118 | 184 |
| 119 @property | 185 @property |
| 120 def using_old_chromite_layout(self): | 186 def using_old_chromite_layout(self): |
| 121 """Returns (bool): True if we're using old Chromite checkout layout. | 187 """Returns (bool): True if we're using old Chromite checkout layout. |
| 122 """ | 188 """ |
| 123 return self.c.chromite_branch in self.c.old_chromite_branches | 189 return self.c.chromite_branch in self.c.old_chromite_branches |
| 124 | 190 |
| 125 def cbuildbot(self, name, config, args=None, chromite_path=None, **kwargs): | 191 def cbuildbot(self, name, config, args=None, **kwargs): |
| 126 """Runs the cbuildbot command defined by the arguments. | 192 """Runs the cbuildbot command defined by the arguments. |
| 127 | 193 |
| 128 Args: | 194 Args: |
| 129 name: (str) The name of the command step. | 195 name: (str) The name of the command step. |
| 130 config: (str) The name of the 'cbuildbot' configuration to invoke. | 196 config: (str) The name of the 'cbuildbot' configuration to invoke. |
| 131 args: (list) If not None, addition arguments to pass to 'cbuildbot'. | 197 args: (list) If not None, addition arguments to pass to 'cbuildbot'. |
| 132 chromite_path: (str) The path to the Chromite checkout; if None, the | |
| 133 'default_chromite_path' will be used. | |
| 134 | 198 |
| 135 Returns: (Step) The step that was run. | 199 Returns: (Step) The step that was run. |
| 136 """ | 200 """ |
| 137 chromite_path = chromite_path or self.default_chromite_path | |
| 138 args = (args or [])[:] | 201 args = (args or [])[:] |
| 139 args.append(config) | 202 args.append(config) |
| 140 | 203 |
| 141 bindir = 'bin' | 204 bindir = 'bin' |
| 142 if self.using_old_chromite_layout: | 205 if self.using_old_chromite_layout: |
| 143 bindir = 'buildbot' | 206 bindir = 'buildbot' |
| 144 cmd = [self.m.path.join(chromite_path, bindir, 'cbuildbot')] + args | 207 cmd = [self.chromite_path.join(bindir, 'cbuildbot')] + args |
| 145 return self.m.step(name, cmd, allow_subannotations=True, **kwargs) | 208 return self.m.step(name, cmd, allow_subannotations=True, **kwargs) |
| 146 | 209 |
| 147 def cros_sdk(self, name, cmd, args=None, environ=None, chromite_path=None, | 210 def cros_sdk(self, name, cmd, args=None, environ=None, **kwargs): |
| 148 **kwargs): | |
| 149 """Return a step to run a command inside the cros_sdk.""" | 211 """Return a step to run a command inside the cros_sdk.""" |
| 150 chromite_path = chromite_path or self.default_chromite_path | 212 chroot_cmd = self.chromite_path.join('bin', 'cros_sdk') |
| 151 | |
| 152 chroot_cmd = self.m.path.join(chromite_path, 'bin', 'cros_sdk') | |
| 153 | 213 |
| 154 arg_list = (args or [])[:] | 214 arg_list = (args or [])[:] |
| 155 for t in sorted((environ or {}).items()): | 215 for t in sorted((environ or {}).items()): |
| 156 arg_list.append('%s=%s' % t) | 216 arg_list.append('%s=%s' % t) |
| 157 arg_list.append('--') | 217 arg_list.append('--') |
| 158 arg_list.extend(cmd) | 218 arg_list.extend(cmd) |
| 159 | 219 |
| 160 self.m.python(name, chroot_cmd, arg_list, **kwargs) | 220 self.m.python(name, chroot_cmd, arg_list, **kwargs) |
| 161 | 221 |
| 162 def setup_board(self, board, args=None, **kwargs): | 222 def setup_board(self, board, args=None, **kwargs): |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 192 if variant: | 252 if variant: |
| 193 for config_name in config_map.get('variants', {}).get(variant, ()): | 253 for config_name in config_map.get('variants', {}).get(variant, ()): |
| 194 self.apply_config(config_name) | 254 self.apply_config(config_name) |
| 195 | 255 |
| 196 | 256 |
| 197 # If a config repo was specified, use it. | 257 # If a config repo was specified, use it. |
| 198 if 'config_repo' in properties: | 258 if 'config_repo' in properties: |
| 199 self.c.cbb.config_repo = self.m.properties['config_repo'] | 259 self.c.cbb.config_repo = self.m.properties['config_repo'] |
| 200 | 260 |
| 201 def run_cbuildbot(self, args=[]): | 261 def run_cbuildbot(self, args=[]): |
| 202 """Runs a 'cbuildbot' checkout-and-build workflow. | 262 self.checkout_chromite() |
| 263 self.run(args=args) | |
| 264 | |
| 265 def checkout_chromite(self): | |
| 266 """Checks out the configured Chromite branch. | |
| 267 """ | |
| 268 self.m.bot_update.ensure_checkout( | |
| 269 gclient_config=self.gclient_config(), | |
| 270 update_presentation=False, | |
| 271 force=True) | |
| 272 | |
| 273 if self.c.chromite_branch and self.c.cbb.disable_bootstrap: | |
| 274 # Chromite auto-detects which branch to build for based on its current | |
| 275 # checkout. "bot_update" checks out remote branches, but Chromite requires | |
| 276 # a local branch. | |
| 277 # | |
| 278 # Normally we'd bootstrap, but if we're disabling bootstrapping, we have | |
| 279 # to checkout the local branch to let Chromite know which branch to build. | |
| 280 self.m.git('checkout', self.c.chromite_branch, | |
| 281 name=str('checkout chromite branch [%s]' % (self.c.chromite_branch))) | |
| 282 self._set_chromite_path(self.m.path['checkout']) | |
| 283 return self.chromite_path | |
| 284 | |
| 285 def run(self, args=[]): | |
| 286 """Runs the configured 'cbuildbot' build. | |
| 203 | 287 |
| 204 This workflow uses the registered configuration dictionary to make master- | 288 This workflow uses the registered configuration dictionary to make master- |
| 205 and builder-specific changes to the standard workflow. | 289 and builder-specific changes to the standard workflow. |
| 206 | 290 |
| 207 The specific workflow paths that are taken are also influenced by several | 291 The specific workflow paths that are taken are also influenced by several |
| 208 build properties. | 292 build properties. |
| 209 | 293 |
| 210 TODO(dnj): When CrOS migrates away from BuildBot, replace property | 294 TODO(dnj): When CrOS migrates away from BuildBot, replace property |
| 211 inferences with command-line parameters. | 295 inferences with command-line parameters. |
| 212 | 296 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 264 if self.c.cbb.config_repo: | 348 if self.c.cbb.config_repo: |
| 265 cbb_args.extend(['--config_repo', self.c.cbb.config_repo]) | 349 cbb_args.extend(['--config_repo', self.c.cbb.config_repo]) |
| 266 | 350 |
| 267 # Set the build ID, if specified. | 351 # Set the build ID, if specified. |
| 268 if self.c.cbb.build_id: | 352 if self.c.cbb.build_id: |
| 269 cbb_args.extend(['--master-build-id', self.c.cbb.build_id]) | 353 cbb_args.extend(['--master-build-id', self.c.cbb.build_id]) |
| 270 | 354 |
| 271 # Add custom args, if there are any. | 355 # Add custom args, if there are any. |
| 272 cbb_args.extend(args) | 356 cbb_args.extend(args) |
| 273 | 357 |
| 274 # Checkout Chromite. | |
| 275 self.m.bot_update.ensure_checkout( | |
| 276 gclient_config=self.gclient_config(), | |
| 277 update_presentation=False, | |
| 278 force=True) | |
| 279 if self.c.chromite_branch and self.c.cbb.disable_bootstrap: | |
| 280 # Chromite auto-detects which branch to build for based on its current | |
| 281 # checkout. "bot_update" checks out remote branches, but Chromite requires | |
| 282 # a local branch. | |
| 283 # | |
| 284 # Normally we'd bootstrap, but if we're disabling bootstrapping, we have | |
| 285 # to checkout the local branch to let Chromite know which branch to build. | |
| 286 self.m.git('checkout', self.c.chromite_branch, | |
| 287 name=str('checkout chromite branch [%s]' % (self.c.chromite_branch))) | |
| 288 | |
| 289 # Run cbuildbot. | 358 # Run cbuildbot. |
| 290 return self.cbuildbot(str('cbuildbot [%s]' % (self.c.cbb.config,)), | 359 return self.cbuildbot(str('cbuildbot [%s]' % (self.c.cbb.config,)), |
| 291 self.c.cbb.config, | 360 self.c.cbb.config, |
| 292 args=cbb_args, | 361 args=cbb_args, |
| 293 chromite_path=self.m.path['checkout'], | |
| 294 cwd=self.m.path['slave_root']) | 362 cwd=self.m.path['slave_root']) |
| OLD | NEW |