1*89a63228SAndroid Build Coastguard Worker#!/usr/bin/python 2*89a63228SAndroid Build Coastguard Worker# 3*89a63228SAndroid Build Coastguard Worker# Copyright (C) 2017 The Android Open Source Project 4*89a63228SAndroid Build Coastguard Worker# 5*89a63228SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*89a63228SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*89a63228SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*89a63228SAndroid Build Coastguard Worker# 9*89a63228SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*89a63228SAndroid Build Coastguard Worker# 11*89a63228SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*89a63228SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*89a63228SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*89a63228SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*89a63228SAndroid Build Coastguard Worker# limitations under the License. 16*89a63228SAndroid Build Coastguard Worker 17*89a63228SAndroid Build Coastguard Worker""" 18*89a63228SAndroid Build Coastguard WorkerCompares one or more corresponding files from ojluni against one or 19*89a63228SAndroid Build Coastguard Workermore upstream or from upstreams against each other. 20*89a63228SAndroid Build Coastguard WorkerThe repositories (default: ojluni vs. expected current upstream) and 21*89a63228SAndroid Build Coastguard Workerthe diff tool (default: meld) can be specified by command line options. 22*89a63228SAndroid Build Coastguard Worker 23*89a63228SAndroid Build Coastguard WorkerThis tool is for libcore maintenance; if you're not maintaining libcore, 24*89a63228SAndroid Build Coastguard Workeryou won't need it (and might not have access to some of the instructions 25*89a63228SAndroid Build Coastguard Workerbelow). 26*89a63228SAndroid Build Coastguard Worker 27*89a63228SAndroid Build Coastguard WorkerThe naming of the repositories (expected, ojluni, 7u40, 8u121-b13, 28*89a63228SAndroid Build Coastguard Worker9b113+, 9+181) is based on the directory name where corresponding 29*89a63228SAndroid Build Coastguard Workersnapshots are stored when following the instructions at 30*89a63228SAndroid Build Coastguard Workerhttp://go/libcore-o-verify 31*89a63228SAndroid Build Coastguard Worker 32*89a63228SAndroid Build Coastguard WorkerThis in turn derives from the instructions at the top of: 33*89a63228SAndroid Build Coastguard Workerlibcore/tools/upstream/src/main/java/libcore/CompareUpstreams.java 34*89a63228SAndroid Build Coastguard Worker 35*89a63228SAndroid Build Coastguard WorkerPossible uses: 36*89a63228SAndroid Build Coastguard Worker 37*89a63228SAndroid Build Coastguard WorkerTo verify that ArrayList has been updated to the expected upstream 38*89a63228SAndroid Build Coastguard Workerand that all local patches carry change markers, we compare that 39*89a63228SAndroid Build Coastguard Workerfile from ojluni against the expected upstream (the default): 40*89a63228SAndroid Build Coastguard Worker upstream-diff java/util/ArrayList.java 41*89a63228SAndroid Build Coastguard Worker 42*89a63228SAndroid Build Coastguard WorkerTo verify multiple files: 43*89a63228SAndroid Build Coastguard Worker upstream-diff java.util.ArrayList java.util.LinkedList 44*89a63228SAndroid Build Coastguard Worker 45*89a63228SAndroid Build Coastguard WorkerTo verify a folder: 46*89a63228SAndroid Build Coastguard Worker upstream-diff java/util/concurrent 47*89a63228SAndroid Build Coastguard Worker 48*89a63228SAndroid Build Coastguard WorkerTo verify a package: 49*89a63228SAndroid Build Coastguard Worker upstream-diff java.util.concurrent 50*89a63228SAndroid Build Coastguard Worker 51*89a63228SAndroid Build Coastguard WorkerUse a three-way merge to integrate changes from 9+181 into ArrayList: 52*89a63228SAndroid Build Coastguard Worker upstream-diff -r 8u121-b13,ojluni,9+181 java/util/ArrayList.java 53*89a63228SAndroid Build Coastguard Workeror to investigate which version of upstream introduced a change: 54*89a63228SAndroid Build Coastguard Worker upstream-diff -r 7u40,8u60,8u121-b13 java/util/ArrayList.java 55*89a63228SAndroid Build Coastguard Worker""" 56*89a63228SAndroid Build Coastguard Worker 57*89a63228SAndroid Build Coastguard Workerimport argparse 58*89a63228SAndroid Build Coastguard Workerimport os 59*89a63228SAndroid Build Coastguard Workerimport os.path 60*89a63228SAndroid Build Coastguard Workerimport re 61*89a63228SAndroid Build Coastguard Workerimport subprocess 62*89a63228SAndroid Build Coastguard Workerimport sys 63*89a63228SAndroid Build Coastguard Worker 64*89a63228SAndroid Build Coastguard Worker 65*89a63228SAndroid Build Coastguard Workerdef get_path_type(rel_path): 66*89a63228SAndroid Build Coastguard Worker ext = os.path.splitext(rel_path)[-1] 67*89a63228SAndroid Build Coastguard Worker # Check the extension and if it is a C/C++ extension then we're dealing 68*89a63228SAndroid Build Coastguard Worker # with a native path. Otherwise we would be dealing with a java path, which 69*89a63228SAndroid Build Coastguard Worker # can be a filename, folder name, package name, or fully qualified class 70*89a63228SAndroid Build Coastguard Worker # name. 71*89a63228SAndroid Build Coastguard Worker if re.match('\\.(c|cc|cpp|cxx|h|hpp|hxx|icc)$', ext): 72*89a63228SAndroid Build Coastguard Worker return 'native' 73*89a63228SAndroid Build Coastguard Worker return 'java' 74*89a63228SAndroid Build Coastguard Worker 75*89a63228SAndroid Build Coastguard Worker 76*89a63228SAndroid Build Coastguard Workerdef normalize_java_path(rel_path): 77*89a63228SAndroid Build Coastguard Worker if re.match('.+\\.java$', rel_path): 78*89a63228SAndroid Build Coastguard Worker # Path ends in '.java' so a filename with its path is expected 79*89a63228SAndroid Build Coastguard Worker return rel_path 80*89a63228SAndroid Build Coastguard Worker 81*89a63228SAndroid Build Coastguard Worker if '/' not in rel_path: 82*89a63228SAndroid Build Coastguard Worker # Convert package name, or fully qualified class name into path 83*89a63228SAndroid Build Coastguard Worker rel_path = rel_path.replace('.', '/') 84*89a63228SAndroid Build Coastguard Worker 85*89a63228SAndroid Build Coastguard Worker if any(c.isupper() for c in rel_path): 86*89a63228SAndroid Build Coastguard Worker # If the name includes an uppercase character, we guess that this is a 87*89a63228SAndroid Build Coastguard Worker # class rather than a package name, so the extension needs to be appended 88*89a63228SAndroid Build Coastguard Worker # to get the full filename 89*89a63228SAndroid Build Coastguard Worker # Note: Test packages may have upper case characters in the package name, 90*89a63228SAndroid Build Coastguard Worker # so, if trying to diff a test package, the ".java" suffix will be added 91*89a63228SAndroid Build Coastguard Worker # unnecessarily, causing a wrong diff input. Therefore, for test packages, 92*89a63228SAndroid Build Coastguard Worker # the tool should be used for each file individually 93*89a63228SAndroid Build Coastguard Worker rel_path += '.java' 94*89a63228SAndroid Build Coastguard Worker elif rel_path[-1] != '/': 95*89a63228SAndroid Build Coastguard Worker # No upper case characters, so this must be a folder/package 96*89a63228SAndroid Build Coastguard Worker rel_path += '/' 97*89a63228SAndroid Build Coastguard Worker 98*89a63228SAndroid Build Coastguard Worker return rel_path 99*89a63228SAndroid Build Coastguard Worker 100*89a63228SAndroid Build Coastguard Worker 101*89a63228SAndroid Build Coastguard Workerdef run_diff(diff, repositories, rel_paths): 102*89a63228SAndroid Build Coastguard Worker # Root of checked-out Android sources, set by the "lunch" command. 103*89a63228SAndroid Build Coastguard Worker android_build_top = os.environ['ANDROID_BUILD_TOP'] 104*89a63228SAndroid Build Coastguard Worker # Root of repository snapshots. See go/libcore-o-verify for how you'd want 105*89a63228SAndroid Build Coastguard Worker # to set this. 106*89a63228SAndroid Build Coastguard Worker ojluni_upstreams = os.environ['OJLUNI_UPSTREAMS'] 107*89a63228SAndroid Build Coastguard Worker for rel_path in rel_paths: 108*89a63228SAndroid Build Coastguard Worker path_type = get_path_type(rel_path) 109*89a63228SAndroid Build Coastguard Worker if path_type == 'java': 110*89a63228SAndroid Build Coastguard Worker rel_path = normalize_java_path(rel_path) 111*89a63228SAndroid Build Coastguard Worker paths = [] 112*89a63228SAndroid Build Coastguard Worker 113*89a63228SAndroid Build Coastguard Worker for repository in repositories: 114*89a63228SAndroid Build Coastguard Worker if repository == 'ojluni': 115*89a63228SAndroid Build Coastguard Worker paths.append('%s/libcore/ojluni/src/main/%s/%s' 116*89a63228SAndroid Build Coastguard Worker % (android_build_top, path_type, rel_path)) 117*89a63228SAndroid Build Coastguard Worker else: 118*89a63228SAndroid Build Coastguard Worker paths.append('%s/%s/%s' % (ojluni_upstreams, repository, rel_path)) 119*89a63228SAndroid Build Coastguard Worker subprocess.call([diff] + paths) 120*89a63228SAndroid Build Coastguard Worker 121*89a63228SAndroid Build Coastguard Worker 122*89a63228SAndroid Build Coastguard Workerdef main(): 123*89a63228SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 124*89a63228SAndroid Build Coastguard Worker description='Compare files between libcore/ojluni and ${OJLUNI_UPSTREAMS}.', 125*89a63228SAndroid Build Coastguard Worker formatter_class=argparse.ArgumentDefaultsHelpFormatter, # include default values in help 126*89a63228SAndroid Build Coastguard Worker ) 127*89a63228SAndroid Build Coastguard Worker upstreams = os.environ['OJLUNI_UPSTREAMS'] 128*89a63228SAndroid Build Coastguard Worker # natsort.natsorted() would be a nicer sort order, but I'd rather avoid the dependency 129*89a63228SAndroid Build Coastguard Worker repositories = ['ojluni'] + sorted( 130*89a63228SAndroid Build Coastguard Worker [d for d in os.listdir(upstreams) if os.path.isdir(os.path.join(upstreams, d))] 131*89a63228SAndroid Build Coastguard Worker ) 132*89a63228SAndroid Build Coastguard Worker parser.add_argument('-r', '--repositories', default='ojluni,expected', 133*89a63228SAndroid Build Coastguard Worker help='Comma-separated list of 2-3 repositories, to compare, in order; ' 134*89a63228SAndroid Build Coastguard Worker 'available repositories: ' + ' '.join(repositories) + '.') 135*89a63228SAndroid Build Coastguard Worker parser.add_argument('-d', '--diff', default='meld', 136*89a63228SAndroid Build Coastguard Worker help='Application to use for diffing.') 137*89a63228SAndroid Build Coastguard Worker parser.add_argument('rel_path', nargs="+", 138*89a63228SAndroid Build Coastguard Worker help='File to compare: either a relative path below libcore/ojluni/' 139*89a63228SAndroid Build Coastguard Worker 'src/main/{java,native}, or a fully qualified class name.') 140*89a63228SAndroid Build Coastguard Worker args = parser.parse_args() 141*89a63228SAndroid Build Coastguard Worker repositories = args.repositories.split(',') 142*89a63228SAndroid Build Coastguard Worker if (len(repositories) < 2): 143*89a63228SAndroid Build Coastguard Worker print('Expected >= 2 repositories to compare, got: ' + str(repositories)) 144*89a63228SAndroid Build Coastguard Worker parser.print_help() 145*89a63228SAndroid Build Coastguard Worker sys.exit(1) 146*89a63228SAndroid Build Coastguard Worker run_diff(args.diff, repositories, args.rel_path) 147*89a63228SAndroid Build Coastguard Worker 148*89a63228SAndroid Build Coastguard Worker 149*89a63228SAndroid Build Coastguard Workerif __name__ == "__main__": 150*89a63228SAndroid Build Coastguard Worker main() 151