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