| #!/usr/bin/python |
| # |
| # Copyright (C) 2017 The Android Open Source Project |
| # |
| # Licensed 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. |
| |
| """ |
| Compares one or more corresponding files from ojluni against one or |
| more upstream or from upstreams against each other. |
| The repositories (default: ojluni vs. expected current upstream) and |
| the diff tool (default: meld) can be specified by command line options. |
| |
| This tool is for libcore maintenance; if you're not maintaining libcore, |
| you won't need it (and might not have access to some of the instructions |
| below). |
| |
| The naming of the repositories (expected, ojluni, 7u40, 8u121-b13, |
| 9b113+, 9+181) is based on the directory name where corresponding |
| snapshots are stored when following the instructions at |
| http://go/libcore-o-verify |
| |
| This in turn derives from the instructions at the top of: |
| libcore/tools/upstream/src/main/java/libcore/CompareUpstreams.java |
| |
| Possible uses: |
| |
| To verify that ArrayList has been updated to the expected upstream |
| and that all local patches carry change markers, we compare that |
| file from ojluni against the expected upstream (the default): |
| upstream-diff java/util/ArrayList.java |
| |
| To verify multiple files: |
| upstream-diff java.util.ArrayList java.util.LinkedList |
| |
| To verify a folder: |
| upstream-diff java/util/concurrent |
| |
| To verify a package: |
| upstream-diff java.util.concurrent |
| |
| Use a three-way merge to integrate changes from 9+181 into ArrayList: |
| upstream-diff -r 8u121-b13,ojluni,9+181 java/util/ArrayList.java |
| or to investigate which version of upstream introduced a change: |
| upstream-diff -r 7u40,8u60,8u121-b13 java/util/ArrayList.java |
| """ |
| |
| import argparse |
| import os |
| import os.path |
| import re |
| import subprocess |
| import sys |
| |
| |
| def get_path_type(rel_path): |
| ext = os.path.splitext(rel_path)[-1] |
| # Check the extension and if it is a C/C++ extension then we're dealing |
| # with a native path. Otherwise we would be dealing with a java path, which |
| # can be a filename, folder name, package name, or fully qualified class |
| # name. |
| if re.match('\\.(c|cc|cpp|cxx|h|hpp|hxx|icc)$', ext): |
| return 'native' |
| return 'java' |
| |
| |
| def normalize_java_path(rel_path): |
| if re.match('.+\\.java$', rel_path): |
| # Path ends in '.java' so a filename with its path is expected |
| return rel_path |
| |
| if '/' not in rel_path: |
| # Convert package name, or fully qualified class name into path |
| rel_path = rel_path.replace('.', '/') |
| |
| if any(c.isupper() for c in rel_path): |
| # If the name includes an uppercase character, we guess that this is a |
| # class rather than a package name, so the extension needs to be appended |
| # to get the full filename |
| # Note: Test packages may have upper case characters in the package name, |
| # so, if trying to diff a test package, the ".java" suffix will be added |
| # unnecessarily, causing a wrong diff input. Therefore, for test packages, |
| # the tool should be used for each file individually |
| rel_path += '.java' |
| elif rel_path[-1] != '/': |
| # No upper case characters, so this must be a folder/package |
| rel_path += '/' |
| |
| return rel_path |
| |
| |
| def run_diff(diff, repositories, rel_paths): |
| # Root of checked-out Android sources, set by the "lunch" command. |
| android_build_top = os.environ['ANDROID_BUILD_TOP'] |
| # Root of repository snapshots. See go/libcore-o-verify for how you'd want |
| # to set this. |
| ojluni_upstreams = os.environ['OJLUNI_UPSTREAMS'] |
| for rel_path in rel_paths: |
| path_type = get_path_type(rel_path) |
| if path_type == 'java': |
| rel_path = normalize_java_path(rel_path) |
| paths = [] |
| |
| for repository in repositories: |
| if repository == 'ojluni': |
| paths.append('%s/libcore/ojluni/src/main/%s/%s' |
| % (android_build_top, path_type, rel_path)) |
| else: |
| paths.append('%s/%s/%s' % (ojluni_upstreams, repository, rel_path)) |
| subprocess.call([diff] + paths) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description='Compare files between libcore/ojluni and ${OJLUNI_UPSTREAMS}.', |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, # include default values in help |
| ) |
| upstreams = os.environ['OJLUNI_UPSTREAMS'] |
| # natsort.natsorted() would be a nicer sort order, but I'd rather avoid the dependency |
| repositories = ['ojluni'] + sorted( |
| [d for d in os.listdir(upstreams) if os.path.isdir(os.path.join(upstreams, d))] |
| ) |
| parser.add_argument('-r', '--repositories', default='ojluni,expected', |
| help='Comma-separated list of 2-3 repositories, to compare, in order; ' |
| 'available repositories: ' + ' '.join(repositories) + '.') |
| parser.add_argument('-d', '--diff', default='meld', |
| help='Application to use for diffing.') |
| parser.add_argument('rel_path', nargs="+", |
| help='File to compare: either a relative path below libcore/ojluni/' |
| 'src/main/{java,native}, or a fully qualified class name.') |
| args = parser.parse_args() |
| repositories = args.repositories.split(',') |
| if (len(repositories) < 2): |
| print('Expected >= 2 repositories to compare, got: ' + str(repositories)) |
| parser.print_help() |
| sys.exit(1) |
| run_diff(args.diff, repositories, args.rel_path) |
| |
| |
| if __name__ == "__main__": |
| main() |