xref: /aosp_15_r20/libcore/tools/upstream/upstream-diff (revision 89a6322812dc8573315e60046e7959c50dad91d4)
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