blob: ac4576ddc7daed7b63521cb324f38cfa9a298d40 [file] [log] [blame]
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +00001#!/usr/bin/python
2# Copyright 2014 Google Inc.
3#
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7
8"""Parse a DEPS file and git checkout all of the dependencies.
9
10Args:
11 An optional list of deps_os values.
12
13Environment Variables:
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +000014 GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset,
15 will use the file ../DEPS relative to this script's directory.
16
17 GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages.
18
19Git Config:
20 To disable syncing of a single repository:
21 cd path/to/repository
22 git config sync-deps.disable true
23
24 To re-enable sync:
25 cd path/to/repository
26 git config --unset sync-deps.disable
27"""
28
29
30import os
31import subprocess
32import sys
33import threading
34
Eric Borenbb0ef0a2014-06-25 11:13:27 -040035import fix_pythonpath
36from common.py.utils.git_utils import GIT
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +000037
38
39DEFAULT_DEPS_PATH = os.path.normpath(
40 os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS'))
41
42
43def usage(deps_file_path = None):
44 sys.stderr.write(
45 'Usage: run to grab dependencies, with optional platform support:\n')
46 sys.stderr.write(' python %s' % __file__)
47 if deps_file_path:
48 for deps_os in parse_file_to_dict(deps_file_path)['deps_os']:
49 sys.stderr.write(' [%s]' % deps_os)
50 else:
51 sys.stderr.write(' [DEPS_OS...]')
52 sys.stderr.write('\n\n')
53 sys.stderr.write(__doc__)
54
55
56def git_repository_sync_is_disabled(git, directory):
57 try:
58 disable = subprocess.check_output(
59 [git, 'config', 'sync-deps.disable'], cwd=directory)
60 return disable.lower().strip() in ['true', '1', 'yes', 'on']
61 except subprocess.CalledProcessError:
62 return False
63
64
commit-bot@chromium.orgaae6e6f2014-04-21 19:03:05 +000065def is_git_toplevel(git, directory):
66 """Return true iff the directory is the top level of a Git repository.
67
68 Args:
69 git (string) the git executable
70
71 directory (string) the path into which the repository
72 is expected to be checked out.
73 """
74 try:
75 toplevel = subprocess.check_output(
76 [git, 'rev-parse', '--show-toplevel'], cwd=directory).strip()
commit-bot@chromium.org2349a1e2014-04-28 13:39:47 +000077 return os.path.realpath(directory) == os.path.realpath(toplevel)
commit-bot@chromium.orgaae6e6f2014-04-21 19:03:05 +000078 except subprocess.CalledProcessError:
79 return False
80
81
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +000082def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
83 """Checkout (and clone if needed) a Git repository.
84
85 Args:
86 git (string) the git executable
87
88 repo (string) the location of the repository, suitable
89 for passing to `git clone`.
90
91 checkoutable (string) a tag, branch, or commit, suitable for
92 passing to `git checkout`
93
94 directory (string) the path into which the repository
95 should be checked out.
96
97 verbose (boolean)
98
99 Raises an exception if any calls to git fail.
100 """
101 if not os.path.isdir(directory):
102 subprocess.check_call(
103 [git, 'clone', '--quiet', repo, directory])
104
commit-bot@chromium.orgaae6e6f2014-04-21 19:03:05 +0000105 if not is_git_toplevel(git, directory):
106 # if the directory exists, but isn't a git repo, you will modify
107 # the parent repostory, which isn't what you want.
108 sys.stdout.write('%s\n IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory)
109 return
110
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +0000111 # Check to see if this repo is disabled. Quick return.
112 if git_repository_sync_is_disabled(git, directory):
113 sys.stdout.write('%s\n SYNC IS DISABLED.\n' % directory)
114 return
115
116 subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory)
117
118 subprocess.check_call(
119 [git, 'checkout', '--quiet', checkoutable], cwd=directory)
120
121 if verbose:
122 sys.stdout.write('%s\n @ %s\n' % (directory, checkoutable)) # Success.
123
124
125def parse_file_to_dict(path):
126 dictionary = {}
127 execfile(path, dictionary)
128 return dictionary
129
130
131class DepsError(Exception):
132 """Raised if deps_os is a bad key.
133 """
134 pass
135
136
137def git_sync_deps(deps_file_path, deps_os_list, verbose):
138 """Grab dependencies, with optional platform support.
139
140 Args:
141 deps_file_path (string) Path to the DEPS file.
142
143 deps_os_list (list of strings) Can be empty list. List of
144 strings that should each be a key in the deps_os
145 dictionary in the DEPS file.
146
147 Raises DepsError exception and git Exceptions.
148 """
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +0000149 deps_file_directory = os.path.dirname(deps_file_path)
150 deps = parse_file_to_dict(deps_file_path)
151 dependencies = deps['deps'].copy()
152 for deps_os in deps_os_list:
153 # Add OS-specific dependencies
154 if deps_os not in deps['deps_os']:
155 raise DepsError(
156 'Argument "%s" not found within deps_os keys %r' %
157 (deps_os, deps['deps_os'].keys()))
158 for dep in deps['deps_os'][deps_os]:
159 dependencies[dep] = deps['deps_os'][deps_os][dep]
160 list_of_arg_lists = []
161 for directory in dependencies:
162 if '@' in dependencies[directory]:
163 repo, checkoutable = dependencies[directory].split('@', 1)
164 else:
165 repo, checkoutable = dependencies[directory], 'origin/master'
166
167 relative_directory = os.path.join(deps_file_directory, directory)
168
169 list_of_arg_lists.append(
Eric Borenbb0ef0a2014-06-25 11:13:27 -0400170 (GIT, repo, checkoutable, relative_directory, verbose))
commit-bot@chromium.org71e800a2014-04-16 19:21:00 +0000171
172 multithread(git_checkout_to_directory, list_of_arg_lists)
173
174
175def multithread(function, list_of_arg_lists):
176 # for args in list_of_arg_lists:
177 # function(*args)
178 # return
179 threads = []
180 for args in list_of_arg_lists:
181 thread = threading.Thread(None, function, None, args)
182 thread.start()
183 threads.append(thread)
184 for thread in threads:
185 thread.join()
186
187
188def main(argv):
189 deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH)
190 verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False))
191 try:
192 git_sync_deps(deps_file_path, argv, verbose)
193 return 0
194 except DepsError:
195 usage(deps_file_path)
196 return 1
197
198
199if __name__ == '__main__':
200 exit(main(sys.argv[1:]))