blob: a84429101e069c3af7580b09a864beb4a642a71e [file] [log] [blame]
Don Garrettc4114cc2016-11-01 20:04:06 -07001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Bootstrap for cbuildbot.
6
7This script is intended to checkout chromite on the branch specified by -b or
8--branch (as normally accepted by cbuildbot), and then invoke cbuildbot. Most
9arguments are not parsed, only passed along. If a branch is not specified, this
10script will use 'master'.
11
12Among other things, this allows us to invoke build configs that exist on a given
13branch, but not on TOT.
14"""
15
16from __future__ import print_function
17
Don Garrett125d4dc2017-04-25 16:26:03 -070018import functools
Don Garrettc4114cc2016-11-01 20:04:06 -070019import os
20
21from chromite.cbuildbot import repository
Don Garrett597ddff2017-02-17 18:29:37 -080022from chromite.cbuildbot.stages import sync_stages
Don Garrett86881cb2017-02-15 15:41:55 -080023from chromite.lib import config_lib
Don Garrettc4114cc2016-11-01 20:04:06 -070024from chromite.lib import cros_build_lib
25from chromite.lib import cros_logging as logging
Don Garrettc4114cc2016-11-01 20:04:06 -070026from chromite.lib import osutils
Don Garrett86881cb2017-02-15 15:41:55 -080027from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070028
Don Garrett597ddff2017-02-17 18:29:37 -080029
Don Garrett60967922017-04-12 18:51:44 -070030# This number should be incremented when we change the layout of the buildroot
31# in a non-backwards compatible way. This wipes all buildroots.
32BUILDROOT_BUILDROOT_LAYOUT = 1
33
34
Don Garrett125d4dc2017-04-25 16:26:03 -070035def StageDecorator(functor):
36 """A Decorator that adds buildbot stage tags around a method.
37
38 It uses the method name as the stage name, and assumes failure on exception.
39 """
40 @functools.wraps(functor)
41 def wrapped_functor(*args, **kwargs):
42 try:
43 logging.PrintBuildbotStepName(functor.__name__)
44 return functor(*args, **kwargs)
45 except Exception:
46 logging.PrintBuildbotStepFailure()
47 raise
48
49 return wrapped_functor
50
51
Don Garrett86881cb2017-02-15 15:41:55 -080052def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -070053 """Extract the branch name from cbuildbot command line arguments.
54
55 Ignores all arguments, other than the branch name.
56
57 Args:
58 argv: The command line arguments to parse.
59
60 Returns:
61 Branch as a string ('master' if nothing is specified).
62 """
Don Garrett86881cb2017-02-15 15:41:55 -080063 parser = cbuildbot.CreateParser()
Don Garrett597ddff2017-02-17 18:29:37 -080064 options, args = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett86881cb2017-02-15 15:41:55 -080065
66 # This option isn't required for cbuildbot, but is for us.
67 if not options.buildroot:
68 cros_build_lib.Die('--buildroot is a required option.')
69
Don Garrett597ddff2017-02-17 18:29:37 -080070 # Save off the build targets, in a mirror of cbuildbot code.
71 options.build_targets = args
72 options.Freeze()
73
Don Garrett86881cb2017-02-15 15:41:55 -080074 return options
Don Garrettc4114cc2016-11-01 20:04:06 -070075
76
Don Garrett60967922017-04-12 18:51:44 -070077def GetBuildrootState(buildroot):
78 state_file = os.path.join(buildroot, '.cbuildbot_launch_state')
79
80 try:
81 state = osutils.ReadFile(state_file)
82 buildroot_layout, branchname = state.split()
83 buildroot_layout = int(buildroot_layout)
84 return buildroot_layout, branchname
85 except (IOError, ValueError):
86 # If we are unable to either read or parse the state file, we get here.
87 return 0, ''
88
89
90def SetBuildrootState(branchname, buildroot):
91 assert branchname
92 state_file = os.path.join(buildroot, '.cbuildbot_launch_state')
93 new_state = '%d %s' % (BUILDROOT_BUILDROOT_LAYOUT, branchname)
94 osutils.WriteFile(state_file, new_state)
95
96
Don Garrett125d4dc2017-04-25 16:26:03 -070097@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -070098def CleanBuildroot(buildroot, repo):
Don Garrett7ade05a2017-02-17 13:31:47 -080099 """Some kinds of branch transitions break builds.
100
101 This method tries to detect cases where that can happen, and clobber what's
102 needed to succeed. However, the clobbers are costly, and should be avoided
103 if necessary.
104
Don Garrett7ade05a2017-02-17 13:31:47 -0800105 Args:
Don Garrett7ade05a2017-02-17 13:31:47 -0800106 buildroot: Directory with old buildroot to clean as needed.
Don Garrettf324bc32017-05-23 14:00:53 -0700107 repo: repository.RepoRepository instance.
Don Garrett7ade05a2017-02-17 13:31:47 -0800108 """
Don Garrett60967922017-04-12 18:51:44 -0700109 old_buildroot_layout, old_branch = GetBuildrootState(buildroot)
Don Garrette17e1d92017-04-12 15:28:19 -0700110
Don Garrett60967922017-04-12 18:51:44 -0700111 if old_buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700112 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrett60967922017-04-12 18:51:44 -0700113 osutils.RmDir(buildroot, ignore_missing=True, sudo=True)
Don Garrette17e1d92017-04-12 15:28:19 -0700114
Don Garrettf324bc32017-05-23 14:00:53 -0700115 else:
116 if old_branch != repo.branch:
117 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
118 logging.info('Unmatched branch: %s -> %s', old_branch, repo.branch)
Don Garrett39963602017-02-27 14:41:58 -0800119
Don Garrettf324bc32017-05-23 14:00:53 -0700120 logging.info('Remove Chroot.')
121 osutils.RmDir(os.path.join(buildroot, 'chroot'),
122 ignore_missing=True, sudo=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800123
Don Garrettf324bc32017-05-23 14:00:53 -0700124 logging.info('Remove Chrome checkout.')
125 osutils.RmDir(os.path.join(buildroot, '.cache', 'distfiles'),
126 ignore_missing=True, sudo=True)
127
128 try:
129 # If there is any failure doing the cleanup, wipe everything.
130 repo.BuildRootGitCleanup(prune_all=True)
131 except Exception:
132 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
133 repository.ClearBuildRoot(buildroot)
Don Garrett39963602017-02-27 14:41:58 -0800134
Don Garrett60967922017-04-12 18:51:44 -0700135 # Ensure buildroot exists.
136 osutils.SafeMakedirs(buildroot)
Don Garrettf324bc32017-05-23 14:00:53 -0700137 SetBuildrootState(repo.branch, buildroot)
Don Garrett7ade05a2017-02-17 13:31:47 -0800138
139
Don Garrett125d4dc2017-04-25 16:26:03 -0700140@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700141def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800142 """Preliminary ChromeOS checkout.
143
144 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
145 match what the build needs, but ensures the buildroot both has a 'hot'
146 checkout, and is close enough that the branched cbuildbot can successfully get
147 the right checkout.
148
149 This checks out full ChromeOS, even if a ChromiumOS build is going to be
150 performed. This is because we have no knowledge of the build config to be
151 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700152
153 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700154 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700155 """
Don Garrettf324bc32017-05-23 14:00:53 -0700156 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800157 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700158 repo.branch)
Don Garrett76496912017-05-11 16:59:11 -0700159 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700160
161
Don Garrett125d4dc2017-04-25 16:26:03 -0700162@StageDecorator
Don Garrett597ddff2017-02-17 18:29:37 -0800163def RunCbuildbot(options):
Don Garrettc4114cc2016-11-01 20:04:06 -0700164 """Start cbuildbot in specified directory with all arguments.
165
166 Args:
Don Garrett597ddff2017-02-17 18:29:37 -0800167 options: Parse command line options.
Don Garrettc4114cc2016-11-01 20:04:06 -0700168
169 Returns:
170 Return code of cbuildbot as an integer.
171 """
Don Garrett597ddff2017-02-17 18:29:37 -0800172 logging.info('Bootstrap cbuildbot in: %s', options.buildroot)
173 cbuildbot_path = os.path.join(
174 options.buildroot, 'chromite', 'bin', 'cbuildbot')
175
176 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
177 options.buildroot, cbuildbot_path, options)
178
Don Garrett125d4dc2017-04-25 16:26:03 -0700179 cros_build_lib.RunCommand(cmd, cwd=options.buildroot)
Don Garrettc4114cc2016-11-01 20:04:06 -0700180
Don Garrett60967922017-04-12 18:51:44 -0700181
Don Garrettf15d65b2017-04-12 12:39:55 -0700182def ConfigureGlobalEnvironment():
183 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700184 # Set umask to 022 so files created by buildbot are readable.
185 os.umask(0o22)
186
Don Garrett86fec482017-05-17 18:13:33 -0700187 # These variables can interfere with LANG / locale behavior.
188 unwanted_local_vars = [
189 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
190 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
191 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
192 ]
193 for v in unwanted_local_vars:
194 os.environ.pop(v, None)
195
196 # This variable is required for repo sync's to work in all cases.
197 os.environ['LANG'] = 'en_US.UTF-8'
198
Don Garrettc4114cc2016-11-01 20:04:06 -0700199
200def main(argv):
201 """main method of script.
202
203 Args:
204 argv: All command line arguments to pass as list of strings.
205
206 Returns:
207 Return code of cbuildbot as an integer.
208 """
Don Garrett125d4dc2017-04-25 16:26:03 -0700209 logging.EnableBuildbotMarkers()
Don Garrettf15d65b2017-04-12 12:39:55 -0700210 ConfigureGlobalEnvironment()
211
Don Garrett86881cb2017-02-15 15:41:55 -0800212 options = PreParseArguments(argv)
Don Garrettc4114cc2016-11-01 20:04:06 -0700213
Don Garrett125d4dc2017-04-25 16:26:03 -0700214 branchname = options.branch or 'master'
Don Garrett86881cb2017-02-15 15:41:55 -0800215 buildroot = options.buildroot
216 git_cache_dir = options.git_cache_dir
217
Don Garrettf324bc32017-05-23 14:00:53 -0700218 site_config = config_lib.GetConfig()
219 manifest_url = site_config.params['MANIFEST_INT_URL']
220
221 repo = repository.RepoRepository(manifest_url, buildroot,
222 branch=branchname,
223 git_cache_dir=git_cache_dir)
224
Don Garrett7ade05a2017-02-17 13:31:47 -0800225 # Sometimes, we have to cleanup things that can break cbuildbot, especially
226 # on the branch.
Don Garrettf324bc32017-05-23 14:00:53 -0700227 CleanBuildroot(buildroot, repo)
Don Garrett7ade05a2017-02-17 13:31:47 -0800228
Don Garrett86881cb2017-02-15 15:41:55 -0800229 # Get a checkout close enough the branched cbuildbot can handle it.
Don Garrettf324bc32017-05-23 14:00:53 -0700230 InitialCheckout(repo)
Don Garrett86881cb2017-02-15 15:41:55 -0800231
232 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Don Garrett125d4dc2017-04-25 16:26:03 -0700233 try:
234 RunCbuildbot(options)
235 except cros_build_lib.RunCommandError as e:
236 return e.result.returncode