219 lines
7.4 KiB
Python
Executable File
219 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import os
|
|
import re
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
|
|
msg_prefix = "\n> NFC-Mode:"
|
|
|
|
def get_relevant_bolt_changes(dir: str) -> str:
|
|
# Return a list of bolt source changes that are relevant to testing.
|
|
all_changes = subprocess.run(
|
|
shlex.split("git show HEAD --name-only --pretty=''"),
|
|
cwd=dir,
|
|
text=True,
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
keep_bolt = subprocess.run(
|
|
shlex.split("grep '^bolt'"),
|
|
input=all_changes.stdout,
|
|
text=True,
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
keep_relevant = subprocess.run(
|
|
shlex.split(
|
|
"grep -v -e '^bolt/docs' -e '^bolt/utils/docker' -e '^bolt/utils/dot2html'"
|
|
),
|
|
input=keep_bolt.stdout,
|
|
text=True,
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
return keep_relevant.stdout
|
|
|
|
def get_git_ref_or_rev(dir: str) -> str:
|
|
# Run 'git symbolic-ref -q --short HEAD || git rev-parse --short HEAD'
|
|
cmd_ref = "git symbolic-ref -q --short HEAD"
|
|
ref = subprocess.run(
|
|
shlex.split(cmd_ref), cwd=dir, text=True, stdout=subprocess.PIPE
|
|
)
|
|
if not ref.returncode:
|
|
return ref.stdout.strip()
|
|
cmd_rev = "git rev-parse --short HEAD"
|
|
return subprocess.check_output(shlex.split(cmd_rev), cwd=dir, text=True).strip()
|
|
|
|
def switch_back(
|
|
switch_back: bool, stash: bool, source_dir: str, old_ref: str, new_ref: str
|
|
):
|
|
# Switch back to the current revision if needed and inform the user of where
|
|
# the HEAD is. Must be called after checking out the previous commit on all
|
|
# exit paths.
|
|
if switch_back:
|
|
print(f"{msg_prefix} Switching back to current revision..")
|
|
if stash:
|
|
subprocess.run(shlex.split("git stash pop"), cwd=source_dir)
|
|
subprocess.run(shlex.split(f"git checkout {old_ref}"), cwd=source_dir)
|
|
else:
|
|
print(
|
|
f"The repository {source_dir} has been switched from {old_ref} "
|
|
f"to {new_ref}. Local changes were stashed. Switch back using\n\t"
|
|
f"git checkout {old_ref}\n"
|
|
)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description=textwrap.dedent(
|
|
"""
|
|
This script builds two versions of BOLT:
|
|
llvm-bolt.new, using the current revision, and llvm-bolt.old using
|
|
the previous revision. These can be used to check whether the
|
|
current revision changes BOLT's functional behavior.
|
|
"""
|
|
)
|
|
)
|
|
parser.add_argument(
|
|
"build_dir",
|
|
nargs="?",
|
|
default=os.getcwd(),
|
|
help="Path to BOLT build directory, default is current " "directory",
|
|
)
|
|
parser.add_argument(
|
|
"--create-wrapper",
|
|
default=False,
|
|
action="store_true",
|
|
help="Sets up llvm-bolt as a symlink to llvm-bolt-wrapper. Passes the options through to llvm-bolt-wrapper.",
|
|
)
|
|
parser.add_argument(
|
|
"--check-bolt-sources",
|
|
default=False,
|
|
action="store_true",
|
|
help="Create a marker file (.llvm-bolt.changes) if any relevant BOLT sources are modified",
|
|
)
|
|
parser.add_argument(
|
|
"--switch-back",
|
|
default=False,
|
|
action="store_true",
|
|
help="Checkout back to the starting revision",
|
|
)
|
|
parser.add_argument(
|
|
"--cmp-rev",
|
|
default="HEAD^",
|
|
help="Revision to checkout to compare vs HEAD",
|
|
)
|
|
|
|
# When creating a wrapper, pass any unknown arguments to it. Otherwise, die.
|
|
args, wrapper_args = parser.parse_known_args()
|
|
if not args.create_wrapper and len(wrapper_args) > 0:
|
|
parser.parse_args()
|
|
|
|
# Find the repo directory.
|
|
source_dir = None
|
|
try:
|
|
CMCacheFilename = f"{args.build_dir}/CMakeCache.txt"
|
|
with open(CMCacheFilename) as f:
|
|
for line in f:
|
|
m = re.match(r"LLVM_SOURCE_DIR:STATIC=(.*)", line)
|
|
if m:
|
|
source_dir = m.groups()[0]
|
|
if not source_dir:
|
|
raise Exception(f"Source directory not found: '{CMCacheFilename}'")
|
|
except Exception as e:
|
|
sys.exit(e)
|
|
|
|
# Clean the previous llvm-bolt if it exists.
|
|
bolt_path = f"{args.build_dir}/bin/llvm-bolt"
|
|
if os.path.exists(bolt_path):
|
|
os.remove(bolt_path)
|
|
|
|
# Build the current commit.
|
|
print(f"{msg_prefix} Building current revision..")
|
|
subprocess.run(
|
|
shlex.split("cmake --build . --target llvm-bolt"), cwd=args.build_dir
|
|
)
|
|
|
|
if not os.path.exists(bolt_path):
|
|
sys.exit(f"Failed to build the current revision: '{bolt_path}'")
|
|
|
|
# Rename llvm-bolt and memorize the old hash for logging.
|
|
os.replace(bolt_path, f"{bolt_path}.new")
|
|
old_ref = get_git_ref_or_rev(source_dir)
|
|
|
|
if args.check_bolt_sources:
|
|
marker = f"{args.build_dir}/.llvm-bolt.changes"
|
|
if os.path.exists(marker):
|
|
os.remove(marker)
|
|
file_changes = get_relevant_bolt_changes(source_dir)
|
|
# Create a marker file if any relevant BOLT source files changed.
|
|
if len(file_changes) > 0:
|
|
print(f"BOLT source changes were found:\n{file_changes}")
|
|
open(marker, "a").close()
|
|
|
|
# Determine whether a stash is needed.
|
|
stash = subprocess.run(
|
|
shlex.split("git status --porcelain"),
|
|
cwd=source_dir,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
).stdout
|
|
if stash:
|
|
# Save local changes before checkout.
|
|
subprocess.run(shlex.split("git stash push -u"), cwd=source_dir)
|
|
|
|
# Check out the previous/cmp commit and get its commit hash for logging.
|
|
subprocess.run(shlex.split(f"git checkout -f {args.cmp_rev}"), cwd=source_dir)
|
|
new_ref = get_git_ref_or_rev(source_dir)
|
|
|
|
# Build the previous commit.
|
|
print(f"{msg_prefix} Building previous revision..")
|
|
subprocess.run(
|
|
shlex.split("cmake --build . --target llvm-bolt"), cwd=args.build_dir
|
|
)
|
|
|
|
# Rename llvm-bolt.
|
|
if not os.path.exists(bolt_path):
|
|
print(f"Failed to build the previous revision: '{bolt_path}'")
|
|
switch_back(args.switch_back, stash, source_dir, old_ref, new_ref)
|
|
sys.exit(1)
|
|
os.replace(bolt_path, f"{bolt_path}.old")
|
|
|
|
# Symlink llvm-bolt-wrapper
|
|
if args.create_wrapper:
|
|
print(f"{msg_prefix} Creating llvm-bolt wrapper..")
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
wrapper_path = f"{script_dir}/llvm-bolt-wrapper.py"
|
|
try:
|
|
# Set up llvm-bolt-wrapper.ini
|
|
ini = subprocess.check_output(
|
|
shlex.split(f"{wrapper_path} {bolt_path}.old {bolt_path}.new")
|
|
+ wrapper_args,
|
|
text=True,
|
|
)
|
|
with open(f"{args.build_dir}/bin/llvm-bolt-wrapper.ini", "w") as f:
|
|
f.write(ini)
|
|
os.symlink(wrapper_path, bolt_path)
|
|
except Exception as e:
|
|
print("Failed to create a wrapper:\n" + str(e))
|
|
switch_back(args.switch_back, stash, source_dir, old_ref, new_ref)
|
|
sys.exit(1)
|
|
|
|
switch_back(args.switch_back, stash, source_dir, old_ref, new_ref)
|
|
|
|
print(
|
|
f"{msg_prefix} Completed!\nBuild directory {args.build_dir} is ready for"
|
|
" NFC-Mode comparison between the two revisions."
|
|
)
|
|
|
|
if args.create_wrapper:
|
|
print(
|
|
"Can run BOLT tests using:\n"
|
|
"\tbin/llvm-lit -sv tools/bolt/test\nor\n"
|
|
"\tbin/llvm-lit -sv tools/bolttests"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|