#!/usr/bin/env python3 import argparse import os import subprocess import xml.etree.ElementTree as ET import sys from scripts.common import git from scripts.common import Colors from scripts.common import accept_command SCRIPTDIR = os.path.normpath(os.path.dirname(__file__)) def manifest_get_commits(manifest): res = {} tree = ET.parse(manifest) root = tree.getroot() remotes = {} for child in root: if child.tag == 'remote': remotes[child.attrib['name']] = child.attrib['fetch'] if child.tag == 'project': name = child.attrib['name'] path = child.attrib.get('path', name) remote = child.attrib.get('remote') if remote: res[path] = [child.attrib["revision"], [os.path.join(remotes[remote], name), child.attrib.get('refname', child.attrib["revision"])]] else: res[path] = [child.attrib["revision"], []] return res def get_branch_name(repo_dir): return git('-C', repo_dir, 'rev-parse', '--symbolic-full-name', 'HEAD').strip() def ensure_revision_if_necessary(repo_dir, revision): """ Makes sure that @revision is set if the current repo is detached. """ if not revision: if get_branch_name(repo_dir) == 'HEAD': revision = git('-C', repo_dir, 'rev-parse', 'HEAD').strip() return revision def update_subprojects(repos_commits, no_interaction=False): subprojects_dir = os.path.join(SCRIPTDIR, "subprojects") for repo_name in os.listdir(subprojects_dir): repo_dir = os.path.normpath(os.path.join(SCRIPTDIR, subprojects_dir, repo_name)) if not os.path.exists(os.path.join(repo_dir, '.git')): continue revision, args = repos_commits.get(repo_name, [None, []]) if not update_repo(repo_name, repo_dir, revision, no_interaction, args): return False return True def update_repo(repo_name, repo_dir, revision, no_interaction, fetch_args=[], recurse_i=0): revision = ensure_revision_if_necessary(repo_dir, revision) git("config", "rebase.autoStash", "true", repository_path=repo_dir) try: if revision: print("Checking out %s in %s" % (revision, repo_name)) git("fetch", *fetch_args, repository_path=repo_dir) git("checkout", "--detach", revision, repository_path=repo_dir) else: print("Updating branch %s in %s" % (get_branch_name(repo_dir), repo_name)) git("pull", "--rebase", repository_path=repo_dir) git("submodule", "update", repository_path=repo_dir) except Exception as e: out = getattr(e, "output", b"").decode() if not no_interaction: print("=====================================" "\n%s\nEntering a shell in %s to fix that" " just `exit 0` once done, or `exit 255`" " to skip update for that repository" "\n=====================================" % ( out, repo_dir)) try: if os.name is 'nt': shell = os.environ.get("COMSPEC", r"C:\WINDOWS\system32\cmd.exe") else: shell = os.environ.get("SHELL", os.path.realpath("/bin/sh")) subprocess.check_call(shell, cwd=repo_dir) except subprocess.CalledProcessError as e: if e.returncode == 255: print("Skipping '%s' update" % repo_name) return True except: # Result of subshell does not really matter pass if recurse_i < 3: return update_repo(repo_name, repo_dir, revision, no_interaction, recurse_i + 1) return False else: print("\nCould not rebase %s, please fix and try again." " Error:\n\n%s %s" % (repo_dir, out, e)) return False commit_message = git("show", "--shortstat", repository_path=repo_dir).split("\n") print(u" -> %s%s%s - %s" % (Colors.HEADER, commit_message[0][7:14], Colors.ENDC, commit_message[4].strip())) return True if __name__ == "__main__": parser = argparse.ArgumentParser(prog="git-update") parser.add_argument("--no-color", default=False, action='store_true', help="Do not output ansi colors.") parser.add_argument("--builddir", default=None, help="Specifies the build directory where to" " invoke ninja after updating.") parser.add_argument("--no-interaction", default=False, action='store_true', help="Do not allow interaction with the user.") parser.add_argument("--manifest", default=None, help="Use a android repo manifest to sync repositories" " Note that it will let all repositories in detached state") options = parser.parse_args() if options.no_color or not Colors.can_enable(): Colors.disable() if options.no_interaction: sys.stdin.close() if options.manifest: repos_commits = manifest_get_commits(options.manifest) else: repos_commits = {} revision, args = repos_commits.get('gst-build', [None, []]) if not update_repo('gst-build', SCRIPTDIR, revision, options.no_interaction, args): exit(1) if not update_subprojects(repos_commits, options.no_interaction): exit(1) if options.builddir: ninja = accept_command(["ninja", "ninja-build"]) if not ninja: print("Can't find ninja, other backends are not supported for rebuilding") exit(1) if not os.path.exists(os.path.join (options.builddir, 'build.ninja')): print("Can't rebuild in %s as no build.ninja file found." % options.builddir) print("Rebuilding all GStreamer modules.") exit(subprocess.call([ninja, '-C', options.builddir]))