#!/usr/bin/python # # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Generates a pinned solutions file with relative git repository URLs. make_relative_solution reads a pinned solution file generated by 'gclient revinfo --snapshot' and writes to stdout a pinned solution file with relative git repository URLs. The resulting solution file can be used to check out a fixed version of a gclient set of repositories. The base URL to fetch from can be changed by editing one line in the generated solution file. """ import optparse import sys def ReadSnapshot(filename): """Reads a gclient revinfo snapshot file. Minimal verification of the structure of the file is performed. Args: filename: The name of a snapshot file to read. Returns: The solutions array parsed from the snapshot file. """ env = {} execfile(filename, env) assert 'solutions' in env assert env['solutions'] return env['solutions'] def BaseRepository(url): """Finds the base repository path. This only works if the top level repository is not in a subdirectory relative to the other repositories on the server. Args: url: git repository url Returns: The prefix of the URL that does not contain the repository name and SHA. """ base, versioned_repository = url.rsplit('/', 1) assert base and versioned_repository return base def WriteBaseURL(base, solution): print ' "%s": "%s",' % (solution['name'], base) def IsRelativeRepository(base, url): return url.startswith(base) def RelativeRepository(base, url): if IsRelativeRepository(base, url): return url[len(base):] else: return url def RelativeDep(base, dep): path, repository = dep return (path, RelativeRepository(base, repository), IsRelativeRepository(base, repository)) def RelativeDeps(base, solution): return [RelativeDep(base, dep) for dep in solution['custom_deps'].items()] def WritePinnedDep(name, dep, indent): """Writes a pinned dep. The output is indented so that the URLs all line up for ease of reading. If the dep is for a relative git repository then we emit the base_url lookup as well. Args: name: The name of the solution that is being written out. dep: The relative dep that is to be written out. indent: The total number of characters to use for the path component. Returns: Nothing """ path, repository, relative = dep remainder = path.partition('/')[2] spaces = indent - len(path) if remainder == 'deps': return if relative: print ' "%s": %*sbase_url["%s"] + "%s",' % (path, spaces, '', name, repository) else: print ' "%s": %*s"%s",' % (path, spaces, '', repository) def WritePinnedSolution(solution): """Writes out a pinned and solution file with relative repository paths. The relative repository paths make it easier for a user to modify where they are pulling source from. Args: solution: gclient solution object. Returns: Nothing """ base = BaseRepository(solution['url']) url = RelativeRepository(base, solution['url']) deps = RelativeDeps(base, solution) indent = max(len(dep[0]) for dep in deps) deps.sort(key=lambda dep: dep[1]) print (' { "name" : "%s",\n' ' "url" : base_url["%s"] + "%s",\n' ' "custom_deps" : {') % (solution['name'], solution['name'], url) for dep in deps: WritePinnedDep(solution['name'], dep, indent) print (' },\n' ' },') def main(argv): usage = 'Usage: %prog [options] filename' option_parser = optparse.OptionParser(usage=usage) option_parser.disable_interspersed_args() option_parser.add_option('-s', '--substitute', action='store_true', dest='substitute', default=False, help='substitute a new base git URL') option_parser.add_option('-b', '--base', dest='base', default='http://src.chromium.org/git', metavar='URL', help='base git URL to substitute [%default]') options, args = option_parser.parse_args(argv[1:]) if len(args) != 1: option_parser.print_help() return 1 filename = args.pop(0) solutions = ReadSnapshot(filename) print ('#\n' '# Autogenerated pinned gclient solution file. This file was\n' '# created by running make_relative_solution.\n' '#\n' '\n' 'base_url = {') for solution in solutions: if options.substitute: base = options.base else: base = BaseRepository(solution['url']) WriteBaseURL(base, solution) print ('}\n' '\n' 'solutions = [') for solution in solutions: WritePinnedSolution(solution) print ']\n' if __name__ == '__main__': main(sys.argv)