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 WorkerReports on merge status of Java files in a package based on four 19*89a63228SAndroid Build Coastguard Workerrepositories: 20*89a63228SAndroid Build Coastguard Worker 21*89a63228SAndroid Build Coastguard Workerbaseline - upstream baseline used for previous Android release 22*89a63228SAndroid Build Coastguard Workerrelease - files in previous Android release 23*89a63228SAndroid Build Coastguard Workercurrent - target for merge 24*89a63228SAndroid Build Coastguard Workerupstream - new upstream being merged 25*89a63228SAndroid Build Coastguard Worker 26*89a63228SAndroid Build Coastguard WorkerExample output: 27*89a63228SAndroid Build Coastguard Worker$ tools/upstream/pkg-status java.security.spec 28*89a63228SAndroid Build Coastguard WorkerAlgorithmParameterSpec.java: Unchanged, Done 29*89a63228SAndroid Build Coastguard WorkerDSAGenParameterSpec.java: Added, TO DO 30*89a63228SAndroid Build Coastguard WorkerDSAParameterSpec.java: Unchanged, Done 31*89a63228SAndroid Build Coastguard WorkerDSAPrivateKeySpec.java: Unchanged, Done 32*89a63228SAndroid Build Coastguard WorkerDSAPublicKeySpec.java: Unchanged, Done 33*89a63228SAndroid Build Coastguard WorkerECField.java: Unchanged, Done 34*89a63228SAndroid Build Coastguard WorkerECFieldF2m.java: Unchanged, Done 35*89a63228SAndroid Build Coastguard WorkerECFieldFp.java: Unchanged, Done 36*89a63228SAndroid Build Coastguard WorkerECGenParameterSpec.java: Updated, TO DO 37*89a63228SAndroid Build Coastguard Worker[...] 38*89a63228SAndroid Build Coastguard Worker""" 39*89a63228SAndroid Build Coastguard Worker 40*89a63228SAndroid Build Coastguard Workerimport argparse 41*89a63228SAndroid Build Coastguard Workerimport hashlib 42*89a63228SAndroid Build Coastguard Workerimport os 43*89a63228SAndroid Build Coastguard Workerimport os.path 44*89a63228SAndroid Build Coastguard Workerimport sys 45*89a63228SAndroid Build Coastguard Workerfrom enum import Enum 46*89a63228SAndroid Build Coastguard Workerfrom pathlib import Path 47*89a63228SAndroid Build Coastguard Worker 48*89a63228SAndroid Build Coastguard WorkerRED = '\u001b[31m' 49*89a63228SAndroid Build Coastguard WorkerGREEN = "\u001b[32m" 50*89a63228SAndroid Build Coastguard WorkerYELLOW = "\u001b[33m" 51*89a63228SAndroid Build Coastguard WorkerRESET = "\u001b[0m" 52*89a63228SAndroid Build Coastguard Worker 53*89a63228SAndroid Build Coastguard Worker 54*89a63228SAndroid Build Coastguard Workerdef colourise(colour, string): 55*89a63228SAndroid Build Coastguard Worker """Wrap a string with an ANSI colour code""" 56*89a63228SAndroid Build Coastguard Worker return "%s%s%s" % (colour, string, RESET) 57*89a63228SAndroid Build Coastguard Worker 58*89a63228SAndroid Build Coastguard Worker 59*89a63228SAndroid Build Coastguard Workerdef red(string): 60*89a63228SAndroid Build Coastguard Worker """Wrap a string with a red ANSI colour code""" 61*89a63228SAndroid Build Coastguard Worker return colourise(RED, string) 62*89a63228SAndroid Build Coastguard Worker 63*89a63228SAndroid Build Coastguard Worker 64*89a63228SAndroid Build Coastguard Workerdef green(string): 65*89a63228SAndroid Build Coastguard Worker """Wrap a string with a green ANSI colour code""" 66*89a63228SAndroid Build Coastguard Worker return colourise(GREEN, string) 67*89a63228SAndroid Build Coastguard Worker 68*89a63228SAndroid Build Coastguard Worker 69*89a63228SAndroid Build Coastguard Workerdef yellow(string): 70*89a63228SAndroid Build Coastguard Worker """Wrap a string with a yellow ANSI colour code""" 71*89a63228SAndroid Build Coastguard Worker return colourise(YELLOW, string) 72*89a63228SAndroid Build Coastguard Worker 73*89a63228SAndroid Build Coastguard Worker 74*89a63228SAndroid Build Coastguard Workerclass WorkStatus(Enum): 75*89a63228SAndroid Build Coastguard Worker """Enum for a file's work completion status""" 76*89a63228SAndroid Build Coastguard Worker UNKNOWN = ('Unknown', red) 77*89a63228SAndroid Build Coastguard Worker TODO = ('TO DO', yellow) 78*89a63228SAndroid Build Coastguard Worker DONE = ('Done', green) 79*89a63228SAndroid Build Coastguard Worker PROBABLY_DONE = ('Probably done', green) 80*89a63228SAndroid Build Coastguard Worker ERROR = ('Error', red) 81*89a63228SAndroid Build Coastguard Worker 82*89a63228SAndroid Build Coastguard Worker def colourise(self, string): 83*89a63228SAndroid Build Coastguard Worker """Colourise a string using the method for this enum value""" 84*89a63228SAndroid Build Coastguard Worker return self.colourfunc(string) 85*89a63228SAndroid Build Coastguard Worker 86*89a63228SAndroid Build Coastguard Worker def __init__(self, description, colourfunc): 87*89a63228SAndroid Build Coastguard Worker self.description = description 88*89a63228SAndroid Build Coastguard Worker self.colourfunc = colourfunc 89*89a63228SAndroid Build Coastguard Worker 90*89a63228SAndroid Build Coastguard Worker 91*89a63228SAndroid Build Coastguard Workerclass MergeStatus(Enum): 92*89a63228SAndroid Build Coastguard Worker """Enum for a file's merge status""" 93*89a63228SAndroid Build Coastguard Worker UNKNOWN = 'Unknown!' 94*89a63228SAndroid Build Coastguard Worker MISSING = 'Missing' 95*89a63228SAndroid Build Coastguard Worker ADDED = 'Added' 96*89a63228SAndroid Build Coastguard Worker DELETED = 'Deleted or moved' 97*89a63228SAndroid Build Coastguard Worker UNCHANGED = 'Unchanged' 98*89a63228SAndroid Build Coastguard Worker UPDATED = 'Updated' 99*89a63228SAndroid Build Coastguard Worker 100*89a63228SAndroid Build Coastguard Worker def __init__(self, description): 101*89a63228SAndroid Build Coastguard Worker self.description = description 102*89a63228SAndroid Build Coastguard Worker 103*89a63228SAndroid Build Coastguard Worker 104*89a63228SAndroid Build Coastguard Workerclass MergeConfig: 105*89a63228SAndroid Build Coastguard Worker """ 106*89a63228SAndroid Build Coastguard Worker Configuration for an upstream merge. 107*89a63228SAndroid Build Coastguard Worker 108*89a63228SAndroid Build Coastguard Worker Encapsulates the paths to each of the required code repositories. 109*89a63228SAndroid Build Coastguard Worker """ 110*89a63228SAndroid Build Coastguard Worker def __init__(self, baseline, release, current, upstream) -> None: 111*89a63228SAndroid Build Coastguard Worker self.baseline = baseline 112*89a63228SAndroid Build Coastguard Worker self.release = release 113*89a63228SAndroid Build Coastguard Worker self.current = current 114*89a63228SAndroid Build Coastguard Worker self.upstream = upstream 115*89a63228SAndroid Build Coastguard Worker try: 116*89a63228SAndroid Build Coastguard Worker # Root of checked-out Android sources, set by the "lunch" command. 117*89a63228SAndroid Build Coastguard Worker self.android_build_top = os.environ['ANDROID_BUILD_TOP'] 118*89a63228SAndroid Build Coastguard Worker # Root of repository snapshots. 119*89a63228SAndroid Build Coastguard Worker self.ojluni_upstreams = os.environ['OJLUNI_UPSTREAMS'] 120*89a63228SAndroid Build Coastguard Worker except KeyError: 121*89a63228SAndroid Build Coastguard Worker sys.exit('`lunch` and set OJLUNI_UPSTREAMS first.') 122*89a63228SAndroid Build Coastguard Worker 123*89a63228SAndroid Build Coastguard Worker 124*89a63228SAndroid Build Coastguard Worker def java_dir(self, repo, pkg): 125*89a63228SAndroid Build Coastguard Worker relpath = pkg.replace('.', '/') 126*89a63228SAndroid Build Coastguard Worker if repo == self.current: 127*89a63228SAndroid Build Coastguard Worker return '%s/libcore/%s/src/main/java/%s' % ( 128*89a63228SAndroid Build Coastguard Worker self.android_build_top, self.current, relpath) 129*89a63228SAndroid Build Coastguard Worker else: 130*89a63228SAndroid Build Coastguard Worker return '%s/%s/%s' % (self.ojluni_upstreams, repo, relpath) 131*89a63228SAndroid Build Coastguard Worker 132*89a63228SAndroid Build Coastguard Worker def baseline_dir(self, pkg): 133*89a63228SAndroid Build Coastguard Worker return self.java_dir(self.baseline, pkg) 134*89a63228SAndroid Build Coastguard Worker 135*89a63228SAndroid Build Coastguard Worker def release_dir(self, pkg): 136*89a63228SAndroid Build Coastguard Worker return self.java_dir(self.release, pkg) 137*89a63228SAndroid Build Coastguard Worker 138*89a63228SAndroid Build Coastguard Worker def current_dir(self, pkg): 139*89a63228SAndroid Build Coastguard Worker return self.java_dir(self.current, pkg) 140*89a63228SAndroid Build Coastguard Worker 141*89a63228SAndroid Build Coastguard Worker def upstream_dir(self, pkg): 142*89a63228SAndroid Build Coastguard Worker return self.java_dir(self.upstream, pkg) 143*89a63228SAndroid Build Coastguard Worker 144*89a63228SAndroid Build Coastguard Worker 145*89a63228SAndroid Build Coastguard Workerclass JavaPackage: 146*89a63228SAndroid Build Coastguard Worker """ 147*89a63228SAndroid Build Coastguard Worker Encapsulates information about a single Java package, notably paths 148*89a63228SAndroid Build Coastguard Worker to it within each repository. 149*89a63228SAndroid Build Coastguard Worker """ 150*89a63228SAndroid Build Coastguard Worker def __init__(self, config, name) -> None: 151*89a63228SAndroid Build Coastguard Worker self.name = name 152*89a63228SAndroid Build Coastguard Worker self.baseline_dir = config.baseline_dir(name) 153*89a63228SAndroid Build Coastguard Worker self.release_dir = config.release_dir(name) 154*89a63228SAndroid Build Coastguard Worker self.current_dir = config.current_dir(name) 155*89a63228SAndroid Build Coastguard Worker self.upstream_dir = config.upstream_dir(name) 156*89a63228SAndroid Build Coastguard Worker 157*89a63228SAndroid Build Coastguard Worker @staticmethod 158*89a63228SAndroid Build Coastguard Worker def list_candidate_files(path): 159*89a63228SAndroid Build Coastguard Worker """Returns a list of all the Java filenames in a directory.""" 160*89a63228SAndroid Build Coastguard Worker return list(filter( 161*89a63228SAndroid Build Coastguard Worker lambda f: f.endswith('.java') and f != 'package-info.java', 162*89a63228SAndroid Build Coastguard Worker os.listdir(path))) 163*89a63228SAndroid Build Coastguard Worker 164*89a63228SAndroid Build Coastguard Worker def all_files(self): 165*89a63228SAndroid Build Coastguard Worker """Returns the union of all the Java filenames in all repositories.""" 166*89a63228SAndroid Build Coastguard Worker files = set(self.list_candidate_files(self.baseline_dir)) 167*89a63228SAndroid Build Coastguard Worker files.update(self.list_candidate_files(self.release_dir)) 168*89a63228SAndroid Build Coastguard Worker files.update(self.list_candidate_files(self.upstream_dir)) 169*89a63228SAndroid Build Coastguard Worker files.update(self.list_candidate_files(self.current_dir)) 170*89a63228SAndroid Build Coastguard Worker return sorted(list(files)) 171*89a63228SAndroid Build Coastguard Worker 172*89a63228SAndroid Build Coastguard Worker def java_files(self): 173*89a63228SAndroid Build Coastguard Worker """Returns a list of JavaFiles corresponding to all filenames.""" 174*89a63228SAndroid Build Coastguard Worker return map(lambda f: JavaFile(self, f), self.all_files()) 175*89a63228SAndroid Build Coastguard Worker 176*89a63228SAndroid Build Coastguard Worker def baseline_path(self, filename): 177*89a63228SAndroid Build Coastguard Worker return Path(self.baseline_dir + '/' + filename) 178*89a63228SAndroid Build Coastguard Worker 179*89a63228SAndroid Build Coastguard Worker def release_path(self, filename): 180*89a63228SAndroid Build Coastguard Worker return Path(self.release_dir + '/' + filename) 181*89a63228SAndroid Build Coastguard Worker 182*89a63228SAndroid Build Coastguard Worker def current_path(self, filename): 183*89a63228SAndroid Build Coastguard Worker return Path(self.current_dir + '/' + filename) 184*89a63228SAndroid Build Coastguard Worker 185*89a63228SAndroid Build Coastguard Worker def upstream_path(self, filename): 186*89a63228SAndroid Build Coastguard Worker return Path(self.upstream_dir + '/' + filename) 187*89a63228SAndroid Build Coastguard Worker 188*89a63228SAndroid Build Coastguard Worker def report_merge_status(self): 189*89a63228SAndroid Build Coastguard Worker """Report on the mergse status of this package.""" 190*89a63228SAndroid Build Coastguard Worker for file in self.java_files(): 191*89a63228SAndroid Build Coastguard Worker merge_status, work_status = file.status() 192*89a63228SAndroid Build Coastguard Worker text = '%s: %s, %s' % \ 193*89a63228SAndroid Build Coastguard Worker ( 194*89a63228SAndroid Build Coastguard Worker file.name, merge_status.description, 195*89a63228SAndroid Build Coastguard Worker work_status.description) 196*89a63228SAndroid Build Coastguard Worker print(work_status.colourise(text)) 197*89a63228SAndroid Build Coastguard Worker if work_status == WorkStatus.ERROR: 198*89a63228SAndroid Build Coastguard Worker print(file.baseline_sum, file.baseline) 199*89a63228SAndroid Build Coastguard Worker print(file.release_sum, file.release) 200*89a63228SAndroid Build Coastguard Worker print(file.current_sum, file.current) 201*89a63228SAndroid Build Coastguard Worker print(file.upstream_sum, file.upstream) 202*89a63228SAndroid Build Coastguard Worker 203*89a63228SAndroid Build Coastguard Worker 204*89a63228SAndroid Build Coastguard Workerclass JavaFile: 205*89a63228SAndroid Build Coastguard Worker """ 206*89a63228SAndroid Build Coastguard Worker Encapsulates information about a single Java file in a package across 207*89a63228SAndroid Build Coastguard Worker all of the repositories involved in a merge. 208*89a63228SAndroid Build Coastguard Worker """ 209*89a63228SAndroid Build Coastguard Worker def __init__(self, package, name): 210*89a63228SAndroid Build Coastguard Worker self.package = package 211*89a63228SAndroid Build Coastguard Worker self.name = name 212*89a63228SAndroid Build Coastguard Worker # Paths for this file in each repository 213*89a63228SAndroid Build Coastguard Worker self.baseline = package.baseline_path(name) 214*89a63228SAndroid Build Coastguard Worker self.release = package.release_path(name) 215*89a63228SAndroid Build Coastguard Worker self.upstream = package.upstream_path(name) 216*89a63228SAndroid Build Coastguard Worker self.current = package.current_path(name) 217*89a63228SAndroid Build Coastguard Worker # Checksums for this file in each repository, or None if absent 218*89a63228SAndroid Build Coastguard Worker self.baseline_sum = self.checksum(self.baseline) 219*89a63228SAndroid Build Coastguard Worker self.release_sum = self.checksum(self.release) 220*89a63228SAndroid Build Coastguard Worker self.upstream_sum = self.checksum(self.upstream) 221*89a63228SAndroid Build Coastguard Worker self.current_sum = self.checksum(self.current) 222*89a63228SAndroid Build Coastguard Worker # List of methods for determining file's merge status. 223*89a63228SAndroid Build Coastguard Worker # Order matters - see merge_status() for details 224*89a63228SAndroid Build Coastguard Worker self.merge_status_methods = [ 225*89a63228SAndroid Build Coastguard Worker (self.check_for_missing, MergeStatus.MISSING), 226*89a63228SAndroid Build Coastguard Worker (self.check_for_unchanged, MergeStatus.UNCHANGED), 227*89a63228SAndroid Build Coastguard Worker (self.check_for_added_upstream, MergeStatus.ADDED), 228*89a63228SAndroid Build Coastguard Worker (self.check_for_removed_upstream, MergeStatus.DELETED), 229*89a63228SAndroid Build Coastguard Worker (self.check_for_changed_upstream, MergeStatus.UPDATED), 230*89a63228SAndroid Build Coastguard Worker ] 231*89a63228SAndroid Build Coastguard Worker # Map of methods from merge status to determine work status 232*89a63228SAndroid Build Coastguard Worker self.work_status_methods = { 233*89a63228SAndroid Build Coastguard Worker MergeStatus.MISSING: self.calculate_missing_work_status, 234*89a63228SAndroid Build Coastguard Worker MergeStatus.UNCHANGED: self.calculate_unchanged_work_status, 235*89a63228SAndroid Build Coastguard Worker MergeStatus.ADDED: self.calculate_added_work_status, 236*89a63228SAndroid Build Coastguard Worker MergeStatus.DELETED: self.calculate_deleted_work_status, 237*89a63228SAndroid Build Coastguard Worker MergeStatus.UPDATED: self.calculate_updated_work_status, 238*89a63228SAndroid Build Coastguard Worker } 239*89a63228SAndroid Build Coastguard Worker 240*89a63228SAndroid Build Coastguard Worker def is_android_changed(self): 241*89a63228SAndroid Build Coastguard Worker """ 242*89a63228SAndroid Build Coastguard Worker Returns true if the file was changed between the baseline and Android 243*89a63228SAndroid Build Coastguard Worker release. 244*89a63228SAndroid Build Coastguard Worker """ 245*89a63228SAndroid Build Coastguard Worker return self.is_in_release() and self.baseline_sum != self.release_sum 246*89a63228SAndroid Build Coastguard Worker 247*89a63228SAndroid Build Coastguard Worker def is_android_unchanged(self): 248*89a63228SAndroid Build Coastguard Worker """ 249*89a63228SAndroid Build Coastguard Worker Returns true if the file is in the Android release and is unchanged. 250*89a63228SAndroid Build Coastguard Worker """ 251*89a63228SAndroid Build Coastguard Worker return self.is_in_release() and self.baseline_sum == self.release_sum 252*89a63228SAndroid Build Coastguard Worker 253*89a63228SAndroid Build Coastguard Worker def check_for_changed_upstream(self): 254*89a63228SAndroid Build Coastguard Worker """Returns true if the file is changed upstream since the baseline.""" 255*89a63228SAndroid Build Coastguard Worker return self.baseline_sum != self.upstream_sum 256*89a63228SAndroid Build Coastguard Worker 257*89a63228SAndroid Build Coastguard Worker def is_in_baseline(self): 258*89a63228SAndroid Build Coastguard Worker return self.baseline_sum is not None 259*89a63228SAndroid Build Coastguard Worker 260*89a63228SAndroid Build Coastguard Worker def is_in_release(self): 261*89a63228SAndroid Build Coastguard Worker """Returns true if the file is present in the baseline and release.""" 262*89a63228SAndroid Build Coastguard Worker return self.is_in_baseline() and self.release_sum is not None 263*89a63228SAndroid Build Coastguard Worker 264*89a63228SAndroid Build Coastguard Worker def is_in_current(self): 265*89a63228SAndroid Build Coastguard Worker """Returns true if the file is in current, release and baseline.""" 266*89a63228SAndroid Build Coastguard Worker return self.is_in_release() and self.current_sum is not None 267*89a63228SAndroid Build Coastguard Worker 268*89a63228SAndroid Build Coastguard Worker def is_in_upstream(self): 269*89a63228SAndroid Build Coastguard Worker return self.upstream_sum is not None 270*89a63228SAndroid Build Coastguard Worker 271*89a63228SAndroid Build Coastguard Worker def check_for_missing(self): 272*89a63228SAndroid Build Coastguard Worker """ 273*89a63228SAndroid Build Coastguard Worker Returns true if the file is expected to be in current, but isn't. 274*89a63228SAndroid Build Coastguard Worker """ 275*89a63228SAndroid Build Coastguard Worker return self.is_in_release() and self.is_in_upstream() \ 276*89a63228SAndroid Build Coastguard Worker and not self.is_in_current() 277*89a63228SAndroid Build Coastguard Worker 278*89a63228SAndroid Build Coastguard Worker def removed_in_release(self): 279*89a63228SAndroid Build Coastguard Worker """Returns true if the file was removed by Android in the release.""" 280*89a63228SAndroid Build Coastguard Worker return self.is_in_baseline() and not self.is_in_release() 281*89a63228SAndroid Build Coastguard Worker 282*89a63228SAndroid Build Coastguard Worker def check_for_removed_upstream(self): 283*89a63228SAndroid Build Coastguard Worker """Returns true if the file was removed upstream since the baseline.""" 284*89a63228SAndroid Build Coastguard Worker return self.is_in_baseline() and not self.is_in_upstream() 285*89a63228SAndroid Build Coastguard Worker 286*89a63228SAndroid Build Coastguard Worker def check_for_added_upstream(self): 287*89a63228SAndroid Build Coastguard Worker """Returns true if the file was added upstream since the baseline.""" 288*89a63228SAndroid Build Coastguard Worker return self.is_in_upstream() and not self.is_in_baseline() 289*89a63228SAndroid Build Coastguard Worker 290*89a63228SAndroid Build Coastguard Worker def check_for_unchanged(self): 291*89a63228SAndroid Build Coastguard Worker """Returns true if the file is unchanged upstream since the baseline.""" 292*89a63228SAndroid Build Coastguard Worker return not self.check_for_changed_upstream() 293*89a63228SAndroid Build Coastguard Worker 294*89a63228SAndroid Build Coastguard Worker def merge_status(self): 295*89a63228SAndroid Build Coastguard Worker """ 296*89a63228SAndroid Build Coastguard Worker Returns the merge status for this file, or UNKNOWN. 297*89a63228SAndroid Build Coastguard Worker Tries each merge_status_method in turn, and if one returns true 298*89a63228SAndroid Build Coastguard Worker then this method returns the associated merge status. 299*89a63228SAndroid Build Coastguard Worker """ 300*89a63228SAndroid Build Coastguard Worker for (method, status) in self.merge_status_methods: 301*89a63228SAndroid Build Coastguard Worker if method(): 302*89a63228SAndroid Build Coastguard Worker return status 303*89a63228SAndroid Build Coastguard Worker return MergeStatus.UNKNOWN 304*89a63228SAndroid Build Coastguard Worker 305*89a63228SAndroid Build Coastguard Worker def work_status(self): 306*89a63228SAndroid Build Coastguard Worker """ 307*89a63228SAndroid Build Coastguard Worker Returns the work status for this file. 308*89a63228SAndroid Build Coastguard Worker Looks up a status method based on the merge statis and uses that to 309*89a63228SAndroid Build Coastguard Worker determine the work status. 310*89a63228SAndroid Build Coastguard Worker """ 311*89a63228SAndroid Build Coastguard Worker status = self.merge_status() 312*89a63228SAndroid Build Coastguard Worker if status in self.work_status_methods: 313*89a63228SAndroid Build Coastguard Worker return self.work_status_methods[status]() 314*89a63228SAndroid Build Coastguard Worker return WorkStatus.ERROR 315*89a63228SAndroid Build Coastguard Worker 316*89a63228SAndroid Build Coastguard Worker @staticmethod 317*89a63228SAndroid Build Coastguard Worker def calculate_missing_work_status(): 318*89a63228SAndroid Build Coastguard Worker """Missing files are always an error.""" 319*89a63228SAndroid Build Coastguard Worker return WorkStatus.ERROR 320*89a63228SAndroid Build Coastguard Worker 321*89a63228SAndroid Build Coastguard Worker def calculate_unchanged_work_status(self): 322*89a63228SAndroid Build Coastguard Worker """ 323*89a63228SAndroid Build Coastguard Worker File is unchanged upstream, so should be unchanged between release and 324*89a63228SAndroid Build Coastguard Worker current. 325*89a63228SAndroid Build Coastguard Worker """ 326*89a63228SAndroid Build Coastguard Worker if self.current_sum == self.release_sum: 327*89a63228SAndroid Build Coastguard Worker return WorkStatus.DONE 328*89a63228SAndroid Build Coastguard Worker return WorkStatus.UNKNOWN 329*89a63228SAndroid Build Coastguard Worker 330*89a63228SAndroid Build Coastguard Worker def calculate_added_work_status(self): 331*89a63228SAndroid Build Coastguard Worker """File was added upstream so needs to be added to current.""" 332*89a63228SAndroid Build Coastguard Worker if self.current_sum is None: 333*89a63228SAndroid Build Coastguard Worker return WorkStatus.TODO 334*89a63228SAndroid Build Coastguard Worker if self.current_sum == self.upstream_sum: 335*89a63228SAndroid Build Coastguard Worker return WorkStatus.DONE 336*89a63228SAndroid Build Coastguard Worker # XXX check for change markers if android changed 337*89a63228SAndroid Build Coastguard Worker return WorkStatus.UNKNOWN 338*89a63228SAndroid Build Coastguard Worker 339*89a63228SAndroid Build Coastguard Worker def calculate_deleted_work_status(self): 340*89a63228SAndroid Build Coastguard Worker """File was removed upstream so needs to be removed from current.""" 341*89a63228SAndroid Build Coastguard Worker if self.is_in_current(): 342*89a63228SAndroid Build Coastguard Worker return WorkStatus.TODO 343*89a63228SAndroid Build Coastguard Worker return WorkStatus.DONE 344*89a63228SAndroid Build Coastguard Worker 345*89a63228SAndroid Build Coastguard Worker def calculate_updated_work_status(self): 346*89a63228SAndroid Build Coastguard Worker """File was updated upstream.""" 347*89a63228SAndroid Build Coastguard Worker if self.current_sum == self.upstream_sum: 348*89a63228SAndroid Build Coastguard Worker if self.is_android_unchanged(): 349*89a63228SAndroid Build Coastguard Worker return WorkStatus.DONE 350*89a63228SAndroid Build Coastguard Worker # Looks like Android changes are missing in current 351*89a63228SAndroid Build Coastguard Worker return WorkStatus.ERROR 352*89a63228SAndroid Build Coastguard Worker if self.is_android_unchanged(): 353*89a63228SAndroid Build Coastguard Worker return WorkStatus.TODO 354*89a63228SAndroid Build Coastguard Worker # If we get here there are upstream and Android changes that need 355*89a63228SAndroid Build Coastguard Worker # to be merged, If possible use the file copyright date as a 356*89a63228SAndroid Build Coastguard Worker # heuristic to determine if upstream has been merged into current 357*89a63228SAndroid Build Coastguard Worker release_copyright = self.get_copyright(self.release) 358*89a63228SAndroid Build Coastguard Worker current_copyright = self.get_copyright(self.current) 359*89a63228SAndroid Build Coastguard Worker upstream_copyright = self.get_copyright(self.upstream) 360*89a63228SAndroid Build Coastguard Worker if release_copyright == upstream_copyright: 361*89a63228SAndroid Build Coastguard Worker # Upstream copyright same as last release, so can't infer anything 362*89a63228SAndroid Build Coastguard Worker return WorkStatus.UNKNOWN 363*89a63228SAndroid Build Coastguard Worker if current_copyright == upstream_copyright: 364*89a63228SAndroid Build Coastguard Worker return WorkStatus.PROBABLY_DONE 365*89a63228SAndroid Build Coastguard Worker if current_copyright == release_copyright: 366*89a63228SAndroid Build Coastguard Worker return WorkStatus.TODO 367*89a63228SAndroid Build Coastguard Worker # Give up 368*89a63228SAndroid Build Coastguard Worker return WorkStatus.UNKNOWN 369*89a63228SAndroid Build Coastguard Worker 370*89a63228SAndroid Build Coastguard Worker def status(self): 371*89a63228SAndroid Build Coastguard Worker return self.merge_status(), self.work_status() 372*89a63228SAndroid Build Coastguard Worker 373*89a63228SAndroid Build Coastguard Worker @staticmethod 374*89a63228SAndroid Build Coastguard Worker def checksum(path): 375*89a63228SAndroid Build Coastguard Worker """Returns a checksum string for a file, SHA256 as a hex string.""" 376*89a63228SAndroid Build Coastguard Worker try: 377*89a63228SAndroid Build Coastguard Worker with open(path, 'rb') as file: 378*89a63228SAndroid Build Coastguard Worker bytes = file.read() 379*89a63228SAndroid Build Coastguard Worker return hashlib.sha256(bytes).hexdigest() 380*89a63228SAndroid Build Coastguard Worker except: 381*89a63228SAndroid Build Coastguard Worker return None 382*89a63228SAndroid Build Coastguard Worker 383*89a63228SAndroid Build Coastguard Worker @staticmethod 384*89a63228SAndroid Build Coastguard Worker def get_copyright(file): 385*89a63228SAndroid Build Coastguard Worker """Returns the upstream copyright line for a file.""" 386*89a63228SAndroid Build Coastguard Worker try: 387*89a63228SAndroid Build Coastguard Worker with open(file, 'r') as file: 388*89a63228SAndroid Build Coastguard Worker for count in range(5): 389*89a63228SAndroid Build Coastguard Worker line = file.readline() 390*89a63228SAndroid Build Coastguard Worker if line.startswith( 391*89a63228SAndroid Build Coastguard Worker ' * Copyright') and 'Android' not in line: 392*89a63228SAndroid Build Coastguard Worker return line 393*89a63228SAndroid Build Coastguard Worker return None 394*89a63228SAndroid Build Coastguard Worker except: 395*89a63228SAndroid Build Coastguard Worker return None 396*89a63228SAndroid Build Coastguard Worker 397*89a63228SAndroid Build Coastguard Worker 398*89a63228SAndroid Build Coastguard Workerdef main(): 399*89a63228SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 400*89a63228SAndroid Build Coastguard Worker description='Report on merge status of Java packages', 401*89a63228SAndroid Build Coastguard Worker formatter_class=argparse.ArgumentDefaultsHelpFormatter) 402*89a63228SAndroid Build Coastguard Worker 403*89a63228SAndroid Build Coastguard Worker # TODO(prb): Add help for available repositories 404*89a63228SAndroid Build Coastguard Worker parser.add_argument('-b', '--baseline', default='expected', 405*89a63228SAndroid Build Coastguard Worker help='Baseline repo') 406*89a63228SAndroid Build Coastguard Worker parser.add_argument('-r', '--release', default='sc-release', 407*89a63228SAndroid Build Coastguard Worker help='Last released repo') 408*89a63228SAndroid Build Coastguard Worker parser.add_argument('-u', '--upstream', default='11+28', 409*89a63228SAndroid Build Coastguard Worker help='Upstream repo.') 410*89a63228SAndroid Build Coastguard Worker parser.add_argument('-c', '--current', default='ojluni', 411*89a63228SAndroid Build Coastguard Worker help='Current repo.') 412*89a63228SAndroid Build Coastguard Worker parser.add_argument('pkgs', nargs="+", 413*89a63228SAndroid Build Coastguard Worker help='Packages to report on') 414*89a63228SAndroid Build Coastguard Worker 415*89a63228SAndroid Build Coastguard Worker args = parser.parse_args() 416*89a63228SAndroid Build Coastguard Worker config = MergeConfig(args.baseline, args.release, args.current, 417*89a63228SAndroid Build Coastguard Worker args.upstream) 418*89a63228SAndroid Build Coastguard Worker 419*89a63228SAndroid Build Coastguard Worker for pkg_name in args.pkgs: 420*89a63228SAndroid Build Coastguard Worker try: 421*89a63228SAndroid Build Coastguard Worker package = JavaPackage(config, pkg_name) 422*89a63228SAndroid Build Coastguard Worker package.report_merge_status() 423*89a63228SAndroid Build Coastguard Worker except Exception as e: 424*89a63228SAndroid Build Coastguard Worker print(red("ERROR: Unable to process package " + pkg_name + e)) 425*89a63228SAndroid Build Coastguard Worker 426*89a63228SAndroid Build Coastguard Worker 427*89a63228SAndroid Build Coastguard Workerif __name__ == "__main__": 428*89a63228SAndroid Build Coastguard Worker main() 429