1*2538Sesaxe#!/usr/sfw/bin/python 2*2538Sesaxe# 3*2538Sesaxe# CDDL HEADER START 4*2538Sesaxe# 5*2538Sesaxe# The contents of this file are subject to the terms of the 6*2538Sesaxe# Common Development and Distribution License (the "License"). 7*2538Sesaxe# You may not use this file except in compliance with the License. 8*2538Sesaxe# 9*2538Sesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*2538Sesaxe# or http://www.opensolaris.org/os/licensing. 11*2538Sesaxe# See the License for the specific language governing permissions 12*2538Sesaxe# and limitations under the License. 13*2538Sesaxe# 14*2538Sesaxe# When distributing Covered Code, include this CDDL HEADER in each 15*2538Sesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*2538Sesaxe# If applicable, add the following below this CDDL HEADER, with the 17*2538Sesaxe# fields enclosed by brackets "[]" replaced with your own identifying 18*2538Sesaxe# information: Portions Copyright [yyyy] [name of copyright owner] 19*2538Sesaxe# 20*2538Sesaxe# CDDL HEADER END 21*2538Sesaxe# 22*2538Sesaxe# Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*2538Sesaxe# Use is subject to license terms. 24*2538Sesaxe# 25*2538Sesaxe#ident "%Z%%M% %I% %E% SMI" 26*2538Sesaxe 27*2538Sesaxe# 28*2538Sesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects 29*2538Sesaxe# have changed as a result of a given source change. Developers backporting 30*2538Sesaxe# new features, RFEs and bug fixes need to be able to identify the set of 31*2538Sesaxe# patch deliverables necessary for feature/fix realization on a patched system. 32*2538Sesaxe# 33*2538Sesaxe# The tool works by comparing objects in two trees/proto areas (one build with, 34*2538Sesaxe# and without the source changes. 35*2538Sesaxe# 36*2538Sesaxe# Using wsdiff(1) is fairly simple: 37*2538Sesaxe# - Bringover to a fresh workspace 38*2538Sesaxe# - Perform a full non-debug build (clobber if workspace isn't fresh) 39*2538Sesaxe# - Move the proto area aside, call it proto.old, or something. 40*2538Sesaxe# - Integrate your changes to the workspace 41*2538Sesaxe# - Perform another full non-debug clobber build. 42*2538Sesaxe# - Use wsdiff(1) to see what changed: 43*2538Sesaxe# $ wsdiff proto.old proto 44*2538Sesaxe# 45*2538Sesaxe# By default, wsdiff will print the list of changed objects / deliverables to 46*2538Sesaxe# stdout. If a results file is specified via -r, the list of differing objects, 47*2538Sesaxe# and details about why wsdiff(1) thinks they are different will be logged to 48*2538Sesaxe# the results file. 49*2538Sesaxe# 50*2538Sesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use 51*2538Sesaxe# wsdiff(1) to report on what objects changed since the last build. 52*2538Sesaxe# 53*2538Sesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber, 54*2538Sesaxe# non-debug build. 55*2538Sesaxe# 56*2538Sesaxe# Think about the results. Was something flagged that you don't expect? Go look 57*2538Sesaxe# at the results file to see details about the differences. 58*2538Sesaxe# 59*2538Sesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1) 60*2538Sesaxe# report with more verbosity. 61*2538Sesaxe# 62*2538Sesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 63*2538Sesaxe# 64*2538Sesaxe# Where "old" is the path to the proto area build without the changes, and 65*2538Sesaxe# "new" is the path to the proto area built with the changes. The following 66*2538Sesaxe# options are supported: 67*2538Sesaxe# 68*2538Sesaxe# -v Do not truncate observed diffs in results 69*2538Sesaxe# -V Log *all* ELF sect diffs vs. logging the first diff found 70*2538Sesaxe# -t Use onbld tools in $SRC/tools 71*2538Sesaxe# -r Log results and observed differences 72*2538Sesaxe# -i Tell wsdiff which objects to compare via an input file list 73*2538Sesaxe 74*2538Sesaxeimport datetime, fnmatch, getopt, profile, os, popen2, commands 75*2538Sesaxeimport re, select, string, struct, sys, tempfile, time 76*2538Sesaxefrom stat import * 77*2538Sesaxe 78*2538Sesaxe# Human readable diffs truncated by default if longer than this 79*2538Sesaxe# Specifying -v on the command line will override 80*2538Sesaxediffs_sz_thresh = 4096 81*2538Sesaxe 82*2538Sesaxe# Default search path for wsdiff 83*2538Sesaxewsdiff_path = [ "/usr/bin", 84*2538Sesaxe "/usr/ccs/bin", 85*2538Sesaxe "/lib/svc/bin", 86*2538Sesaxe "/opt/onbld/bin" ] 87*2538Sesaxe 88*2538Sesaxe# These are objects that wsdiff will notice look different, but will not report. 89*2538Sesaxe# Existence of an exceptions list, and adding things here is *dangerous*, 90*2538Sesaxe# and therefore the *only* reasons why anything would be listed here is because 91*2538Sesaxe# the objects do not build deterministically, yet we *cannot* fix this. 92*2538Sesaxe# 93*2538Sesaxe# These perl libraries use __DATE__ and therefore always look different. 94*2538Sesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because 95*2538Sesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify. 96*2538Sesaxe# 97*2538Sesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1", 98*2538Sesaxe "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 99*2538Sesaxe "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1", 100*2538Sesaxe "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1" 101*2538Sesaxe ] 102*2538Sesaxe 103*2538Sesaxe##### 104*2538Sesaxe# Logging routines 105*2538Sesaxe# 106*2538Sesaxe 107*2538Sesaxe# Informational message to be printed to the screen, and the log file 108*2538Sesaxedef info(msg) : 109*2538Sesaxe 110*2538Sesaxe print >> sys.stdout, msg 111*2538Sesaxe if logging : 112*2538Sesaxe print >> log, msg 113*2538Sesaxe sys.stdout.flush() 114*2538Sesaxe 115*2538Sesaxe# Error message to be printed to the screen, and the log file 116*2538Sesaxedef error(msg) : 117*2538Sesaxe 118*2538Sesaxe print >> sys.stderr, "ERROR:", msg 119*2538Sesaxe sys.stderr.flush() 120*2538Sesaxe if logging : 121*2538Sesaxe print >> log, "ERROR:", msg 122*2538Sesaxe log.flush() 123*2538Sesaxe 124*2538Sesaxe# Informational message to be printed only to the log, if there is one. 125*2538Sesaxedef v_info(msg) : 126*2538Sesaxe 127*2538Sesaxe if logging : 128*2538Sesaxe print >> log, msg 129*2538Sesaxe log.flush() 130*2538Sesaxe 131*2538Sesaxe# 132*2538Sesaxe# Flag a detected file difference 133*2538Sesaxe# Display the fileName to stdout, and log the difference 134*2538Sesaxe# 135*2538Sesaxedef difference(f, dtype, diffs) : 136*2538Sesaxe 137*2538Sesaxe if f in wsdiff_exceptions : 138*2538Sesaxe return 139*2538Sesaxe 140*2538Sesaxe print >> sys.stdout, f 141*2538Sesaxe sys.stdout.flush() 142*2538Sesaxe 143*2538Sesaxe log_difference(f, dtype, diffs) 144*2538Sesaxe 145*2538Sesaxe# 146*2538Sesaxe# Do the actual logging of the difference to the results file 147*2538Sesaxe# 148*2538Sesaxedef log_difference(f, dtype, diffs) : 149*2538Sesaxe if logging : 150*2538Sesaxe print >> log, f 151*2538Sesaxe print >> log, "NOTE:", dtype, "difference detected." 152*2538Sesaxe 153*2538Sesaxe difflen = len(diffs) 154*2538Sesaxe if difflen > 0 : 155*2538Sesaxe print >> log 156*2538Sesaxe 157*2538Sesaxe if not vdiffs and difflen > diffs_sz_thresh : 158*2538Sesaxe print >> log, diffs[:diffs_sz_thresh] 159*2538Sesaxe print >> log, \ 160*2538Sesaxe "... truncated due to length: " \ 161*2538Sesaxe "use -v to override ..." 162*2538Sesaxe else : 163*2538Sesaxe print >> log, diffs 164*2538Sesaxe print >> log, "\n" 165*2538Sesaxe log.flush() 166*2538Sesaxe 167*2538Sesaxe 168*2538Sesaxe##### 169*2538Sesaxe# diff generating routines 170*2538Sesaxe# 171*2538Sesaxe 172*2538Sesaxe# 173*2538Sesaxe# Return human readable diffs from two temporary files 174*2538Sesaxe# 175*2538Sesaxedef diffFileData(tmpf1, tmpf2) : 176*2538Sesaxe 177*2538Sesaxe # Filter the data through od(1) if the data is detected 178*2538Sesaxe # as being binary 179*2538Sesaxe if isBinary(tmpf1) or isBinary(tmpf2) : 180*2538Sesaxe tmp_od1 = tmpf1 + ".od" 181*2538Sesaxe tmp_od2 = tmpf2 + ".od" 182*2538Sesaxe 183*2538Sesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1 184*2538Sesaxe os.system(cmd) 185*2538Sesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2 186*2538Sesaxe os.system(cmd) 187*2538Sesaxe 188*2538Sesaxe tmpf1 = tmp_od1 189*2538Sesaxe tmpf2 = tmp_od2 190*2538Sesaxe 191*2538Sesaxe data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2) 192*2538Sesaxe 193*2538Sesaxe return data 194*2538Sesaxe 195*2538Sesaxe# 196*2538Sesaxe# Return human readable diffs betweeen two datasets 197*2538Sesaxe# 198*2538Sesaxedef diffData(d1, d2) : 199*2538Sesaxe 200*2538Sesaxe global tmpFile1 201*2538Sesaxe global tmpFile2 202*2538Sesaxe 203*2538Sesaxe try: 204*2538Sesaxe fd1 = open(tmpFile1, "w") 205*2538Sesaxe except: 206*2538Sesaxe error("failed to open: " + tmpFile1) 207*2538Sesaxe cleanup(1) 208*2538Sesaxe try: 209*2538Sesaxe fd2 = open(tmpFile2, "w") 210*2538Sesaxe except: 211*2538Sesaxe error("failed to open: " + tmpFile2) 212*2538Sesaxe cleanup(1) 213*2538Sesaxe 214*2538Sesaxe fd1.write(d1) 215*2538Sesaxe fd2.write(d2) 216*2538Sesaxe fd1.close() 217*2538Sesaxe fd2.close() 218*2538Sesaxe 219*2538Sesaxe return diffFileData(tmpFile1, tmpFile2) 220*2538Sesaxe 221*2538Sesaxe##### 222*2538Sesaxe# Misc utility functions 223*2538Sesaxe# 224*2538Sesaxe 225*2538Sesaxe# Prune off the leading prefix from string s 226*2538Sesaxedef str_prefix_trunc(s, prefix) : 227*2538Sesaxe snipLen = len(prefix) 228*2538Sesaxe return s[snipLen:] 229*2538Sesaxe 230*2538Sesaxe# 231*2538Sesaxe# Prune off leading proto path goo (if there is one) to yield 232*2538Sesaxe# the deliverable's eventual path relative to root 233*2538Sesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat 234*2538Sesaxe# 235*2538Sesaxedef fnFormat(fn) : 236*2538Sesaxe root_arch_str = "root_" + arch 237*2538Sesaxe 238*2538Sesaxe pos = fn.find(root_arch_str) 239*2538Sesaxe if pos == -1 : 240*2538Sesaxe return fn 241*2538Sesaxe 242*2538Sesaxe pos = fn.find("/", pos) 243*2538Sesaxe if pos == -1 : 244*2538Sesaxe return fn 245*2538Sesaxe 246*2538Sesaxe return fn[pos + 1:] 247*2538Sesaxe 248*2538Sesaxe##### 249*2538Sesaxe# Usage / argument processing 250*2538Sesaxe# 251*2538Sesaxe 252*2538Sesaxe# 253*2538Sesaxe# Display usage message 254*2538Sesaxe# 255*2538Sesaxedef usage() : 256*2538Sesaxe sys.stdout.flush() 257*2538Sesaxe print >> sys.stderr, """Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 258*2538Sesaxe -v Do not truncate observed diffs in results 259*2538Sesaxe -V Log *all* ELF sect diffs vs. logging the first diff found 260*2538Sesaxe -t Use onbld tools in $SRC/tools 261*2538Sesaxe -r Log results and observed differences 262*2538Sesaxe -i Tell wsdiff which objects to compare via an input file list""" 263*2538Sesaxe sys.exit(1) 264*2538Sesaxe 265*2538Sesaxe# 266*2538Sesaxe# Process command line options 267*2538Sesaxe# 268*2538Sesaxedef args() : 269*2538Sesaxe 270*2538Sesaxe global logging 271*2538Sesaxe global vdiffs 272*2538Sesaxe global reportAllSects 273*2538Sesaxe 274*2538Sesaxe validOpts = 'i:r:vVt?' 275*2538Sesaxe 276*2538Sesaxe baseRoot = "" 277*2538Sesaxe ptchRoot = "" 278*2538Sesaxe fileNamesFile = "" 279*2538Sesaxe results = "" 280*2538Sesaxe localTools = False 281*2538Sesaxe 282*2538Sesaxe # getopt.getopt() returns: 283*2538Sesaxe # an option/value tuple 284*2538Sesaxe # a list of remaining non-option arguments 285*2538Sesaxe # 286*2538Sesaxe # A correct wsdiff invocation will have exactly two non option 287*2538Sesaxe # arguments, the paths to the base (old), ptch (new) proto areas 288*2538Sesaxe try: 289*2538Sesaxe optlist, args = getopt.getopt(sys.argv[1:], validOpts) 290*2538Sesaxe except getopt.error, val: 291*2538Sesaxe usage() 292*2538Sesaxe 293*2538Sesaxe if len(args) != 2 : 294*2538Sesaxe usage(); 295*2538Sesaxe 296*2538Sesaxe for opt,val in optlist : 297*2538Sesaxe if opt == '-i' : 298*2538Sesaxe fileNamesFile = val 299*2538Sesaxe elif opt == '-r' : 300*2538Sesaxe results = val 301*2538Sesaxe logging = True 302*2538Sesaxe elif opt == '-v' : 303*2538Sesaxe vdiffs = True 304*2538Sesaxe elif opt == '-V' : 305*2538Sesaxe reportAllSects = True 306*2538Sesaxe elif opt == '-t': 307*2538Sesaxe localTools = True 308*2538Sesaxe else: 309*2538Sesaxe usage() 310*2538Sesaxe 311*2538Sesaxe baseRoot = args[0] 312*2538Sesaxe ptchRoot = args[1] 313*2538Sesaxe 314*2538Sesaxe if len(baseRoot) == 0 or len(ptchRoot) == 0 : 315*2538Sesaxe usage() 316*2538Sesaxe 317*2538Sesaxe if logging and len(results) == 0 : 318*2538Sesaxe usage() 319*2538Sesaxe 320*2538Sesaxe if vdiffs and not logging : 321*2538Sesaxe error("The -v option requires a results file (-r)") 322*2538Sesaxe sys.exit(1) 323*2538Sesaxe 324*2538Sesaxe if reportAllSects and not logging : 325*2538Sesaxe error("The -V option requires a results file (-r)") 326*2538Sesaxe sys.exit(1) 327*2538Sesaxe 328*2538Sesaxe # alphabetical order 329*2538Sesaxe return baseRoot, fileNamesFile, localTools, ptchRoot, results 330*2538Sesaxe 331*2538Sesaxe##### 332*2538Sesaxe# File identification 333*2538Sesaxe# 334*2538Sesaxe 335*2538Sesaxe# 336*2538Sesaxe# Identify the file type. 337*2538Sesaxe# If it's not ELF, use the file extension to identify 338*2538Sesaxe# certain file types that require special handling to 339*2538Sesaxe# compare. Otherwise just return a basic "ASCII" type. 340*2538Sesaxe# 341*2538Sesaxedef getTheFileType(f) : 342*2538Sesaxe 343*2538Sesaxe extensions = { 'a' : 'ELF Object Archive', 344*2538Sesaxe 'jar' : 'Java Archive', 345*2538Sesaxe 'html' : 'HTML', 346*2538Sesaxe 'ln' : 'Lint Library', 347*2538Sesaxe 'db' : 'Sqlite Database' } 348*2538Sesaxe 349*2538Sesaxe if os.stat(f)[ST_SIZE] == 0 : 350*2538Sesaxe return 'ASCII' 351*2538Sesaxe 352*2538Sesaxe if isELF(f) == 1 : 353*2538Sesaxe return 'ELF' 354*2538Sesaxe 355*2538Sesaxe fnamelist = f.split('.') 356*2538Sesaxe if len(fnamelist) > 1 : # Test the file extension 357*2538Sesaxe extension = fnamelist[-1] 358*2538Sesaxe if extension in extensions.keys(): 359*2538Sesaxe return extensions[extension] 360*2538Sesaxe 361*2538Sesaxe return 'ASCII' 362*2538Sesaxe 363*2538Sesaxe# 364*2538Sesaxe# Return non-zero if "f" is an ELF file 365*2538Sesaxe# 366*2538Sesaxeelfmagic = '\177ELF' 367*2538Sesaxedef isELF(f) : 368*2538Sesaxe try: 369*2538Sesaxe fd = open(f) 370*2538Sesaxe except: 371*2538Sesaxe error("failed to open: " + f) 372*2538Sesaxe return 0 373*2538Sesaxe magic = fd.read(len(elfmagic)) 374*2538Sesaxe fd.close() 375*2538Sesaxe 376*2538Sesaxe if magic == elfmagic : 377*2538Sesaxe return 1 378*2538Sesaxe return 0 379*2538Sesaxe 380*2538Sesaxe# 381*2538Sesaxe# Return non-zero is "f" is binary. 382*2538Sesaxe# Consider the file to be binary if it contains any null characters 383*2538Sesaxe# 384*2538Sesaxedef isBinary(f) : 385*2538Sesaxe try: 386*2538Sesaxe fd = open(f) 387*2538Sesaxe except: 388*2538Sesaxe error("failed to open: " + f) 389*2538Sesaxe return 0 390*2538Sesaxe s = fd.read() 391*2538Sesaxe fd.close() 392*2538Sesaxe 393*2538Sesaxe if s.find('\0') == -1 : 394*2538Sesaxe return 0 395*2538Sesaxe else : 396*2538Sesaxe return 1 397*2538Sesaxe 398*2538Sesaxe##### 399*2538Sesaxe# Directory traversal and file finding 400*2538Sesaxe# 401*2538Sesaxe 402*2538Sesaxe# 403*2538Sesaxe# Return a sorted list of files found under the specified directory 404*2538Sesaxe# 405*2538Sesaxedef findFiles(d) : 406*2538Sesaxe for path, subdirs, files in os.walk(d) : 407*2538Sesaxe files.sort() 408*2538Sesaxe for name in files : 409*2538Sesaxe yield os.path.join(path, name) 410*2538Sesaxe 411*2538Sesaxe# 412*2538Sesaxe# Examine all files in base, ptch 413*2538Sesaxe# 414*2538Sesaxe# Return a list of files appearing in both proto areas, 415*2538Sesaxe# a list of new files (files found only in ptch) and 416*2538Sesaxe# a list of deleted files (files found only in base) 417*2538Sesaxe# 418*2538Sesaxedef protoCatalog(base, ptch) : 419*2538Sesaxe compFiles = [] # List of files in both proto areas 420*2538Sesaxe ptchList = [] # List of file in patch proto area 421*2538Sesaxe 422*2538Sesaxe newFiles = [] # New files detected 423*2538Sesaxe deletedFiles = [] # Deleted files 424*2538Sesaxe 425*2538Sesaxe baseFilesList = list(findFiles(base)) 426*2538Sesaxe baseStringLength = len(base) 427*2538Sesaxe 428*2538Sesaxe ptchFilesList = list(findFiles(ptch)) 429*2538Sesaxe ptchStringLength = len(ptch) 430*2538Sesaxe 431*2538Sesaxe # Inventory files in the base proto area 432*2538Sesaxe for fn in baseFilesList : 433*2538Sesaxe if os.path.islink(fn) : 434*2538Sesaxe continue 435*2538Sesaxe 436*2538Sesaxe fileName = fn[baseStringLength:] 437*2538Sesaxe compFiles.append(fileName) 438*2538Sesaxe 439*2538Sesaxe # Inventory files in the patch proto area 440*2538Sesaxe for fn in ptchFilesList : 441*2538Sesaxe if os.path.islink(fn) : 442*2538Sesaxe continue 443*2538Sesaxe 444*2538Sesaxe fileName = fn[ptchStringLength:] 445*2538Sesaxe ptchList.append(fileName) 446*2538Sesaxe 447*2538Sesaxe # Deleted files appear in the base area, but not the patch area 448*2538Sesaxe for fileName in compFiles : 449*2538Sesaxe if not fileName in ptchList : 450*2538Sesaxe deletedFiles.append(fileName) 451*2538Sesaxe 452*2538Sesaxe # Eliminate "deleted" files from the list of objects appearing 453*2538Sesaxe # in both the base and patch proto areas 454*2538Sesaxe for fileName in deletedFiles : 455*2538Sesaxe try: 456*2538Sesaxe compFiles.remove(fileName) 457*2538Sesaxe except: 458*2538Sesaxe error("filelist.remove() failed") 459*2538Sesaxe 460*2538Sesaxe # New files appear in the patch area, but not the base 461*2538Sesaxe for fileName in ptchList : 462*2538Sesaxe if not fileName in compFiles : 463*2538Sesaxe newFiles.append(fileName) 464*2538Sesaxe 465*2538Sesaxe return compFiles, newFiles, deletedFiles 466*2538Sesaxe 467*2538Sesaxe# 468*2538Sesaxe# Examine the files listed in the input file list 469*2538Sesaxe# 470*2538Sesaxe# Return a list of files appearing in both proto areas, 471*2538Sesaxe# a list of new files (files found only in ptch) and 472*2538Sesaxe# a list of deleted files (files found only in base) 473*2538Sesaxe# 474*2538Sesaxedef flistCatalog(base, ptch, flist) : 475*2538Sesaxe compFiles = [] # List of files in both proto areas 476*2538Sesaxe newFiles = [] # New files detected 477*2538Sesaxe deletedFiles = [] # Deleted files 478*2538Sesaxe 479*2538Sesaxe try: 480*2538Sesaxe fd = open(flist, "r") 481*2538Sesaxe except: 482*2538Sesaxe error("could not open: " + flist) 483*2538Sesaxe cleanup(1) 484*2538Sesaxe 485*2538Sesaxe files = [] 486*2538Sesaxe files = fd.readlines() 487*2538Sesaxe 488*2538Sesaxe for f in files : 489*2538Sesaxe ptch_present = True 490*2538Sesaxe base_present = True 491*2538Sesaxe 492*2538Sesaxe if f == '\n' : 493*2538Sesaxe continue 494*2538Sesaxe 495*2538Sesaxe # the fileNames have a trailing '\n' 496*2538Sesaxe f = f.rstrip() 497*2538Sesaxe 498*2538Sesaxe # The objects in the file list have paths relative 499*2538Sesaxe # to $ROOT or to the base/ptch directory specified on 500*2538Sesaxe # the command line. 501*2538Sesaxe # If it's relative to $ROOT, we'll need to add back the 502*2538Sesaxe # root_`uname -p` goo we stripped off in fnFormat() 503*2538Sesaxe if os.path.exists(base + f) : 504*2538Sesaxe fn = f; 505*2538Sesaxe elif os.path.exists(base + "root_" + arch + "/" + f) : 506*2538Sesaxe fn = "root_" + arch + "/" + f 507*2538Sesaxe else : 508*2538Sesaxe base_present = False 509*2538Sesaxe 510*2538Sesaxe if base_present : 511*2538Sesaxe if not os.path.exists(ptch + fn) : 512*2538Sesaxe ptch_present = False 513*2538Sesaxe else : 514*2538Sesaxe if os.path.exists(ptch + f) : 515*2538Sesaxe fn = f 516*2538Sesaxe elif os.path.exists(ptch + "root_" + arch + "/" + f) : 517*2538Sesaxe fn = "root_" + arch + "/" + f 518*2538Sesaxe else : 519*2538Sesaxe ptch_present = False 520*2538Sesaxe 521*2538Sesaxe if os.path.islink(base + fn) : # ignore links 522*2538Sesaxe base_present = False 523*2538Sesaxe if os.path.islink(ptch + fn) : 524*2538Sesaxe ptch_present = False 525*2538Sesaxe 526*2538Sesaxe if base_present and ptch_present : 527*2538Sesaxe compFiles.append(fn) 528*2538Sesaxe elif base_present : 529*2538Sesaxe deletedFiles.append(fn) 530*2538Sesaxe elif ptch_present : 531*2538Sesaxe newFiles.append(fn) 532*2538Sesaxe else : 533*2538Sesaxe if os.path.islink(base + fn) and os.path.islink(ptch + fn) : 534*2538Sesaxe continue 535*2538Sesaxe error(f + " in file list, but not in either tree. Skipping...") 536*2538Sesaxe 537*2538Sesaxe return compFiles, newFiles, deletedFiles 538*2538Sesaxe 539*2538Sesaxe 540*2538Sesaxe# 541*2538Sesaxe# Build a fully qualified path to an external tool/utility. 542*2538Sesaxe# Consider the default system locations. For onbld tools, if 543*2538Sesaxe# the -t option was specified, we'll try to use built tools in $SRC tools, 544*2538Sesaxe# and otherwise, we'll fall back on /opt/onbld/ 545*2538Sesaxe# 546*2538Sesaxedef find_tool(tool) : 547*2538Sesaxe 548*2538Sesaxe # First, check what was passed 549*2538Sesaxe if os.path.exists(tool) : 550*2538Sesaxe return tool 551*2538Sesaxe 552*2538Sesaxe # Next try in wsdiff path 553*2538Sesaxe for pdir in wsdiff_path : 554*2538Sesaxe location = pdir + "/" + tool 555*2538Sesaxe if os.path.exists(location) : 556*2538Sesaxe return location + " " 557*2538Sesaxe 558*2538Sesaxe location = pdir + "/" + arch + "/" + tool 559*2538Sesaxe if os.path.exists(location) : 560*2538Sesaxe return location + " " 561*2538Sesaxe 562*2538Sesaxe error("Could not find path to: " + tool); 563*2538Sesaxe sys.exit(1); 564*2538Sesaxe 565*2538Sesaxe 566*2538Sesaxe##### 567*2538Sesaxe# ELF file comparison helper routines 568*2538Sesaxe# 569*2538Sesaxe 570*2538Sesaxe# 571*2538Sesaxe# Return a dictionary of ELF section types keyed by section name 572*2538Sesaxe# 573*2538Sesaxedef get_elfheader(f) : 574*2538Sesaxe 575*2538Sesaxe header = {} 576*2538Sesaxe 577*2538Sesaxe hstring = commands.getoutput(elfdump_cmd + " -c " + f) 578*2538Sesaxe 579*2538Sesaxe if len(hstring) == 0 : 580*2538Sesaxe error("Failed to dump ELF header for " + f) 581*2538Sesaxe return 582*2538Sesaxe 583*2538Sesaxe # elfdump(1) dumps the section headers with the section name 584*2538Sesaxe # following "sh_name:", and the section type following "sh_type:" 585*2538Sesaxe sections = hstring.split("Section Header") 586*2538Sesaxe for sect in sections : 587*2538Sesaxe datap = sect.find("sh_name:"); 588*2538Sesaxe if datap == -1 : 589*2538Sesaxe continue 590*2538Sesaxe section = sect[datap:].split()[1] 591*2538Sesaxe datap = sect.find("sh_type:"); 592*2538Sesaxe if datap == -1 : 593*2538Sesaxe error("Could not get type for sect: " + section + \ 594*2538Sesaxe " in " + f) 595*2538Sesaxe sh_type = sect[datap:].split()[2] 596*2538Sesaxe header[section] = sh_type 597*2538Sesaxe 598*2538Sesaxe return header 599*2538Sesaxe 600*2538Sesaxe# 601*2538Sesaxe# Extract data in the specified ELF section from the given file 602*2538Sesaxe# 603*2538Sesaxedef extract_elf_section(f, section) : 604*2538Sesaxe 605*2538Sesaxe data = commands.getoutput(dump_cmd + " -sn " + section + " " + f) 606*2538Sesaxe 607*2538Sesaxe if len(data) == 0 : 608*2538Sesaxe error(cmd + " yielded no data") 609*2538Sesaxe return 610*2538Sesaxe 611*2538Sesaxe # dump(1) displays the file name to start... 612*2538Sesaxe # get past it to the data itself 613*2538Sesaxe dbegin = data.find(":") + 1 614*2538Sesaxe data = data[dbegin:]; 615*2538Sesaxe 616*2538Sesaxe return (data) 617*2538Sesaxe 618*2538Sesaxe# 619*2538Sesaxe# Return a (hopefully meaningful) human readable set of diffs 620*2538Sesaxe# for the specified ELF section between f1 and f2 621*2538Sesaxe# 622*2538Sesaxe# Depending on the section, various means for dumping and diffing 623*2538Sesaxe# the data may be employed. 624*2538Sesaxe# 625*2538Sesaxetext_sections = [ '.text', '.init', '.fini' ] 626*2538Sesaxedef diff_elf_section(f1, f2, section, sh_type) : 627*2538Sesaxe 628*2538Sesaxe if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA 629*2538Sesaxe cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1 630*2538Sesaxe cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2 631*2538Sesaxe elif (section == ".group") : 632*2538Sesaxe cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1 633*2538Sesaxe cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2 634*2538Sesaxe elif (section == ".hash") : 635*2538Sesaxe cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1 636*2538Sesaxe cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2 637*2538Sesaxe elif (section == ".dynamic") : 638*2538Sesaxe cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1 639*2538Sesaxe cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2 640*2538Sesaxe elif (section == ".got") : 641*2538Sesaxe cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1 642*2538Sesaxe cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2 643*2538Sesaxe elif (section == ".SUNW_cap") : 644*2538Sesaxe cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1 645*2538Sesaxe cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2 646*2538Sesaxe elif (section == ".interp") : 647*2538Sesaxe cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1 648*2538Sesaxe cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2 649*2538Sesaxe elif (section == ".symtab" or section == ".dynsym") : 650*2538Sesaxe cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1 651*2538Sesaxe cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2 652*2538Sesaxe elif (section in text_sections) : 653*2538Sesaxe # dis sometimes complains when it hits something it doesn't 654*2538Sesaxe # know how to disassemble. Just ignore it, as the output 655*2538Sesaxe # being generated here is human readable, and we've already 656*2538Sesaxe # correctly flagged the difference. 657*2538Sesaxe cmd1 = dis_cmd + " -t " + section + " " + f1 + \ 658*2538Sesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile1 659*2538Sesaxe cmd2 = dis_cmd + " -t " + section + " " + f2 + \ 660*2538Sesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile2 661*2538Sesaxe else : 662*2538Sesaxe cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \ 663*2538Sesaxe section + " " + f1 664*2538Sesaxe cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \ 665*2538Sesaxe section + " " + f2 666*2538Sesaxe 667*2538Sesaxe os.system(cmd1) 668*2538Sesaxe os.system(cmd2) 669*2538Sesaxe 670*2538Sesaxe data = diffFileData(tmpFile1, tmpFile2) 671*2538Sesaxe 672*2538Sesaxe return (data) 673*2538Sesaxe 674*2538Sesaxe# 675*2538Sesaxe# compare the relevant sections of two ELF binaries 676*2538Sesaxe# and report any differences 677*2538Sesaxe# 678*2538Sesaxe# Returns: 1 if any differenes found 679*2538Sesaxe# 0 if no differences found 680*2538Sesaxe# -1 on error 681*2538Sesaxe# 682*2538Sesaxe 683*2538Sesaxe# Sections deliberately not considered when comparing two ELF 684*2538Sesaxe# binaries. Differences observed in these sections are not considered 685*2538Sesaxe# significant where patch deliverable identification is concerned. 686*2538Sesaxesections_to_skip = [ ".SUNW_signature", 687*2538Sesaxe ".comment", 688*2538Sesaxe ".SUNW_ctf", 689*2538Sesaxe ".debug", 690*2538Sesaxe ".plt", 691*2538Sesaxe ".rela.bss", 692*2538Sesaxe ".rela.plt", 693*2538Sesaxe ".line", 694*2538Sesaxe ".note", 695*2538Sesaxe ] 696*2538Sesaxe 697*2538Sesaxesections_preferred = [ ".rodata.str1.8", 698*2538Sesaxe ".rodata.str1.1", 699*2538Sesaxe ".rodata", 700*2538Sesaxe ".data1", 701*2538Sesaxe ".data", 702*2538Sesaxe ".text", 703*2538Sesaxe ] 704*2538Sesaxe 705*2538Sesaxedef compareElfs(base, ptch, quiet) : 706*2538Sesaxe 707*2538Sesaxe global logging 708*2538Sesaxe 709*2538Sesaxe base_header = get_elfheader(base) 710*2538Sesaxe sections = base_header.keys() 711*2538Sesaxe 712*2538Sesaxe ptch_header = get_elfheader(ptch) 713*2538Sesaxe e2_only_sections = ptch_header.keys() 714*2538Sesaxe 715*2538Sesaxe e1_only_sections = [] 716*2538Sesaxe 717*2538Sesaxe fileName = fnFormat(base) 718*2538Sesaxe 719*2538Sesaxe # Derive the list of ELF sections found only in 720*2538Sesaxe # either e1 or e2. 721*2538Sesaxe for sect in sections : 722*2538Sesaxe if not sect in e2_only_sections : 723*2538Sesaxe e1_only_sections.append(sect) 724*2538Sesaxe else : 725*2538Sesaxe e2_only_sections.remove(sect) 726*2538Sesaxe 727*2538Sesaxe if len(e1_only_sections) > 0 : 728*2538Sesaxe if quiet : 729*2538Sesaxe return 1 730*2538Sesaxe info(fileName); 731*2538Sesaxe if not logging : 732*2538Sesaxe return 1 733*2538Sesaxe 734*2538Sesaxe slist = "" 735*2538Sesaxe for sect in e1_only_sections : 736*2538Sesaxe slist = slist + sect + "\t" 737*2538Sesaxe v_info("\nELF sections found in " + \ 738*2538Sesaxe base + " but not in " + ptch) 739*2538Sesaxe v_info("\n" + slist) 740*2538Sesaxe return 1 741*2538Sesaxe 742*2538Sesaxe if len(e2_only_sections) > 0 : 743*2538Sesaxe if quiet : 744*2538Sesaxe return 1 745*2538Sesaxe 746*2538Sesaxe info(fileName); 747*2538Sesaxe if not logging : 748*2538Sesaxe return 1 749*2538Sesaxe 750*2538Sesaxe slist = "" 751*2538Sesaxe for sect in e2_only_sections : 752*2538Sesaxe slist = slist + sect + "\t" 753*2538Sesaxe v_info("\nELF sections found in " + \ 754*2538Sesaxe ptch + " but not in " + base) 755*2538Sesaxe v_info("\n" + slist) 756*2538Sesaxe return 1 757*2538Sesaxe 758*2538Sesaxe # Look for preferred sections, and put those at the 759*2538Sesaxe # top of the list of sections to compare 760*2538Sesaxe for psect in sections_preferred : 761*2538Sesaxe if psect in sections : 762*2538Sesaxe sections.remove(psect) 763*2538Sesaxe sections.insert(0, psect) 764*2538Sesaxe 765*2538Sesaxe # Compare ELF sections 766*2538Sesaxe first_section = True 767*2538Sesaxe for sect in sections : 768*2538Sesaxe 769*2538Sesaxe if sect in sections_to_skip : 770*2538Sesaxe continue 771*2538Sesaxe 772*2538Sesaxe s1 = extract_elf_section(base, sect); 773*2538Sesaxe s2 = extract_elf_section(ptch, sect); 774*2538Sesaxe 775*2538Sesaxe if len(s1) != len (s2) or s1 != s2: 776*2538Sesaxe if not quiet: 777*2538Sesaxe sh_type = base_header[sect] 778*2538Sesaxe data = diff_elf_section(base, ptch, sect, \ 779*2538Sesaxe sh_type) 780*2538Sesaxe 781*2538Sesaxe # If all ELF sections are being reported, then 782*2538Sesaxe # invoke difference() to flag the file name to 783*2538Sesaxe # stdout only once. Any other section differences 784*2538Sesaxe # should be logged to the results file directly 785*2538Sesaxe if not first_section : 786*2538Sesaxe log_difference(fileName, "ELF " + sect, data) 787*2538Sesaxe else : 788*2538Sesaxe difference(fileName, "ELF " + sect, data) 789*2538Sesaxe 790*2538Sesaxe if not reportAllSects : 791*2538Sesaxe return 1 792*2538Sesaxe first_section = False 793*2538Sesaxe return 0 794*2538Sesaxe 795*2538Sesaxe##### 796*2538Sesaxe# Archive object comparison 797*2538Sesaxe# 798*2538Sesaxe# Returns 1 if difference detected 799*2538Sesaxe# 0 if no difference detected 800*2538Sesaxe# -1 on error 801*2538Sesaxe# 802*2538Sesaxedef compareArchives(base, ptch, fileType) : 803*2538Sesaxe 804*2538Sesaxe fileName = fnFormat(base) 805*2538Sesaxe 806*2538Sesaxe # clear the temp directories 807*2538Sesaxe baseCmd = "rm -rf " + tmpDir1 + "*" 808*2538Sesaxe status, output = commands.getstatusoutput(baseCmd) 809*2538Sesaxe if status != 0 : 810*2538Sesaxe error(baseCmd + " failed: " + output) 811*2538Sesaxe return -1 812*2538Sesaxe 813*2538Sesaxe ptchCmd = "rm -rf " + tmpDir2 + "*" 814*2538Sesaxe status, output = commands.getstatusoutput(ptchCmd) 815*2538Sesaxe if status != 0 : 816*2538Sesaxe error(ptchCmd + " failed: " + output) 817*2538Sesaxe return -1 818*2538Sesaxe 819*2538Sesaxe # 820*2538Sesaxe # Be optimistic and first try a straight file compare 821*2538Sesaxe # as it will allow us to finish up quickly. 822*2538Sesaxe if compareBasic(base, ptch, True, fileType) == 0 : 823*2538Sesaxe return 0 824*2538Sesaxe 825*2538Sesaxe # copy over the objects to the temp areas, and 826*2538Sesaxe # unpack them 827*2538Sesaxe baseCmd = "cp -fp " + base + " " + tmpDir1 828*2538Sesaxe status, output = commands.getstatusoutput(baseCmd) 829*2538Sesaxe if status != 0 : 830*2538Sesaxe error(baseCmd + " failed: " + output) 831*2538Sesaxe return -1 832*2538Sesaxe 833*2538Sesaxe ptchCmd = "cp -fp " + ptch + " " + tmpDir2 834*2538Sesaxe status, output = commands.getstatusoutput(ptchCmd) 835*2538Sesaxe if status != 0 : 836*2538Sesaxe error(ptchCmd + " failed: " + output) 837*2538Sesaxe return -1 838*2538Sesaxe 839*2538Sesaxe bname = string.split(fileName, '/')[-1] 840*2538Sesaxe if fileType == "Java Archive" : 841*2538Sesaxe baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \ 842*2538Sesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 843*2538Sesaxe ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \ 844*2538Sesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 845*2538Sesaxe elif fileType == "ELF Object Archive" : 846*2538Sesaxe baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \ 847*2538Sesaxe bname + "; rm -f " + bname 848*2538Sesaxe ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \ 849*2538Sesaxe bname + "; rm -f " + bname 850*2538Sesaxe else : 851*2538Sesaxe error("unexpected file type: " + fileType) 852*2538Sesaxe return -1 853*2538Sesaxe 854*2538Sesaxe os.system(baseCmd) 855*2538Sesaxe os.system(ptchCmd) 856*2538Sesaxe 857*2538Sesaxe baseFlist = list(findFiles(tmpDir1)) 858*2538Sesaxe ptchFlist = list(findFiles(tmpDir2)) 859*2538Sesaxe 860*2538Sesaxe # Trim leading path off base/ptch file lists 861*2538Sesaxe flist = [] 862*2538Sesaxe for fn in baseFlist : 863*2538Sesaxe flist.append(str_prefix_trunc(fn, tmpDir1)) 864*2538Sesaxe baseFlist = flist 865*2538Sesaxe 866*2538Sesaxe flist = [] 867*2538Sesaxe for fn in ptchFlist : 868*2538Sesaxe flist.append(str_prefix_trunc(fn, tmpDir2)) 869*2538Sesaxe ptchFlist = flist 870*2538Sesaxe 871*2538Sesaxe for fn in ptchFlist : 872*2538Sesaxe if not fn in baseFlist : 873*2538Sesaxe difference(fileName, fileType, \ 874*2538Sesaxe fn + " added to " + fileName) 875*2538Sesaxe return 1 876*2538Sesaxe 877*2538Sesaxe for fn in baseFlist : 878*2538Sesaxe if not fn in ptchFlist : 879*2538Sesaxe difference(fileName, fileType, \ 880*2538Sesaxe fn + " removed from " + fileName) 881*2538Sesaxe return 1 882*2538Sesaxe 883*2538Sesaxe differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True) 884*2538Sesaxe if differs : 885*2538Sesaxe difference(fileName, fileType, \ 886*2538Sesaxe fn + " in " + fileName + " differs") 887*2538Sesaxe return 1 888*2538Sesaxe return 0 889*2538Sesaxe 890*2538Sesaxe##### 891*2538Sesaxe# (Basic) file comparison 892*2538Sesaxe# 893*2538Sesaxe# There's some special case code here for Javadoc HTML files 894*2538Sesaxe# 895*2538Sesaxe# Returns 1 if difference detected 896*2538Sesaxe# 0 if no difference detected 897*2538Sesaxe# -1 on error 898*2538Sesaxe# 899*2538Sesaxedef compareBasic(base, ptch, quiet, fileType) : 900*2538Sesaxe 901*2538Sesaxe fileName = fnFormat(base); 902*2538Sesaxe 903*2538Sesaxe if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] : 904*2538Sesaxe return 1 905*2538Sesaxe 906*2538Sesaxe try: 907*2538Sesaxe baseFile = open(base) 908*2538Sesaxe except: 909*2538Sesaxe error("could not open " + base) 910*2538Sesaxe return -1 911*2538Sesaxe try: 912*2538Sesaxe ptchFile = open(ptch) 913*2538Sesaxe except: 914*2538Sesaxe error("could not open " + ptch) 915*2538Sesaxe return -1 916*2538Sesaxe 917*2538Sesaxe baseData = baseFile.read() 918*2538Sesaxe ptchData = ptchFile.read() 919*2538Sesaxe 920*2538Sesaxe baseFile.close() 921*2538Sesaxe ptchFile.close() 922*2538Sesaxe 923*2538Sesaxe needToSnip = False 924*2538Sesaxe if fileType == "HTML" : 925*2538Sesaxe needToSnip = True 926*2538Sesaxe toSnipBeginStr = "<!-- Generated by javadoc" 927*2538Sesaxe toSnipEndStr = "-->\n" 928*2538Sesaxe 929*2538Sesaxe if needToSnip : 930*2538Sesaxe toSnipBegin = string.find(baseData, toSnipBeginStr) 931*2538Sesaxe if toSnipBegin != -1 : 932*2538Sesaxe toSnipEnd = string.find(baseData[toSnipBegin:], \ 933*2538Sesaxe toSnipEndStr) + \ 934*2538Sesaxe len(toSnipEndStr) 935*2538Sesaxe baseData = baseData[:toSnipBegin] + \ 936*2538Sesaxe baseData[toSnipBegin + toSnipEnd:] 937*2538Sesaxe ptchData = ptchData[:toSnipBegin] + \ 938*2538Sesaxe ptchData[toSnipBegin + toSnipEnd:] 939*2538Sesaxe 940*2538Sesaxe if quiet : 941*2538Sesaxe if baseData != ptchData : 942*2538Sesaxe return 1 943*2538Sesaxe else : 944*2538Sesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 945*2538Sesaxe diffs = diffData(baseData, ptchData) 946*2538Sesaxe difference(fileName, fileType, diffs) 947*2538Sesaxe return 1 948*2538Sesaxe return 0 949*2538Sesaxe 950*2538Sesaxe 951*2538Sesaxe##### 952*2538Sesaxe# Compare two objects by producing a data dump from 953*2538Sesaxe# each object, and then comparing the dump data 954*2538Sesaxe# 955*2538Sesaxe# Returns: 1 if a difference is detected 956*2538Sesaxe# 0 if no difference detected 957*2538Sesaxe# -1 upon error 958*2538Sesaxe# 959*2538Sesaxedef compareByDumping(base, ptch, quiet, fileType) : 960*2538Sesaxe 961*2538Sesaxe fileName = fnFormat(base); 962*2538Sesaxe 963*2538Sesaxe if fileType == "Lint Library" : 964*2538Sesaxe baseCmd = lintdump_cmd + " -ir " + base + \ 965*2538Sesaxe " | grep -v LINTLIB:" + " > " + tmpFile1 966*2538Sesaxe ptchCmd = lintdump_cmd + " -ir " + ptch + \ 967*2538Sesaxe " | grep -v LINTLIB:" + " > " + tmpFile2 968*2538Sesaxe elif fileType == "Sqlite Database" : 969*2538Sesaxe baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \ 970*2538Sesaxe tmpFile1 971*2538Sesaxe ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \ 972*2538Sesaxe tmpFile2 973*2538Sesaxe 974*2538Sesaxe os.system(baseCmd) 975*2538Sesaxe os.system(ptchCmd) 976*2538Sesaxe 977*2538Sesaxe try: 978*2538Sesaxe baseFile = open(tmpFile1) 979*2538Sesaxe except: 980*2538Sesaxe error("could not open: " + tmpFile1) 981*2538Sesaxe try: 982*2538Sesaxe ptchFile = open(tmpFile2) 983*2538Sesaxe except: 984*2538Sesaxe error("could not open: " + tmpFile2) 985*2538Sesaxe 986*2538Sesaxe baseData = baseFile.read() 987*2538Sesaxe ptchData = ptchFile.read() 988*2538Sesaxe 989*2538Sesaxe baseFile.close() 990*2538Sesaxe ptchFile.close() 991*2538Sesaxe 992*2538Sesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 993*2538Sesaxe if not quiet : 994*2538Sesaxe data = diffFileData(tmpFile1, tmpFile2); 995*2538Sesaxe difference(fileName, fileType, data) 996*2538Sesaxe return 1 997*2538Sesaxe return 0 998*2538Sesaxe 999*2538Sesaxe##### 1000*2538Sesaxe# Compare two objects. Detect type changes. 1001*2538Sesaxe# Vector off to the appropriate type specific 1002*2538Sesaxe# compare routine based on the type. 1003*2538Sesaxe# 1004*2538Sesaxedef compareOneFile(base, ptch, quiet) : 1005*2538Sesaxe 1006*2538Sesaxe # Verify the file types. 1007*2538Sesaxe # If they are different, indicate this and move on 1008*2538Sesaxe btype = getTheFileType(base) 1009*2538Sesaxe ptype = getTheFileType(ptch) 1010*2538Sesaxe 1011*2538Sesaxe fileName = fnFormat(base) 1012*2538Sesaxe 1013*2538Sesaxe if (btype != ptype) : 1014*2538Sesaxe difference(fileName, "file type", btype + " to " + ptype) 1015*2538Sesaxe return 1 1016*2538Sesaxe else : 1017*2538Sesaxe fileType = btype 1018*2538Sesaxe 1019*2538Sesaxe if (fileType == 'ELF') : 1020*2538Sesaxe return compareElfs(base, ptch, quiet) 1021*2538Sesaxe 1022*2538Sesaxe elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') : 1023*2538Sesaxe return compareArchives(base, ptch, fileType) 1024*2538Sesaxe 1025*2538Sesaxe elif (fileType == 'HTML') : 1026*2538Sesaxe return compareBasic(base, ptch, quiet, fileType) 1027*2538Sesaxe 1028*2538Sesaxe elif ( fileType == 'Lint Library' ) : 1029*2538Sesaxe return compareByDumping(base, ptch, quiet, fileType) 1030*2538Sesaxe 1031*2538Sesaxe elif ( fileType == 'Sqlite Database' ) : 1032*2538Sesaxe return compareByDumping(base, ptch, quiet, fileType) 1033*2538Sesaxe else : 1034*2538Sesaxe # it has to be some variety of text file 1035*2538Sesaxe return compareBasic(base, ptch, quiet, fileType) 1036*2538Sesaxe 1037*2538Sesaxe# Cleanup and self-terminate 1038*2538Sesaxedef cleanup(ret) : 1039*2538Sesaxe 1040*2538Sesaxe if len(tmpDir1) > 0 and len(tmpDir2) > 0 : 1041*2538Sesaxe 1042*2538Sesaxe baseCmd = "rm -rf " + tmpDir1 1043*2538Sesaxe ptchCmd = "rm -rf " + tmpDir2 1044*2538Sesaxe 1045*2538Sesaxe os.system(baseCmd) 1046*2538Sesaxe os.system(ptchCmd) 1047*2538Sesaxe 1048*2538Sesaxe if logging : 1049*2538Sesaxe log.close() 1050*2538Sesaxe 1051*2538Sesaxe sys.exit(ret) 1052*2538Sesaxe 1053*2538Sesaxedef main() : 1054*2538Sesaxe 1055*2538Sesaxe # Log file handle 1056*2538Sesaxe global log 1057*2538Sesaxe 1058*2538Sesaxe # Globals relating to command line options 1059*2538Sesaxe global logging, vdiffs, reportAllSects 1060*2538Sesaxe 1061*2538Sesaxe # Named temporary files / directories 1062*2538Sesaxe global tmpDir1, tmpDir2, tmpFile1, tmpFile2 1063*2538Sesaxe 1064*2538Sesaxe # Command paths 1065*2538Sesaxe global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd 1066*2538Sesaxe 1067*2538Sesaxe # Default search path 1068*2538Sesaxe global wsdiff_path 1069*2538Sesaxe 1070*2538Sesaxe # Essentially "uname -p" 1071*2538Sesaxe global arch 1072*2538Sesaxe 1073*2538Sesaxe # Some globals need to be initialized 1074*2538Sesaxe logging = vdiffs = reportAllSects = False 1075*2538Sesaxe 1076*2538Sesaxe 1077*2538Sesaxe # Process command line arguments 1078*2538Sesaxe # Return values are returned from args() in alpha order 1079*2538Sesaxe # (Yes, python functions can return multiple values (ewww)) 1080*2538Sesaxe # Note that args() also set the globals: 1081*2538Sesaxe # logging to True if verbose logging (to a file) was enabled 1082*2538Sesaxe # vdiffs to True if logged differences aren't to be truncated 1083*2538Sesaxe # reportAllSects to True if all ELF section differences are to be reported 1084*2538Sesaxe # 1085*2538Sesaxe baseRoot, fileNamesFile, localTools, ptchRoot, results = args() 1086*2538Sesaxe 1087*2538Sesaxe # 1088*2538Sesaxe # Set up the results/log file 1089*2538Sesaxe # 1090*2538Sesaxe if logging : 1091*2538Sesaxe try: 1092*2538Sesaxe log = open(results, "w") 1093*2538Sesaxe except: 1094*2538Sesaxe logging = False 1095*2538Sesaxe error("failed to open log file: " + log) 1096*2538Sesaxe sys.exit(1) 1097*2538Sesaxe 1098*2538Sesaxe dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6] 1099*2538Sesaxe v_info("# This file was produced by wsdiff") 1100*2538Sesaxe v_info(dateTimeStr) 1101*2538Sesaxe 1102*2538Sesaxe # 1103*2538Sesaxe # Build paths to the tools required tools 1104*2538Sesaxe # 1105*2538Sesaxe # Try to look for tools in $SRC/tools if the "-t" option 1106*2538Sesaxe # was specified 1107*2538Sesaxe # 1108*2538Sesaxe arch = commands.getoutput("uname -p") 1109*2538Sesaxe if localTools : 1110*2538Sesaxe try: 1111*2538Sesaxe src = os.environ['SRC'] 1112*2538Sesaxe except: 1113*2538Sesaxe error("-t specified, but $SRC not set. Cannot find $SRC/tools") 1114*2538Sesaxe src = "" 1115*2538Sesaxe if len(src) > 0 : 1116*2538Sesaxe wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin") 1117*2538Sesaxe 1118*2538Sesaxe lintdump_cmd = find_tool("lintdump") 1119*2538Sesaxe elfdump_cmd = find_tool("elfdump") 1120*2538Sesaxe dump_cmd = find_tool("dump") 1121*2538Sesaxe od_cmd = find_tool("od") 1122*2538Sesaxe dis_cmd = find_tool("dis") 1123*2538Sesaxe diff_cmd = find_tool("diff") 1124*2538Sesaxe sqlite_cmd = find_tool("sqlite") 1125*2538Sesaxe 1126*2538Sesaxe # 1127*2538Sesaxe # validate the base and patch paths 1128*2538Sesaxe # 1129*2538Sesaxe if baseRoot[-1] != '/' : 1130*2538Sesaxe baseRoot += '/' 1131*2538Sesaxe 1132*2538Sesaxe if ptchRoot[-1] != '/' : 1133*2538Sesaxe ptchRoot += '/' 1134*2538Sesaxe 1135*2538Sesaxe if not os.path.exists(baseRoot) : 1136*2538Sesaxe error("old proto area: " + baseRoot + " does not exist") 1137*2538Sesaxe sys.exit(1) 1138*2538Sesaxe 1139*2538Sesaxe if not os.path.exists(ptchRoot) : 1140*2538Sesaxe error("new proto area: " + ptchRoot + \ 1141*2538Sesaxe " does not exist") 1142*2538Sesaxe sys.exit(1) 1143*2538Sesaxe 1144*2538Sesaxe # 1145*2538Sesaxe # log some information identifying the run 1146*2538Sesaxe # 1147*2538Sesaxe v_info("Old proto area: " + baseRoot) 1148*2538Sesaxe v_info("New proto area: " + ptchRoot) 1149*2538Sesaxe v_info("Results file: " + results + "\n") 1150*2538Sesaxe 1151*2538Sesaxe # 1152*2538Sesaxe # Set up the temporary directories / files 1153*2538Sesaxe # Could use python's tmpdir routines, but these should 1154*2538Sesaxe # be easier to identify / keep around for debugging 1155*2538Sesaxe pid = os.getpid() 1156*2538Sesaxe tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/" 1157*2538Sesaxe tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/" 1158*2538Sesaxe if not os.path.exists(tmpDir1) : 1159*2538Sesaxe os.makedirs(tmpDir1) 1160*2538Sesaxe if not os.path.exists(tmpDir2) : 1161*2538Sesaxe os.makedirs(tmpDir2) 1162*2538Sesaxe 1163*2538Sesaxe tmpFile1 = tmpDir1 + "f1" 1164*2538Sesaxe tmpFile2 = tmpDir2 + "f2" 1165*2538Sesaxe 1166*2538Sesaxe # Derive a catalog of new, deleted, and to-be-compared objects 1167*2538Sesaxe # either from the specified base and patch proto areas, or from 1168*2538Sesaxe # from an input file list 1169*2538Sesaxe newOrDeleted = False 1170*2538Sesaxe 1171*2538Sesaxe if fileNamesFile != "" : 1172*2538Sesaxe changedFiles, newFiles, deletedFiles = \ 1173*2538Sesaxe flistCatalog(baseRoot, ptchRoot, fileNamesFile) 1174*2538Sesaxe else : 1175*2538Sesaxe changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot) 1176*2538Sesaxe 1177*2538Sesaxe if len(newFiles) > 0 : 1178*2538Sesaxe newOrDeleted = True 1179*2538Sesaxe info("\nNew objects found: ") 1180*2538Sesaxe 1181*2538Sesaxe for fn in newFiles : 1182*2538Sesaxe info(fnFormat(fn)) 1183*2538Sesaxe 1184*2538Sesaxe if len(deletedFiles) > 0 : 1185*2538Sesaxe newOrDeleted = True 1186*2538Sesaxe info("\nObjects removed: ") 1187*2538Sesaxe 1188*2538Sesaxe for fn in deletedFiles : 1189*2538Sesaxe info(fnFormat(fn)) 1190*2538Sesaxe 1191*2538Sesaxe if newOrDeleted : 1192*2538Sesaxe info("\nChanged objects: "); 1193*2538Sesaxe 1194*2538Sesaxe 1195*2538Sesaxe # Here's where all the heavy lifting happens 1196*2538Sesaxe # Perform a comparison on each object appearing in 1197*2538Sesaxe # both proto areas. compareOneFile will examine the 1198*2538Sesaxe # file types of each object, and will vector off to 1199*2538Sesaxe # the appropriate comparison routine, where the compare 1200*2538Sesaxe # will happen, and any differences will be reported / logged 1201*2538Sesaxe for fn in changedFiles : 1202*2538Sesaxe base = baseRoot + fn 1203*2538Sesaxe ptch = ptchRoot + fn 1204*2538Sesaxe 1205*2538Sesaxe compareOneFile(base, ptch, False) 1206*2538Sesaxe 1207*2538Sesaxe # We're done, cleanup. 1208*2538Sesaxe cleanup(0) 1209*2538Sesaxe 1210*2538Sesaxeif __name__ == '__main__' : 1211*2538Sesaxe try: 1212*2538Sesaxe main() 1213*2538Sesaxe except KeyboardInterrupt : 1214*2538Sesaxe cleanup(1); 1215*2538Sesaxe 1216*2538Sesaxe 1217