1e5c31af7Sopenharmony_ci#!/usr/bin/env python3 2e5c31af7Sopenharmony_ci# Copyright 2019 Google LLC 3e5c31af7Sopenharmony_ci# 4e5c31af7Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 5e5c31af7Sopenharmony_ci# you may not use this file except in compliance with the License. 6e5c31af7Sopenharmony_ci# You may obtain a copy of the License at 7e5c31af7Sopenharmony_ci# 8e5c31af7Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 9e5c31af7Sopenharmony_ci# 10e5c31af7Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 11e5c31af7Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 12e5c31af7Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e5c31af7Sopenharmony_ci# See the License for the specific language governing permissions and 14e5c31af7Sopenharmony_ci# limitations under the License. 15e5c31af7Sopenharmony_ci 16e5c31af7Sopenharmony_ci"""Parse a DEPS file and git checkout all of the dependencies. 17e5c31af7Sopenharmony_ci 18e5c31af7Sopenharmony_ciArgs: 19e5c31af7Sopenharmony_ci An optional list of deps_os values. 20e5c31af7Sopenharmony_ci 21e5c31af7Sopenharmony_ci --with-swiftshader Checkout Swiftshader dependencies 22e5c31af7Sopenharmony_ci --with-clspv Checkout clspv dependencies 23e5c31af7Sopenharmony_ci --with-dxc Checkout dxc dependencies 24e5c31af7Sopenharmony_ci 25e5c31af7Sopenharmony_ciEnvironment Variables: 26e5c31af7Sopenharmony_ci GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of 27e5c31af7Sopenharmony_ci ['git', 'git.exe', 'git.bat'] in your default path. 28e5c31af7Sopenharmony_ci 29e5c31af7Sopenharmony_ci GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset, 30e5c31af7Sopenharmony_ci will use the file ../DEPS relative to this script's directory. 31e5c31af7Sopenharmony_ci 32e5c31af7Sopenharmony_ci GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages. 33e5c31af7Sopenharmony_ci 34e5c31af7Sopenharmony_ciGit Config: 35e5c31af7Sopenharmony_ci To disable syncing of a single repository: 36e5c31af7Sopenharmony_ci cd path/to/repository 37e5c31af7Sopenharmony_ci git config sync-deps.disable true 38e5c31af7Sopenharmony_ci 39e5c31af7Sopenharmony_ci To re-enable sync: 40e5c31af7Sopenharmony_ci cd path/to/repository 41e5c31af7Sopenharmony_ci git config --unset sync-deps.disable 42e5c31af7Sopenharmony_ci""" 43e5c31af7Sopenharmony_ci 44e5c31af7Sopenharmony_ciimport os 45e5c31af7Sopenharmony_ciimport re 46e5c31af7Sopenharmony_ciimport subprocess 47e5c31af7Sopenharmony_ciimport sys 48e5c31af7Sopenharmony_ciimport threading 49e5c31af7Sopenharmony_cifrom builtins import bytes 50e5c31af7Sopenharmony_ci 51e5c31af7Sopenharmony_ciwith_clspv = False 52e5c31af7Sopenharmony_ciwith_dxc = False 53e5c31af7Sopenharmony_ciwith_swiftshader = False 54e5c31af7Sopenharmony_ci 55e5c31af7Sopenharmony_cidef git_executable(): 56e5c31af7Sopenharmony_ci """Find the git executable. 57e5c31af7Sopenharmony_ci 58e5c31af7Sopenharmony_ci Returns: 59e5c31af7Sopenharmony_ci A string suitable for passing to subprocess functions, or None. 60e5c31af7Sopenharmony_ci """ 61e5c31af7Sopenharmony_ci envgit = os.environ.get('GIT_EXECUTABLE') 62e5c31af7Sopenharmony_ci searchlist = ['git', 'git.exe', 'git.bat'] 63e5c31af7Sopenharmony_ci if envgit: 64e5c31af7Sopenharmony_ci searchlist.insert(0, envgit) 65e5c31af7Sopenharmony_ci with open(os.devnull, 'w') as devnull: 66e5c31af7Sopenharmony_ci for git in searchlist: 67e5c31af7Sopenharmony_ci try: 68e5c31af7Sopenharmony_ci subprocess.call([git, '--version'], stdout=devnull) 69e5c31af7Sopenharmony_ci except (OSError,): 70e5c31af7Sopenharmony_ci continue 71e5c31af7Sopenharmony_ci return git 72e5c31af7Sopenharmony_ci return None 73e5c31af7Sopenharmony_ci 74e5c31af7Sopenharmony_ci 75e5c31af7Sopenharmony_ciDEFAULT_DEPS_PATH = os.path.normpath( 76e5c31af7Sopenharmony_ci os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS')) 77e5c31af7Sopenharmony_ci 78e5c31af7Sopenharmony_ci 79e5c31af7Sopenharmony_cidef usage(deps_file_path = None): 80e5c31af7Sopenharmony_ci sys.stderr.write( 81e5c31af7Sopenharmony_ci 'Usage: run to grab dependencies, with optional platform support:\n') 82e5c31af7Sopenharmony_ci sys.stderr.write(' %s %s' % (sys.executable, __file__)) 83e5c31af7Sopenharmony_ci if deps_file_path: 84e5c31af7Sopenharmony_ci parsed_deps = parse_file_to_dict(deps_file_path) 85e5c31af7Sopenharmony_ci if 'deps_os' in parsed_deps: 86e5c31af7Sopenharmony_ci for deps_os in parsed_deps['deps_os']: 87e5c31af7Sopenharmony_ci sys.stderr.write(' [%s]' % deps_os) 88e5c31af7Sopenharmony_ci sys.stderr.write('\n\n') 89e5c31af7Sopenharmony_ci sys.stderr.write(__doc__) 90e5c31af7Sopenharmony_ci 91e5c31af7Sopenharmony_ci 92e5c31af7Sopenharmony_cidef git_repository_sync_is_disabled(git, directory): 93e5c31af7Sopenharmony_ci try: 94e5c31af7Sopenharmony_ci disable = subprocess.check_output( 95e5c31af7Sopenharmony_ci [git, 'config', 'sync-deps.disable'], cwd=directory) 96e5c31af7Sopenharmony_ci return disable.lower().strip() in ['true', '1', 'yes', 'on'] 97e5c31af7Sopenharmony_ci except subprocess.CalledProcessError: 98e5c31af7Sopenharmony_ci return False 99e5c31af7Sopenharmony_ci 100e5c31af7Sopenharmony_ci 101e5c31af7Sopenharmony_cidef is_git_toplevel(git, directory): 102e5c31af7Sopenharmony_ci """Return true iff the directory is the top level of a Git repository. 103e5c31af7Sopenharmony_ci 104e5c31af7Sopenharmony_ci Args: 105e5c31af7Sopenharmony_ci git (string) the git executable 106e5c31af7Sopenharmony_ci 107e5c31af7Sopenharmony_ci directory (string) the path into which the repository 108e5c31af7Sopenharmony_ci is expected to be checked out. 109e5c31af7Sopenharmony_ci """ 110e5c31af7Sopenharmony_ci try: 111e5c31af7Sopenharmony_ci toplevel = subprocess.check_output( 112e5c31af7Sopenharmony_ci [git, 'rev-parse', '--show-toplevel'], cwd=directory).strip() 113e5c31af7Sopenharmony_ci return os.path.realpath(bytes(directory, 'utf8')) == os.path.realpath(toplevel) 114e5c31af7Sopenharmony_ci except subprocess.CalledProcessError: 115e5c31af7Sopenharmony_ci return False 116e5c31af7Sopenharmony_ci 117e5c31af7Sopenharmony_ci 118e5c31af7Sopenharmony_cidef status(directory, checkoutable): 119e5c31af7Sopenharmony_ci def tail_truncate(s, length): 120e5c31af7Sopenharmony_ci return s if len(s) <= length else '...' + s[-(length - 3):] 121e5c31af7Sopenharmony_ci dlen = 36 122e5c31af7Sopenharmony_ci directory = tail_truncate(directory, dlen) 123e5c31af7Sopenharmony_ci checkoutable = tail_truncate(checkoutable, 40) 124e5c31af7Sopenharmony_ci sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable)) 125e5c31af7Sopenharmony_ci 126e5c31af7Sopenharmony_ci 127e5c31af7Sopenharmony_cidef git_checkout_to_directory(git, repo, checkoutable, directory, verbose): 128e5c31af7Sopenharmony_ci """Checkout (and clone if needed) a Git repository. 129e5c31af7Sopenharmony_ci 130e5c31af7Sopenharmony_ci Args: 131e5c31af7Sopenharmony_ci git (string) the git executable 132e5c31af7Sopenharmony_ci 133e5c31af7Sopenharmony_ci repo (string) the location of the repository, suitable 134e5c31af7Sopenharmony_ci for passing to `git clone`. 135e5c31af7Sopenharmony_ci 136e5c31af7Sopenharmony_ci checkoutable (string) a tag, branch, or commit, suitable for 137e5c31af7Sopenharmony_ci passing to `git checkout` 138e5c31af7Sopenharmony_ci 139e5c31af7Sopenharmony_ci directory (string) the path into which the repository 140e5c31af7Sopenharmony_ci should be checked out. 141e5c31af7Sopenharmony_ci 142e5c31af7Sopenharmony_ci verbose (boolean) 143e5c31af7Sopenharmony_ci 144e5c31af7Sopenharmony_ci Raises an exception if any calls to git fail. 145e5c31af7Sopenharmony_ci """ 146e5c31af7Sopenharmony_ci if verbose: 147e5c31af7Sopenharmony_ci status(directory, checkoutable) 148e5c31af7Sopenharmony_ci 149e5c31af7Sopenharmony_ci if not os.path.isdir(directory): 150e5c31af7Sopenharmony_ci subprocess.check_call( 151e5c31af7Sopenharmony_ci [git, 'clone', '--quiet', repo, directory]) 152e5c31af7Sopenharmony_ci 153e5c31af7Sopenharmony_ci if not is_git_toplevel(git, directory): 154e5c31af7Sopenharmony_ci # if the directory exists, but isn't a git repo, you will modify 155e5c31af7Sopenharmony_ci # the parent repostory, which isn't what you want. 156e5c31af7Sopenharmony_ci sys.stdout.write('%s\n IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory) 157e5c31af7Sopenharmony_ci return 158e5c31af7Sopenharmony_ci 159e5c31af7Sopenharmony_ci # Check to see if this repo is disabled. Quick return. 160e5c31af7Sopenharmony_ci if git_repository_sync_is_disabled(git, directory): 161e5c31af7Sopenharmony_ci sys.stdout.write('%s\n SYNC IS DISABLED.\n' % directory) 162e5c31af7Sopenharmony_ci return 163e5c31af7Sopenharmony_ci 164e5c31af7Sopenharmony_ci with open(os.devnull, 'w') as devnull: 165e5c31af7Sopenharmony_ci # If this fails, we will fetch before trying again. Don't spam user 166e5c31af7Sopenharmony_ci # with error infomation. 167e5c31af7Sopenharmony_ci if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], 168e5c31af7Sopenharmony_ci cwd=directory, stderr=devnull): 169e5c31af7Sopenharmony_ci # if this succeeds, skip slow `git fetch`. 170e5c31af7Sopenharmony_ci return 171e5c31af7Sopenharmony_ci 172e5c31af7Sopenharmony_ci # If the repo has changed, always force use of the correct repo. 173e5c31af7Sopenharmony_ci # If origin already points to repo, this is a quick no-op. 174e5c31af7Sopenharmony_ci subprocess.check_call( 175e5c31af7Sopenharmony_ci [git, 'remote', 'set-url', 'origin', repo], cwd=directory) 176e5c31af7Sopenharmony_ci 177e5c31af7Sopenharmony_ci subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory) 178e5c31af7Sopenharmony_ci 179e5c31af7Sopenharmony_ci subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory) 180e5c31af7Sopenharmony_ci 181e5c31af7Sopenharmony_ci 182e5c31af7Sopenharmony_cidef parse_file_to_dict(path): 183e5c31af7Sopenharmony_ci dictionary = {} 184e5c31af7Sopenharmony_ci contents = open(path).read() 185e5c31af7Sopenharmony_ci # Need to convert Var() to vars[], so that the DEPS is actually Python. Var() 186e5c31af7Sopenharmony_ci # comes from Autoroller using gclient which has a slightly different DEPS 187e5c31af7Sopenharmony_ci # format. 188e5c31af7Sopenharmony_ci contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents) 189e5c31af7Sopenharmony_ci exec(contents, dictionary) 190e5c31af7Sopenharmony_ci return dictionary 191e5c31af7Sopenharmony_ci 192e5c31af7Sopenharmony_ci 193e5c31af7Sopenharmony_cidef git_sync_deps(deps_file_path, command_line_os_requests, verbose): 194e5c31af7Sopenharmony_ci """Grab dependencies, with optional platform support. 195e5c31af7Sopenharmony_ci 196e5c31af7Sopenharmony_ci Args: 197e5c31af7Sopenharmony_ci deps_file_path (string) Path to the DEPS file. 198e5c31af7Sopenharmony_ci 199e5c31af7Sopenharmony_ci command_line_os_requests (list of strings) Can be empty list. 200e5c31af7Sopenharmony_ci List of strings that should each be a key in the deps_os 201e5c31af7Sopenharmony_ci dictionary in the DEPS file. 202e5c31af7Sopenharmony_ci 203e5c31af7Sopenharmony_ci Raises git Exceptions. 204e5c31af7Sopenharmony_ci """ 205e5c31af7Sopenharmony_ci git = git_executable() 206e5c31af7Sopenharmony_ci assert git 207e5c31af7Sopenharmony_ci 208e5c31af7Sopenharmony_ci deps_file_directory = os.path.dirname(deps_file_path) 209e5c31af7Sopenharmony_ci deps_file = parse_file_to_dict(deps_file_path) 210e5c31af7Sopenharmony_ci dependencies = deps_file['deps'].copy() 211e5c31af7Sopenharmony_ci os_specific_dependencies = deps_file.get('deps_os', dict()) 212e5c31af7Sopenharmony_ci if 'all' in command_line_os_requests: 213e5c31af7Sopenharmony_ci for value in os_specific_dependencies.values(): 214e5c31af7Sopenharmony_ci dependencies.update(value) 215e5c31af7Sopenharmony_ci else: 216e5c31af7Sopenharmony_ci for os_name in command_line_os_requests: 217e5c31af7Sopenharmony_ci # Add OS-specific dependencies 218e5c31af7Sopenharmony_ci if os_name in os_specific_dependencies: 219e5c31af7Sopenharmony_ci dependencies.update(os_specific_dependencies[os_name]) 220e5c31af7Sopenharmony_ci for directory in dependencies: 221e5c31af7Sopenharmony_ci for other_dir in dependencies: 222e5c31af7Sopenharmony_ci if directory.startswith(other_dir + '/'): 223e5c31af7Sopenharmony_ci raise Exception('%r is parent of %r' % (other_dir, directory)) 224e5c31af7Sopenharmony_ci list_of_arg_lists = [] 225e5c31af7Sopenharmony_ci for directory in sorted(dependencies): 226e5c31af7Sopenharmony_ci if '@' in dependencies[directory]: 227e5c31af7Sopenharmony_ci repo, checkoutable = dependencies[directory].rsplit('@', 1) 228e5c31af7Sopenharmony_ci else: 229e5c31af7Sopenharmony_ci raise Exception("please specify commit or tag") 230e5c31af7Sopenharmony_ci 231e5c31af7Sopenharmony_ci if not with_clspv and directory == 'third_party/clspv': 232e5c31af7Sopenharmony_ci continue 233e5c31af7Sopenharmony_ci 234e5c31af7Sopenharmony_ci if not with_clspv and directory == 'third_party/clspv-llvm': 235e5c31af7Sopenharmony_ci continue 236e5c31af7Sopenharmony_ci 237e5c31af7Sopenharmony_ci if not with_dxc and directory == 'third_party/dxc': 238e5c31af7Sopenharmony_ci continue 239e5c31af7Sopenharmony_ci 240e5c31af7Sopenharmony_ci if not with_swiftshader and directory == 'third_party/swiftshader': 241e5c31af7Sopenharmony_ci continue 242e5c31af7Sopenharmony_ci 243e5c31af7Sopenharmony_ci relative_directory = os.path.join(deps_file_directory, directory) 244e5c31af7Sopenharmony_ci 245e5c31af7Sopenharmony_ci list_of_arg_lists.append( 246e5c31af7Sopenharmony_ci (git, repo, checkoutable, relative_directory, verbose)) 247e5c31af7Sopenharmony_ci 248e5c31af7Sopenharmony_ci multithread(git_checkout_to_directory, list_of_arg_lists) 249e5c31af7Sopenharmony_ci 250e5c31af7Sopenharmony_ci for directory in deps_file.get('recursedeps', []): 251e5c31af7Sopenharmony_ci recursive_path = os.path.join(deps_file_directory, directory, 'DEPS') 252e5c31af7Sopenharmony_ci git_sync_deps(recursive_path, command_line_os_requests, verbose) 253e5c31af7Sopenharmony_ci 254e5c31af7Sopenharmony_ci 255e5c31af7Sopenharmony_cidef multithread(function, list_of_arg_lists): 256e5c31af7Sopenharmony_ci # for args in list_of_arg_lists: 257e5c31af7Sopenharmony_ci # function(*args) 258e5c31af7Sopenharmony_ci # return 259e5c31af7Sopenharmony_ci threads = [] 260e5c31af7Sopenharmony_ci for args in list_of_arg_lists: 261e5c31af7Sopenharmony_ci thread = threading.Thread(None, function, None, args) 262e5c31af7Sopenharmony_ci thread.start() 263e5c31af7Sopenharmony_ci threads.append(thread) 264e5c31af7Sopenharmony_ci for thread in threads: 265e5c31af7Sopenharmony_ci thread.join() 266e5c31af7Sopenharmony_ci 267e5c31af7Sopenharmony_ci 268e5c31af7Sopenharmony_cidef main(argv): 269e5c31af7Sopenharmony_ci global with_clspv 270e5c31af7Sopenharmony_ci global with_dxc 271e5c31af7Sopenharmony_ci global with_swiftshader 272e5c31af7Sopenharmony_ci deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH) 273e5c31af7Sopenharmony_ci verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False)) 274e5c31af7Sopenharmony_ci 275e5c31af7Sopenharmony_ci if '--help' in argv or '-h' in argv: 276e5c31af7Sopenharmony_ci usage(deps_file_path) 277e5c31af7Sopenharmony_ci return 1 278e5c31af7Sopenharmony_ci 279e5c31af7Sopenharmony_ci if '--with-clspv' in argv: 280e5c31af7Sopenharmony_ci with_clspv = True 281e5c31af7Sopenharmony_ci 282e5c31af7Sopenharmony_ci if '--with-dxc' in argv: 283e5c31af7Sopenharmony_ci with_dxc = True 284e5c31af7Sopenharmony_ci 285e5c31af7Sopenharmony_ci if '--with-swiftshader' in argv: 286e5c31af7Sopenharmony_ci with_swiftshader = True 287e5c31af7Sopenharmony_ci 288e5c31af7Sopenharmony_ci git_sync_deps(deps_file_path, argv, verbose) 289e5c31af7Sopenharmony_ci # subprocess.check_call( 290e5c31af7Sopenharmony_ci # [sys.executable, 291e5c31af7Sopenharmony_ci # os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')]) 292e5c31af7Sopenharmony_ci return 0 293e5c31af7Sopenharmony_ci 294e5c31af7Sopenharmony_ci 295e5c31af7Sopenharmony_ciif __name__ == '__main__': 296e5c31af7Sopenharmony_ci exit(main(sys.argv[1:])) 297