| Index: checkdeps/README.md
|
| diff --git a/checkdeps/builddeps.py b/checkdeps/README.md
|
| old mode 100755
|
| new mode 100644
|
| similarity index 5%
|
| copy from checkdeps/builddeps.py
|
| copy to checkdeps/README.md
|
| index eb3eb1e211c30c978f955a2c4b22a9037715338a..ff51884ba77990163d7be6aa590ab500d33f27cf
|
| --- a/checkdeps/builddeps.py
|
| +++ b/checkdeps/README.md
|
| @@ -1,62 +1,65 @@
|
| -#!/usr/bin/env python
|
| -# Copyright 2013 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.
|
| +# DEPS Files
|
|
|
| -"""Traverses the source tree, parses all found DEPS files, and constructs
|
| -a dependency rule table to be used by subclasses.
|
| +DEPS files specify which files the sources in a directory tree may include.
|
|
|
| -The format of the deps file:
|
| +## File format
|
|
|
| First you have the normal module-level deps. These are the ones used by
|
| gclient. An example would be:
|
|
|
| - deps = {
|
| - "base":"http://foo.bar/trunk/base"
|
| - }
|
| -
|
| -DEPS files not in the top-level of a module won't need this. Then you
|
| -have any additional include rules. You can add (using "+") or subtract
|
| -(using "-") from the previously specified rules (including
|
| -module-level deps). You can also specify a path that is allowed for
|
| -now but that we intend to remove, using "!"; this is treated the same
|
| -as "+" when check_deps is run by our bots, but a presubmit step will
|
| -show a warning if you add a new include of a file that is only allowed
|
| -by "!".
|
| -
|
| -Note that for .java files, there is currently no difference between
|
| -"+" and "!", even in the presubmit step.
|
| -
|
| - include_rules = [
|
| - # Code should be able to use base (it's specified in the module-level
|
| - # deps above), but nothing in "base/evil" because it's evil.
|
| - "-base/evil",
|
| -
|
| - # But this one subdirectory of evil is OK.
|
| - "+base/evil/not",
|
| -
|
| - # And it can include files from this other directory even though there is
|
| - # no deps rule for it.
|
| - "+tools/crime_fighter",
|
| -
|
| - # This dependency is allowed for now but work is ongoing to remove it,
|
| - # so you shouldn't add further dependencies on it.
|
| - "!base/evil/ok_for_now.h",
|
| - ]
|
| -
|
| -If you have certain include rules that should only be applied for some
|
| -files within this directory and subdirectories, you can write a
|
| -section named specific_include_rules that is a hash map of regular
|
| -expressions to the list of rules that should apply to files matching
|
| -them. Note that such rules will always be applied before the rules
|
| -from 'include_rules' have been applied, but the order in which rules
|
| -associated with different regular expressions is applied is arbitrary.
|
| -
|
| - specific_include_rules = {
|
| - ".*_(unit|browser|api)test\.cc": [
|
| - "+libraries/testsupport",
|
| - ],
|
| - }
|
| +```
|
| +deps = {
|
| + "base":"http://foo.bar/trunk/base"
|
| +}
|
| +```
|
| +
|
| +DEPS files not in the top-level of a module won't need this. Then you have any
|
| +additional include rules. You can add (using `+`) or subtract (using `-`) from
|
| +the previously specified rules (including module-level deps). You can also
|
| +specify a path that is allowed for now but that we intend to remove, using `!`;
|
| +this is treated the same as `+` when `check_deps` is run by our bots, but a
|
| +presubmit step will show a warning if you add a new include of a file that is
|
| +only allowed by `!`.
|
| +
|
| +Note that for .java files, there is currently no difference between `+` and
|
| +`!`, even in the presubmit step.
|
| +
|
| +```
|
| +include_rules = [
|
| + # Code should be able to use base (it's specified in the module-level
|
| + # deps above), but nothing in "base/evil" because it's evil.
|
| + "-base/evil",
|
| +
|
| + # But this one subdirectory of evil is OK.
|
| + "+base/evil/not",
|
| +
|
| + # And it can include files from this other directory even though there is
|
| + # no deps rule for it.
|
| + "+tools/crime_fighter",
|
| +
|
| + # This dependency is allowed for now but work is ongoing to remove it,
|
| + # so you shouldn't add further dependencies on it.
|
| + "!base/evil/ok_for_now.h",
|
| +]
|
| +```
|
| +
|
| +If you have certain include rules that should only be applied for some files
|
| +within this directory and subdirectories, you can write a section named
|
| +`specific_include_rules` that is a hash map of regular expressions to the list
|
| +of rules that should apply to files matching them. Note that such rules will
|
| +always be applied before the rules from `include_rules` have been applied, but
|
| +the order in which rules associated with different regular expressions is
|
| +applied is arbitrary.
|
| +
|
| +```
|
| +specific_include_rules = {
|
| + ".*_(unit|browser|api)test\.cc": [
|
| + "+libraries/testsupport",
|
| + ],
|
| +}
|
| +```
|
| +
|
| +# Directory structure
|
|
|
| DEPS files may be placed anywhere in the tree. Each one applies to all
|
| subdirectories, where there may be more DEPS files that provide additions or
|
| @@ -64,370 +67,12 @@ subtractions for their own sub-trees.
|
|
|
| There is an implicit rule for the current directory (where the DEPS file lives)
|
| and all of its subdirectories. This prevents you from having to explicitly
|
| -allow the current directory everywhere. This implicit rule is applied first,
|
| -so you can modify or remove it using the normal include rules.
|
| +allow the current directory everywhere. This implicit rule is applied first, so
|
| +you can modify or remove it using the normal include rules.
|
|
|
| The rules are processed in order. This means you can explicitly allow a higher
|
| directory and then take away permissions from sub-parts, or the reverse.
|
|
|
| -Note that all directory separators must be slashes (Unix-style) and not
|
| +Note that all directory separators must be `/` slashes (Unix-style) and not
|
| backslashes. All directories should be relative to the source root and use
|
| only lowercase.
|
| -"""
|
| -
|
| -import copy
|
| -import os.path
|
| -import posixpath
|
| -import subprocess
|
| -
|
| -from rules import Rule, Rules
|
| -
|
| -
|
| -# Variable name used in the DEPS file to add or subtract include files from
|
| -# the module-level deps.
|
| -INCLUDE_RULES_VAR_NAME = 'include_rules'
|
| -
|
| -# Variable name used in the DEPS file to add or subtract include files
|
| -# from module-level deps specific to files whose basename (last
|
| -# component of path) matches a given regular expression.
|
| -SPECIFIC_INCLUDE_RULES_VAR_NAME = 'specific_include_rules'
|
| -
|
| -# Optionally present in the DEPS file to list subdirectories which should not
|
| -# be checked. This allows us to skip third party code, for example.
|
| -SKIP_SUBDIRS_VAR_NAME = 'skip_child_includes'
|
| -
|
| -
|
| -class DepsBuilderError(Exception):
|
| - """Base class for exceptions in this module."""
|
| - pass
|
| -
|
| -
|
| -def NormalizePath(path):
|
| - """Returns a path normalized to how we write DEPS rules and compare paths."""
|
| - return os.path.normcase(path).replace(os.path.sep, posixpath.sep)
|
| -
|
| -
|
| -def _GitSourceDirectories(base_directory):
|
| - """Returns set of normalized paths to subdirectories containing sources
|
| - managed by git."""
|
| - base_dir_norm = NormalizePath(base_directory)
|
| - git_source_directories = set([base_dir_norm])
|
| -
|
| - git_cmd = 'git.bat' if os.name == 'nt' else 'git'
|
| - git_ls_files_cmd = [git_cmd, 'ls-files']
|
| - # FIXME: Use a context manager in Python 3.2+
|
| - popen = subprocess.Popen(git_ls_files_cmd,
|
| - stdout=subprocess.PIPE,
|
| - bufsize=1, # line buffering, since read by line
|
| - cwd=base_directory)
|
| - try:
|
| - try:
|
| - for line in popen.stdout:
|
| - dir_path = os.path.join(base_directory, os.path.dirname(line))
|
| - dir_path_norm = NormalizePath(dir_path)
|
| - # Add the directory as well as all the parent directories,
|
| - # stopping once we reach an already-listed directory.
|
| - while dir_path_norm not in git_source_directories:
|
| - git_source_directories.add(dir_path_norm)
|
| - dir_path_norm = posixpath.dirname(dir_path_norm)
|
| - finally:
|
| - popen.stdout.close()
|
| - finally:
|
| - popen.wait()
|
| -
|
| - return git_source_directories
|
| -
|
| -
|
| -class DepsBuilder(object):
|
| - """Parses include_rules from DEPS files."""
|
| -
|
| - def __init__(self,
|
| - base_directory=None,
|
| - extra_repos=[],
|
| - verbose=False,
|
| - being_tested=False,
|
| - ignore_temp_rules=False,
|
| - ignore_specific_rules=False):
|
| - """Creates a new DepsBuilder.
|
| -
|
| - Args:
|
| - base_directory: local path to root of checkout, e.g. C:\chr\src.
|
| - verbose: Set to True for debug output.
|
| - being_tested: Set to True to ignore the DEPS file at tools/checkdeps/DEPS.
|
| - ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!").
|
| - """
|
| - base_directory = (base_directory or
|
| - os.path.join(os.path.dirname(__file__),
|
| - os.path.pardir, os.path.pardir))
|
| - self.base_directory = os.path.abspath(base_directory) # Local absolute path
|
| - self.extra_repos = extra_repos
|
| - self.verbose = verbose
|
| - self._under_test = being_tested
|
| - self._ignore_temp_rules = ignore_temp_rules
|
| - self._ignore_specific_rules = ignore_specific_rules
|
| - self._git_source_directories = None
|
| -
|
| - if os.path.exists(os.path.join(base_directory, '.git')):
|
| - self.is_git = True
|
| - elif os.path.exists(os.path.join(base_directory, '.svn')):
|
| - self.is_git = False
|
| - else:
|
| - raise DepsBuilderError("%s is not a repository root" % base_directory)
|
| -
|
| - # Map of normalized directory paths to rules to use for those
|
| - # directories, or None for directories that should be skipped.
|
| - # Normalized is: absolute, lowercase, / for separator.
|
| - self.directory_rules = {}
|
| - self._ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory)
|
| -
|
| - def _ApplyRules(self, existing_rules, includes, specific_includes,
|
| - cur_dir_norm):
|
| - """Applies the given include rules, returning the new rules.
|
| -
|
| - Args:
|
| - existing_rules: A set of existing rules that will be combined.
|
| - include: The list of rules from the "include_rules" section of DEPS.
|
| - specific_includes: E.g. {'.*_unittest\.cc': ['+foo', '-blat']} rules
|
| - from the "specific_include_rules" section of DEPS.
|
| - cur_dir_norm: The current directory, normalized path. We will create an
|
| - implicit rule that allows inclusion from this directory.
|
| -
|
| - Returns: A new set of rules combining the existing_rules with the other
|
| - arguments.
|
| - """
|
| - rules = copy.deepcopy(existing_rules)
|
| -
|
| - # First apply the implicit "allow" rule for the current directory.
|
| - base_dir_norm = NormalizePath(self.base_directory)
|
| - if not cur_dir_norm.startswith(base_dir_norm):
|
| - raise Exception(
|
| - 'Internal error: base directory is not at the beginning for\n'
|
| - ' %s and base dir\n'
|
| - ' %s' % (cur_dir_norm, base_dir_norm))
|
| - relative_dir = posixpath.relpath(cur_dir_norm, base_dir_norm)
|
| -
|
| - # Make the help string a little more meaningful.
|
| - source = relative_dir or 'top level'
|
| - rules.AddRule('+' + relative_dir,
|
| - relative_dir,
|
| - 'Default rule for ' + source)
|
| -
|
| - def ApplyOneRule(rule_str, dependee_regexp=None):
|
| - """Deduces a sensible description for the rule being added, and
|
| - adds the rule with its description to |rules|.
|
| -
|
| - If we are ignoring temporary rules, this function does nothing
|
| - for rules beginning with the Rule.TEMP_ALLOW character.
|
| - """
|
| - if self._ignore_temp_rules and rule_str.startswith(Rule.TEMP_ALLOW):
|
| - return
|
| -
|
| - rule_block_name = 'include_rules'
|
| - if dependee_regexp:
|
| - rule_block_name = 'specific_include_rules'
|
| - if relative_dir:
|
| - rule_description = relative_dir + "'s %s" % rule_block_name
|
| - else:
|
| - rule_description = 'the top level %s' % rule_block_name
|
| - rules.AddRule(rule_str, relative_dir, rule_description, dependee_regexp)
|
| -
|
| - # Apply the additional explicit rules.
|
| - for rule_str in includes:
|
| - ApplyOneRule(rule_str)
|
| -
|
| - # Finally, apply the specific rules.
|
| - if self._ignore_specific_rules:
|
| - return rules
|
| -
|
| - for regexp, specific_rules in specific_includes.iteritems():
|
| - for rule_str in specific_rules:
|
| - ApplyOneRule(rule_str, regexp)
|
| -
|
| - return rules
|
| -
|
| - def _ApplyDirectoryRules(self, existing_rules, dir_path_local_abs):
|
| - """Combines rules from the existing rules and the new directory.
|
| -
|
| - Any directory can contain a DEPS file. Top-level DEPS files can contain
|
| - module dependencies which are used by gclient. We use these, along with
|
| - additional include rules and implicit rules for the given directory, to
|
| - come up with a combined set of rules to apply for the directory.
|
| -
|
| - Args:
|
| - existing_rules: The rules for the parent directory. We'll add-on to these.
|
| - dir_path_local_abs: The directory path that the DEPS file may live in (if
|
| - it exists). This will also be used to generate the
|
| - implicit rules. This is a local path.
|
| -
|
| - Returns: A 2-tuple of:
|
| - (1) the combined set of rules to apply to the sub-tree,
|
| - (2) a list of all subdirectories that should NOT be checked, as specified
|
| - in the DEPS file (if any).
|
| - Subdirectories are single words, hence no OS dependence.
|
| - """
|
| - dir_path_norm = NormalizePath(dir_path_local_abs)
|
| -
|
| - # Check the DEPS file in this directory.
|
| - if self.verbose:
|
| - print 'Applying rules from', dir_path_local_abs
|
| - def FromImpl(*_):
|
| - pass # NOP function so "From" doesn't fail.
|
| -
|
| - def FileImpl(_):
|
| - pass # NOP function so "File" doesn't fail.
|
| -
|
| - class _VarImpl:
|
| - def __init__(self, local_scope):
|
| - self._local_scope = local_scope
|
| -
|
| - def Lookup(self, var_name):
|
| - """Implements the Var syntax."""
|
| - try:
|
| - return self._local_scope['vars'][var_name]
|
| - except KeyError:
|
| - raise Exception('Var is not defined: %s' % var_name)
|
| -
|
| - local_scope = {}
|
| - global_scope = {
|
| - 'File': FileImpl,
|
| - 'From': FromImpl,
|
| - 'Var': _VarImpl(local_scope).Lookup,
|
| - }
|
| - deps_file_path = os.path.join(dir_path_local_abs, 'DEPS')
|
| -
|
| - # The second conditional here is to disregard the
|
| - # tools/checkdeps/DEPS file while running tests. This DEPS file
|
| - # has a skip_child_includes for 'testdata' which is necessary for
|
| - # running production tests, since there are intentional DEPS
|
| - # violations under the testdata directory. On the other hand when
|
| - # running tests, we absolutely need to verify the contents of that
|
| - # directory to trigger those intended violations and see that they
|
| - # are handled correctly.
|
| - if os.path.isfile(deps_file_path) and not (
|
| - self._under_test and
|
| - os.path.basename(dir_path_local_abs) == 'checkdeps'):
|
| - execfile(deps_file_path, global_scope, local_scope)
|
| - elif self.verbose:
|
| - print ' No deps file found in', dir_path_local_abs
|
| -
|
| - # Even if a DEPS file does not exist we still invoke ApplyRules
|
| - # to apply the implicit "allow" rule for the current directory
|
| - include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, [])
|
| - specific_include_rules = local_scope.get(SPECIFIC_INCLUDE_RULES_VAR_NAME,
|
| - {})
|
| - skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, [])
|
| -
|
| - return (self._ApplyRules(existing_rules, include_rules,
|
| - specific_include_rules, dir_path_norm),
|
| - skip_subdirs)
|
| -
|
| - def _ApplyDirectoryRulesAndSkipSubdirs(self, parent_rules,
|
| - dir_path_local_abs):
|
| - """Given |parent_rules| and a subdirectory |dir_path_local_abs| of the
|
| - directory that owns the |parent_rules|, add |dir_path_local_abs|'s rules to
|
| - |self.directory_rules|, and add None entries for any of its
|
| - subdirectories that should be skipped.
|
| - """
|
| - directory_rules, excluded_subdirs = self._ApplyDirectoryRules(
|
| - parent_rules, dir_path_local_abs)
|
| - dir_path_norm = NormalizePath(dir_path_local_abs)
|
| - self.directory_rules[dir_path_norm] = directory_rules
|
| - for subdir in excluded_subdirs:
|
| - subdir_path_norm = posixpath.join(dir_path_norm, subdir)
|
| - self.directory_rules[subdir_path_norm] = None
|
| -
|
| - def GetAllRulesAndFiles(self, dir_name=None):
|
| - """Yields (rules, filenames) for each repository directory with DEPS rules.
|
| -
|
| - This walks the directory tree while staying in the repository. Specify
|
| - |dir_name| to walk just one directory and its children; omit |dir_name| to
|
| - walk the entire repository.
|
| -
|
| - Yields:
|
| - Two-element (rules, filenames) tuples. |rules| is a rules.Rules object
|
| - for a directory, and |filenames| is a list of the absolute local paths
|
| - of all files in that directory.
|
| - """
|
| - if self.is_git and self._git_source_directories is None:
|
| - self._git_source_directories = _GitSourceDirectories(self.base_directory)
|
| - for repo in self.extra_repos:
|
| - repo_path = os.path.join(self.base_directory, repo)
|
| - self._git_source_directories.update(_GitSourceDirectories(repo_path))
|
| -
|
| - # Collect a list of all files and directories to check.
|
| - files_to_check = []
|
| - if dir_name and not os.path.isabs(dir_name):
|
| - dir_name = os.path.join(self.base_directory, dir_name)
|
| - dirs_to_check = [dir_name or self.base_directory]
|
| - while dirs_to_check:
|
| - current_dir = dirs_to_check.pop()
|
| -
|
| - # Check that this directory is part of the source repository. This
|
| - # prevents us from descending into third-party code or directories
|
| - # generated by the build system.
|
| - if self.is_git:
|
| - if NormalizePath(current_dir) not in self._git_source_directories:
|
| - continue
|
| - elif not os.path.exists(os.path.join(current_dir, '.svn')):
|
| - continue
|
| -
|
| - current_dir_rules = self.GetDirectoryRules(current_dir)
|
| -
|
| - if not current_dir_rules:
|
| - continue # Handle the 'skip_child_includes' case.
|
| -
|
| - current_dir_contents = sorted(os.listdir(current_dir))
|
| - file_names = []
|
| - sub_dirs = []
|
| - for file_name in current_dir_contents:
|
| - full_name = os.path.join(current_dir, file_name)
|
| - if os.path.isdir(full_name):
|
| - sub_dirs.append(full_name)
|
| - else:
|
| - file_names.append(full_name)
|
| - dirs_to_check.extend(reversed(sub_dirs))
|
| -
|
| - yield (current_dir_rules, file_names)
|
| -
|
| - def GetDirectoryRules(self, dir_path_local):
|
| - """Returns a Rules object to use for the given directory, or None
|
| - if the given directory should be skipped.
|
| -
|
| - Also modifies |self.directory_rules| to store the Rules.
|
| - This takes care of first building rules for parent directories (up to
|
| - |self.base_directory|) if needed, which may add rules for skipped
|
| - subdirectories.
|
| -
|
| - Args:
|
| - dir_path_local: A local path to the directory you want rules for.
|
| - Can be relative and unnormalized. It is the caller's responsibility
|
| - to ensure that this is part of the repository rooted at
|
| - |self.base_directory|.
|
| - """
|
| - if os.path.isabs(dir_path_local):
|
| - dir_path_local_abs = dir_path_local
|
| - else:
|
| - dir_path_local_abs = os.path.join(self.base_directory, dir_path_local)
|
| - dir_path_norm = NormalizePath(dir_path_local_abs)
|
| -
|
| - if dir_path_norm in self.directory_rules:
|
| - return self.directory_rules[dir_path_norm]
|
| -
|
| - parent_dir_local_abs = os.path.dirname(dir_path_local_abs)
|
| - parent_rules = self.GetDirectoryRules(parent_dir_local_abs)
|
| - # We need to check for an entry for our dir_path again, since
|
| - # GetDirectoryRules can modify entries for subdirectories, namely setting
|
| - # to None if they should be skipped, via _ApplyDirectoryRulesAndSkipSubdirs.
|
| - # For example, if dir_path == 'A/B/C' and A/B/DEPS specifies that the C
|
| - # subdirectory be skipped, GetDirectoryRules('A/B') will fill in the entry
|
| - # for 'A/B/C' as None.
|
| - if dir_path_norm in self.directory_rules:
|
| - return self.directory_rules[dir_path_norm]
|
| -
|
| - if parent_rules:
|
| - self._ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path_local_abs)
|
| - else:
|
| - # If the parent directory should be skipped, then the current
|
| - # directory should also be skipped.
|
| - self.directory_rules[dir_path_norm] = None
|
| - return self.directory_rules[dir_path_norm]
|
|
|