| # Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import base64 |
| import json |
| import os |
| import re |
| import time |
| import urllib2 |
| |
| from common import utils |
| |
| |
| _THIS_DIR = os.path.abspath(os.path.dirname(__file__)) |
| CONFIG = json.loads(open(os.path.join(_THIS_DIR, |
| 'deps_config.json'), 'r').read()) |
| OLD_GIT_URL_PATTERN = re.compile(r'https?://git.chromium.org/(.*)') |
| |
| |
| class _VarImpl(object): |
| |
| def __init__(self, local_scope): |
| self._local_scope = local_scope |
| |
| def Lookup(self, var_name): |
| if var_name in self._local_scope.get('vars', {}): |
| return self._local_scope['vars'][var_name] |
| raise Exception('Var is not defined: %s' % var_name) |
| |
| |
| def _ParseDEPS(content): |
| """Parse the DEPS file of chromium.""" |
| local_scope = {} |
| var = _VarImpl(local_scope) |
| global_scope = { |
| 'Var': var.Lookup, |
| 'deps': {}, |
| 'deps_os': {}, |
| 'include_rules': [], |
| 'skip_child_includes': [], |
| 'hooks': [], |
| } |
| exec(content, global_scope, local_scope) |
| |
| local_scope.setdefault('deps', {}) |
| local_scope.setdefault('deps_os', {}) |
| |
| return (local_scope['deps'], local_scope['deps_os']) |
| |
| |
| def _GetComponentName(path, host_dirs): |
| """Return the component name of a path.""" |
| components_renamed = { |
| 'webkit': 'blink', |
| } |
| |
| for host_dir in host_dirs: |
| if path.startswith(host_dir): |
| path = path[len(host_dir):] |
| name = path.split('/')[0].lower() |
| if name in components_renamed: |
| return components_renamed[name].lower() |
| else: |
| return name.lower() |
| |
| # Unknown path, return the whole path as component name. |
| return '_'.join(path.split('/')) |
| |
| |
| def _GetContentOfDEPS(revision): |
| chromium_git_file_url_template = CONFIG['chromium_git_file_url'] |
| |
| # Try .DEPS.git first, because before migration from SVN to GIT, the .DEPS.git |
| # has the dependency in GIT repo while DEPS has dependency in SVN repo. |
| url = chromium_git_file_url_template % (revision, '.DEPS.git') |
| http_status_code, content = utils.GetHttpClient().Get( |
| url, retries=5, retry_if_not=404) |
| |
| # If .DEPS.git is not found, use DEPS, assuming it is a commit after migration |
| # from SVN to GIT. |
| if http_status_code == 404: |
| url = chromium_git_file_url_template % (revision, 'DEPS') |
| http_status_code, content = utils.GetHttpClient().Get(url, retries=5) |
| |
| if http_status_code == 200: |
| return base64.b64decode(content) |
| else: |
| return '' |
| |
| |
| def GetChromiumComponents(chromium_revision, |
| os_platform='unix', |
| deps_file_downloader=_GetContentOfDEPS): |
| """Return a list of components used by Chrome of the given revision. |
| |
| Args: |
| chromium_revision: Revision of the Chrome build: svn revision, or git hash. |
| os_platform: The target platform of the Chrome build, eg. win, mac, etc. |
| deps_file_downloader: A function that takes the chromium_revision as input, |
| and returns the content of the DEPS file. The returned |
| content is assumed to be trusted input and will be |
| evaluated as python code. |
| |
| Returns: |
| A map from component path to parsed component name, repository URL, |
| repository type and revision. |
| Return None if an error occurs. |
| """ |
| if os_platform.lower() == 'linux': |
| os_platform = 'unix' |
| |
| chromium_git_base_url = CONFIG['chromium_git_base_url'] |
| |
| if not utils.IsGitHash(chromium_revision): |
| # Convert svn revision or commit position to Git hash. |
| cr_rev_url_template = CONFIG['cr_rev_url'] |
| url = cr_rev_url_template % chromium_revision |
| status_code, content = utils.GetHttpClient().Get( |
| url, timeout=120, retries=5, retry_if_not=404) |
| if status_code != 200 or not content: |
| if status_code == 404: |
| print 'Chromium commit position %s is not found.' % chromium_revision |
| return None |
| |
| cr_rev_data = json.loads(content) |
| if 'git_sha' not in cr_rev_data: |
| return None |
| |
| if 'repo' not in cr_rev_data or cr_rev_data['repo'] != 'chromium/src': |
| print ('%s seems like a commit position of "%s", but not "chromium/src".' |
| % (chromium_revision, cr_rev_data['repo'])) |
| return None |
| |
| chromium_revision = cr_rev_data.get('git_sha') |
| if not chromium_revision: |
| return None |
| |
| # Download the content of DEPS file in chromium. |
| deps_content = deps_file_downloader(chromium_revision) |
| if not deps_content: |
| return None |
| |
| all_deps = {} |
| |
| # Parse the content of DEPS file. |
| deps, deps_os = _ParseDEPS(deps_content) |
| all_deps.update(deps) |
| if os_platform is not None: |
| all_deps.update(deps_os.get(os_platform, {})) |
| |
| # Figure out components based on the dependencies. |
| components = {} |
| host_dirs = CONFIG['host_directories'] |
| for component_path, component_repo_url in all_deps.iteritems(): |
| if component_repo_url is None: |
| # For some platform like iso, some component is ignored. |
| continue |
| |
| name = _GetComponentName(component_path, host_dirs) |
| repository, revision = component_repo_url.split('@') |
| match = OLD_GIT_URL_PATTERN.match(repository) |
| if match: |
| repository = 'https://chromium.googlesource.com/%s' % match.group(1) |
| is_git_hash = utils.IsGitHash(revision) |
| if is_git_hash: |
| repository_type = 'git' |
| else: |
| repository_type = 'svn' |
| if not component_path.endswith('/'): |
| component_path += '/' |
| components[component_path] = { |
| 'path': component_path, |
| 'name': name, |
| 'repository': repository, |
| 'repository_type': repository_type, |
| 'revision': revision |
| } |
| |
| # Add chromium as a component. |
| components['src/'] = { |
| 'path': 'src/', |
| 'name': 'chromium', |
| 'repository': chromium_git_base_url, |
| 'repository_type': 'git', |
| 'revision': chromium_revision |
| } |
| |
| return components |
| |
| |
| def GetChromiumComponentRange(old_revision, |
| new_revision, |
| os_platform='unix', |
| deps_file_downloader=_GetContentOfDEPS): |
| """Return a list of components with their revision ranges. |
| |
| Args: |
| old_revision: The old revision of a Chrome build. |
| new_revision: The new revision of a Chrome build. |
| os_platform: The target platform of the Chrome build, eg. win, mac, etc. |
| deps_file_downloader: A function that takes the chromium_revision as input, |
| and returns the content of the DEPS file. The returned |
| content is assumed to be trusted input and will be |
| evaluated as python code. |
| |
| Returns: |
| A map from component path to its parsed regression and other information. |
| Return None if an error occurs. |
| """ |
| old_components = GetChromiumComponents(old_revision, os_platform, |
| deps_file_downloader) |
| if not old_components: |
| return None |
| |
| new_components = GetChromiumComponents(new_revision, os_platform, |
| deps_file_downloader) |
| if not new_components: |
| return None |
| |
| components = {} |
| for path in new_components: |
| new_component = new_components[path] |
| old_revision = None |
| |
| if path in old_components: |
| old_component = old_components[path] |
| old_revision = old_component['revision'] |
| |
| components[path] = { |
| 'path': path, |
| 'rolled': new_component['revision'] != old_revision, |
| 'name': new_component['name'], |
| 'old_revision': old_revision, |
| 'new_revision': new_component['revision'], |
| 'repository': new_component['repository'], |
| 'repository_type': new_component['repository_type'] |
| } |
| |
| return components |