From fa0814009381066f1304b17726af318abae712e1 Mon Sep 17 00:00:00 2001 From: robotboy Date: Thu, 20 May 2010 10:18:55 -0700 Subject: [PATCH] Create a tool to save a pinned dep file for the current sync. Review URL: http://codereview.chromium.org/2112003 --- make_relative_solution | 207 +++++++++++++++++++++++++++++++++++++++++ save_pinned_deps | 128 +++++++++++++++++++++++++ 2 files changed, 335 insertions(+) create mode 100755 make_relative_solution create mode 100755 save_pinned_deps diff --git a/make_relative_solution b/make_relative_solution new file mode 100755 index 0000000000..d12159e1b2 --- /dev/null +++ b/make_relative_solution @@ -0,0 +1,207 @@ +#!/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) diff --git a/save_pinned_deps b/save_pinned_deps new file mode 100755 index 0000000000..ab2061359c --- /dev/null +++ b/save_pinned_deps @@ -0,0 +1,128 @@ +#!/bin/bash + +# 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. + +# Save the current state of the tree into a pinned deps file that can later +# be used to reconstruct the same tree. + +# Load common constants. This should be the first executable line. +# The path to common.sh should be relative to your script's location. +. "$(dirname "$0")/common.sh" + +# Script must be run outside the chroot, I am not sure why this is but inside +# the chroot "gclient" is aliased to a warning about using gclient in the +# chroot. +assert_outside_chroot + +# Flags +BASE_URL="http://src.chromium.org/git" + +DEFINE_string depfile "" "The path to the depfile to create." +DEFINE_boolean commit ${FLAGS_FALSE} "Commit the resulting depfile." +DEFINE_boolean substitute ${FLAGS_FALSE} "Substitute a new base git URL." +DEFINE_string base ${BASE_URL} "Base git URL to substitute" + +# Parse command line +FLAGS_HELP="usage: $0 [flags]" +FLAGS "$@" || exit 1 +eval set -- "${FLAGS_ARGV}" +check_flags_only_and_allow_null_arg "$@" && set -- + +# Die on any errors. +set -e + +if [ -z "$FLAGS_depfile" ] ; then + echo "Error: --depfile is required." + exit 1 +fi + +DEPPATH="${GCLIENT_ROOT}/deps" +DEPFILE="${DEPPATH}/${FLAGS_depfile}" + +TEMPFILE=$(tempfile) +DIRNAME=$(dirname "${DEPFILE}") +FILENAME=$(basename "${DEPFILE}") + +cleanup() { + # Disable die on error. + set +e + + if [ -f "${TEMPFILE}" ]; then + rm "${TEMPFILE}" + fi + + if [ -f "${DEPFILE}" ]; then + rm "${DEPFILE}" + fi + + # Turn die on error back on. + set -e +} + +reset_repository() { + echo "Resetting DEPS repository" + + pushd "${DEPPATH}" + + [ -d ".git" ] || die "${DEPPATH} is not a git repository." + + git reset --hard origin/master + + popd +} + +generate_depfile() { + echo "Writing pinned DEPS file to ${DEPFILE}" + + mkdir -p "${DIRNAME}" + + gclient revinfo --snapshot > "${TEMPFILE}" + + ARGS="" + + if [[ $FLAGS_substitute -eq $FLAGS_TRUE ]]; then + ARGS="${ARGS} -s -b ${FLAGS_base}" + fi + + ARGS="${ARGS} ${TEMPFILE}" + + "${SCRIPTS_DIR}/make_relative_solution" ${ARGS} > ${DEPFILE} + + rm -f "${TEMPFILE}" +} + +commit_depfile() { + echo "Commiting pinned DEPS file" + + pushd "${DEPPATH}" + + git add "${FLAGS_depfile}" + git commit -m "Automated buildbot update of pinned DEPS file." + git reset --hard HEAD + git clean -f + git remote update + git rebase -s ours origin/master + git push + + popd +} + +# +# Generate a pinned deps file from the current gclient sync and check it into +# the deps.git repository. +# +trap "cleanup" EXIT + +if [[ $FLAGS_commit -eq $FLAGS_TRUE ]]; then + reset_repository +fi + +generate_depfile + +if [[ $FLAGS_commit -eq $FLAGS_TRUE ]]; then + commit_depfile +fi + +trap - EXIT