#!/usr/bin/python import hashlib import operator import os import shutil import stat import subprocess import sys import tempfile def rel_to_abs(rel_path): return os.path.join(script_path, rel_path) java_bin_path = os.getenv('JAVA_HOME', '') if java_bin_path: java_bin_path = os.path.join(java_bin_path, 'bin') main_class = 'org.chromium.devtools.jsdoc.JsDocValidator' jar_name = 'jsdoc_validator.jar' hashes_name = 'hashes' src_dir = 'src' script_path = os.path.dirname(os.path.abspath(__file__)) closure_jar_relpath = os.path.join('..', 'closure', 'compiler.jar') src_path = rel_to_abs(src_dir) hashes_path = rel_to_abs(hashes_name) def get_file_hash(file, blocksize=65536): sha = hashlib.sha256() buf = file.read(blocksize) while len(buf) > 0: sha.update(buf) buf = file.read(blocksize) return sha.hexdigest() def traverse(hasher, path): abs_path = rel_to_abs(path) info = os.lstat(abs_path) quoted_name = repr(path.replace('\\', '/')) if stat.S_ISDIR(info.st_mode) and not os.path.basename(path).startswith('.'): hasher.update('d ' + quoted_name + '\n') for entry in sorted(os.listdir(abs_path)): traverse(hasher, os.path.join(path, entry)) elif stat.S_ISREG(info.st_mode) and path.endswith('.java'): hasher.update('r ' + quoted_name + ' ') hasher.update(str(info.st_size) + ' ') with open(abs_path, 'Ur') as file: f_hash = get_file_hash(file) hasher.update(f_hash + '\n') def get_src_dir_hash(dir): sha = hashlib.sha256() traverse(sha, dir) return sha.hexdigest() def get_actual_hashes(): hashed_files = [(jar_name, True)] hashes = {} for (file_name, binary) in hashed_files: try: hash = get_file_hash(open(file_name, 'rb' if binary else 'r')) hashes[file_name] = hash except IOError: hashes[file_name] = '0' hashes[src_dir] = get_src_dir_hash(src_dir) return hashes def get_expected_hashes(): try: with open(hashes_path, 'r') as file: return { file_name: hash for (file_name, hash) in [(name.strip(), hash.strip()) for (hash, name) in [line.split(' ', 1) for line in file]] } except: return None def run_and_communicate(command, error_template): proc = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) proc.communicate() if proc.returncode: print >> sys.stderr, error_template % proc.returncode sys.exit(proc.returncode) def build_artifacts(): print 'Compiling...' java_files = [] for root, dirs, files in sorted(os.walk(src_path)): for file_name in files: if file_name.endswith('.java'): java_files.append(os.path.join(root, file_name)) bin_path = tempfile.mkdtemp() manifest_file = tempfile.NamedTemporaryFile(mode='wt', delete=False) try: manifest_file.write('Class-Path: %s\n' % closure_jar_relpath) manifest_file.close() javac_path = os.path.join(java_bin_path, 'javac') javac_command = '%s -target 7 -source 7 -d %s -cp %s %s' % (javac_path, bin_path, rel_to_abs(closure_jar_relpath), ' '.join(java_files)) run_and_communicate(javac_command, 'Error: javac returned %d') print 'Building jar...' artifact_path = rel_to_abs(jar_name) jar_path = os.path.join(java_bin_path, 'jar') jar_command = '%s cvfme %s %s %s -C %s .' % (jar_path, artifact_path, manifest_file.name, main_class, bin_path) run_and_communicate(jar_command, 'Error: jar returned %d') finally: os.remove(manifest_file.name) shutil.rmtree(bin_path, True) def update_hashes(): print 'Updating hashes...' with open(hashes_path, 'w') as file: file.writelines(['%s %s\n' % (hash, name) for (name, hash) in get_actual_hashes().iteritems()]) def hashes_modified(): expected_hashes = get_expected_hashes() if not expected_hashes: return [('', 1, 0)] actual_hashes = get_actual_hashes() results = [] for name, expected_hash in expected_hashes.iteritems(): actual_hash = actual_hashes.get(name) if expected_hash != actual_hash: results.append((name, expected_hash, actual_hash)) return results def help(): print 'usage: %s [option]' % os.path.basename(__file__) print 'Options:' print '--force-rebuild: Rebuild classes and jar even if there are no source file changes' print '--no-rebuild: Do not rebuild jar, just update hashes' def main(): no_rebuild = False force_rebuild = False if len(sys.argv) > 1: if sys.argv[1] == '--help': help() return no_rebuild = sys.argv[1] == '--no-rebuild' force_rebuild = sys.argv[1] == '--force-rebuild' if not hashes_modified() and not force_rebuild: print 'No modifications found, rebuild not required.' return if not no_rebuild: build_artifacts() update_hashes() print 'Done.' if __name__ == '__main__': main()