+++ /dev/null
-#!/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
-from scripts.common import get_meson
-
-
-SCRIPTDIR = os.path.normpath(os.path.dirname(__file__))
-# Force a checkout to happen and throw away local changes
-FORCE_CHECKOUT = False
-CI = os.environ.get('CI', False)
-
-
-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(manifest, no_interaction=False, status=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, fetch_args=args, status=status):
- return False
-
- return True
-
-def repo_status(commit_message):
- status = "clean"
- for message in commit_message:
- if message.startswith('??'):
- status = "%sclean but untracked files%s" % (Colors.WARNING,Colors.ENDC)
- elif message.startswith(' M'):
- status = "%shas local modifications%s" % (Colors.WARNING,Colors.ENDC)
- break;
- return status
-
-def check_repo_status(repo_name, worktree_dir):
- branch_message = git("status", repository_path=worktree_dir).split("\n")
- commit_message = git("status", "--porcelain", repository_path=worktree_dir).split("\n")
-
- print(u"%s%s%s - %s - %s" % (Colors.HEADER, repo_name, Colors.ENDC,
- branch_message[0].strip(), repo_status(commit_message)))
- return True
-
-def fatal_git_fetches(repo_dir):
- '''
- When running on the CI, we usually have a cache, in which case we don't
- want the git update to be fatal since we don't want our CI to fail when
- there's downtime on external repos.
- '''
- if not CI:
- return True
- remote = git("remote", "get-url", "origin", repository_path=repo_dir)
- if 'gitlab.freedesktop.org' in remote:
- return True
- return False
-
-def update_repo(repo_name, repo_dir, revision, no_interaction, fetch_args=None, recurse_i=0, status=False):
- if status:
- return check_repo_status(repo_name, repo_dir)
- revision = ensure_revision_if_necessary(repo_dir, revision)
- git("config", "rebase.autoStash", "true", repository_path=repo_dir)
-
- fetch_args = fetch_args if fetch_args is not None else []
- fetch_args.append('--tags')
- fatal = fatal_git_fetches(repo_dir)
- try:
- if revision:
- print("Checking out %s in %s" % (revision, repo_name))
- git("fetch", *fetch_args, repository_path=repo_dir, fatal=fatal)
- checkout_args = ["--force"] if FORCE_CHECKOUT else []
- checkout_args += ["--detach", revision]
- git("checkout", *checkout_args, 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, fatal=fatal)
- 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 == '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=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
-
-
-# Update gst-plugins-rs dependencies
-def update_cargo(build_dir):
- cargo_toml = os.path.join('subprojects', 'gst-plugins-rs', 'Cargo.toml')
- if not os.path.exists(cargo_toml):
- return True
-
- cmd = ['cargo', 'update', '--manifest-path', cargo_toml]
-
- try:
- ret = subprocess.run(cmd)
- except FileNotFoundError:
- # silenty ignore if cargo isn't installed
- return False
-
- return ret == 0
-
-
-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("--status",
- default=False,
- action='store_true',
- help="Check repositories status only.")
- 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:
- meson = get_meson()
- try:
- targets_s = subprocess.check_output(meson + ['subprojects', 'download'], shell=False, universal_newlines=True)
- except subprocess.CalledProcessError as err:
- print(f"Subproject download Failed")
- print(f"Output: {err.output}")
- print(f"Exit code: {err.returncode}")
- exit(err.returncode)
-
- repos_commits = manifest_get_commits(options.manifest)
- FORCE_CHECKOUT = True
- else:
- repos_commits = {}
-
- revision, args = repos_commits.get('gst-build', [None, []])
- if not update_repo('gst-build', SCRIPTDIR, revision, options.no_interaction, fetch_args=args, status=options.status):
- exit(1)
- if not update_subprojects(options.manifest, options.no_interaction, status=options.status):
- exit(1)
- if not options.status:
- update_cargo(options.builddir)
-
- 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]))
+++ /dev/null
-#!/usr/bin/env python3
-
-import os
-import glob
-import argparse
-import subprocess
-import configparser
-
-from scripts.common import git
-from scripts.common import Colors
-
-
-SCRIPTDIR = os.path.normpath(os.path.dirname(__file__))
-SUBPROJECTS_DIR = os.path.normpath(os.path.join(SCRIPTDIR, "subprojects"))
-
-
-def repo_has_branch(repo_dir, branch):
- if not branch:
- return False
- try:
- git("describe", branch, repository_path=repo_dir)
- except subprocess.CalledProcessError:
- return False
- return True
-
-def parse_wrapfile(wrapf):
- cgp = configparser.ConfigParser()
- cgp.read(wrapf)
- if 'wrap-git' not in cgp:
- return None
- section = cgp['wrap-git']
- # Default to the wrapper filename if 'directory' field is missing
- directory = section.get('directory', os.path.splitext(os.path.basename(wrapf))[0])
- return directory, section['revision']
-
-def get_wrap_subprojects(srcdir, gst_branch):
- '''
- Parses wrap files in the subprojects directory for the specified source
- tree and gets the revisions for all common repos.
- '''
- for wrapf in glob.glob(os.path.join(srcdir, 'subprojects', '*.wrap')):
- entries = parse_wrapfile(wrapf)
- if not entries:
- continue
-
- repo_name, repo_branch = entries
- parent_repo_dir = os.path.join(SUBPROJECTS_DIR, repo_name)
- if not os.path.exists(os.path.join(parent_repo_dir, '.git')):
- continue
- # If a branch of the same name exists in the gst subproject, use it
- if repo_name.startswith('gst') and repo_has_branch(parent_repo_dir, gst_branch):
- repo_branch = gst_branch
-
- yield repo_name, repo_branch, parent_repo_dir
-
-def checkout_worktree(repo_name, repo_dir, worktree_dir, branch, new_branch, force=False):
- print('Checking out worktree for project {!r} into {!r} '
- '(branch {})'.format(repo_name, worktree_dir, branch))
- try:
- args = ["worktree", "add"]
- if force:
- args += ["-f", "-f"]
- args += [worktree_dir, branch]
- if new_branch:
- args += ["-b", new_branch]
- git(*args, repository_path=repo_dir)
- except subprocess.CalledProcessError as e:
- out = getattr(e, "output", b"").decode()
- print("\nCould not checkout worktree %s, please fix and try again."
- " Error:\n\n%s %s" % (repo_dir, out, e))
-
- return False
-
- commit_message = git("show", "--format=medium", "--shortstat", repository_path=repo_dir).split("\n")
- print(u" -> %s%s%s - %s" % (Colors.HEADER, repo_dir, Colors.ENDC,
- commit_message[4].strip()))
- return True
-
-def checkout_subprojects(worktree_dir, branch, new_branch):
- worktree_subdir = os.path.join(worktree_dir, "subprojects")
-
- for repo_name, repo_branch, parent_repo_dir in get_wrap_subprojects(worktree_dir, branch):
- workdir = os.path.normpath(os.path.join(worktree_subdir, repo_name))
- if not checkout_worktree(repo_name, parent_repo_dir, workdir, repo_branch, new_branch, force=True):
- return False
-
- return True
-
-def remove_worktree(worktree_dir):
- worktree_subdir = os.path.join(worktree_dir, "subprojects")
-
- for repo_name, _, parent_repo_dir in get_wrap_subprojects(worktree_dir, None):
- workdir = os.path.normpath(os.path.join(worktree_subdir, repo_name))
- if not os.path.exists(workdir):
- continue
-
- subprojdir = os.path.normpath(os.path.join(SUBPROJECTS_DIR, repo_name))
- if not os.path.exists(subprojdir):
- continue
-
- print('Removing worktree {!r}'.format(workdir))
- try:
- git('worktree', 'remove', '-f', workdir, repository_path=subprojdir)
- except subprocess.CalledProcessError as e:
- out = getattr(e, "output", b"").decode()
- print('Ignoring error while removing worktree {!r}:\n\n{}'.format(workdir, out))
-
- try:
- git('worktree', 'remove', '-f', worktree_dir, repository_path=SCRIPTDIR)
- except subprocess.CalledProcessError:
- print('Failed to remove worktree {!r}'.format(worktree_dir))
- return False
- return True
-
-
-if __name__ == "__main__":
- parser = argparse.ArgumentParser(prog="gst-worktree")
- parser.add_argument("--no-color", default=False, action='store_true',
- help="Do not output ANSI colors")
-
- subparsers = parser.add_subparsers(help='The sub-command to run', dest='command')
-
- parser_add = subparsers.add_parser('add',
- help='Create a worktree for gst-build and all subprojects')
- parser_add.add_argument('worktree_dir', type=str,
- help='Directory where to create the new worktree')
- parser_add.add_argument('branch', type=str, default=None,
- help='Branch to checkout')
- parser_add.add_argument('-b', '--new-branch', type=str, default=None,
- help='Branch to create')
-
- parser_rm = subparsers.add_parser('rm',
- help='Remove a gst-build worktree and the subproject worktrees inside it')
- parser_rm.add_argument('worktree_dir', type=str,
- help='Worktree directory to remove')
-
- options = parser.parse_args()
-
- if options.no_color or not Colors.can_enable():
- Colors.disable()
-
- if not options.command:
- parser.print_usage()
- exit(1)
-
- worktree_dir = os.path.abspath(options.worktree_dir)
-
- if options.command == 'add':
- if not checkout_worktree('gst-build', SCRIPTDIR, worktree_dir, options.branch, options.new_branch):
- exit(1)
- if not checkout_subprojects(worktree_dir, options.branch, options.new_branch):
- exit(1)
- elif options.command == 'rm':
- if not os.path.exists(worktree_dir):
- print('Cannot remove worktree directory {!r}, it does not exist'.format(worktree_dir))
- exit(1)
- if not remove_worktree(worktree_dir):
- exit(1)
- else:
- # Unreachable code
- raise AssertionError