2015-01-09 19:19:57 -05:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
#
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
# or more contributor license agreements. See the NOTICE file
|
|
|
|
# distributed with this work for additional information
|
|
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
|
|
# to you under the Apache License, Version 2.0 (the
|
|
|
|
# "License"); you may not use this file except in compliance
|
|
|
|
# with the License. You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# check_compatibility.sh
|
|
|
|
# A script that uses the Java API Compliance Checker (Java ACC) to gauge the binary and source
|
|
|
|
# compatibility of two arbitrary versions of Apache HBase.
|
|
|
|
#
|
|
|
|
# Special thanks to Andrey Ponomarenko, the leader of the Java ACC project, for introducing
|
|
|
|
# support for class annotation filtering into the tool at our request.
|
|
|
|
#
|
|
|
|
# Usage: This script checks out two versions of HBase (via a tag, branch, or commit hash in Git),
|
|
|
|
# builds the releases, and generates XML descriptors of relevant JARs (i.e. excluding
|
|
|
|
# test JARs, as well as external HBase dependencies). Next, the Java API Compliance
|
|
|
|
# Checker (http://ispras.linuxbase.org/index.php/Java_API_Compliance_Checker) is
|
|
|
|
# downloaded and run using these XML descriptor files to generate a report
|
|
|
|
# of the degree of binary and source compatibility of the two HBase versions. Finally,
|
|
|
|
# the resulting report is scraped and some of its results output to stdout.
|
|
|
|
#
|
|
|
|
# Note that GNU getopt is required for this script to work properly. If you're running
|
|
|
|
# a different variant (e.g. OS X ships with BSD getopt), you need to get the GNU variant
|
|
|
|
# and either put it on the PATH or set GETOPT to the location of GNU getopt before
|
|
|
|
# executing this script.
|
|
|
|
#
|
|
|
|
# Example: To compare the binary and source compatibility of the 0.98.6 release and the
|
|
|
|
# tip of the master branch:
|
|
|
|
# $ ./check_compatibility.sh 0.98.6
|
|
|
|
# (i.e. if -b is omitted, a check is implicitly run against master).
|
|
|
|
#
|
|
|
|
# To compare the binary and source compatibility of the HBase 0.98.5 and 0.98.6
|
|
|
|
# releases:
|
|
|
|
# $ ./check_compatibility.sh 0.98.5 0.98.6
|
|
|
|
|
|
|
|
SCRIPT_DIRECTORY=$(dirname ${BASH_SOURCE[0]})
|
2016-07-18 15:48:16 -04:00
|
|
|
# Save the InterfaceAudience package name as a variable to make annotation listing more convenient.
|
|
|
|
IA_PACKAGE="org.apache.hadoop.hbase.classification.InterfaceAudience"
|
2015-01-09 19:19:57 -05:00
|
|
|
|
|
|
|
# Usage message.
|
|
|
|
usage () {
|
|
|
|
SCRIPT=$(basename "${BASH_SOURCE}")
|
|
|
|
|
|
|
|
cat << __EOF
|
|
|
|
|
|
|
|
check_compatibility.sh
|
|
|
|
A script that uses the Java API Compliance Checker to gauge the binary and source
|
|
|
|
compatibility of two arbitrary versions of Apache HBase.
|
|
|
|
|
|
|
|
Usage: [<options>] <ref1> [<ref2>]
|
|
|
|
|
|
|
|
The positional arguments are Git references; this can be a tag (e.g. 0.98.6),
|
|
|
|
a branch (e.g. 0.98), or a particular commit hash. If ref2 is omitted, master
|
|
|
|
will be used.
|
|
|
|
|
|
|
|
Options:
|
2016-07-18 15:48:16 -04:00
|
|
|
-a, --all Do not filter by interface annotations.
|
|
|
|
-b, --binary-only Only run the check for binary compatibility.
|
|
|
|
-f, --force-download Download dependencies (i.e. Java ACC), even if they are
|
|
|
|
already present.
|
|
|
|
-h, --help Show this screen.
|
|
|
|
-j, --java-acc Specify which version of Java ACC to use to run the
|
|
|
|
analysis. This can be a tag, branch, or commit hash.
|
|
|
|
Defaults to master.
|
|
|
|
-l <list>, --annotation-list=<list> A comma-separated list of annotations to limit compatibility
|
|
|
|
checks to. Defaults to
|
|
|
|
"${IA_PACKAGE}.Public,${IA_PACKAGE}.LimitedPrivate".
|
|
|
|
-n, --no-checkout Run the tool without first using Git to checkout the two
|
|
|
|
HBase versions. If this option is selected,
|
|
|
|
dev-support/target/compatibility/1 and
|
|
|
|
dev-support/target compatibility/2 must each be Git
|
|
|
|
repositories. Also note that the references must still be
|
|
|
|
specified as these are used when naming the compatibility
|
|
|
|
report.
|
|
|
|
-o <opts>, --options=<opts> A comma-separated list of options to pass directly to Java
|
|
|
|
ACC.
|
|
|
|
-q, --quick Runs Java ACC in quick analysis mode, which disables a
|
|
|
|
number of checks for things that may break compatibility.
|
|
|
|
-r <url>, --repo=<url> URL of the HBase Git repository to use. Defaults to Apache
|
|
|
|
HBase's GitHub (https://github.com/apache/hbase.git).
|
|
|
|
-s, --source-only Only run the check for source compatibility.
|
2015-01-09 19:19:57 -05:00
|
|
|
__EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
# Allow a user to override which GETOPT to use, as described in the header.
|
|
|
|
GETOPT=${GETOPT:-/usr/bin/env getopt}
|
|
|
|
|
2016-07-18 15:48:16 -04:00
|
|
|
# Parse command line arguments. We split long options (-l) to stay under 100 chars.
|
|
|
|
if ! ARG_LIST=$(${GETOPT} -q -o abfhj:nl:o:qr:s \
|
|
|
|
-l all,annotation-list:,binary-only,force-download,help \
|
|
|
|
-l java-acc:,no-checkout,options:,quick,repo:,source-only \
|
2015-01-09 19:19:57 -05:00
|
|
|
-- "${@}"); then
|
|
|
|
usage >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
|
|
|
eval set -- "${ARG_LIST[@]}"
|
|
|
|
|
2016-06-27 18:18:04 -04:00
|
|
|
# Set defaults for options in case they're not specified on the command line.
|
2016-07-18 15:48:16 -04:00
|
|
|
ANNOTATION_LIST=(${IA_PACKAGE}.Public ${IA_PACKAGE}.LimitedPrivate)
|
2016-06-27 18:18:04 -04:00
|
|
|
JAVA_ACC_COMMIT="master"
|
|
|
|
REPO_URL="https://github.com/apache/hbase.git"
|
|
|
|
|
2015-01-09 19:19:57 -05:00
|
|
|
while ((${#})); do
|
|
|
|
case "${1}" in
|
2016-07-18 15:48:16 -04:00
|
|
|
-a | --all )
|
2015-04-02 00:58:57 -04:00
|
|
|
ALL=true
|
|
|
|
shift 1 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-b | --binary-only )
|
2015-01-09 19:19:57 -05:00
|
|
|
JAVA_ACC_COMMAND+=(-binary)
|
|
|
|
shift 1 ;;
|
|
|
|
-f | --force-download )
|
|
|
|
FORCE_DOWNLOAD=true
|
|
|
|
shift 1 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-h | --help )
|
2015-01-09 19:19:57 -05:00
|
|
|
usage
|
|
|
|
exit 0 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-j | --java-acc )
|
2016-06-27 18:18:04 -04:00
|
|
|
JAVA_ACC_COMMIT="${2}"
|
|
|
|
shift 2 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-l | --annotation-list )
|
|
|
|
# Process the comma-separated list of annotations and overwrite the default list.
|
|
|
|
ANNOTATION_LIST=($(tr "," "\n" <<< "${2}"))
|
|
|
|
shift 2 ;;
|
|
|
|
-n | --no-checkout )
|
2015-01-09 19:19:57 -05:00
|
|
|
NO_CHECKOUT=true
|
|
|
|
shift 1 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-q | --quick )
|
2015-01-09 19:19:57 -05:00
|
|
|
JAVA_ACC_COMMAND+=(-quick)
|
|
|
|
shift 1 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-o | --options )
|
2015-01-09 19:19:57 -05:00
|
|
|
# Process and append the comma-separated list of options into the command array.
|
|
|
|
JAVA_ACC_COMMAND+=($(tr "," "\n" <<< "${2}"))
|
|
|
|
shift 2 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-r | --repo )
|
2015-01-09 19:19:57 -05:00
|
|
|
REPO_URL="${2}"
|
|
|
|
shift 2 ;;
|
2016-07-18 15:48:16 -04:00
|
|
|
-s | --source-only )
|
2015-01-09 19:19:57 -05:00
|
|
|
JAVA_ACC_COMMAND+=(-source)
|
|
|
|
shift 1 ;;
|
|
|
|
# getopt inserts -- to separate options and positional arguments.
|
2016-07-18 15:48:16 -04:00
|
|
|
-- )
|
2015-01-09 19:19:57 -05:00
|
|
|
# First, shift past the -- to get to the positional arguments.
|
|
|
|
shift 1
|
|
|
|
# If there is one positional argument, only <ref1> was specified.
|
|
|
|
if [ ${#} -eq 1 ]; then
|
2016-06-27 18:18:04 -04:00
|
|
|
COMMIT[1]="${1}"
|
|
|
|
COMMIT[2]=master
|
|
|
|
shift 1
|
2015-01-09 19:19:57 -05:00
|
|
|
# If there are two positional arguments, <ref1> and <ref2> were both specified.
|
|
|
|
elif [ ${#} -eq 2 ]; then
|
2016-06-27 18:18:04 -04:00
|
|
|
COMMIT[1]="${1}"
|
2015-01-09 19:19:57 -05:00
|
|
|
COMMIT[2]="${2}"
|
|
|
|
shift 2
|
|
|
|
# If there are no positional arguments or too many, someone needs to reread the usage
|
|
|
|
# message.
|
|
|
|
else
|
|
|
|
usage >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
|
|
# Do identical operations for both HBase versions in a for loop to save some lines of code.
|
|
|
|
for ref in 1 2; do
|
|
|
|
if ! [ "${NO_CHECKOUT}" ]; then
|
|
|
|
# Create empty directories for both versions in question.
|
|
|
|
echo "Creating empty ${SCRIPT_DIRECTORY}/target/compatibility/${ref} directory..."
|
|
|
|
rm -rf ${SCRIPT_DIRECTORY}/target/compatibility/${ref}
|
|
|
|
mkdir -p ${SCRIPT_DIRECTORY}/target/compatibility/${ref}
|
|
|
|
|
|
|
|
if [ "${ref}" = "1" ]; then
|
|
|
|
echo "Cloning ${REPO_URL} into ${SCRIPT_DIRECTORY}/target/compatibility/${ref}..."
|
|
|
|
if ! git clone ${REPO_URL} ${SCRIPT_DIRECTORY}/target/compatibility/${ref}; then
|
2016-06-27 18:18:04 -04:00
|
|
|
echo "Error while cloning ${REPO_URL}. Exiting..." >&2
|
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
|
|
|
elif [ "${ref}" = "2" ]; then
|
|
|
|
# Avoid cloning from Git twice by copying first repo into different folder.
|
|
|
|
echo "Copying Git repository into ${SCRIPT_DIRECTORY}/target/compatibility/${ref}..."
|
|
|
|
cp -a ${SCRIPT_DIRECTORY}/target/compatibility/1/.git \
|
|
|
|
${SCRIPT_DIRECTORY}/target/compatibility/2
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Use pushd and popd to keep track of directories while navigating around (and hide
|
|
|
|
# printing of the stack).
|
|
|
|
pushd ${SCRIPT_DIRECTORY}/target/compatibility/${ref} > /dev/null
|
|
|
|
echo "Checking out ${COMMIT[${ref}]} into ${ref}/..."
|
|
|
|
if ! git checkout -f ${COMMIT[${ref}]}; then
|
|
|
|
echo "Error while checking out ${COMMIT[${ref}]}. Exiting..." >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
|
|
|
echo "Building ${COMMIT[${ref}]}..."
|
2016-06-27 14:29:23 -04:00
|
|
|
if ! mvn clean package --batch-mode -DskipTests; then
|
2015-01-09 19:19:57 -05:00
|
|
|
echo "Maven could not successfully package ${COMMIT[${ref}]}. Exiting..." >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
2016-01-16 18:44:07 -05:00
|
|
|
# grab sha for future reference
|
|
|
|
SHA[${ref}]=$(git rev-parse --short HEAD)
|
2015-01-09 19:19:57 -05:00
|
|
|
popd > /dev/null
|
|
|
|
fi
|
|
|
|
|
2015-04-30 14:56:59 -04:00
|
|
|
JAR_FIND_EXPRESSION=(-name "hbase*.jar" ! -name "*tests*" ! -name "*sources*" ! -name "*shade*")
|
2015-01-09 19:19:57 -05:00
|
|
|
# Create an array of all the HBase JARs matching the find expression.
|
|
|
|
JARS=$(find ${SCRIPT_DIRECTORY}/target/compatibility/${ref} "${JAR_FIND_EXPRESSION[@]}")
|
|
|
|
|
|
|
|
if [ ${#JARS[@]} -eq 0 ]; then
|
|
|
|
# If --no-checkout was specified and no JARs were found, try running mvn package
|
|
|
|
# for the user before failing.
|
|
|
|
if [ ${NO_CHECKOUT} ]; then
|
|
|
|
for ref in 1 2; do
|
|
|
|
pushd ${SCRIPT_DIRECTORY}/target/compatibility/${ref} > /dev/null
|
|
|
|
echo "The --no-checkout option was specified, but no JARs were found." \
|
|
|
|
"Attempting to build ${COMMIT[${ref}]}..."
|
2016-06-27 14:29:23 -04:00
|
|
|
if ! mvn clean package --batch-mode -DskipTests; then
|
2015-01-09 19:19:57 -05:00
|
|
|
echo "Maven could not successfully package ${COMMIT[${ref}]}. Exiting..." >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
2016-01-16 18:44:07 -05:00
|
|
|
SHA[${ref}]=$(git rev-parse --short HEAD)
|
2015-01-09 19:19:57 -05:00
|
|
|
popd > /dev/null
|
|
|
|
done
|
|
|
|
|
|
|
|
JARS=$(find ${SCRIPT_DIRECTORY}/target/compatibility/${ref} "${JAR_FIND_EXPRESSION[@]}")
|
|
|
|
if [ ${#JARS[@]} -eq 0 ]; then
|
2016-06-27 18:18:04 -04:00
|
|
|
echo "Unable to find any JARs matching the find expression. Exiting..." >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
|
|
|
|
|
|
|
# If no JARs were found and --no-checkout was not specified, fail immediately.
|
|
|
|
else
|
|
|
|
echo "Unable to find any JARs matching the find expression. Exiting..." >&2
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
2016-06-27 18:18:04 -04:00
|
|
|
# Create an XML descriptor containing paths to the JARs for Java ACC to use (support for
|
|
|
|
# comma-separated lists of JARs was removed, as described on their issue tracker:
|
|
|
|
# https://github.com/lvc/japi-compliance-checker/issues/27).
|
|
|
|
DESCRIPTOR_PATH="${SCRIPT_DIRECTORY}/target/compatibility/${ref}.xml"
|
|
|
|
echo "<version>${COMMIT[${ref}]}${SHA[${ref}]+"/${SHA[${ref}]}"}</version>" > "${DESCRIPTOR_PATH}"
|
|
|
|
echo "<archives>" >> "${DESCRIPTOR_PATH}"
|
|
|
|
|
2015-01-09 19:19:57 -05:00
|
|
|
echo "The JARs to be analyzed from ${COMMIT[${ref}]} are:"
|
|
|
|
for jar in ${JARS}; do
|
2016-06-27 18:18:04 -04:00
|
|
|
echo " ${jar}" | tee -a "${DESCRIPTOR_PATH}"
|
2015-01-09 19:19:57 -05:00
|
|
|
done
|
2016-06-27 18:18:04 -04:00
|
|
|
echo "</archives>" >> "${DESCRIPTOR_PATH}"
|
2015-01-09 19:19:57 -05:00
|
|
|
done
|
|
|
|
|
2016-06-27 18:18:04 -04:00
|
|
|
# Download the Java API Compliance Checker (Java ACC) into /dev-support/target/compatibility.
|
2015-01-09 19:19:57 -05:00
|
|
|
# Note: Java API Compliance Checker (Java ACC) is licensed under the GNU GPL or LGPL. For more
|
|
|
|
# information, visit http://ispras.linuxbase.org/index.php/Java_API_Compliance_Checker .
|
|
|
|
|
|
|
|
# Only clone Java ACC if it's missing or if option to force dependency download is present.
|
|
|
|
if [ ! -d ${SCRIPT_DIRECTORY}/target/compatibility/javaACC ] || [ -n "${FORCE_DOWNLOAD}" ]; then
|
|
|
|
echo "Downloading Java API Compliance Checker..."
|
|
|
|
rm -rf ${SCRIPT_DIRECTORY}/target/compatibility/javaACC
|
2016-06-27 18:18:04 -04:00
|
|
|
if ! git clone https://github.com/lvc/japi-compliance-checker.git -b "${JAVA_ACC_COMMIT}" \
|
2015-01-09 19:19:57 -05:00
|
|
|
${SCRIPT_DIRECTORY}/target/compatibility/javaACC; then
|
|
|
|
echo "Failed to download Java API Compliance Checker. Exiting..." >&2
|
2015-12-04 14:36:16 -05:00
|
|
|
exit 2
|
2015-01-09 19:19:57 -05:00
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
2016-07-18 15:48:16 -04:00
|
|
|
# Generate one-per-line list of annotations.
|
|
|
|
tr " " "\n" <<< "${ANNOTATION_LIST[@]}" > "${SCRIPT_DIRECTORY}/target/compatibility/annotations"
|
2015-01-09 19:19:57 -05:00
|
|
|
|
|
|
|
# Generate command line arguments for Java ACC.
|
|
|
|
JAVA_ACC_COMMAND+=(-l HBase)
|
2016-06-27 18:18:04 -04:00
|
|
|
JAVA_ACC_COMMAND+=(-old "${SCRIPT_DIRECTORY}/target/compatibility/1.xml")
|
|
|
|
JAVA_ACC_COMMAND+=(-new "${SCRIPT_DIRECTORY}/target/compatibility/2.xml")
|
2015-01-09 19:19:57 -05:00
|
|
|
JAVA_ACC_COMMAND+=(-report-path \
|
|
|
|
${SCRIPT_DIRECTORY}/target/compatibility/report/${COMMIT[1]}_${COMMIT[2]}_compat_report.html)
|
2015-04-02 00:58:57 -04:00
|
|
|
if [ "${ALL}" != "true" ] ; then
|
|
|
|
JAVA_ACC_COMMAND+=(-annotations-list ${SCRIPT_DIRECTORY}/target/compatibility/annotations)
|
|
|
|
fi
|
2015-01-09 19:19:57 -05:00
|
|
|
|
|
|
|
# Delete any existing report folder under /dev-support/target/compatibility.
|
|
|
|
rm -rf ${SCRIPT_DIRECTORY}/target/compatibility/report
|
|
|
|
|
|
|
|
# Run the tool. Note that Java ACC returns an exit code of 0 if the two versions are
|
|
|
|
# compatible, an exit code of 1 if the two versions are not, and several other codes
|
|
|
|
# for various errors. See the tool's website for details.
|
|
|
|
echo "Running the Java API Compliance Checker..."
|
2016-07-18 15:48:16 -04:00
|
|
|
perl "${SCRIPT_DIRECTORY}/target/compatibility/javaACC/japi-compliance-checker.pl" \
|
|
|
|
"${JAVA_ACC_COMMAND[@]}"
|