OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """A wrapper script to manage a set of client modules in different SCM. | 6 """A wrapper script to manage a set of client modules in different SCM. |
7 | 7 |
8 This script is intended to be used to help basic management of client | 8 This script is intended to be used to help basic management of client |
9 program sources residing in one or more Subversion modules and Git | 9 program sources residing in one or more Subversion modules and Git |
10 repositories, along with other modules it depends on, also in Subversion or Git, | 10 repositories, along with other modules it depends on, also in Subversion or Git, |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 # To use the trunk of a component instead of what's in DEPS: | 272 # To use the trunk of a component instead of what's in DEPS: |
273 #"component": "https://svnserver/component/trunk/", | 273 #"component": "https://svnserver/component/trunk/", |
274 # To exclude a component from your working copy: | 274 # To exclude a component from your working copy: |
275 #"data/really_large_component": None, | 275 #"data/really_large_component": None, |
276 }, | 276 }, |
277 "safesync_url": "%(safesync_url)s" | 277 "safesync_url": "%(safesync_url)s" |
278 }, | 278 }, |
279 ] | 279 ] |
280 """) | 280 """) |
281 | 281 |
| 282 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\ |
| 283 { "name" : "%(solution_name)s", |
| 284 "url" : "%(solution_url)s", |
| 285 "custom_deps" : { |
| 286 %(solution_deps)s, |
| 287 }, |
| 288 "safesync_url": "%(safesync_url)s" |
| 289 }, |
| 290 """) |
| 291 |
| 292 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ |
| 293 # An element of this array (a "solution") describes a repository directory |
| 294 # that will be checked out into your working copy. Each solution may |
| 295 # optionally define additional dependencies (via its DEPS file) to be |
| 296 # checked out alongside the solution's directory. A solution may also |
| 297 # specify custom dependencies (via the "custom_deps" property) that |
| 298 # override or augment the dependencies specified by the DEPS file. |
| 299 # If a "safesync_url" is specified, it is assumed to reference the location of |
| 300 # a text file which contains nothing but the last known good SCM revision to |
| 301 # sync against. It is fetched if specified and used unless --head is passed |
| 302 |
| 303 solutions = [ |
| 304 %(solution_list)s |
| 305 ] |
| 306 """) |
| 307 |
282 | 308 |
283 ## GClient implementation. | 309 ## GClient implementation. |
284 | 310 |
285 | 311 |
286 class GClient(object): | 312 class GClient(object): |
287 """Object that represent a gclient checkout.""" | 313 """Object that represent a gclient checkout.""" |
288 | 314 |
289 supported_commands = [ | 315 supported_commands = [ |
290 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update', | 316 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update', |
291 'runhooks' | 317 'runhooks' |
(...skipping 502 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
794 if revision_overrides.has_key(revision_elem[0]) and \ | 820 if revision_overrides.has_key(revision_elem[0]) and \ |
795 revision_overrides[revision_elem[0]] != revision_elem[1]: | 821 revision_overrides[revision_elem[0]] != revision_elem[1]: |
796 raise gclient_utils.Error( | 822 raise gclient_utils.Error( |
797 "Conflicting revision numbers specified.") | 823 "Conflicting revision numbers specified.") |
798 revision_overrides[revision_elem[0]] = revision_elem[1] | 824 revision_overrides[revision_elem[0]] = revision_elem[1] |
799 | 825 |
800 solutions = self.GetVar("solutions") | 826 solutions = self.GetVar("solutions") |
801 if not solutions: | 827 if not solutions: |
802 raise gclient_utils.Error("No solution specified") | 828 raise gclient_utils.Error("No solution specified") |
803 | 829 |
804 entries = {} | |
805 entries_deps_content = {} | |
806 | |
807 # Inner helper to generate base url and rev tuple (including honoring | 830 # Inner helper to generate base url and rev tuple (including honoring |
808 # |revision_overrides|) | 831 # |revision_overrides|) |
809 def GetURLAndRev(name, original_url): | 832 def GetURLAndRev(name, original_url): |
810 url, revision = gclient_utils.SplitUrlRevision(original_url) | 833 url, revision = gclient_utils.SplitUrlRevision(original_url) |
811 if not revision: | 834 if not revision: |
812 if revision_overrides.has_key(name): | 835 if revision_overrides.has_key(name): |
813 return (url, revision_overrides[name]) | 836 return (url, revision_overrides[name]) |
814 else: | 837 else: |
815 scm = gclient_scm.CreateSCM(solution["url"], self._root_dir, name) | 838 scm = gclient_scm.CreateSCM(solution["url"], self._root_dir, name) |
816 return (url, scm.revinfo(self._options, [], None)) | 839 return (url, scm.revinfo(self._options, [], None)) |
817 else: | 840 else: |
818 if revision_overrides.has_key(name): | 841 if revision_overrides.has_key(name): |
819 return (url, revision_overrides[name]) | 842 return (url, revision_overrides[name]) |
820 else: | 843 else: |
821 return (url, revision) | 844 return (url, revision) |
822 | 845 |
| 846 # text of the snapshot gclient file |
| 847 new_gclient = "" |
| 848 # Dictionary of { path : SCM url } to ensure no duplicate solutions |
| 849 solution_names = {} |
823 # Run on the base solutions first. | 850 # Run on the base solutions first. |
824 for solution in solutions: | 851 for solution in solutions: |
| 852 # Dictionary of { path : SCM url } to describe the gclient checkout |
| 853 entries = {} |
| 854 entries_deps_content = {} |
825 name = solution["name"] | 855 name = solution["name"] |
826 if name in entries: | 856 if name in solution_names: |
827 raise gclient_utils.Error("solution %s specified more than once" % name) | 857 raise gclient_utils.Error("solution %s specified more than once" % name) |
828 (url, rev) = GetURLAndRev(name, solution["url"]) | 858 (url, rev) = GetURLAndRev(name, solution["url"]) |
829 entries[name] = "%s@%s" % (url, rev) | 859 entries[name] = "%s@%s" % (url, rev) |
| 860 solution_names[name] = "%s@%s" % (url, rev) |
830 deps_file = solution.get("deps_file", self._options.deps_file) | 861 deps_file = solution.get("deps_file", self._options.deps_file) |
831 if '/' in deps_file or '\\' in deps_file: | 862 if '/' in deps_file or '\\' in deps_file: |
832 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 863 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
833 'filename.') | 864 'filename.') |
834 try: | 865 try: |
835 deps_content = gclient_utils.FileRead( | 866 deps_content = gclient_utils.FileRead( |
836 os.path.join(self._root_dir, name, deps_file)) | 867 os.path.join(self._root_dir, name, deps_file)) |
837 except IOError, e: | 868 except IOError, e: |
838 if e.errno != errno.ENOENT: | 869 if e.errno != errno.ENOENT: |
839 raise | 870 raise |
840 deps_content = "" | 871 deps_content = "" |
841 entries_deps_content[name] = deps_content | 872 entries_deps_content[name] = deps_content |
842 | 873 |
843 # Process the dependencies next (sort alphanumerically to ensure that | 874 # Process the dependencies next (sort alphanumerically to ensure that |
844 # containing directories get populated first and for readability) | 875 # containing directories get populated first and for readability) |
845 deps = self._ParseAllDeps(entries, entries_deps_content) | 876 deps = self._ParseAllDeps(entries, entries_deps_content) |
846 deps_to_process = deps.keys() | 877 deps_to_process = deps.keys() |
847 deps_to_process.sort() | 878 deps_to_process.sort() |
848 | 879 |
849 # First pass for direct dependencies. | 880 # First pass for direct dependencies. |
850 for d in deps_to_process: | 881 for d in deps_to_process: |
851 if type(deps[d]) == str: | 882 if type(deps[d]) == str: |
852 (url, rev) = GetURLAndRev(d, deps[d]) | 883 (url, rev) = GetURLAndRev(d, deps[d]) |
853 entries[d] = "%s@%s" % (url, rev) | 884 entries[d] = "%s@%s" % (url, rev) |
854 | 885 |
855 # Second pass for inherited deps (via the From keyword) | 886 # Second pass for inherited deps (via the From keyword) |
856 for d in deps_to_process: | 887 for d in deps_to_process: |
857 if type(deps[d]) != str: | 888 if type(deps[d]) != str: |
858 deps_parent_url = entries[deps[d].module_name] | 889 deps_parent_url = entries[deps[d].module_name] |
859 if deps_parent_url.find("@") < 0: | 890 if deps_parent_url.find("@") < 0: |
860 raise gclient_utils.Error("From %s missing revisioned url" % | 891 raise gclient_utils.Error("From %s missing revisioned url" % |
861 deps[d].module_name) | 892 deps[d].module_name) |
862 content = gclient_utils.FileRead(os.path.join(self._root_dir, | 893 content = gclient_utils.FileRead(os.path.join( |
863 deps[d].module_name, | 894 self._root_dir, |
864 self._options.deps_file)) | 895 deps[d].module_name, |
865 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {}) | 896 self._options.deps_file)) |
866 (url, rev) = GetURLAndRev(d, sub_deps[d]) | 897 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {}) |
867 entries[d] = "%s@%s" % (url, rev) | 898 (url, rev) = GetURLAndRev(d, sub_deps[d]) |
868 print(";\n".join(["%s: %s" % (x, entries[x]) | 899 entries[d] = "%s@%s" % (url, rev) |
869 for x in sorted(entries.keys())])) | 900 |
870 | 901 # Build the snapshot configuration string |
871 | 902 if self._options.snapshot: |
| 903 url = entries.pop(name) |
| 904 custom_deps = ",\n ".join(["\"%s\": \"%s\"" % (x, entries[x]) |
| 905 for x in sorted(entries.keys())]) |
| 906 |
| 907 new_gclient += DEFAULT_SNAPSHOT_SOLUTION_TEXT % { |
| 908 'solution_name': name, |
| 909 'solution_url': url, |
| 910 'safesync_url' : "", |
| 911 'solution_deps': custom_deps, |
| 912 } |
| 913 else: |
| 914 print(";\n".join(["%s: %s" % (x, entries[x]) |
| 915 for x in sorted(entries.keys())])) |
| 916 |
| 917 # Print the snapshot configuration file |
| 918 if self._options.snapshot: |
| 919 config = DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient} |
| 920 snapclient = GClient(self._root_dir, self._options) |
| 921 snapclient.SetConfig(config) |
| 922 print(snapclient._config_content) |
| 923 |
| 924 |
872 ## gclient commands. | 925 ## gclient commands. |
873 | 926 |
874 | 927 |
875 def DoCleanup(options, args): | 928 def DoCleanup(options, args): |
876 """Handle the cleanup subcommand. | 929 """Handle the cleanup subcommand. |
877 | 930 |
878 Raises: | 931 Raises: |
879 Error: if client isn't configured properly. | 932 Error: if client isn't configured properly. |
880 """ | 933 """ |
881 client = GClient.LoadCurrentConfig(options) | 934 client = GClient.LoadCurrentConfig(options) |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1144 default=False, | 1197 default=False, |
1145 help="Skip svn up whenever possible by requesting " | 1198 help="Skip svn up whenever possible by requesting " |
1146 "actual HEAD revision from the repository") | 1199 "actual HEAD revision from the repository") |
1147 option_parser.add_option("", "--head", action="store_true", default=False, | 1200 option_parser.add_option("", "--head", action="store_true", default=False, |
1148 help=("skips any safesync_urls specified in " | 1201 help=("skips any safesync_urls specified in " |
1149 "configured solutions")) | 1202 "configured solutions")) |
1150 option_parser.add_option("", "--delete_unversioned_trees", | 1203 option_parser.add_option("", "--delete_unversioned_trees", |
1151 action="store_true", default=False, | 1204 action="store_true", default=False, |
1152 help=("on update, delete any unexpected " | 1205 help=("on update, delete any unexpected " |
1153 "unversioned trees that are in the checkout")) | 1206 "unversioned trees that are in the checkout")) |
| 1207 option_parser.add_option("", "--snapshot", action="store_true", default=False, |
| 1208 help=("(revinfo only), create a snapshot file " |
| 1209 "of the current version of all repositories")) |
| 1210 option_parser.add_option("", "--gclientfile", default=None, |
| 1211 metavar="FILENAME", |
| 1212 help=("specify an alternate .gclient file")) |
1154 | 1213 |
1155 if len(argv) < 2: | 1214 if len(argv) < 2: |
1156 # Users don't need to be told to use the 'help' command. | 1215 # Users don't need to be told to use the 'help' command. |
1157 option_parser.print_help() | 1216 option_parser.print_help() |
1158 return 1 | 1217 return 1 |
1159 # Add manual support for --version as first argument. | 1218 # Add manual support for --version as first argument. |
1160 if argv[1] == '--version': | 1219 if argv[1] == '--version': |
1161 option_parser.print_version() | 1220 option_parser.print_version() |
1162 return 0 | 1221 return 0 |
1163 | 1222 |
1164 # Add manual support for --help as first argument. | 1223 # Add manual support for --help as first argument. |
1165 if argv[1] == '--help': | 1224 if argv[1] == '--help': |
1166 argv[1] = 'help' | 1225 argv[1] = 'help' |
1167 | 1226 |
1168 command = argv[1] | 1227 command = argv[1] |
1169 options, args = option_parser.parse_args(argv[2:]) | 1228 options, args = option_parser.parse_args(argv[2:]) |
1170 | 1229 |
1171 if len(argv) < 3 and command == "help": | 1230 if len(argv) < 3 and command == "help": |
1172 option_parser.print_help() | 1231 option_parser.print_help() |
1173 return 0 | 1232 return 0 |
1174 | 1233 |
1175 if options.verbose > 1: | 1234 if options.verbose > 1: |
1176 logging.basicConfig(level=logging.DEBUG) | 1235 logging.basicConfig(level=logging.DEBUG) |
1177 | 1236 |
1178 # Files used for configuration and state saving. | 1237 # Files used for configuration and state saving. |
1179 options.config_filename = os.environ.get("GCLIENT_FILE", ".gclient") | 1238 options.config_filename = os.environ.get("GCLIENT_FILE", ".gclient") |
1180 options.entries_filename = ".gclient_entries" | 1239 if options.gclientfile: |
| 1240 options.config_filename = options.gclientfile |
| 1241 options.entries_filename = options.config_filename + "_entries" |
1181 options.deps_file = "DEPS" | 1242 options.deps_file = "DEPS" |
1182 | 1243 |
1183 options.platform = sys.platform | 1244 options.platform = sys.platform |
1184 return DispatchCommand(command, options, args) | 1245 return DispatchCommand(command, options, args) |
1185 | 1246 |
1186 | 1247 |
1187 if "__main__" == __name__: | 1248 if "__main__" == __name__: |
1188 try: | 1249 try: |
1189 result = Main(sys.argv) | 1250 result = Main(sys.argv) |
1190 except gclient_utils.Error, e: | 1251 except gclient_utils.Error, e: |
1191 print >> sys.stderr, "Error: %s" % str(e) | 1252 print >> sys.stderr, "Error: %s" % str(e) |
1192 result = 1 | 1253 result = 1 |
1193 sys.exit(result) | 1254 sys.exit(result) |
1194 | 1255 |
1195 # vim: ts=2:sw=2:tw=80:et: | 1256 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |