1cb93a386Sopenharmony_ci#!/usr/bin/env python 2cb93a386Sopenharmony_ci# Copyright 2013 The Chromium Authors. All rights reserved. 3cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 4cb93a386Sopenharmony_ci# found in the LICENSE file. 5cb93a386Sopenharmony_ci 6cb93a386Sopenharmony_ci"""Traverses the source tree, parses all found DEPS files, and constructs 7cb93a386Sopenharmony_cia dependency rule table to be used by subclasses. 8cb93a386Sopenharmony_ci 9cb93a386Sopenharmony_ciSee README.md for the format of the deps file. 10cb93a386Sopenharmony_ci""" 11cb93a386Sopenharmony_ci 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ciimport copy 15cb93a386Sopenharmony_ciimport os.path 16cb93a386Sopenharmony_ciimport posixpath 17cb93a386Sopenharmony_ciimport subprocess 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_cifrom rules import Rule, Rules 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_ci# Variable name used in the DEPS file to add or subtract include files from 23cb93a386Sopenharmony_ci# the module-level deps. 24cb93a386Sopenharmony_ciINCLUDE_RULES_VAR_NAME = 'include_rules' 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci# Variable name used in the DEPS file to add or subtract include files 27cb93a386Sopenharmony_ci# from module-level deps specific to files whose basename (last 28cb93a386Sopenharmony_ci# component of path) matches a given regular expression. 29cb93a386Sopenharmony_ciSPECIFIC_INCLUDE_RULES_VAR_NAME = 'specific_include_rules' 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_ci# Optionally present in the DEPS file to list subdirectories which should not 32cb93a386Sopenharmony_ci# be checked. This allows us to skip third party code, for example. 33cb93a386Sopenharmony_ciSKIP_SUBDIRS_VAR_NAME = 'skip_child_includes' 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci# Optionally discard rules from parent directories, similar to "noparent" in 36cb93a386Sopenharmony_ci# OWNERS files. For example, if //ash/components has "noparent = True" then 37cb93a386Sopenharmony_ci# it will not inherit rules from //ash/DEPS, forcing each //ash/component/foo 38cb93a386Sopenharmony_ci# to declare all its dependencies. 39cb93a386Sopenharmony_ciNOPARENT_VAR_NAME = 'noparent' 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ciclass DepsBuilderError(Exception): 43cb93a386Sopenharmony_ci """Base class for exceptions in this module.""" 44cb93a386Sopenharmony_ci pass 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_cidef NormalizePath(path): 48cb93a386Sopenharmony_ci """Returns a path normalized to how we write DEPS rules and compare paths.""" 49cb93a386Sopenharmony_ci return os.path.normcase(path).replace(os.path.sep, posixpath.sep) 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_cidef _GitSourceDirectories(base_directory): 53cb93a386Sopenharmony_ci """Returns set of normalized paths to subdirectories containing sources 54cb93a386Sopenharmony_ci managed by git.""" 55cb93a386Sopenharmony_ci base_dir_norm = NormalizePath(base_directory) 56cb93a386Sopenharmony_ci git_source_directories = set([base_dir_norm]) 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci git_cmd = 'git.bat' if os.name == 'nt' else 'git' 59cb93a386Sopenharmony_ci git_ls_files_cmd = [git_cmd, 'ls-files'] 60cb93a386Sopenharmony_ci # FIXME: Use a context manager in Python 3.2+ 61cb93a386Sopenharmony_ci popen = subprocess.Popen(git_ls_files_cmd, 62cb93a386Sopenharmony_ci stdout=subprocess.PIPE, 63cb93a386Sopenharmony_ci cwd=base_directory) 64cb93a386Sopenharmony_ci try: 65cb93a386Sopenharmony_ci try: 66cb93a386Sopenharmony_ci for line in popen.stdout.read().decode('utf-8').splitlines(): 67cb93a386Sopenharmony_ci dir_path = os.path.join(base_directory, os.path.dirname(line)) 68cb93a386Sopenharmony_ci dir_path_norm = NormalizePath(dir_path) 69cb93a386Sopenharmony_ci # Add the directory as well as all the parent directories, 70cb93a386Sopenharmony_ci # stopping once we reach an already-listed directory. 71cb93a386Sopenharmony_ci while dir_path_norm not in git_source_directories: 72cb93a386Sopenharmony_ci git_source_directories.add(dir_path_norm) 73cb93a386Sopenharmony_ci dir_path_norm = posixpath.dirname(dir_path_norm) 74cb93a386Sopenharmony_ci finally: 75cb93a386Sopenharmony_ci popen.stdout.close() 76cb93a386Sopenharmony_ci finally: 77cb93a386Sopenharmony_ci popen.wait() 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci return git_source_directories 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ciclass DepsBuilder(object): 83cb93a386Sopenharmony_ci """Parses include_rules from DEPS files.""" 84cb93a386Sopenharmony_ci 85cb93a386Sopenharmony_ci def __init__(self, 86cb93a386Sopenharmony_ci base_directory=None, 87cb93a386Sopenharmony_ci extra_repos=[], 88cb93a386Sopenharmony_ci verbose=False, 89cb93a386Sopenharmony_ci being_tested=False, 90cb93a386Sopenharmony_ci ignore_temp_rules=False, 91cb93a386Sopenharmony_ci ignore_specific_rules=False): 92cb93a386Sopenharmony_ci """Creates a new DepsBuilder. 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci Args: 95cb93a386Sopenharmony_ci base_directory: local path to root of checkout, e.g. C:\chr\src. 96cb93a386Sopenharmony_ci verbose: Set to True for debug output. 97cb93a386Sopenharmony_ci being_tested: Set to True to ignore the DEPS file at 98cb93a386Sopenharmony_ci buildtools/checkdeps/DEPS. 99cb93a386Sopenharmony_ci ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!"). 100cb93a386Sopenharmony_ci """ 101cb93a386Sopenharmony_ci base_directory = (base_directory or 102cb93a386Sopenharmony_ci os.path.join(os.path.dirname(__file__), 103cb93a386Sopenharmony_ci os.path.pardir, os.path.pardir)) 104cb93a386Sopenharmony_ci self.base_directory = os.path.abspath(base_directory) # Local absolute path 105cb93a386Sopenharmony_ci self.extra_repos = extra_repos 106cb93a386Sopenharmony_ci self.verbose = verbose 107cb93a386Sopenharmony_ci self._under_test = being_tested 108cb93a386Sopenharmony_ci self._ignore_temp_rules = ignore_temp_rules 109cb93a386Sopenharmony_ci self._ignore_specific_rules = ignore_specific_rules 110cb93a386Sopenharmony_ci self._git_source_directories = None 111cb93a386Sopenharmony_ci 112cb93a386Sopenharmony_ci if os.path.exists(os.path.join(base_directory, '.git')): 113cb93a386Sopenharmony_ci self.is_git = True 114cb93a386Sopenharmony_ci elif os.path.exists(os.path.join(base_directory, '.svn')): 115cb93a386Sopenharmony_ci self.is_git = False 116cb93a386Sopenharmony_ci else: 117cb93a386Sopenharmony_ci raise DepsBuilderError("%s is not a repository root" % base_directory) 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci # Map of normalized directory paths to rules to use for those 120cb93a386Sopenharmony_ci # directories, or None for directories that should be skipped. 121cb93a386Sopenharmony_ci # Normalized is: absolute, lowercase, / for separator. 122cb93a386Sopenharmony_ci self.directory_rules = {} 123cb93a386Sopenharmony_ci self._ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory) 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci def _ApplyRules(self, existing_rules, includes, specific_includes, 126cb93a386Sopenharmony_ci cur_dir_norm): 127cb93a386Sopenharmony_ci """Applies the given include rules, returning the new rules. 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci Args: 130cb93a386Sopenharmony_ci existing_rules: A set of existing rules that will be combined. 131cb93a386Sopenharmony_ci include: The list of rules from the "include_rules" section of DEPS. 132cb93a386Sopenharmony_ci specific_includes: E.g. {'.*_unittest\.cc': ['+foo', '-blat']} rules 133cb93a386Sopenharmony_ci from the "specific_include_rules" section of DEPS. 134cb93a386Sopenharmony_ci cur_dir_norm: The current directory, normalized path. We will create an 135cb93a386Sopenharmony_ci implicit rule that allows inclusion from this directory. 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci Returns: A new set of rules combining the existing_rules with the other 138cb93a386Sopenharmony_ci arguments. 139cb93a386Sopenharmony_ci """ 140cb93a386Sopenharmony_ci rules = copy.deepcopy(existing_rules) 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci # First apply the implicit "allow" rule for the current directory. 143cb93a386Sopenharmony_ci base_dir_norm = NormalizePath(self.base_directory) 144cb93a386Sopenharmony_ci if not cur_dir_norm.startswith(base_dir_norm): 145cb93a386Sopenharmony_ci raise Exception( 146cb93a386Sopenharmony_ci 'Internal error: base directory is not at the beginning for\n' 147cb93a386Sopenharmony_ci ' %s and base dir\n' 148cb93a386Sopenharmony_ci ' %s' % (cur_dir_norm, base_dir_norm)) 149cb93a386Sopenharmony_ci relative_dir = posixpath.relpath(cur_dir_norm, base_dir_norm) 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci # Make the help string a little more meaningful. 152cb93a386Sopenharmony_ci source = relative_dir or 'top level' 153cb93a386Sopenharmony_ci rules.AddRule('+' + relative_dir, 154cb93a386Sopenharmony_ci relative_dir, 155cb93a386Sopenharmony_ci 'Default rule for ' + source) 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci def ApplyOneRule(rule_str, dependee_regexp=None): 158cb93a386Sopenharmony_ci """Deduces a sensible description for the rule being added, and 159cb93a386Sopenharmony_ci adds the rule with its description to |rules|. 160cb93a386Sopenharmony_ci 161cb93a386Sopenharmony_ci If we are ignoring temporary rules, this function does nothing 162cb93a386Sopenharmony_ci for rules beginning with the Rule.TEMP_ALLOW character. 163cb93a386Sopenharmony_ci """ 164cb93a386Sopenharmony_ci if self._ignore_temp_rules and rule_str.startswith(Rule.TEMP_ALLOW): 165cb93a386Sopenharmony_ci return 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_ci rule_block_name = 'include_rules' 168cb93a386Sopenharmony_ci if dependee_regexp: 169cb93a386Sopenharmony_ci rule_block_name = 'specific_include_rules' 170cb93a386Sopenharmony_ci if relative_dir: 171cb93a386Sopenharmony_ci rule_description = relative_dir + "'s %s" % rule_block_name 172cb93a386Sopenharmony_ci else: 173cb93a386Sopenharmony_ci rule_description = 'the top level %s' % rule_block_name 174cb93a386Sopenharmony_ci rules.AddRule(rule_str, relative_dir, rule_description, dependee_regexp) 175cb93a386Sopenharmony_ci 176cb93a386Sopenharmony_ci # Apply the additional explicit rules. 177cb93a386Sopenharmony_ci for rule_str in includes: 178cb93a386Sopenharmony_ci ApplyOneRule(rule_str) 179cb93a386Sopenharmony_ci 180cb93a386Sopenharmony_ci # Finally, apply the specific rules. 181cb93a386Sopenharmony_ci if self._ignore_specific_rules: 182cb93a386Sopenharmony_ci return rules 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_ci for regexp, specific_rules in specific_includes.items(): 185cb93a386Sopenharmony_ci for rule_str in specific_rules: 186cb93a386Sopenharmony_ci ApplyOneRule(rule_str, regexp) 187cb93a386Sopenharmony_ci 188cb93a386Sopenharmony_ci return rules 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_ci def _ApplyDirectoryRules(self, existing_rules, dir_path_local_abs): 191cb93a386Sopenharmony_ci """Combines rules from the existing rules and the new directory. 192cb93a386Sopenharmony_ci 193cb93a386Sopenharmony_ci Any directory can contain a DEPS file. Top-level DEPS files can contain 194cb93a386Sopenharmony_ci module dependencies which are used by gclient. We use these, along with 195cb93a386Sopenharmony_ci additional include rules and implicit rules for the given directory, to 196cb93a386Sopenharmony_ci come up with a combined set of rules to apply for the directory. 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ci Args: 199cb93a386Sopenharmony_ci existing_rules: The rules for the parent directory. We'll add-on to these. 200cb93a386Sopenharmony_ci dir_path_local_abs: The directory path that the DEPS file may live in (if 201cb93a386Sopenharmony_ci it exists). This will also be used to generate the 202cb93a386Sopenharmony_ci implicit rules. This is a local path. 203cb93a386Sopenharmony_ci 204cb93a386Sopenharmony_ci Returns: A 2-tuple of: 205cb93a386Sopenharmony_ci (1) the combined set of rules to apply to the sub-tree, 206cb93a386Sopenharmony_ci (2) a list of all subdirectories that should NOT be checked, as specified 207cb93a386Sopenharmony_ci in the DEPS file (if any). 208cb93a386Sopenharmony_ci Subdirectories are single words, hence no OS dependence. 209cb93a386Sopenharmony_ci """ 210cb93a386Sopenharmony_ci dir_path_norm = NormalizePath(dir_path_local_abs) 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_ci # Check the DEPS file in this directory. 213cb93a386Sopenharmony_ci if self.verbose: 214cb93a386Sopenharmony_ci print('Applying rules from', dir_path_local_abs) 215cb93a386Sopenharmony_ci def FromImpl(*_): 216cb93a386Sopenharmony_ci pass # NOP function so "From" doesn't fail. 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci def FileImpl(_): 219cb93a386Sopenharmony_ci pass # NOP function so "File" doesn't fail. 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_ci class _VarImpl: 222cb93a386Sopenharmony_ci def __init__(self, local_scope): 223cb93a386Sopenharmony_ci self._local_scope = local_scope 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ci def Lookup(self, var_name): 226cb93a386Sopenharmony_ci """Implements the Var syntax.""" 227cb93a386Sopenharmony_ci try: 228cb93a386Sopenharmony_ci return self._local_scope['vars'][var_name] 229cb93a386Sopenharmony_ci except KeyError: 230cb93a386Sopenharmony_ci raise Exception('Var is not defined: %s' % var_name) 231cb93a386Sopenharmony_ci 232cb93a386Sopenharmony_ci local_scope = {} 233cb93a386Sopenharmony_ci global_scope = { 234cb93a386Sopenharmony_ci 'File': FileImpl, 235cb93a386Sopenharmony_ci 'From': FromImpl, 236cb93a386Sopenharmony_ci 'Var': _VarImpl(local_scope).Lookup, 237cb93a386Sopenharmony_ci 'Str': str, 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci deps_file_path = os.path.join(dir_path_local_abs, 'DEPS') 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci # The second conditional here is to disregard the 242cb93a386Sopenharmony_ci # buildtools/checkdeps/DEPS file while running tests. This DEPS file 243cb93a386Sopenharmony_ci # has a skip_child_includes for 'testdata' which is necessary for 244cb93a386Sopenharmony_ci # running production tests, since there are intentional DEPS 245cb93a386Sopenharmony_ci # violations under the testdata directory. On the other hand when 246cb93a386Sopenharmony_ci # running tests, we absolutely need to verify the contents of that 247cb93a386Sopenharmony_ci # directory to trigger those intended violations and see that they 248cb93a386Sopenharmony_ci # are handled correctly. 249cb93a386Sopenharmony_ci if os.path.isfile(deps_file_path) and not ( 250cb93a386Sopenharmony_ci self._under_test and 251cb93a386Sopenharmony_ci os.path.basename(dir_path_local_abs) == 'checkdeps'): 252cb93a386Sopenharmony_ci try: 253cb93a386Sopenharmony_ci with open(deps_file_path) as file: 254cb93a386Sopenharmony_ci exec(file.read(), global_scope, local_scope) 255cb93a386Sopenharmony_ci except Exception as e: 256cb93a386Sopenharmony_ci print(' Error reading %s: %s' % (deps_file_path, str(e))) 257cb93a386Sopenharmony_ci raise 258cb93a386Sopenharmony_ci elif self.verbose: 259cb93a386Sopenharmony_ci print(' No deps file found in', dir_path_local_abs) 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci # Even if a DEPS file does not exist we still invoke ApplyRules 262cb93a386Sopenharmony_ci # to apply the implicit "allow" rule for the current directory 263cb93a386Sopenharmony_ci include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) 264cb93a386Sopenharmony_ci specific_include_rules = local_scope.get(SPECIFIC_INCLUDE_RULES_VAR_NAME, 265cb93a386Sopenharmony_ci {}) 266cb93a386Sopenharmony_ci skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) 267cb93a386Sopenharmony_ci noparent = local_scope.get(NOPARENT_VAR_NAME, False) 268cb93a386Sopenharmony_ci if noparent: 269cb93a386Sopenharmony_ci parent_rules = Rules() 270cb93a386Sopenharmony_ci else: 271cb93a386Sopenharmony_ci parent_rules = existing_rules 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci return (self._ApplyRules(parent_rules, include_rules, 274cb93a386Sopenharmony_ci specific_include_rules, dir_path_norm), 275cb93a386Sopenharmony_ci skip_subdirs) 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_ci def _ApplyDirectoryRulesAndSkipSubdirs(self, parent_rules, 278cb93a386Sopenharmony_ci dir_path_local_abs): 279cb93a386Sopenharmony_ci """Given |parent_rules| and a subdirectory |dir_path_local_abs| of the 280cb93a386Sopenharmony_ci directory that owns the |parent_rules|, add |dir_path_local_abs|'s rules to 281cb93a386Sopenharmony_ci |self.directory_rules|, and add None entries for any of its 282cb93a386Sopenharmony_ci subdirectories that should be skipped. 283cb93a386Sopenharmony_ci """ 284cb93a386Sopenharmony_ci directory_rules, excluded_subdirs = self._ApplyDirectoryRules( 285cb93a386Sopenharmony_ci parent_rules, dir_path_local_abs) 286cb93a386Sopenharmony_ci dir_path_norm = NormalizePath(dir_path_local_abs) 287cb93a386Sopenharmony_ci self.directory_rules[dir_path_norm] = directory_rules 288cb93a386Sopenharmony_ci for subdir in excluded_subdirs: 289cb93a386Sopenharmony_ci subdir_path_norm = posixpath.join(dir_path_norm, subdir) 290cb93a386Sopenharmony_ci self.directory_rules[subdir_path_norm] = None 291cb93a386Sopenharmony_ci 292cb93a386Sopenharmony_ci def GetAllRulesAndFiles(self, dir_name=None): 293cb93a386Sopenharmony_ci """Yields (rules, filenames) for each repository directory with DEPS rules. 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci This walks the directory tree while staying in the repository. Specify 296cb93a386Sopenharmony_ci |dir_name| to walk just one directory and its children; omit |dir_name| to 297cb93a386Sopenharmony_ci walk the entire repository. 298cb93a386Sopenharmony_ci 299cb93a386Sopenharmony_ci Yields: 300cb93a386Sopenharmony_ci Two-element (rules, filenames) tuples. |rules| is a rules.Rules object 301cb93a386Sopenharmony_ci for a directory, and |filenames| is a list of the absolute local paths 302cb93a386Sopenharmony_ci of all files in that directory. 303cb93a386Sopenharmony_ci """ 304cb93a386Sopenharmony_ci if self.is_git and self._git_source_directories is None: 305cb93a386Sopenharmony_ci self._git_source_directories = _GitSourceDirectories(self.base_directory) 306cb93a386Sopenharmony_ci for repo in self.extra_repos: 307cb93a386Sopenharmony_ci repo_path = os.path.join(self.base_directory, repo) 308cb93a386Sopenharmony_ci self._git_source_directories.update(_GitSourceDirectories(repo_path)) 309cb93a386Sopenharmony_ci 310cb93a386Sopenharmony_ci # Collect a list of all files and directories to check. 311cb93a386Sopenharmony_ci if dir_name and not os.path.isabs(dir_name): 312cb93a386Sopenharmony_ci dir_name = os.path.join(self.base_directory, dir_name) 313cb93a386Sopenharmony_ci dirs_to_check = [dir_name or self.base_directory] 314cb93a386Sopenharmony_ci while dirs_to_check: 315cb93a386Sopenharmony_ci current_dir = dirs_to_check.pop() 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_ci # Check that this directory is part of the source repository. This 318cb93a386Sopenharmony_ci # prevents us from descending into third-party code or directories 319cb93a386Sopenharmony_ci # generated by the build system. 320cb93a386Sopenharmony_ci if self.is_git: 321cb93a386Sopenharmony_ci if NormalizePath(current_dir) not in self._git_source_directories: 322cb93a386Sopenharmony_ci continue 323cb93a386Sopenharmony_ci elif not os.path.exists(os.path.join(current_dir, '.svn')): 324cb93a386Sopenharmony_ci continue 325cb93a386Sopenharmony_ci 326cb93a386Sopenharmony_ci current_dir_rules = self.GetDirectoryRules(current_dir) 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ci if not current_dir_rules: 329cb93a386Sopenharmony_ci continue # Handle the 'skip_child_includes' case. 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci current_dir_contents = sorted(os.listdir(current_dir)) 332cb93a386Sopenharmony_ci file_names = [] 333cb93a386Sopenharmony_ci sub_dirs = [] 334cb93a386Sopenharmony_ci for file_name in current_dir_contents: 335cb93a386Sopenharmony_ci full_name = os.path.join(current_dir, file_name) 336cb93a386Sopenharmony_ci if os.path.isdir(full_name): 337cb93a386Sopenharmony_ci sub_dirs.append(full_name) 338cb93a386Sopenharmony_ci else: 339cb93a386Sopenharmony_ci file_names.append(full_name) 340cb93a386Sopenharmony_ci dirs_to_check.extend(reversed(sub_dirs)) 341cb93a386Sopenharmony_ci 342cb93a386Sopenharmony_ci yield (current_dir_rules, file_names) 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_ci def GetDirectoryRules(self, dir_path_local): 345cb93a386Sopenharmony_ci """Returns a Rules object to use for the given directory, or None 346cb93a386Sopenharmony_ci if the given directory should be skipped. 347cb93a386Sopenharmony_ci 348cb93a386Sopenharmony_ci Also modifies |self.directory_rules| to store the Rules. 349cb93a386Sopenharmony_ci This takes care of first building rules for parent directories (up to 350cb93a386Sopenharmony_ci |self.base_directory|) if needed, which may add rules for skipped 351cb93a386Sopenharmony_ci subdirectories. 352cb93a386Sopenharmony_ci 353cb93a386Sopenharmony_ci Args: 354cb93a386Sopenharmony_ci dir_path_local: A local path to the directory you want rules for. 355cb93a386Sopenharmony_ci Can be relative and unnormalized. It is the caller's responsibility 356cb93a386Sopenharmony_ci to ensure that this is part of the repository rooted at 357cb93a386Sopenharmony_ci |self.base_directory|. 358cb93a386Sopenharmony_ci """ 359cb93a386Sopenharmony_ci if os.path.isabs(dir_path_local): 360cb93a386Sopenharmony_ci dir_path_local_abs = dir_path_local 361cb93a386Sopenharmony_ci else: 362cb93a386Sopenharmony_ci dir_path_local_abs = os.path.join(self.base_directory, dir_path_local) 363cb93a386Sopenharmony_ci dir_path_norm = NormalizePath(dir_path_local_abs) 364cb93a386Sopenharmony_ci 365cb93a386Sopenharmony_ci if dir_path_norm in self.directory_rules: 366cb93a386Sopenharmony_ci return self.directory_rules[dir_path_norm] 367cb93a386Sopenharmony_ci 368cb93a386Sopenharmony_ci parent_dir_local_abs = os.path.dirname(dir_path_local_abs) 369cb93a386Sopenharmony_ci parent_rules = self.GetDirectoryRules(parent_dir_local_abs) 370cb93a386Sopenharmony_ci # We need to check for an entry for our dir_path again, since 371cb93a386Sopenharmony_ci # GetDirectoryRules can modify entries for subdirectories, namely setting 372cb93a386Sopenharmony_ci # to None if they should be skipped, via _ApplyDirectoryRulesAndSkipSubdirs. 373cb93a386Sopenharmony_ci # For example, if dir_path == 'A/B/C' and A/B/DEPS specifies that the C 374cb93a386Sopenharmony_ci # subdirectory be skipped, GetDirectoryRules('A/B') will fill in the entry 375cb93a386Sopenharmony_ci # for 'A/B/C' as None. 376cb93a386Sopenharmony_ci if dir_path_norm in self.directory_rules: 377cb93a386Sopenharmony_ci return self.directory_rules[dir_path_norm] 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_ci if parent_rules: 380cb93a386Sopenharmony_ci self._ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path_local_abs) 381cb93a386Sopenharmony_ci else: 382cb93a386Sopenharmony_ci # If the parent directory should be skipped, then the current 383cb93a386Sopenharmony_ci # directory should also be skipped. 384cb93a386Sopenharmony_ci self.directory_rules[dir_path_norm] = None 385cb93a386Sopenharmony_ci return self.directory_rules[dir_path_norm] 386