xref: /aosp_15_r20/libcore/tools/upstream/merge-from-upstream (revision 89a6322812dc8573315e60046e7959c50dad91d4)
1*89a63228SAndroid Build Coastguard Worker#!/usr/bin/python
2*89a63228SAndroid Build Coastguard Worker#
3*89a63228SAndroid Build Coastguard Worker# Copyright (C) 2021 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 Worker"""
19*89a63228SAndroid Build Coastguard WorkerMerges upstream files to ojluni. This is done by using git to perform a 3-way
20*89a63228SAndroid Build Coastguard Workermerge between the current (base) upstream version, ojluni and the new (target)
21*89a63228SAndroid Build Coastguard Workerupstream version. The 3-way merge is needed because ojluni sometimes contains
22*89a63228SAndroid Build Coastguard Workersome Android-specific changes from the upstream version.
23*89a63228SAndroid Build Coastguard Worker
24*89a63228SAndroid Build Coastguard WorkerThis tool is for libcore maintenance; if you're not maintaining libcore,
25*89a63228SAndroid Build Coastguard Workeryou won't need it (and might not have access to some of the instructions
26*89a63228SAndroid Build Coastguard Workerbelow).
27*89a63228SAndroid Build Coastguard Worker
28*89a63228SAndroid Build Coastguard WorkerThe naming of the repositories (expected, ojluni, 7u40, 8u121-b13,
29*89a63228SAndroid Build Coastguard Worker9b113+, 9+181) is based on the directory name where corresponding
30*89a63228SAndroid Build Coastguard Workersnapshots are stored when following the instructions at
31*89a63228SAndroid Build Coastguard Workerhttp://go/libcore-o-verify
32*89a63228SAndroid Build Coastguard Worker
33*89a63228SAndroid Build Coastguard WorkerThis script tries to preserve Android changes to upstream code when moving to a
34*89a63228SAndroid Build Coastguard Workernewer version.
35*89a63228SAndroid Build Coastguard Worker
36*89a63228SAndroid Build Coastguard WorkerAll the work is made in a new directory which is initialized as a git
37*89a63228SAndroid Build Coastguard Workerrepository. An example of the repository structure, where an update is made
38*89a63228SAndroid Build Coastguard Workerfrom version 9b113+ to 11+28, would be:
39*89a63228SAndroid Build Coastguard Worker
40*89a63228SAndroid Build Coastguard Worker    *   5593705 (HEAD -> main) Merge branch 'ojluni'
41*89a63228SAndroid Build Coastguard Worker    |\
42*89a63228SAndroid Build Coastguard Worker    | * 2effe03 (ojluni) Ojluni commit
43*89a63228SAndroid Build Coastguard Worker    * | 1bef5f3 Target commit (11+28)
44*89a63228SAndroid Build Coastguard Worker    |/
45*89a63228SAndroid Build Coastguard Worker    * 9ae2fbf Base commit (9b113+)
46*89a63228SAndroid Build Coastguard Worker
47*89a63228SAndroid Build Coastguard WorkerThe conflicts during the merge get resolved by git whenever possible. However,
48*89a63228SAndroid Build Coastguard Workersometimes there are conflicts that need to be resolved manually. If that is the
49*89a63228SAndroid Build Coastguard Workercase, the script will terminate to allow for the resolving. Once the user has
50*89a63228SAndroid Build Coastguard Workerresolved the conflicts, they should rerun the script with the --continue
51*89a63228SAndroid Build Coastguard Workeroption.
52*89a63228SAndroid Build Coastguard Worker
53*89a63228SAndroid Build Coastguard WorkerOnce the merge is complete, the script will copy the merged version back to
54*89a63228SAndroid Build Coastguard Workerojluni within the $ANDROID_BUILD_TOP location.
55*89a63228SAndroid Build Coastguard Worker
56*89a63228SAndroid Build Coastguard WorkerFor the script to run correctly, it needs the following environment variables
57*89a63228SAndroid Build Coastguard Workerdefined:
58*89a63228SAndroid Build Coastguard Worker    - OJLUNI_UPSTREAMS
59*89a63228SAndroid Build Coastguard Worker    - ANDROID_BUILD_TOP
60*89a63228SAndroid Build Coastguard Worker
61*89a63228SAndroid Build Coastguard WorkerPossible uses:
62*89a63228SAndroid Build Coastguard Worker
63*89a63228SAndroid Build Coastguard WorkerTo merge in changes from a newer version of the upstream using a default
64*89a63228SAndroid Build Coastguard Workerworking dir created in /tmp:
65*89a63228SAndroid Build Coastguard Worker    merge-from-upstream -f expected -t 11+28 java/util/concurrent
66*89a63228SAndroid Build Coastguard Worker
67*89a63228SAndroid Build Coastguard WorkerTo merge in changes from a newer version of the upstream using a custom
68*89a63228SAndroid Build Coastguard Workerworking dir:
69*89a63228SAndroid Build Coastguard Worker    merge-from-upstream -f expected -t 11+28 \
70*89a63228SAndroid Build Coastguard Worker            -d $HOME/tmp/ojluni-merge java/util/concurrent
71*89a63228SAndroid Build Coastguard Worker
72*89a63228SAndroid Build Coastguard WorkerTo merge in changes for a single file:
73*89a63228SAndroid Build Coastguard Worker    merge-from-upstream -f 9b113+ -t 11+28 \
74*89a63228SAndroid Build Coastguard Worker        java/util/concurrent/atomic/AtomicInteger.java
75*89a63228SAndroid Build Coastguard Worker
76*89a63228SAndroid Build Coastguard WorkerTo merge in changes, using a custom folder, that require conflict resolution:
77*89a63228SAndroid Build Coastguard Worker    merge-from-upstream -f expected -t 11+28 \
78*89a63228SAndroid Build Coastguard Worker        -d $HOME/tmp/ojluni-merge \
79*89a63228SAndroid Build Coastguard Worker        java/util/concurrent
80*89a63228SAndroid Build Coastguard Worker    <manually resolve conflicts and add them to git staging>
81*89a63228SAndroid Build Coastguard Worker    merge-from-upstream --continue \
82*89a63228SAndroid Build Coastguard Worker        -d $HOME/tmp/ojluni-merge java/util/concurrent
83*89a63228SAndroid Build Coastguard Worker"""
84*89a63228SAndroid Build Coastguard Worker
85*89a63228SAndroid Build Coastguard Workerimport argparse
86*89a63228SAndroid Build Coastguard Workerimport os
87*89a63228SAndroid Build Coastguard Workerimport os.path
88*89a63228SAndroid Build Coastguard Workerimport subprocess
89*89a63228SAndroid Build Coastguard Workerimport sys
90*89a63228SAndroid Build Coastguard Workerimport shutil
91*89a63228SAndroid Build Coastguard Worker
92*89a63228SAndroid Build Coastguard Worker
93*89a63228SAndroid Build Coastguard Workerdef printerr(msg):
94*89a63228SAndroid Build Coastguard Worker    sys.stderr.write(msg + "\r\n")
95*89a63228SAndroid Build Coastguard Worker
96*89a63228SAndroid Build Coastguard Worker
97*89a63228SAndroid Build Coastguard Workerdef user_check(msg):
98*89a63228SAndroid Build Coastguard Worker    choice = str(input(msg + " [y/N] ")).strip().lower()
99*89a63228SAndroid Build Coastguard Worker    if choice[:1] == 'y':
100*89a63228SAndroid Build Coastguard Worker        return True
101*89a63228SAndroid Build Coastguard Worker    return False
102*89a63228SAndroid Build Coastguard Worker
103*89a63228SAndroid Build Coastguard Worker
104*89a63228SAndroid Build Coastguard Workerdef check_env_vars():
105*89a63228SAndroid Build Coastguard Worker    keys = [
106*89a63228SAndroid Build Coastguard Worker        'OJLUNI_UPSTREAMS',
107*89a63228SAndroid Build Coastguard Worker        'ANDROID_BUILD_TOP',
108*89a63228SAndroid Build Coastguard Worker    ]
109*89a63228SAndroid Build Coastguard Worker    result = True
110*89a63228SAndroid Build Coastguard Worker    for key in keys:
111*89a63228SAndroid Build Coastguard Worker        if key not in os.environ:
112*89a63228SAndroid Build Coastguard Worker            printerr("Unable to run, you must have {} defined".format(key))
113*89a63228SAndroid Build Coastguard Worker            result = False
114*89a63228SAndroid Build Coastguard Worker    return result
115*89a63228SAndroid Build Coastguard Worker
116*89a63228SAndroid Build Coastguard Worker
117*89a63228SAndroid Build Coastguard Workerdef get_upstream_path(version, rel_path):
118*89a63228SAndroid Build Coastguard Worker    upstreams = os.environ['OJLUNI_UPSTREAMS']
119*89a63228SAndroid Build Coastguard Worker    return '{}/{}/{}'.format(upstreams, version, rel_path)
120*89a63228SAndroid Build Coastguard Worker
121*89a63228SAndroid Build Coastguard Worker
122*89a63228SAndroid Build Coastguard Workerdef get_ojluni_path(rel_path):
123*89a63228SAndroid Build Coastguard Worker    android_build_top = os.environ['ANDROID_BUILD_TOP']
124*89a63228SAndroid Build Coastguard Worker    return '{}/libcore/ojluni/src/main/java/{}'.format(
125*89a63228SAndroid Build Coastguard Worker        android_build_top, rel_path)
126*89a63228SAndroid Build Coastguard Worker
127*89a63228SAndroid Build Coastguard Worker
128*89a63228SAndroid Build Coastguard Workerdef make_copy(src, dst):
129*89a63228SAndroid Build Coastguard Worker    print("Copy " + src + " -> " + dst)
130*89a63228SAndroid Build Coastguard Worker    if os.path.isfile(src):
131*89a63228SAndroid Build Coastguard Worker        if os.path.exists(dst) and os.path.isfile(dst):
132*89a63228SAndroid Build Coastguard Worker            os.remove(dst)
133*89a63228SAndroid Build Coastguard Worker        shutil.copy(src, dst)
134*89a63228SAndroid Build Coastguard Worker    else:
135*89a63228SAndroid Build Coastguard Worker        shutil.copytree(src, dst, dirs_exist_ok=True)
136*89a63228SAndroid Build Coastguard Worker
137*89a63228SAndroid Build Coastguard Worker
138*89a63228SAndroid Build Coastguard Workerclass Repo:
139*89a63228SAndroid Build Coastguard Worker    def __init__(self, dir):
140*89a63228SAndroid Build Coastguard Worker        self.dir = dir
141*89a63228SAndroid Build Coastguard Worker
142*89a63228SAndroid Build Coastguard Worker    def init(self):
143*89a63228SAndroid Build Coastguard Worker        if 0 != subprocess.call(['git', 'init', '-b', 'main', self.dir]):
144*89a63228SAndroid Build Coastguard Worker            raise RuntimeError(
145*89a63228SAndroid Build Coastguard Worker                "Unable to initialize working git repository.")
146*89a63228SAndroid Build Coastguard Worker        subprocess.call(['git', '-C', self.dir,
147*89a63228SAndroid Build Coastguard Worker                         'config', 'rerere.enabled', 'true'])
148*89a63228SAndroid Build Coastguard Worker
149*89a63228SAndroid Build Coastguard Worker    def commit_all(self, id, msg):
150*89a63228SAndroid Build Coastguard Worker        if 0 != subprocess.call(['git', '-C', self.dir, 'add', '*']):
151*89a63228SAndroid Build Coastguard Worker            raise RuntimeError("Unable to add the {} files.".format(id))
152*89a63228SAndroid Build Coastguard Worker        if 0 != subprocess.call(['git', '-C', self.dir, 'commit',
153*89a63228SAndroid Build Coastguard Worker                                '-m', msg]):
154*89a63228SAndroid Build Coastguard Worker            raise RuntimeError("Unable to commit the {} files.".format(id))
155*89a63228SAndroid Build Coastguard Worker
156*89a63228SAndroid Build Coastguard Worker    def checkout_branch(self, branch, is_new=False):
157*89a63228SAndroid Build Coastguard Worker        cmd = ['git', '-C', self.dir, 'checkout']
158*89a63228SAndroid Build Coastguard Worker        if is_new:
159*89a63228SAndroid Build Coastguard Worker            cmd.append('-b')
160*89a63228SAndroid Build Coastguard Worker        cmd.append(branch)
161*89a63228SAndroid Build Coastguard Worker        if 0 != subprocess.call(cmd):
162*89a63228SAndroid Build Coastguard Worker            raise RuntimeError("Unable to checkout the {} branch."
163*89a63228SAndroid Build Coastguard Worker                               .format(branch))
164*89a63228SAndroid Build Coastguard Worker
165*89a63228SAndroid Build Coastguard Worker    def merge(self, branch):
166*89a63228SAndroid Build Coastguard Worker        """
167*89a63228SAndroid Build Coastguard Worker        Tries to merge in a branch and returns True if the merge commit has
168*89a63228SAndroid Build Coastguard Worker        been created. If there are conflicts to be resolved, this returns
169*89a63228SAndroid Build Coastguard Worker        False.
170*89a63228SAndroid Build Coastguard Worker        """
171*89a63228SAndroid Build Coastguard Worker        if 0 == subprocess.call(['git', '-C', self.dir,
172*89a63228SAndroid Build Coastguard Worker                                'merge', branch, '--no-edit']):
173*89a63228SAndroid Build Coastguard Worker            return True
174*89a63228SAndroid Build Coastguard Worker        if not self.is_merging():
175*89a63228SAndroid Build Coastguard Worker            raise RuntimeError("Unable to run merge for the {} branch."
176*89a63228SAndroid Build Coastguard Worker                               .format(branch))
177*89a63228SAndroid Build Coastguard Worker        subprocess.call(['git', '-C', self.dir, 'rerere'])
178*89a63228SAndroid Build Coastguard Worker        return False
179*89a63228SAndroid Build Coastguard Worker
180*89a63228SAndroid Build Coastguard Worker    def check_resolved_from_cache(self):
181*89a63228SAndroid Build Coastguard Worker        """
182*89a63228SAndroid Build Coastguard Worker        Checks if some conflicts have been resolved by the git rerere tool. The
183*89a63228SAndroid Build Coastguard Worker        tool only applies the previous resolution, but does not mark the file
184*89a63228SAndroid Build Coastguard Worker        as resolved afterwards. Therefore this function will go through the
185*89a63228SAndroid Build Coastguard Worker        unresolved files and see if there are outstanding conflicts. If all
186*89a63228SAndroid Build Coastguard Worker        conflicts have been resolved, the file gets stages.
187*89a63228SAndroid Build Coastguard Worker
188*89a63228SAndroid Build Coastguard Worker        Returns True if all conflicts are resolved, False otherwise.
189*89a63228SAndroid Build Coastguard Worker        """
190*89a63228SAndroid Build Coastguard Worker        # git diff --check will exit with error if there are conflicts to be
191*89a63228SAndroid Build Coastguard Worker        # resolved, therefore we need to use check=False option to avoid an
192*89a63228SAndroid Build Coastguard Worker        # exception to be raised
193*89a63228SAndroid Build Coastguard Worker        conflict_markers = subprocess.run(['git', '-C', self.dir,
194*89a63228SAndroid Build Coastguard Worker                                           'diff', '--check'],
195*89a63228SAndroid Build Coastguard Worker                                          stdout=subprocess.PIPE,
196*89a63228SAndroid Build Coastguard Worker                                          check=False).stdout
197*89a63228SAndroid Build Coastguard Worker        conflicts = subprocess.check_output(['git', '-C', self.dir, 'diff',
198*89a63228SAndroid Build Coastguard Worker                                             '--name-only', '--diff-filter=U'])
199*89a63228SAndroid Build Coastguard Worker
200*89a63228SAndroid Build Coastguard Worker        for filename in conflicts.splitlines():
201*89a63228SAndroid Build Coastguard Worker            if conflict_markers.find(filename) != -1:
202*89a63228SAndroid Build Coastguard Worker                print("{} still has conflicts, please resolve manually".
203*89a63228SAndroid Build Coastguard Worker                      format(filename))
204*89a63228SAndroid Build Coastguard Worker            else:
205*89a63228SAndroid Build Coastguard Worker                print("{} has been resolved, staging it".format(filename))
206*89a63228SAndroid Build Coastguard Worker                subprocess.call(['git', '-C', self.dir, 'add', filename])
207*89a63228SAndroid Build Coastguard Worker
208*89a63228SAndroid Build Coastguard Worker        return not self.has_conflicts()
209*89a63228SAndroid Build Coastguard Worker
210*89a63228SAndroid Build Coastguard Worker    def has_changes(self):
211*89a63228SAndroid Build Coastguard Worker        result = subprocess.check_output(['git', '-C', self.dir, 'status',
212*89a63228SAndroid Build Coastguard Worker                                          '--porcelain'])
213*89a63228SAndroid Build Coastguard Worker        return len(result) != 0
214*89a63228SAndroid Build Coastguard Worker
215*89a63228SAndroid Build Coastguard Worker    def has_conflicts(self):
216*89a63228SAndroid Build Coastguard Worker        conflicts = subprocess.check_output(['git', '-C', self.dir, 'diff',
217*89a63228SAndroid Build Coastguard Worker                                             '--name-only', '--diff-filter=U'])
218*89a63228SAndroid Build Coastguard Worker        return len(conflicts) != 0
219*89a63228SAndroid Build Coastguard Worker
220*89a63228SAndroid Build Coastguard Worker    def is_merging(self):
221*89a63228SAndroid Build Coastguard Worker        return 0 == subprocess.call(['git', '-C', self.dir, 'rev-parse',
222*89a63228SAndroid Build Coastguard Worker                                     '-q', '--verify', 'MERGE_HEAD'],
223*89a63228SAndroid Build Coastguard Worker                                    stdout=subprocess.DEVNULL)
224*89a63228SAndroid Build Coastguard Worker
225*89a63228SAndroid Build Coastguard Worker    def complete_merge(self):
226*89a63228SAndroid Build Coastguard Worker        print("Completing merge in {}".format(self.dir))
227*89a63228SAndroid Build Coastguard Worker        subprocess.call(['git', '-C', self.dir, 'rerere'])
228*89a63228SAndroid Build Coastguard Worker        if 0 != subprocess.call(['git', '-C', self.dir,
229*89a63228SAndroid Build Coastguard Worker                                 'commit', '--no-edit']):
230*89a63228SAndroid Build Coastguard Worker            raise RuntimeError("Unable to complete the merge in {}."
231*89a63228SAndroid Build Coastguard Worker                               .format(self.dir))
232*89a63228SAndroid Build Coastguard Worker        if self.is_merging():
233*89a63228SAndroid Build Coastguard Worker            raise RuntimeError(
234*89a63228SAndroid Build Coastguard Worker                "Merging in {} is not complete".format(self.dir))
235*89a63228SAndroid Build Coastguard Worker
236*89a63228SAndroid Build Coastguard Worker    def load_resolve_files(self, resolve_dir):
237*89a63228SAndroid Build Coastguard Worker        print("Loading resolve files from {}".format(resolve_dir))
238*89a63228SAndroid Build Coastguard Worker        if not os.path.lexists(resolve_dir):
239*89a63228SAndroid Build Coastguard Worker            print("Resolve dir {} not found, no resolutions will be used"
240*89a63228SAndroid Build Coastguard Worker                  .format(resolve_dir))
241*89a63228SAndroid Build Coastguard Worker            return
242*89a63228SAndroid Build Coastguard Worker        make_copy(resolve_dir, self.dir + "/.git/rr-cache")
243*89a63228SAndroid Build Coastguard Worker
244*89a63228SAndroid Build Coastguard Worker    def save_resolve_files(self, resolve_dir):
245*89a63228SAndroid Build Coastguard Worker        print("Saving resolve files to {}".format(resolve_dir))
246*89a63228SAndroid Build Coastguard Worker        if not os.path.lexists(resolve_dir):
247*89a63228SAndroid Build Coastguard Worker            os.makedirs(resolve_dir)
248*89a63228SAndroid Build Coastguard Worker        make_copy(self.dir + "/.git/rr-cache", resolve_dir)
249*89a63228SAndroid Build Coastguard Worker
250*89a63228SAndroid Build Coastguard Worker
251*89a63228SAndroid Build Coastguard Workerclass Merger:
252*89a63228SAndroid Build Coastguard Worker    def __init__(self, repo_dir, rel_path, resolve_dir):
253*89a63228SAndroid Build Coastguard Worker        self.repo = Repo(repo_dir)
254*89a63228SAndroid Build Coastguard Worker        # Have all the source files copied inside a src dir, so we don't have
255*89a63228SAndroid Build Coastguard Worker        # any issue with copying back the .git dir
256*89a63228SAndroid Build Coastguard Worker        self.working_dir = repo_dir + "/src"
257*89a63228SAndroid Build Coastguard Worker        self.rel_path = rel_path
258*89a63228SAndroid Build Coastguard Worker        self.resolve_dir = resolve_dir
259*89a63228SAndroid Build Coastguard Worker
260*89a63228SAndroid Build Coastguard Worker    def create_working_dir(self):
261*89a63228SAndroid Build Coastguard Worker        if os.path.lexists(self.repo.dir):
262*89a63228SAndroid Build Coastguard Worker            if not user_check(
263*89a63228SAndroid Build Coastguard Worker                    '{} already exists. Can it be removed?'
264*89a63228SAndroid Build Coastguard Worker                    .format(self.repo.dir)):
265*89a63228SAndroid Build Coastguard Worker                raise RuntimeError(
266*89a63228SAndroid Build Coastguard Worker                    'Will not remove {}. Consider using another '
267*89a63228SAndroid Build Coastguard Worker                    'working dir'.format(self.repo.dir))
268*89a63228SAndroid Build Coastguard Worker            try:
269*89a63228SAndroid Build Coastguard Worker                shutil.rmtree(self.repo.dir)
270*89a63228SAndroid Build Coastguard Worker            except OSError:
271*89a63228SAndroid Build Coastguard Worker                printerr("Unable to delete {}.".format(self.repo.dir))
272*89a63228SAndroid Build Coastguard Worker                raise
273*89a63228SAndroid Build Coastguard Worker        os.makedirs(self.working_dir)
274*89a63228SAndroid Build Coastguard Worker        self.repo.init()
275*89a63228SAndroid Build Coastguard Worker        if self.resolve_dir is not None:
276*89a63228SAndroid Build Coastguard Worker            self.repo.load_resolve_files(self.resolve_dir)
277*89a63228SAndroid Build Coastguard Worker
278*89a63228SAndroid Build Coastguard Worker    def copy_upstream_files(self, version, msg):
279*89a63228SAndroid Build Coastguard Worker        full_path = get_upstream_path(version, self.rel_path)
280*89a63228SAndroid Build Coastguard Worker        make_copy(full_path, self.working_dir)
281*89a63228SAndroid Build Coastguard Worker        self.repo.commit_all(version, msg)
282*89a63228SAndroid Build Coastguard Worker
283*89a63228SAndroid Build Coastguard Worker    def copy_base_files(self, base_version):
284*89a63228SAndroid Build Coastguard Worker        self.copy_upstream_files(base_version,
285*89a63228SAndroid Build Coastguard Worker                                 'Base commit ({})'.format(base_version))
286*89a63228SAndroid Build Coastguard Worker
287*89a63228SAndroid Build Coastguard Worker    def copy_target_files(self, target_version):
288*89a63228SAndroid Build Coastguard Worker        self.copy_upstream_files(target_version,
289*89a63228SAndroid Build Coastguard Worker                                 'Target commit ({})'.format(target_version))
290*89a63228SAndroid Build Coastguard Worker
291*89a63228SAndroid Build Coastguard Worker    def copy_ojluni_files(self):
292*89a63228SAndroid Build Coastguard Worker        full_path = get_ojluni_path(self.rel_path)
293*89a63228SAndroid Build Coastguard Worker        make_copy(full_path, self.working_dir)
294*89a63228SAndroid Build Coastguard Worker        if self.repo.has_changes():
295*89a63228SAndroid Build Coastguard Worker            self.repo.commit_all('ojluni', 'Ojluni commit')
296*89a63228SAndroid Build Coastguard Worker            return True
297*89a63228SAndroid Build Coastguard Worker        else:
298*89a63228SAndroid Build Coastguard Worker            return False
299*89a63228SAndroid Build Coastguard Worker
300*89a63228SAndroid Build Coastguard Worker    def run_ojluni_merge(self):
301*89a63228SAndroid Build Coastguard Worker        if self.repo.merge('ojluni'):
302*89a63228SAndroid Build Coastguard Worker            return
303*89a63228SAndroid Build Coastguard Worker        if self.repo.check_resolved_from_cache():
304*89a63228SAndroid Build Coastguard Worker            self.repo.complete_merge()
305*89a63228SAndroid Build Coastguard Worker            return
306*89a63228SAndroid Build Coastguard Worker        raise RuntimeError('\r\nThere are conflicts to be resolved.'
307*89a63228SAndroid Build Coastguard Worker                           '\r\nManually merge the changes and rerun '
308*89a63228SAndroid Build Coastguard Worker                           'this script with --continue')
309*89a63228SAndroid Build Coastguard Worker
310*89a63228SAndroid Build Coastguard Worker    def copy_back_to_ojluni(self):
311*89a63228SAndroid Build Coastguard Worker        # Save any resolutions that were made for future reuse
312*89a63228SAndroid Build Coastguard Worker        if self.resolve_dir is not None:
313*89a63228SAndroid Build Coastguard Worker            self.repo.save_resolve_files(self.resolve_dir)
314*89a63228SAndroid Build Coastguard Worker
315*89a63228SAndroid Build Coastguard Worker        src_path = self.working_dir
316*89a63228SAndroid Build Coastguard Worker        dst_path = get_ojluni_path(self.rel_path)
317*89a63228SAndroid Build Coastguard Worker        if os.path.isfile(dst_path):
318*89a63228SAndroid Build Coastguard Worker            src_path += '/' + os.path.basename(self.rel_path)
319*89a63228SAndroid Build Coastguard Worker        make_copy(src_path, dst_path)
320*89a63228SAndroid Build Coastguard Worker
321*89a63228SAndroid Build Coastguard Worker    def run(self, base_version, target_version):
322*89a63228SAndroid Build Coastguard Worker        print("Merging {} from {} into ojluni (based on {}). "
323*89a63228SAndroid Build Coastguard Worker              "Using {} as working dir."
324*89a63228SAndroid Build Coastguard Worker              .format(self.rel_path, target_version,
325*89a63228SAndroid Build Coastguard Worker                      base_version, self.repo.dir))
326*89a63228SAndroid Build Coastguard Worker        self.create_working_dir()
327*89a63228SAndroid Build Coastguard Worker        self.copy_base_files(base_version)
328*89a63228SAndroid Build Coastguard Worker        # The ojluni code should be added in its own branch. This is to make
329*89a63228SAndroid Build Coastguard Worker        # Git perform the 3-way merge once a commit is added with the latest
330*89a63228SAndroid Build Coastguard Worker        # upstream code.
331*89a63228SAndroid Build Coastguard Worker        self.repo.checkout_branch('ojluni', is_new=True)
332*89a63228SAndroid Build Coastguard Worker        merge_needed = self.copy_ojluni_files()
333*89a63228SAndroid Build Coastguard Worker        self.repo.checkout_branch('main')
334*89a63228SAndroid Build Coastguard Worker        self.copy_target_files(target_version)
335*89a63228SAndroid Build Coastguard Worker        if merge_needed:
336*89a63228SAndroid Build Coastguard Worker            # Runs the merge in the working directory, if some conflicts need
337*89a63228SAndroid Build Coastguard Worker            # to be resolved manually, then an exception is raised which will
338*89a63228SAndroid Build Coastguard Worker            # terminate the script, informing the user that manual intervention
339*89a63228SAndroid Build Coastguard Worker            # is needed.
340*89a63228SAndroid Build Coastguard Worker            self.run_ojluni_merge()
341*89a63228SAndroid Build Coastguard Worker        else:
342*89a63228SAndroid Build Coastguard Worker            print("No merging needed as there were no "
343*89a63228SAndroid Build Coastguard Worker                  "Android-specific changes, forwarding to new version ({})"
344*89a63228SAndroid Build Coastguard Worker                  .format(target_version))
345*89a63228SAndroid Build Coastguard Worker        self.copy_back_to_ojluni()
346*89a63228SAndroid Build Coastguard Worker
347*89a63228SAndroid Build Coastguard Worker    def complete_existing_run(self):
348*89a63228SAndroid Build Coastguard Worker        if self.repo.is_merging():
349*89a63228SAndroid Build Coastguard Worker            self.repo.complete_merge()
350*89a63228SAndroid Build Coastguard Worker        self.copy_back_to_ojluni()
351*89a63228SAndroid Build Coastguard Worker
352*89a63228SAndroid Build Coastguard Worker
353*89a63228SAndroid Build Coastguard Workerdef main():
354*89a63228SAndroid Build Coastguard Worker    if not check_env_vars():
355*89a63228SAndroid Build Coastguard Worker        return
356*89a63228SAndroid Build Coastguard Worker
357*89a63228SAndroid Build Coastguard Worker    upstreams = os.environ['OJLUNI_UPSTREAMS']
358*89a63228SAndroid Build Coastguard Worker    repositories = sorted(
359*89a63228SAndroid Build Coastguard Worker        [d for d in os.listdir(upstreams)
360*89a63228SAndroid Build Coastguard Worker         if os.path.isdir(os.path.join(upstreams, d))]
361*89a63228SAndroid Build Coastguard Worker    )
362*89a63228SAndroid Build Coastguard Worker
363*89a63228SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(
364*89a63228SAndroid Build Coastguard Worker        description='''
365*89a63228SAndroid Build Coastguard Worker        Merge upstream files from ${OJLUNI_UPSTREAMS} to libcore/ojluni.
366*89a63228SAndroid Build Coastguard Worker        Needs the base (from) repository as well as the target (to) repository.
367*89a63228SAndroid Build Coastguard Worker        Repositories can be chosen from:
368*89a63228SAndroid Build Coastguard Worker        ''' + ' '.join(repositories) + '.',
369*89a63228SAndroid Build Coastguard Worker        # include default values in help
370*89a63228SAndroid Build Coastguard Worker        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
371*89a63228SAndroid Build Coastguard Worker    )
372*89a63228SAndroid Build Coastguard Worker    parser.add_argument('-f', '--from', default='expected',
373*89a63228SAndroid Build Coastguard Worker                        choices=repositories,
374*89a63228SAndroid Build Coastguard Worker                        dest='base',
375*89a63228SAndroid Build Coastguard Worker                        help='Repository on which the requested ojluni '
376*89a63228SAndroid Build Coastguard Worker                        'files are based.')
377*89a63228SAndroid Build Coastguard Worker    parser.add_argument('-t', '--to',
378*89a63228SAndroid Build Coastguard Worker                        choices=repositories,
379*89a63228SAndroid Build Coastguard Worker                        dest='target',
380*89a63228SAndroid Build Coastguard Worker                        help='Repository to which the requested ojluni '
381*89a63228SAndroid Build Coastguard Worker                        'files will be updated.')
382*89a63228SAndroid Build Coastguard Worker    parser.add_argument('-d', '--work-dir', default='/tmp/ojluni-merge',
383*89a63228SAndroid Build Coastguard Worker                        help='Path where the merge will be performed. '
384*89a63228SAndroid Build Coastguard Worker                        'Any existing files in the path will be removed')
385*89a63228SAndroid Build Coastguard Worker    parser.add_argument('-r', '--resolve-dir', default=None,
386*89a63228SAndroid Build Coastguard Worker                        dest='resolve_dir',
387*89a63228SAndroid Build Coastguard Worker                        help='Path where the git resolutions are cached. '
388*89a63228SAndroid Build Coastguard Worker                        'By default, no cache is used.')
389*89a63228SAndroid Build Coastguard Worker    parser.add_argument('--continue', action='store_true', dest='proceed',
390*89a63228SAndroid Build Coastguard Worker                        help='Flag to specify after merge conflicts '
391*89a63228SAndroid Build Coastguard Worker                        'are resolved')
392*89a63228SAndroid Build Coastguard Worker    parser.add_argument('rel_path', nargs=1, metavar='<relative_path>',
393*89a63228SAndroid Build Coastguard Worker                        help='File to merge: a relative path below '
394*89a63228SAndroid Build Coastguard Worker                        'libcore/ojluni/ which could point to '
395*89a63228SAndroid Build Coastguard Worker                        'a file or folder.')
396*89a63228SAndroid Build Coastguard Worker    args = parser.parse_args()
397*89a63228SAndroid Build Coastguard Worker    try:
398*89a63228SAndroid Build Coastguard Worker        merger = Merger(args.work_dir, args.rel_path[0], args.resolve_dir)
399*89a63228SAndroid Build Coastguard Worker        if args.proceed:
400*89a63228SAndroid Build Coastguard Worker            merger.complete_existing_run()
401*89a63228SAndroid Build Coastguard Worker        else:
402*89a63228SAndroid Build Coastguard Worker            if args.target is None:
403*89a63228SAndroid Build Coastguard Worker                raise RuntimeError('Please specify the target upstream '
404*89a63228SAndroid Build Coastguard Worker                                   'version using the -t/--to argument')
405*89a63228SAndroid Build Coastguard Worker            merger.run(args.base, args.target)
406*89a63228SAndroid Build Coastguard Worker    except Exception as e:
407*89a63228SAndroid Build Coastguard Worker        printerr(str(e))
408*89a63228SAndroid Build Coastguard Worker
409*89a63228SAndroid Build Coastguard Worker
410*89a63228SAndroid Build Coastguard Workerif __name__ == "__main__":
411*89a63228SAndroid Build Coastguard Worker    main()
412