|  | # Copyright 2022 The IREE Authors | 
|  | # | 
|  | # Licensed under the Apache License v2.0 with LLVM Exceptions. | 
|  | # See https://llvm.org/LICENSE.txt for license information. | 
|  | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  |  | 
|  | import os | 
|  | import re | 
|  | import shlex | 
|  | import shutil | 
|  | import subprocess | 
|  | from typing import Tuple | 
|  |  | 
|  | _repo_root = None | 
|  |  | 
|  |  | 
|  | def get_repo_root() -> str: | 
|  | global _repo_root | 
|  | if _repo_root is None: | 
|  | _repo_root = os.getcwd() | 
|  | _validate_repo_root() | 
|  | return _repo_root | 
|  |  | 
|  |  | 
|  | def get_submodule_root(submodule) -> str: | 
|  | path = os.path.join(get_repo_root(), "third_party", submodule) | 
|  | if not os.path.isdir(path): | 
|  | raise SystemExit(f"Could not find submodule: {path}") | 
|  | return path | 
|  |  | 
|  |  | 
|  | def _validate_repo_root(): | 
|  | # Look for something we know is there. | 
|  | known_dir = os.path.join(_repo_root, "compiler") | 
|  | if not os.path.isdir(known_dir): | 
|  | raise SystemExit( | 
|  | f"ERROR: Must run from the iree repository root. " | 
|  | f"Actually in: {_repo_root}" | 
|  | ) | 
|  |  | 
|  |  | 
|  | def git_setup_remote(remote_alias, url, *, repo_dir=None): | 
|  | needs_create = False | 
|  | try: | 
|  | existing_url = git_exec( | 
|  | ["remote", "get-url", remote_alias], | 
|  | capture_output=True, | 
|  | repo_dir=repo_dir, | 
|  | quiet=True, | 
|  | ) | 
|  | existing_url = existing_url.strip() | 
|  | if existing_url == url: | 
|  | return | 
|  | except subprocess.CalledProcessError: | 
|  | # Does not exist. | 
|  | needs_create = True | 
|  |  | 
|  | if needs_create: | 
|  | git_exec(["remote", "add", "--no-tags", remote_alias, url], repo_dir=repo_dir) | 
|  | else: | 
|  | git_exec(["remote", "set-url", remote_alias, url], repo_dir=repo_dir) | 
|  |  | 
|  |  | 
|  | def git_is_porcelain(*, repo_dir=None): | 
|  | output = git_exec( | 
|  | ["status", "--porcelain", "--untracked-files=no"], | 
|  | capture_output=True, | 
|  | quiet=True, | 
|  | repo_dir=repo_dir, | 
|  | ).strip() | 
|  | return not bool(output) | 
|  |  | 
|  |  | 
|  | def git_check_porcelain(*, repo_dir=None): | 
|  | output = git_exec( | 
|  | ["status", "--porcelain", "--untracked-files=no"], | 
|  | capture_output=True, | 
|  | quiet=True, | 
|  | repo_dir=repo_dir, | 
|  | ).strip() | 
|  | if output: | 
|  | actual_repo_dir = get_repo_root() if repo_dir is None else repo_dir | 
|  | raise SystemExit( | 
|  | f"ERROR: git directory {actual_repo_dir} is not clean. " | 
|  | f"Please stash changes:\n{output}" | 
|  | ) | 
|  |  | 
|  |  | 
|  | def git_fetch(*, repository=None, ref=None, repo_dir=None): | 
|  | args = ["fetch"] | 
|  | if repository: | 
|  | args.append(repository) | 
|  | if ref is not None: | 
|  | args.append(ref) | 
|  | git_exec(args, repo_dir=repo_dir) | 
|  |  | 
|  |  | 
|  | def git_checkout(ref, *, repo_dir=None): | 
|  | git_exec(["checkout", ref], repo_dir=repo_dir) | 
|  |  | 
|  |  | 
|  | def git_create_branch( | 
|  | branch_name, *, checkout=True, ref=None, force=False, repo_dir=None | 
|  | ): | 
|  | branch_args = ["branch"] | 
|  | if force: | 
|  | branch_args.append("-f") | 
|  | branch_args.append(branch_name) | 
|  | if ref is not None: | 
|  | branch_args.append(ref) | 
|  | git_exec(branch_args, repo_dir=repo_dir) | 
|  |  | 
|  | if checkout: | 
|  | git_exec(["checkout", branch_name], repo_dir=repo_dir) | 
|  |  | 
|  |  | 
|  | def git_push_branch(repository, branch_name, *, force=False, repo_dir=None): | 
|  | push_args = ["push", "--set-upstream"] | 
|  | if force: | 
|  | push_args.append("-f") | 
|  | push_args.append(repository) | 
|  | push_args.append(f"{branch_name}:{branch_name}") | 
|  | git_exec(push_args, repo_dir=repo_dir) | 
|  |  | 
|  |  | 
|  | def git_branch_exists(branch_name, *, repo_dir=None): | 
|  | output = git_exec( | 
|  | ["branch", "-l", branch_name], | 
|  | repo_dir=repo_dir, | 
|  | quiet=True, | 
|  | capture_output=True, | 
|  | ).strip() | 
|  | return bool(output) | 
|  |  | 
|  |  | 
|  | def git_submodule_set_origin(path, *, url=None, branch=None, repo_dir=None): | 
|  | if url is not None: | 
|  | git_exec(["submodule", "set-url", "--", path, url], repo_dir=repo_dir) | 
|  |  | 
|  | if branch is not None: | 
|  | try: | 
|  | if branch == "--default": | 
|  | git_exec( | 
|  | ["submodule", "set-branch", "--default", "--", path], | 
|  | repo_dir=repo_dir, | 
|  | ) | 
|  | else: | 
|  | git_exec( | 
|  | ["submodule", "set-branch", "--branch", branch, "--", path], | 
|  | repo_dir=repo_dir, | 
|  | ) | 
|  | except subprocess.CalledProcessError: | 
|  | # The set-branch command returns 0 on change and !0 on no change. | 
|  | # This is a bit unfortunate. | 
|  | ... | 
|  |  | 
|  |  | 
|  | def git_reset(ref, *, hard=True, repo_dir=None): | 
|  | args = ["reset"] | 
|  | if hard: | 
|  | args.append("--hard") | 
|  | args.append(ref) | 
|  | git_exec(args, repo_dir=repo_dir) | 
|  |  | 
|  |  | 
|  | def git_current_commit(*, repo_dir=None) -> Tuple[str, str]: | 
|  | output = git_exec( | 
|  | ["log", "-n", "1", "--pretty=format:%H (%ci): %s"], | 
|  | capture_output=True, | 
|  | repo_dir=repo_dir, | 
|  | quiet=True, | 
|  | ) | 
|  | output = output.strip() | 
|  | parts = output.split(" ") | 
|  | # Return commit, full_summary | 
|  | return parts[0], output | 
|  |  | 
|  |  | 
|  | def git_create_commit(*, message, add_all=False, repo_dir=None): | 
|  | if add_all: | 
|  | git_exec(["add", "-A"], repo_dir=repo_dir) | 
|  | git_exec(["commit", "-m", message]) | 
|  |  | 
|  |  | 
|  | def git_ls_remote_branches(repository_url, *, filter=None, repo_dir=None): | 
|  | args = ["ls-remote", "-h", repository_url] | 
|  | if filter: | 
|  | args.extend(filter) | 
|  | output = git_exec(args, quiet=True, capture_output=True) | 
|  | lines = output.strip().splitlines(keepends=False) | 
|  |  | 
|  | # Format is <commit> refs/heads/branch_name | 
|  | def extract_branch(line): | 
|  | parts = re.split("\\s+", line) | 
|  | ref = parts[1] | 
|  | prefix = "refs/heads/" | 
|  | if ref.startswith(prefix): | 
|  | ref = ref[len(prefix) :] | 
|  | return ref | 
|  |  | 
|  | return [extract_branch(l) for l in lines] | 
|  |  | 
|  |  | 
|  | def git_exec(args, *, repo_dir=None, quiet=False, capture_output=False): | 
|  | full_args = ["git"] + args | 
|  | full_args_quoted = [shlex.quote(a) for a in full_args] | 
|  | if not repo_dir: | 
|  | repo_dir = get_repo_root() | 
|  | if not quiet: | 
|  | print(f"  ++ EXEC: (cd {repo_dir} && {' '.join(full_args_quoted)})") | 
|  | if capture_output: | 
|  | return subprocess.check_output(full_args, cwd=repo_dir).decode("utf-8") | 
|  | else: | 
|  | subprocess.check_call(full_args, cwd=repo_dir) |