You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by ha...@apache.org on 2017/08/10 20:04:39 UTC
zookeeper git commit: ZOOKEEPER-2864: Add script to run a java api
compatibility tool
Repository: zookeeper
Updated Branches:
refs/heads/branch-3.5 23962f123 -> 82be7f63c
ZOOKEEPER-2864: Add script to run a java api compatibility tool
For example: `python check_compatibility.py branch-3.5..branch-3.4 `
Author: Abraham Fine <af...@apache.org>
Reviewers: Michael Han <ha...@apache.org>
Closes #329 from afine/ZOOKEEPER-2864
Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo
Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/82be7f63
Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/82be7f63
Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/82be7f63
Branch: refs/heads/branch-3.5
Commit: 82be7f63c69b8aeecf80106c5e637bd80b64657e
Parents: 23962f1
Author: Abraham Fine <af...@apache.org>
Authored: Thu Aug 10 13:04:35 2017 -0700
Committer: Michael Han <ha...@apache.org>
Committed: Thu Aug 10 13:04:35 2017 -0700
----------------------------------------------------------------------
src/java/test/bin/check_compatibility.py | 204 ++++++++++++++++++++++++++
1 file changed, 204 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/82be7f63/src/java/test/bin/check_compatibility.py
----------------------------------------------------------------------
diff --git a/src/java/test/bin/check_compatibility.py b/src/java/test/bin/check_compatibility.py
new file mode 100644
index 0000000..cad8195
--- /dev/null
+++ b/src/java/test/bin/check_compatibility.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+#
+# 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.
+
+# Script which checks Java API compatibility between two revisions of the
+# Java client.
+#
+# Based on the compatibility checker from the HBase project, but ported to
+# Python for better readability.
+
+# Lifted from Kudu: https://github.com/apache/kudu/blob/master/build-support/check_compatibility.py
+
+import logging
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+
+JAVA_ACC_GIT_URL = "https://github.com/lvc/japi-compliance-checker.git"
+
+# The annotations for what we consider our public API.
+PUBLIC_ANNOTATIONS = ["org.apache.yetus.audience.InterfaceAudience.LimitedPrivate",
+ "org.apache.yetus.audience.InterfaceAudience.Public"]
+
+# Various relative paths
+PATH_TO_REPO_DIR = "../../../../"
+PATH_TO_BUILD_DIR = PATH_TO_REPO_DIR + "build/compat-check"
+PATH_TO_JACC_DIR = PATH_TO_REPO_DIR + "build/jacc"
+
+def check_output(*popenargs, **kwargs):
+ # r"""Run command with arguments and return its output as a byte string.
+ # Backported from Python 2.7 as it's implemented as pure python on stdlib.
+ # >>> check_output(['/usr/bin/python', '--version'])
+ # Python 2.6.2
+ # """
+ process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ error = subprocess.CalledProcessError(retcode, cmd)
+ error.output = output
+ raise error
+ return output
+
+def get_repo_dir():
+ """ Return the path to the top of the repo. """
+ dirname, _ = os.path.split(os.path.abspath(__file__))
+ return os.path.abspath(os.path.join(dirname, PATH_TO_REPO_DIR))
+
+
+def get_scratch_dir():
+ """ Return the path to the scratch dir that we build within. """
+ dirname, _ = os.path.split(os.path.abspath(__file__))
+ return os.path.abspath(os.path.join(dirname, PATH_TO_BUILD_DIR))
+
+
+def get_java_acc_dir():
+ dirname, _ = os.path.split(os.path.abspath(__file__))
+ return os.path.abspath(os.path.join(dirname, PATH_TO_JACC_DIR))
+
+
+def clean_scratch_dir(scratch_dir):
+ """ Clean up and re-create the scratch directory. """
+ if os.path.exists(scratch_dir):
+ logging.info("Removing scratch dir %s...", scratch_dir)
+ shutil.rmtree(scratch_dir)
+ logging.info("Creating empty scratch dir %s...", scratch_dir)
+ os.makedirs(scratch_dir)
+
+
+def checkout_tree(rev, path):
+ """ Check out the Java source tree for the given revision into the given path. """
+ logging.info("Checking out %s in %s", rev, path)
+ os.makedirs(path)
+ # Extract java source
+ subprocess.check_call(["bash", '-o', 'pipefail', "-c",
+ ("git archive --format=tar %s | " +
+ "tar -C \"%s\" -xf -") % (rev, path)],
+ cwd=get_repo_dir())
+
+
+def get_git_hash(revname):
+ """ Convert 'revname' to its SHA-1 hash. """
+ return check_output(["git", "rev-parse", revname],
+ cwd=get_repo_dir()).strip()
+
+
+def build_tree(path):
+ """ Run the Java build within 'path'. """
+ logging.info("Building in %s...", path)
+ subprocess.check_call(["ant", "jar"], cwd=path)
+
+
+def checkout_java_acc(force):
+ """
+ Check out the Java API Compliance Checker. If 'force' is true, will re-download even if the
+ directory exists.
+ """
+ acc_dir = get_java_acc_dir()
+ if os.path.exists(acc_dir):
+ logging.info("Java JAVA_ACC is already downloaded.")
+ if not force:
+ return
+ logging.info("Forcing re-download.")
+ shutil.rmtree(acc_dir)
+ logging.info("Checking out Java JAVA_ACC...")
+ subprocess.check_call(["git", "clone", "-b", "2.1", "--single-branch", "--depth=1", JAVA_ACC_GIT_URL, acc_dir])
+
+
+def find_client_jars(path):
+ """ Return a list of jars within 'path' to be checked for compatibility. """
+ return check_output(["find", path, "-name", "zookeeper*.jar"]).rstrip('\n')
+
+
+def run_java_acc(src_name, src, dst_name, dst):
+ """ Run the compliance checker to compare 'src' and 'dst'. """
+ src_jar = find_client_jars(src)
+ dst_jar = find_client_jars(dst)
+ logging.info("Will check compatibility between original jars:\n%s\n" +
+ "and new jars:\n%s",
+ src_jar, dst_jar)
+
+ annotations_path = os.path.join(get_scratch_dir(), "annotations.txt")
+ with file(annotations_path, "w") as f:
+ for ann in PUBLIC_ANNOTATIONS:
+ print >>f, ann
+
+ java_acc_path = os.path.join(get_java_acc_dir(), "japi-compliance-checker.pl")
+
+ out_path = os.path.join(get_scratch_dir(), "report.html")
+ subprocess.check_call(["perl", java_acc_path,
+ "-lib", "ZooKeeper",
+ "-v1", src_name,
+ "-v2", dst_name,
+ "-d1", src_jar,
+ "-d2", dst_jar,
+ "-annotations-list", annotations_path,
+ "-report-path", out_path])
+
+
+def main(argv):
+ logging.basicConfig(level=logging.INFO)
+ parser = optparse.OptionParser(
+ usage="usage: %prog SRC..[DST]")
+ parser.add_option("-f", "--force-download", dest="force_download_deps",
+ help=("Download dependencies (i.e. Java JAVA_ACC) even if they are " +
+ "already present"))
+ opts, args = parser.parse_args()
+
+ if len(args) != 1:
+ parser.error("no src/dst revision specified")
+ sys.exit(1)
+
+ src_rev, dst_rev = args[0].split("..", 1)
+ if dst_rev == "":
+ dst_rev = "HEAD"
+ src_rev = get_git_hash(src_rev)
+ dst_rev = get_git_hash(dst_rev)
+
+ logging.info("Source revision: %s", src_rev)
+ logging.info("Destination revision: %s", dst_rev)
+
+ # Download deps.
+ checkout_java_acc(opts.force_download_deps)
+
+ # Set up the build.
+ scratch_dir = get_scratch_dir()
+ clean_scratch_dir(scratch_dir)
+
+ # Check out the src and dst source trees.
+ src_dir = os.path.join(scratch_dir, "src")
+ dst_dir = os.path.join(scratch_dir, "dst")
+ checkout_tree(src_rev, src_dir)
+ checkout_tree(dst_rev, dst_dir)
+
+ # Run the build in each.
+ build_tree(src_dir)
+ build_tree(dst_dir)
+
+ run_java_acc(src_rev, src_dir + "/build",
+ dst_rev, dst_dir + "/build")
+
+
+if __name__ == "__main__":
+ main(sys.argv)
\ No newline at end of file