OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # | |
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 """Generates a pinned solutions file with relative git repository URLs. | |
8 | |
9 make_relative_solution reads a pinned solution file generated by | |
10 'gclient revinfo --snapshot' and writes to stdout a pinned solution file | |
11 with relative git repository URLs. | |
12 | |
13 | |
14 The resulting solution file can be used to check out a fixed version of | |
15 a gclient set of repositories. The base URL to fetch from can be changed | |
16 by editing one line in the generated solution file. | |
17 """ | |
18 | |
19 import optparse | |
20 import sys | |
21 | |
22 | |
23 def ReadSnapshot(filename): | |
24 """Reads a gclient revinfo snapshot file. | |
25 | |
26 Minimal verification of the structure of the file is performed. | |
27 | |
28 Args: | |
29 filename: The name of a snapshot file to read. | |
30 | |
31 Returns: | |
32 The solutions array parsed from the snapshot file. | |
33 """ | |
34 | |
35 env = {} | |
36 execfile(filename, env) | |
37 | |
38 assert 'solutions' in env | |
39 assert env['solutions'] | |
40 | |
41 return env['solutions'] | |
42 | |
43 | |
44 def BaseRepository(url): | |
45 """Finds the base repository path. | |
46 | |
47 This only works if the top level repository is not in a subdirectory relative | |
48 to the other repositories on the server. | |
49 | |
50 Args: | |
51 url: git repository url | |
52 | |
53 Returns: | |
54 The prefix of the URL that does not contain the repository name and SHA. | |
55 """ | |
56 | |
57 base, versioned_repository = url.rsplit('/', 1) | |
58 | |
59 assert base and versioned_repository | |
60 return base | |
61 | |
62 | |
63 def WriteBaseURL(base, solution): | |
64 print ' "%s": "%s",' % (solution['name'], base) | |
65 | |
66 | |
67 def IsRelativeRepository(base, url): | |
68 return url.startswith(base) | |
69 | |
70 | |
71 def RelativeRepository(base, url): | |
72 if IsRelativeRepository(base, url): | |
73 return url[len(base):] | |
74 else: | |
75 return url | |
76 | |
77 | |
78 def RelativeDep(base, dep): | |
79 path, repository = dep | |
80 | |
81 return (path, | |
82 RelativeRepository(base, repository), | |
83 IsRelativeRepository(base, repository)) | |
84 | |
85 | |
86 def RelativeDeps(base, solution): | |
87 return [RelativeDep(base, dep) for dep in solution['custom_deps'].items()] | |
88 | |
89 | |
90 def WritePinnedDep(name, dep, indent): | |
91 """Writes a pinned dep. | |
92 | |
93 The output is indented so that the URLs all line up for ease of reading. If | |
94 the dep is for a relative git repository then we emit the base_url lookup as | |
95 well. | |
96 | |
97 Args: | |
98 name: The name of the solution that is being written out. | |
99 dep: The relative dep that is to be written out. | |
100 indent: The total number of characters to use for the path component. | |
101 | |
102 Returns: | |
103 Nothing | |
104 """ | |
105 | |
106 path, repository, relative = dep | |
107 remainder = path.partition('/')[2] | |
108 spaces = indent - len(path) | |
109 | |
110 if remainder == 'deps': | |
111 return | |
112 | |
113 if relative: | |
114 print ' "%s": %*sbase_url["%s"] + "%s",' % (path, | |
115 spaces, '', | |
116 name, | |
117 repository) | |
118 else: | |
119 print ' "%s": %*s"%s",' % (path, | |
120 spaces, '', | |
121 repository) | |
122 | |
123 | |
124 def WritePinnedSolution(solution): | |
125 """Writes out a pinned and solution file with relative repository paths. | |
126 | |
127 The relative repository paths make it easier for a user to modify where | |
128 they are pulling source from. | |
129 | |
130 Args: | |
131 solution: gclient solution object. | |
132 | |
133 Returns: | |
134 Nothing | |
135 """ | |
136 | |
137 base = BaseRepository(solution['url']) | |
138 url = RelativeRepository(base, solution['url']) | |
139 deps = RelativeDeps(base, solution) | |
140 indent = max(len(dep[0]) for dep in deps) | |
141 | |
142 deps.sort(key=lambda dep: dep[1]) | |
143 | |
144 print (' { "name" : "%s",\n' | |
145 ' "url" : base_url["%s"] + "%s",\n' | |
146 ' "custom_deps" : {') % (solution['name'], | |
147 solution['name'], | |
148 url) | |
149 | |
150 for dep in deps: | |
151 WritePinnedDep(solution['name'], dep, indent) | |
152 | |
153 print (' },\n' | |
154 ' },') | |
155 | |
156 | |
157 def main(argv): | |
158 usage = 'Usage: %prog [options] filename' | |
159 option_parser = optparse.OptionParser(usage=usage) | |
160 option_parser.disable_interspersed_args() | |
161 option_parser.add_option('-s', '--substitute', | |
162 action='store_true', | |
163 dest='substitute', | |
164 default=False, | |
165 help='substitute a new base git URL') | |
166 option_parser.add_option('-b', '--base', | |
167 dest='base', | |
168 default='http://src.chromium.org/git', | |
169 metavar='URL', | |
170 help='base git URL to substitute [%default]') | |
171 | |
172 options, args = option_parser.parse_args(argv[1:]) | |
173 | |
174 if len(args) != 1: | |
175 option_parser.print_help() | |
176 return 1 | |
177 | |
178 filename = args.pop(0) | |
179 solutions = ReadSnapshot(filename) | |
180 | |
181 print ('#\n' | |
182 '# Autogenerated pinned gclient solution file. This file was\n' | |
183 '# created by running make_relative_solution.\n' | |
184 '#\n' | |
185 '\n' | |
186 'base_url = {') | |
187 | |
188 for solution in solutions: | |
189 if options.substitute: | |
190 base = options.base | |
191 else: | |
192 base = BaseRepository(solution['url']) | |
193 | |
194 WriteBaseURL(base, solution) | |
195 | |
196 print ('}\n' | |
197 '\n' | |
198 'solutions = [') | |
199 | |
200 for solution in solutions: | |
201 WritePinnedSolution(solution) | |
202 | |
203 print ']\n' | |
204 | |
205 | |
206 if __name__ == '__main__': | |
207 main(sys.argv) | |
OLD | NEW |