Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 51F0320049E for ; Thu, 10 Aug 2017 22:04:59 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 5041816C11B; Thu, 10 Aug 2017 20:04:59 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 6E1F416C115 for ; Thu, 10 Aug 2017 22:04:58 +0200 (CEST) Received: (qmail 347 invoked by uid 500); 10 Aug 2017 20:04:57 -0000 Mailing-List: contact commits-help@zookeeper.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zookeeper.apache.org Delivered-To: mailing list commits@zookeeper.apache.org Received: (qmail 336 invoked by uid 99); 10 Aug 2017 20:04:57 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 10 Aug 2017 20:04:57 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 502AFDFC28; Thu, 10 Aug 2017 20:04:57 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: hanm@apache.org To: commits@zookeeper.apache.org Message-Id: <03611fd2a2e6426794367d6ac910791e@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: zookeeper git commit: ZOOKEEPER-2864: Add script to run a java api compatibility tool Date: Thu, 10 Aug 2017 20:04:57 +0000 (UTC) archived-at: Thu, 10 Aug 2017 20:04:59 -0000 Repository: zookeeper Updated Branches: refs/heads/branch-3.4 e4303a37a -> 4beaa69b5 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 Reviewers: Michael Han Closes #329 from afine/ZOOKEEPER-2864 (cherry picked from commit 82be7f63c69b8aeecf80106c5e637bd80b64657e) Signed-off-by: Michael Han Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/4beaa69b Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/4beaa69b Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/4beaa69b Branch: refs/heads/branch-3.4 Commit: 4beaa69b59514ab11a559876edbacf79b8af73f2 Parents: e4303a3 Author: Abraham Fine Authored: Thu Aug 10 13:04:35 2017 -0700 Committer: Michael Han Committed: Thu Aug 10 13:04:53 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/4beaa69b/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