blob: 31d5c5b239b0a963bac01ccd6d55ac5e2b8e22d6 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Common functions used for syncing Chrome."""
from __future__ import print_function
import os
import pprint
from chromite.lib import config_lib
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import git
from chromite.lib import osutils
site_config = config_lib.GetConfig()
CHROME_COMMITTER_URL = 'https://chromium.googlesource.com/chromium/src'
STATUS_URL = 'https://chromium-status.appspot.com/current?format=json'
# Last release for each milestone where a '.DEPS.git' was emitted. After this,
# a Git-only DEPS is emitted as 'DEPS' and '.DEPS.git' is no longer created.
_DEPS_GIT_TRANSITION_MAP = {
45: (45, 0, 2430, 3),
44: (44, 0, 2403, 48),
43: (43, 0, 2357, 125),
}
def FindGclientFile(path):
"""Returns the nearest higher-level gclient file from the specified path.
Args:
path: The path to use. Defaults to cwd.
"""
return osutils.FindInPathParents(
'.gclient', path, test_func=os.path.isfile)
def FindGclientCheckoutRoot(path):
"""Get the root of your gclient managed checkout."""
gclient_path = FindGclientFile(path)
if gclient_path:
return os.path.dirname(gclient_path)
return None
def _LoadGclientFile(path):
"""Load a gclient file and return the solutions defined by the gclient file.
Args:
path: The gclient file to load.
Returns:
A list of solutions defined by the gclient file or an empty list if no
solutions exists.
"""
global_scope = {}
# Similar to depot_tools, we use execfile() to evaluate the gclient file,
# which is essentially a Python script, and then extract the solutions
# defined by the gclient file from the 'solutions' variable in the global
# scope.
execfile(path, global_scope)
return global_scope.get('solutions', [])
def _FindOrAddSolution(solutions, name):
"""Find a solution of the specified name from the given list of solutions.
If no solution with the specified name is found, a solution with the
specified name is appended to the given list of solutions. This function thus
always returns a solution.
Args:
solutions: The list of solutions to search from.
name: The solution name to search for.
Returns:
The solution with the specified name.
"""
for solution in solutions:
if solution['name'] == name:
return solution
solution = {'name': name}
solutions.append(solution)
return solution
def BuildspecUsesDepsGit(rev):
"""Tests if a given buildspec revision uses .DEPS.git or DEPS.
Previous, Chromium emitted two dependency files: DEPS and .DEPS.git, the
latter being a Git-only construction of DEPS. Recently a switch was thrown,
causing .DEPS.git to be emitted exclusively as DEPS.
To support past buildspec checkouts, this logic tests a given Chromium
buildspec revision against the transition thresholds, using .DEPS.git prior
to transition and DEPS after.
"""
rev = tuple(int(d) for d in rev.split('.'))
milestone = rev[0]
threshold = _DEPS_GIT_TRANSITION_MAP.get(milestone)
if threshold:
return rev <= threshold
return all(milestone < k for k in _DEPS_GIT_TRANSITION_MAP.iterkeys())
def _GetGclientURLs(internal, rev):
"""Get the URLs and deps_file values to use in gclient file.
See WriteConfigFile below.
"""
results = []
if rev is None or git.IsSHA1(rev):
# Regular chromium checkout; src may float to origin/master or be pinned.
url = constants.CHROMIUM_GOB_URL
if rev:
url += ('@' + rev)
results.append(('src', url, '.DEPS.git'))
if internal:
results.append(
('src-internal', constants.CHROME_INTERNAL_GOB_URL, '.DEPS.git'))
elif internal:
# Internal buildspec: check out the buildspec repo and set deps_file to
# the path to the desired release spec.
url = site_config.params.INTERNAL_GOB_URL + '/chrome/tools/buildspec.git'
# Chromium switched to DEPS at version 45.0.2432.3.
deps_file = '.DEPS.git' if BuildspecUsesDepsGit(rev) else 'DEPS'
results.append(('CHROME_DEPS', url, 'releases/%s/%s' % (rev, deps_file)))
else:
# External buildspec: use the main chromium src repository, pinned to the
# release tag, with deps_file set to .DEPS.git (which is created by
# publish_deps.py).
url = constants.CHROMIUM_GOB_URL + '@refs/tags/' + rev
results.append(('src', url, '.DEPS.git'))
return results
def _GetGclientSolutions(internal, rev, template, managed):
"""Get the solutions array to write to the gclient file.
See WriteConfigFile below.
"""
urls = _GetGclientURLs(internal, rev)
solutions = _LoadGclientFile(template) if template is not None else []
for (name, url, deps_file) in urls:
solution = _FindOrAddSolution(solutions, name)
# Always override 'url' and 'deps_file' of a solution as we need to specify
# the revision information.
solution['url'] = url
if deps_file:
solution['deps_file'] = deps_file
# Use 'custom_deps' and 'custom_vars' of a solution when specified by the
# template gclient file.
solution.setdefault('custom_deps', {})
solution.setdefault('custom_vars', {})
solution.setdefault('managed', managed)
return solutions
def _GetGclientSpec(internal, rev, template, use_cache, managed):
"""Return a formatted gclient spec.
See WriteConfigFile below.
"""
solutions = _GetGclientSolutions(internal, rev, template, managed)
result = 'solutions = %s\n' % pprint.pformat(solutions)
# Horrible hack, I will go to hell for this. The bots need to have a git
# cache set up; but how can we tell whether this code is running on a bot
# or a developer's machine?
if cros_build_lib.HostIsCIBuilder() and use_cache:
if cros_build_lib.IsInsideChroot():
result += "cache_dir = '/tmp/b/git-cache'\n"
else:
result += "cache_dir = '/b/git-cache'\n"
return result
def WriteConfigFile(gclient, cwd, internal, rev, template=None,
use_cache=True, managed=True):
"""Initialize the specified directory as a gclient checkout.
For gclient documentation, see:
http://src.chromium.org/svn/trunk/tools/depot_tools/README.gclient
Args:
gclient: Path to gclient.
cwd: Directory to sync.
internal: Whether you want an internal checkout.
rev: Revision or tag to use.
- If None, use the latest from trunk.
- If this is a sha1, use the specified revision.
- Otherwise, treat this as a chrome version string.
template: An optional file to provide a template of gclient solutions.
_GetGclientSolutions iterates through the solutions specified
by the template and performs appropriate modifications such as
filling information like url and revision and adding extra
solutions.
use_cache: An optional Boolean flag to indicate if the git cache should
be used when available (on a continuous-integration builder).
managed: Default value of gclient config's 'managed' field. Default True
(see crbug.com/624177).
"""
spec = _GetGclientSpec(internal, rev, template, use_cache, managed)
cmd = [gclient, 'config', '--spec', spec]
cros_build_lib.RunCommand(cmd, cwd=cwd)
def Revert(gclient, cwd):
"""Revert all local changes.
Args:
gclient: Path to gclient.
cwd: Directory to revert.
"""
cros_build_lib.RunCommand([gclient, 'revert', '--nohooks'], cwd=cwd)
def Sync(gclient, cwd, reset=False, nohooks=True, verbose=True,
run_args=None):
"""Sync the specified directory using gclient.
Args:
gclient: Path to gclient.
cwd: Directory to sync.
reset: Reset to pristine version of the source code.
nohooks: If set, add '--nohooks' argument.
verbose: If set, add '--verbose' argument.
run_args: If set (dict), pass to RunCommand as kwargs.
Returns:
A CommandResult object.
"""
if run_args is None:
run_args = {}
cmd = [gclient, 'sync', '--with_branch_heads', '--with_tags']
if reset:
cmd += ['--reset', '--force', '--delete_unversioned_trees']
if nohooks:
cmd.append('--nohooks')
if verbose:
cmd.append('--verbose')
return cros_build_lib.RunCommand(cmd, cwd=cwd, **run_args)