xamarin-macios/tools/compare-commits.sh

254 строки
8.7 KiB
Bash
Исходник Обычный вид История

#!/bin/bash -e
BASE_HASH=
COMP_HASH=HEAD
WORKING_DIR=
WHITE=$(tput setaf 7 2>/dev/null || true)
BLUE=$(tput setaf 6 2>/dev/null || true)
RED=$(tput setaf 9 2>/dev/null || true)
CLEAR=$(tput sgr0 2>/dev/null || true)
# Clone files on High Sierra, instead of copying them. Much faster.
CP="cp"
if df -t apfs / >/dev/null 2>&1; then
CP="cp -c"
fi
function report_error_line ()
{
echo "$@"
if test -n "$FAILURE_FILE"; then
# remove color codes when writing to failure file
# shellcheck disable=SC2001
echo "$@" | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g' >> "$FAILURE_FILE"
fi
}
function show_help ()
{
echo "$(basename "$0"): Compare the managed API and generate a diff for the generated code between the currently built assemblies and a specific hash."
echo "Usage is: $(basename "$0") --base=[TREEISH] [options]"
echo " -h, -?, --help Displays the help"
echo " -b, --base=[TREEISH] The treeish to compare the currently built assemblies against."
#not quite implemented yet# echo " -c, --compare=[TREEISH] Optional, if specified use this hash to build the 'after' assemblies for the comparison."
echo ""
printf "${BLUE} WARNING: This tool will temporarily change the current HEAD of your git checkout.${CLEAR}\\n"
printf "${BLUE} WARNING: The tool will try to restore the current HEAD when done (or if cancelled), but this is not guaranteed.${CLEAR}\\n"
echo ""
}
ORIGINAL_ARGS=("$@")
FAILURE_FILE=
while ! test -z "$1"; do
case "$1" in
--help|-\?|-h)
show_help
exit 0
;;
--base=*|-b=*)
BASE_HASH="${1#*=}"
shift
;;
--base|-b)
BASE_HASH="$2"
shift 2
;;
--compare=*|-c=*)
COMP_HASH="${1#*=}"
shift
;;
--compare|-c)
COMP_HASH="$2"
shift 2
;;
--impl-working-dir=*)
WORKING_DIR="${1#*=}"
shift
;;
--failure-file=*)
FAILURE_FILE="${1#*=}"
shift
;;
--failure-file)
FAILURE_FILE="$2"
shift 2
;;
*)
echo "Error: Unknown argument: $1"
exit 1
;;
esac
done
if test -z "$BASE_HASH"; then
report_error_line "${RED}Error: It's required to specify the hash to compare against (--base=HASH).${CLEAR}"
exit 1
fi
ROOT_DIR=$(git rev-parse --show-toplevel)
# We'll checkout another hash, which may not have this script, and executing a script that is deleted
# sounds like a bad idea. So copy the scripts to /tmp and execute it from there
if test -z "$WORKING_DIR"; then
$CP "$ROOT_DIR/tools/compare-commits.sh" "$ROOT_DIR/tools/diff-to-html" "$TMPDIR/"
exec "$TMPDIR/$(basename "$0")" "${ORIGINAL_ARGS[@]}" "--impl-working-dir=$(pwd)"
exit $?
fi
cd "$WORKING_DIR"
# Go to the root directory of the git repo, so that we don't run into any surprises with paths.
# Also make ROOT_DIR an absolute path.
cd "$ROOT_DIR"
ROOT_DIR=$(pwd)
# Only show colors locally. The normal "has-controlling-terminal" doesn't work, because
# we always capture the output to indent it (thus the git processes never have a controlling
# terminal)
if test -z "$BUILD_REVISION"; then
GIT_COLOR=--color=always
GIT_COLOR_P="-c color.status=always"
fi
if [ -n "$(git status --porcelain --ignore-submodule)" ]; then
report_error_line "${RED}** Error: Working directory isn't clean:${CLEAR}"
git $GIT_COLOR_P status --ignore-submodule | sed 's/^/ /' | while read line; do report_error_line "$line"; done
exit 1
fi
echo "Comparing the changes between $WHITE$BASE_HASH$CLEAR and $WHITE$COMP_HASH$CLEAR:"
git log "$BASE_HASH..$COMP_HASH" --oneline $GIT_COLOR | sed 's/^/ /'
# Resolve any treeish hash value (for instance HEAD^4) to the unique (MD5) hash
COMP_HASH=$(git log -1 --pretty=%H "$COMP_HASH")
BASE_HASH=$(git log -1 --pretty=%H "$BASE_HASH")
# Save the current branch/hash
CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || true)
CURRENT_HASH=$(git log -1 --pretty=%H)
GENERATOR_DIFF_FILE=
APIDIFF_FILE=
function upon_exit ()
{
if test -z "$CURRENT_BRANCH"; then
echo "Restoring the previous hash ${BLUE}${CURRENT_HASH}${CLEAR} (there was no previous branch; probably because HEAD was detached)"
git checkout --force "$CURRENT_HASH"
echo "Previous hash restored successfully."
else
echo "Restoring the previous branch ${BLUE}$CURRENT_BRANCH${CLEAR}..."
git checkout --quiet --force "$CURRENT_BRANCH"
git reset --hard "$CURRENT_HASH"
echo "Previous branch restored successfully."
fi
if ! test -z "$GENERATOR_DIFF_FILE"; then
echo "Generator diff: $GENERATOR_DIFF_FILE"
fi
if ! test -z "$APIDIFF_FILE"; then
echo "API diff: $APIDIFF_FILE"
fi
}
trap upon_exit EXIT
OUTPUT_SUBDIR=tools/comparison
OUTPUT_DIR=$ROOT_DIR/$OUTPUT_SUBDIR
rm -Rf "$OUTPUT_DIR"
# Create fake destination directories in $OUTPUT_DIR
# We will build in src/ setting DESTDIR to these destination directories, but the
# build in src/ depends on a few files installed from builds/, so copy those files
# from the normal destination directories.
echo "${BLUE}Preparing temporary output directory...${CLEAR}"
mkdir -p "$OUTPUT_DIR/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono"
mkdir -p "$OUTPUT_DIR/_mac-build/Library/Frameworks/Xamarin.Mac.framework/Versions/git/lib/mono"
mkdir -p "$OUTPUT_DIR/project-files"
ln -s git "$OUTPUT_DIR/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/Current"
ln -s git "$OUTPUT_DIR/_mac-build/Library/Frameworks/Xamarin.Mac.framework/Versions/Current"
for dir in 2.1 Xamarin.iOS Xamarin.TVOS Xamarin.WatchOS; do
$CP -R "$ROOT_DIR/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono/$dir" "$OUTPUT_DIR/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono"
done
for dir in Xamarin.Mac 4.5; do
$CP -R "$ROOT_DIR/_mac-build/Library/Frameworks/Xamarin.Mac.framework/Versions/git/lib/mono/$dir" "$OUTPUT_DIR/_mac-build/Library/Frameworks/Xamarin.Mac.framework/Versions/git/lib/mono"
done
if test -z "$CURRENT_BRANCH"; then
echo "${BLUE}Current hash: ${WHITE}$(git log -1 --pretty="%h: %s")${BLUE} (detached)${CLEAR}"
else
echo "${BLUE}Current branch: ${WHITE}$CURRENT_BRANCH${BLUE} (${WHITE}$(git log -1 --pretty="%h: %s")${BLUE})${CLEAR}"
fi
echo "${BLUE}Checking out ${WHITE}$(git log -1 --pretty="%h: %s" "$BASE_HASH")${CLEAR}...${CLEAR}"
git checkout --quiet --force --detach "$BASE_HASH"
# To ensure that our logic below doesn't modify files it shouldn't, we create a stamp
# file, and compare the timestamps of all the files that shouldn't be modified to this
# file's timestamp.
touch "$OUTPUT_DIR/stamp"
echo "${BLUE}Building src/...${CLEAR}"
if ! make -C "$ROOT_DIR/src" BUILD_DIR=../tools/comparison/build PROJECT_DIR="$OUTPUT_DIR/project-files" "IOS_DESTDIR=$OUTPUT_DIR/_ios-build" "MAC_DESTDIR=$OUTPUT_DIR/_mac-build" -j8; then
EC=$?
report_error_line "${RED}Failed to build src/${CLEAR}"
exit "$EC"
fi
#
# API diff
#
# Calculate apidiff references according to the temporary build
echo "${BLUE}Updating apidiff references...${CLEAR}"
if ! make update-refs -C "$ROOT_DIR/tools/apidiff" -j8 APIDIFF_DIR="$OUTPUT_DIR/apidiff" IOS_DESTDIR="$OUTPUT_DIR/_ios-build" MAC_DESTDIR="$OUTPUT_DIR/_mac-build"; then
EC=$?
report_error_line "${RED}Failed to update apidiff references${CLEAR}"
exit "$EC"
fi
# Now compare the current build against those references
echo "${BLUE}Running apidiff...${CLEAR}"
APIDIFF_FILE=$OUTPUT_DIR/apidiff/api-diff.html
if ! make all-local -C "$ROOT_DIR/tools/apidiff" -j8 APIDIFF_DIR="$OUTPUT_DIR/apidiff"; then
EC=$?
report_error_line "${RED}Failed to run apidiff${CLEAR}"
exit "$EC"
fi
#
# Generator diff
#
# We make a copy of the generated source code to compare against,
# so that we can remove files we don't want to compare without
# affecting that build.
$CP -R "$ROOT_DIR/src/build" "$OUTPUT_DIR/build-new"
cd "$OUTPUT_DIR"
find build build-new '(' -name '*.dll' -or -name '*.pdb' -or -name 'generated-sources' -or -name 'generated_sources' -or -name '*.exe' -or -name '*.rsp' -or -name 'AssemblyInfo.cs' -or -name 'Constants.cs' -or -name 'generator.csproj.*' ')' -delete
mkdir -p "$OUTPUT_DIR/generator-diff"
GENERATOR_DIFF_FILE="$OUTPUT_DIR/generator-diff/index.html"
git diff --no-index build build-new > "$OUTPUT_DIR/generator-diff/generator.diff" || true
"$TMPDIR/diff-to-html" "$OUTPUT_DIR/generator-diff/generator.diff" "$GENERATOR_DIFF_FILE"
# Check if any files in the normal output paths were modified (there should be none)
MODIFIED_FILES=$(find \
"$ROOT_DIR/_ios-build" \
"$ROOT_DIR/_mac-build" \
"$ROOT_DIR/src" \
"$ROOT_DIR/tools/apidiff" \
-type f \
-newer "$OUTPUT_DIR/stamp")
if test -n "$MODIFIED_FILES"; then
# If this list files, it means something's wrong with the build process
# (the logic to build/work in a different directory is incomplete/broken)
report_error_line "${RED}** Error: The following files were modified, and they shouldn't have been:${CLEAR}"
echo "$MODIFIED_FILES" | sed 's/^/ /' | while read line; do report_error_line "$line"; done
exit 1
fi