| 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 |