1*11300Srichlowe@richlowe.net#!/usr/bin/python2.4 22538Sesaxe# 32538Sesaxe# CDDL HEADER START 42538Sesaxe# 52538Sesaxe# The contents of this file are subject to the terms of the 62538Sesaxe# Common Development and Distribution License (the "License"). 72538Sesaxe# You may not use this file except in compliance with the License. 82538Sesaxe# 92538Sesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 102538Sesaxe# or http://www.opensolaris.org/os/licensing. 112538Sesaxe# See the License for the specific language governing permissions 122538Sesaxe# and limitations under the License. 132538Sesaxe# 142538Sesaxe# When distributing Covered Code, include this CDDL HEADER in each 152538Sesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 162538Sesaxe# If applicable, add the following below this CDDL HEADER, with the 172538Sesaxe# fields enclosed by brackets "[]" replaced with your own identifying 182538Sesaxe# information: Portions Copyright [yyyy] [name of copyright owner] 192538Sesaxe# 202538Sesaxe# CDDL HEADER END 212538Sesaxe# 22*11300Srichlowe@richlowe.net# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 232538Sesaxe# Use is subject to license terms. 242538Sesaxe# 252538Sesaxe 262538Sesaxe# 272538Sesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects 282538Sesaxe# have changed as a result of a given source change. Developers backporting 292538Sesaxe# new features, RFEs and bug fixes need to be able to identify the set of 302538Sesaxe# patch deliverables necessary for feature/fix realization on a patched system. 312538Sesaxe# 322538Sesaxe# The tool works by comparing objects in two trees/proto areas (one build with, 332538Sesaxe# and without the source changes. 342538Sesaxe# 352538Sesaxe# Using wsdiff(1) is fairly simple: 362538Sesaxe# - Bringover to a fresh workspace 372538Sesaxe# - Perform a full non-debug build (clobber if workspace isn't fresh) 382538Sesaxe# - Move the proto area aside, call it proto.old, or something. 392538Sesaxe# - Integrate your changes to the workspace 402538Sesaxe# - Perform another full non-debug clobber build. 412538Sesaxe# - Use wsdiff(1) to see what changed: 422538Sesaxe# $ wsdiff proto.old proto 432538Sesaxe# 442538Sesaxe# By default, wsdiff will print the list of changed objects / deliverables to 452538Sesaxe# stdout. If a results file is specified via -r, the list of differing objects, 462538Sesaxe# and details about why wsdiff(1) thinks they are different will be logged to 472538Sesaxe# the results file. 482538Sesaxe# 492538Sesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use 502538Sesaxe# wsdiff(1) to report on what objects changed since the last build. 512538Sesaxe# 522538Sesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber, 532538Sesaxe# non-debug build. 542538Sesaxe# 552538Sesaxe# Think about the results. Was something flagged that you don't expect? Go look 562538Sesaxe# at the results file to see details about the differences. 572538Sesaxe# 582538Sesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1) 592538Sesaxe# report with more verbosity. 602538Sesaxe# 612538Sesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 622538Sesaxe# 632538Sesaxe# Where "old" is the path to the proto area build without the changes, and 642538Sesaxe# "new" is the path to the proto area built with the changes. The following 652538Sesaxe# options are supported: 662538Sesaxe# 672538Sesaxe# -v Do not truncate observed diffs in results 682538Sesaxe# -V Log *all* ELF sect diffs vs. logging the first diff found 692538Sesaxe# -t Use onbld tools in $SRC/tools 702538Sesaxe# -r Log results and observed differences 712538Sesaxe# -i Tell wsdiff which objects to compare via an input file list 722538Sesaxe 732538Sesaxeimport datetime, fnmatch, getopt, profile, os, popen2, commands 742538Sesaxeimport re, select, string, struct, sys, tempfile, time 752538Sesaxefrom stat import * 762538Sesaxe 772538Sesaxe# Human readable diffs truncated by default if longer than this 782538Sesaxe# Specifying -v on the command line will override 792538Sesaxediffs_sz_thresh = 4096 802538Sesaxe 812538Sesaxe# Default search path for wsdiff 822538Sesaxewsdiff_path = [ "/usr/bin", 832538Sesaxe "/usr/ccs/bin", 842538Sesaxe "/lib/svc/bin", 852538Sesaxe "/opt/onbld/bin" ] 862538Sesaxe 872538Sesaxe# These are objects that wsdiff will notice look different, but will not report. 882538Sesaxe# Existence of an exceptions list, and adding things here is *dangerous*, 892538Sesaxe# and therefore the *only* reasons why anything would be listed here is because 902538Sesaxe# the objects do not build deterministically, yet we *cannot* fix this. 912538Sesaxe# 922538Sesaxe# These perl libraries use __DATE__ and therefore always look different. 932538Sesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because 942538Sesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify. 952538Sesaxe# 962538Sesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1", 972538Sesaxe "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 982538Sesaxe "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1", 992538Sesaxe "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1" 1002538Sesaxe ] 1012538Sesaxe 1022538Sesaxe##### 1032538Sesaxe# Logging routines 1042538Sesaxe# 1052538Sesaxe 1062538Sesaxe# Informational message to be printed to the screen, and the log file 1072538Sesaxedef info(msg) : 1082538Sesaxe 1092538Sesaxe print >> sys.stdout, msg 1102538Sesaxe if logging : 1112538Sesaxe print >> log, msg 1122538Sesaxe sys.stdout.flush() 1132538Sesaxe 1142538Sesaxe# Error message to be printed to the screen, and the log file 1152538Sesaxedef error(msg) : 116*11300Srichlowe@richlowe.net 1172538Sesaxe print >> sys.stderr, "ERROR:", msg 1182538Sesaxe sys.stderr.flush() 1192538Sesaxe if logging : 1202538Sesaxe print >> log, "ERROR:", msg 1212538Sesaxe log.flush() 1222538Sesaxe 1232538Sesaxe# Informational message to be printed only to the log, if there is one. 1242538Sesaxedef v_info(msg) : 1252538Sesaxe 1262538Sesaxe if logging : 1272538Sesaxe print >> log, msg 1282538Sesaxe log.flush() 129*11300Srichlowe@richlowe.net 1302538Sesaxe# 1312538Sesaxe# Flag a detected file difference 1322538Sesaxe# Display the fileName to stdout, and log the difference 1332538Sesaxe# 1342538Sesaxedef difference(f, dtype, diffs) : 1352538Sesaxe 1362538Sesaxe if f in wsdiff_exceptions : 1372538Sesaxe return 1382538Sesaxe 1392538Sesaxe print >> sys.stdout, f 1402538Sesaxe sys.stdout.flush() 1412538Sesaxe 1422538Sesaxe log_difference(f, dtype, diffs) 1432538Sesaxe 1442538Sesaxe# 1452538Sesaxe# Do the actual logging of the difference to the results file 1462538Sesaxe# 1472538Sesaxedef log_difference(f, dtype, diffs) : 1482538Sesaxe if logging : 1492538Sesaxe print >> log, f 1502538Sesaxe print >> log, "NOTE:", dtype, "difference detected." 1512538Sesaxe 1522538Sesaxe difflen = len(diffs) 153*11300Srichlowe@richlowe.net if difflen > 0 : 1542538Sesaxe print >> log 1552538Sesaxe 1562538Sesaxe if not vdiffs and difflen > diffs_sz_thresh : 1572538Sesaxe print >> log, diffs[:diffs_sz_thresh] 1582538Sesaxe print >> log, \ 1592538Sesaxe "... truncated due to length: " \ 1602538Sesaxe "use -v to override ..." 1612538Sesaxe else : 1622538Sesaxe print >> log, diffs 1632538Sesaxe print >> log, "\n" 1642538Sesaxe log.flush() 1652538Sesaxe 1662538Sesaxe 1672538Sesaxe##### 1682538Sesaxe# diff generating routines 1692538Sesaxe# 1702538Sesaxe 1712538Sesaxe# 1722538Sesaxe# Return human readable diffs from two temporary files 1732538Sesaxe# 1742538Sesaxedef diffFileData(tmpf1, tmpf2) : 1752538Sesaxe 1762538Sesaxe # Filter the data through od(1) if the data is detected 1772538Sesaxe # as being binary 1782538Sesaxe if isBinary(tmpf1) or isBinary(tmpf2) : 1792538Sesaxe tmp_od1 = tmpf1 + ".od" 1802538Sesaxe tmp_od2 = tmpf2 + ".od" 181*11300Srichlowe@richlowe.net 1822538Sesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1 1832538Sesaxe os.system(cmd) 1842538Sesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2 1852538Sesaxe os.system(cmd) 186*11300Srichlowe@richlowe.net 1872538Sesaxe tmpf1 = tmp_od1 1882538Sesaxe tmpf2 = tmp_od2 1892538Sesaxe 1902538Sesaxe data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2) 1912538Sesaxe 1922538Sesaxe return data 1932538Sesaxe 1942538Sesaxe# 1952538Sesaxe# Return human readable diffs betweeen two datasets 1962538Sesaxe# 1972538Sesaxedef diffData(d1, d2) : 1982538Sesaxe 1992538Sesaxe global tmpFile1 2002538Sesaxe global tmpFile2 2012538Sesaxe 2022538Sesaxe try: 2032538Sesaxe fd1 = open(tmpFile1, "w") 2042538Sesaxe except: 2052538Sesaxe error("failed to open: " + tmpFile1) 2062538Sesaxe cleanup(1) 2072538Sesaxe try: 2082538Sesaxe fd2 = open(tmpFile2, "w") 2092538Sesaxe except: 2102538Sesaxe error("failed to open: " + tmpFile2) 2112538Sesaxe cleanup(1) 2122538Sesaxe 2132538Sesaxe fd1.write(d1) 2142538Sesaxe fd2.write(d2) 2152538Sesaxe fd1.close() 2162538Sesaxe fd2.close() 2172538Sesaxe 2182538Sesaxe return diffFileData(tmpFile1, tmpFile2) 2192538Sesaxe 2202538Sesaxe##### 2212538Sesaxe# Misc utility functions 2222538Sesaxe# 2232538Sesaxe 2242538Sesaxe# Prune off the leading prefix from string s 2252538Sesaxedef str_prefix_trunc(s, prefix) : 2262538Sesaxe snipLen = len(prefix) 2272538Sesaxe return s[snipLen:] 2282538Sesaxe 2292538Sesaxe# 2302538Sesaxe# Prune off leading proto path goo (if there is one) to yield 2312538Sesaxe# the deliverable's eventual path relative to root 2322538Sesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat 233*11300Srichlowe@richlowe.net# 2342538Sesaxedef fnFormat(fn) : 2352538Sesaxe root_arch_str = "root_" + arch 236*11300Srichlowe@richlowe.net 2372538Sesaxe pos = fn.find(root_arch_str) 2382538Sesaxe if pos == -1 : 2392538Sesaxe return fn 2402538Sesaxe 2412538Sesaxe pos = fn.find("/", pos) 2422538Sesaxe if pos == -1 : 2432538Sesaxe return fn 2442538Sesaxe 2452538Sesaxe return fn[pos + 1:] 2462538Sesaxe 2472538Sesaxe##### 2482538Sesaxe# Usage / argument processing 2492538Sesaxe# 2502538Sesaxe 2512538Sesaxe# 2522538Sesaxe# Display usage message 2532538Sesaxe# 2542538Sesaxedef usage() : 2552538Sesaxe sys.stdout.flush() 2562538Sesaxe print >> sys.stderr, """Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 2572538Sesaxe -v Do not truncate observed diffs in results 2582538Sesaxe -V Log *all* ELF sect diffs vs. logging the first diff found 2592538Sesaxe -t Use onbld tools in $SRC/tools 2602538Sesaxe -r Log results and observed differences 2612538Sesaxe -i Tell wsdiff which objects to compare via an input file list""" 2622538Sesaxe sys.exit(1) 2632538Sesaxe 2642538Sesaxe# 2652538Sesaxe# Process command line options 2662538Sesaxe# 2672538Sesaxedef args() : 2682538Sesaxe 2692538Sesaxe global logging 2702538Sesaxe global vdiffs 2712538Sesaxe global reportAllSects 2722538Sesaxe 2732538Sesaxe validOpts = 'i:r:vVt?' 2742538Sesaxe 2752538Sesaxe baseRoot = "" 2762538Sesaxe ptchRoot = "" 2772538Sesaxe fileNamesFile = "" 2782538Sesaxe results = "" 2792538Sesaxe localTools = False 2802538Sesaxe 2812538Sesaxe # getopt.getopt() returns: 2822538Sesaxe # an option/value tuple 2832538Sesaxe # a list of remaining non-option arguments 2842538Sesaxe # 2852538Sesaxe # A correct wsdiff invocation will have exactly two non option 2862538Sesaxe # arguments, the paths to the base (old), ptch (new) proto areas 2872538Sesaxe try: 2882538Sesaxe optlist, args = getopt.getopt(sys.argv[1:], validOpts) 2892538Sesaxe except getopt.error, val: 2902538Sesaxe usage() 2912538Sesaxe 2922538Sesaxe if len(args) != 2 : 2932538Sesaxe usage(); 2942538Sesaxe 2952538Sesaxe for opt,val in optlist : 2962538Sesaxe if opt == '-i' : 2972538Sesaxe fileNamesFile = val 2982538Sesaxe elif opt == '-r' : 2992538Sesaxe results = val 3002538Sesaxe logging = True 3012538Sesaxe elif opt == '-v' : 3022538Sesaxe vdiffs = True 3032538Sesaxe elif opt == '-V' : 3042538Sesaxe reportAllSects = True 3052538Sesaxe elif opt == '-t': 3062538Sesaxe localTools = True 3072538Sesaxe else: 3082538Sesaxe usage() 3092538Sesaxe 3102538Sesaxe baseRoot = args[0] 3112538Sesaxe ptchRoot = args[1] 3122538Sesaxe 3132538Sesaxe if len(baseRoot) == 0 or len(ptchRoot) == 0 : 3142538Sesaxe usage() 3152538Sesaxe 3162538Sesaxe if logging and len(results) == 0 : 3172538Sesaxe usage() 3182538Sesaxe 3192538Sesaxe if vdiffs and not logging : 3202538Sesaxe error("The -v option requires a results file (-r)") 3212538Sesaxe sys.exit(1) 3222538Sesaxe 3232538Sesaxe if reportAllSects and not logging : 3242538Sesaxe error("The -V option requires a results file (-r)") 3252538Sesaxe sys.exit(1) 3262538Sesaxe 3272538Sesaxe # alphabetical order 3282538Sesaxe return baseRoot, fileNamesFile, localTools, ptchRoot, results 3292538Sesaxe 3302538Sesaxe##### 3312538Sesaxe# File identification 3322538Sesaxe# 3332538Sesaxe 3342538Sesaxe# 3352538Sesaxe# Identify the file type. 3362538Sesaxe# If it's not ELF, use the file extension to identify 3372538Sesaxe# certain file types that require special handling to 3382538Sesaxe# compare. Otherwise just return a basic "ASCII" type. 3392538Sesaxe# 3402538Sesaxedef getTheFileType(f) : 3412538Sesaxe 3422538Sesaxe extensions = { 'a' : 'ELF Object Archive', 3432538Sesaxe 'jar' : 'Java Archive', 3442538Sesaxe 'html' : 'HTML', 3452538Sesaxe 'ln' : 'Lint Library', 3465298Srotondo 'esa' : 'Elfsign Activation', 3472538Sesaxe 'db' : 'Sqlite Database' } 348*11300Srichlowe@richlowe.net 3495298Srotondo try: 3505298Srotondo if os.stat(f)[ST_SIZE] == 0 : 3515298Srotondo return 'ASCII' 3525298Srotondo except: 3535298Srotondo error("failed to stat " + f) 3545298Srotondo return 'Error' 3552538Sesaxe 3562538Sesaxe if isELF(f) == 1 : 3572538Sesaxe return 'ELF' 3582538Sesaxe 3592538Sesaxe fnamelist = f.split('.') 3602538Sesaxe if len(fnamelist) > 1 : # Test the file extension 3612538Sesaxe extension = fnamelist[-1] 3622538Sesaxe if extension in extensions.keys(): 3632538Sesaxe return extensions[extension] 3642538Sesaxe 3652538Sesaxe return 'ASCII' 3662538Sesaxe 3672538Sesaxe# 3682538Sesaxe# Return non-zero if "f" is an ELF file 3692538Sesaxe# 3702538Sesaxeelfmagic = '\177ELF' 3712538Sesaxedef isELF(f) : 3722538Sesaxe try: 3732538Sesaxe fd = open(f) 3742538Sesaxe except: 3752538Sesaxe error("failed to open: " + f) 3762538Sesaxe return 0 3772538Sesaxe magic = fd.read(len(elfmagic)) 3782538Sesaxe fd.close() 3792538Sesaxe 3802538Sesaxe if magic == elfmagic : 3812538Sesaxe return 1 3822538Sesaxe return 0 3832538Sesaxe 3842538Sesaxe# 3852538Sesaxe# Return non-zero is "f" is binary. 3862538Sesaxe# Consider the file to be binary if it contains any null characters 387*11300Srichlowe@richlowe.net# 3882538Sesaxedef isBinary(f) : 3892538Sesaxe try: 3902538Sesaxe fd = open(f) 3912538Sesaxe except: 3922538Sesaxe error("failed to open: " + f) 3932538Sesaxe return 0 3942538Sesaxe s = fd.read() 3952538Sesaxe fd.close() 3962538Sesaxe 3972538Sesaxe if s.find('\0') == -1 : 3982538Sesaxe return 0 3992538Sesaxe else : 4002538Sesaxe return 1 4012538Sesaxe 4022538Sesaxe##### 4032538Sesaxe# Directory traversal and file finding 404*11300Srichlowe@richlowe.net# 4052538Sesaxe 4062538Sesaxe# 4072538Sesaxe# Return a sorted list of files found under the specified directory 4082538Sesaxe# 4092538Sesaxedef findFiles(d) : 4102538Sesaxe for path, subdirs, files in os.walk(d) : 4112538Sesaxe files.sort() 4122538Sesaxe for name in files : 4132538Sesaxe yield os.path.join(path, name) 4142538Sesaxe 4152538Sesaxe# 4162538Sesaxe# Examine all files in base, ptch 4172538Sesaxe# 4182538Sesaxe# Return a list of files appearing in both proto areas, 4192538Sesaxe# a list of new files (files found only in ptch) and 4202538Sesaxe# a list of deleted files (files found only in base) 4212538Sesaxe# 4222538Sesaxedef protoCatalog(base, ptch) : 4232538Sesaxe compFiles = [] # List of files in both proto areas 4242538Sesaxe ptchList = [] # List of file in patch proto area 4252538Sesaxe 4262538Sesaxe newFiles = [] # New files detected 4272538Sesaxe deletedFiles = [] # Deleted files 428*11300Srichlowe@richlowe.net 4292538Sesaxe baseFilesList = list(findFiles(base)) 4302538Sesaxe baseStringLength = len(base) 431*11300Srichlowe@richlowe.net 4322538Sesaxe ptchFilesList = list(findFiles(ptch)) 4332538Sesaxe ptchStringLength = len(ptch) 4342538Sesaxe 4352538Sesaxe # Inventory files in the base proto area 4362538Sesaxe for fn in baseFilesList : 4372538Sesaxe if os.path.islink(fn) : 4382538Sesaxe continue 4392538Sesaxe 4402538Sesaxe fileName = fn[baseStringLength:] 4412538Sesaxe compFiles.append(fileName) 4422538Sesaxe 4432538Sesaxe # Inventory files in the patch proto area 4442538Sesaxe for fn in ptchFilesList : 4452538Sesaxe if os.path.islink(fn) : 4462538Sesaxe continue 4472538Sesaxe 4482538Sesaxe fileName = fn[ptchStringLength:] 4492538Sesaxe ptchList.append(fileName) 4502538Sesaxe 4512538Sesaxe # Deleted files appear in the base area, but not the patch area 4522538Sesaxe for fileName in compFiles : 4532538Sesaxe if not fileName in ptchList : 4542538Sesaxe deletedFiles.append(fileName) 4552538Sesaxe 4562538Sesaxe # Eliminate "deleted" files from the list of objects appearing 4572538Sesaxe # in both the base and patch proto areas 4582538Sesaxe for fileName in deletedFiles : 4592538Sesaxe try: 4602538Sesaxe compFiles.remove(fileName) 4612538Sesaxe except: 4622538Sesaxe error("filelist.remove() failed") 4632538Sesaxe 4642538Sesaxe # New files appear in the patch area, but not the base 4652538Sesaxe for fileName in ptchList : 4662538Sesaxe if not fileName in compFiles : 4672538Sesaxe newFiles.append(fileName) 4682538Sesaxe 4692538Sesaxe return compFiles, newFiles, deletedFiles 4702538Sesaxe 4712538Sesaxe# 4722538Sesaxe# Examine the files listed in the input file list 4732538Sesaxe# 4742538Sesaxe# Return a list of files appearing in both proto areas, 4752538Sesaxe# a list of new files (files found only in ptch) and 4762538Sesaxe# a list of deleted files (files found only in base) 4772538Sesaxe# 4782538Sesaxedef flistCatalog(base, ptch, flist) : 4792538Sesaxe compFiles = [] # List of files in both proto areas 4802538Sesaxe newFiles = [] # New files detected 4812538Sesaxe deletedFiles = [] # Deleted files 482*11300Srichlowe@richlowe.net 4832538Sesaxe try: 4842538Sesaxe fd = open(flist, "r") 4852538Sesaxe except: 4862538Sesaxe error("could not open: " + flist) 4872538Sesaxe cleanup(1) 4882538Sesaxe 4892538Sesaxe files = [] 4902538Sesaxe files = fd.readlines() 491*11300Srichlowe@richlowe.net 4922538Sesaxe for f in files : 4932538Sesaxe ptch_present = True 4942538Sesaxe base_present = True 495*11300Srichlowe@richlowe.net 4962538Sesaxe if f == '\n' : 4972538Sesaxe continue 4982538Sesaxe 4992538Sesaxe # the fileNames have a trailing '\n' 5002538Sesaxe f = f.rstrip() 5012538Sesaxe 5022538Sesaxe # The objects in the file list have paths relative 5032538Sesaxe # to $ROOT or to the base/ptch directory specified on 5042538Sesaxe # the command line. 5052538Sesaxe # If it's relative to $ROOT, we'll need to add back the 5062538Sesaxe # root_`uname -p` goo we stripped off in fnFormat() 5072538Sesaxe if os.path.exists(base + f) : 5082538Sesaxe fn = f; 5092538Sesaxe elif os.path.exists(base + "root_" + arch + "/" + f) : 5102538Sesaxe fn = "root_" + arch + "/" + f 5112538Sesaxe else : 5122538Sesaxe base_present = False 5132538Sesaxe 5142538Sesaxe if base_present : 5152538Sesaxe if not os.path.exists(ptch + fn) : 5162538Sesaxe ptch_present = False 5172538Sesaxe else : 5182538Sesaxe if os.path.exists(ptch + f) : 5192538Sesaxe fn = f 5202538Sesaxe elif os.path.exists(ptch + "root_" + arch + "/" + f) : 5212538Sesaxe fn = "root_" + arch + "/" + f 5222538Sesaxe else : 5232538Sesaxe ptch_present = False 5242538Sesaxe 5252538Sesaxe if os.path.islink(base + fn) : # ignore links 5262538Sesaxe base_present = False 5272538Sesaxe if os.path.islink(ptch + fn) : 5282538Sesaxe ptch_present = False 5292538Sesaxe 5302538Sesaxe if base_present and ptch_present : 5312538Sesaxe compFiles.append(fn) 5322538Sesaxe elif base_present : 5332538Sesaxe deletedFiles.append(fn) 5342538Sesaxe elif ptch_present : 5352538Sesaxe newFiles.append(fn) 5362538Sesaxe else : 5372538Sesaxe if os.path.islink(base + fn) and os.path.islink(ptch + fn) : 5382538Sesaxe continue 5392538Sesaxe error(f + " in file list, but not in either tree. Skipping...") 540*11300Srichlowe@richlowe.net 5412538Sesaxe return compFiles, newFiles, deletedFiles 5422538Sesaxe 5432538Sesaxe 5442538Sesaxe# 5452538Sesaxe# Build a fully qualified path to an external tool/utility. 5462538Sesaxe# Consider the default system locations. For onbld tools, if 5472538Sesaxe# the -t option was specified, we'll try to use built tools in $SRC tools, 5482538Sesaxe# and otherwise, we'll fall back on /opt/onbld/ 5492538Sesaxe# 5502538Sesaxedef find_tool(tool) : 5512538Sesaxe 5522538Sesaxe # First, check what was passed 5532538Sesaxe if os.path.exists(tool) : 5542538Sesaxe return tool 5552538Sesaxe 5562538Sesaxe # Next try in wsdiff path 5572538Sesaxe for pdir in wsdiff_path : 5582538Sesaxe location = pdir + "/" + tool 5592538Sesaxe if os.path.exists(location) : 5602538Sesaxe return location + " " 5612538Sesaxe 5622538Sesaxe location = pdir + "/" + arch + "/" + tool 5632538Sesaxe if os.path.exists(location) : 5642538Sesaxe return location + " " 565*11300Srichlowe@richlowe.net 5662538Sesaxe error("Could not find path to: " + tool); 5672538Sesaxe sys.exit(1); 568*11300Srichlowe@richlowe.net 5692538Sesaxe 5702538Sesaxe##### 5712538Sesaxe# ELF file comparison helper routines 5722538Sesaxe# 5732538Sesaxe 5742538Sesaxe# 5752538Sesaxe# Return a dictionary of ELF section types keyed by section name 576*11300Srichlowe@richlowe.net# 5772538Sesaxedef get_elfheader(f) : 5782538Sesaxe 5792538Sesaxe header = {} 5802538Sesaxe 5812538Sesaxe hstring = commands.getoutput(elfdump_cmd + " -c " + f) 5822538Sesaxe 5832538Sesaxe if len(hstring) == 0 : 5842538Sesaxe error("Failed to dump ELF header for " + f) 5852538Sesaxe return 5862538Sesaxe 5872538Sesaxe # elfdump(1) dumps the section headers with the section name 5882538Sesaxe # following "sh_name:", and the section type following "sh_type:" 5892538Sesaxe sections = hstring.split("Section Header") 5902538Sesaxe for sect in sections : 5912538Sesaxe datap = sect.find("sh_name:"); 5922538Sesaxe if datap == -1 : 5932538Sesaxe continue 5942538Sesaxe section = sect[datap:].split()[1] 5952538Sesaxe datap = sect.find("sh_type:"); 5962538Sesaxe if datap == -1 : 5972538Sesaxe error("Could not get type for sect: " + section + \ 5982538Sesaxe " in " + f) 5992538Sesaxe sh_type = sect[datap:].split()[2] 6002538Sesaxe header[section] = sh_type 6012538Sesaxe 6022538Sesaxe return header 6032538Sesaxe 6042538Sesaxe# 6052538Sesaxe# Extract data in the specified ELF section from the given file 6062538Sesaxe# 6072538Sesaxedef extract_elf_section(f, section) : 6082538Sesaxe 6092538Sesaxe data = commands.getoutput(dump_cmd + " -sn " + section + " " + f) 6102538Sesaxe 6112538Sesaxe if len(data) == 0 : 6122538Sesaxe error(cmd + " yielded no data") 6132538Sesaxe return 6142538Sesaxe 6152538Sesaxe # dump(1) displays the file name to start... 6162538Sesaxe # get past it to the data itself 6172538Sesaxe dbegin = data.find(":") + 1 6182538Sesaxe data = data[dbegin:]; 6192538Sesaxe 6202538Sesaxe return (data) 6212538Sesaxe 6222538Sesaxe# 6232538Sesaxe# Return a (hopefully meaningful) human readable set of diffs 6242538Sesaxe# for the specified ELF section between f1 and f2 6252538Sesaxe# 6262538Sesaxe# Depending on the section, various means for dumping and diffing 6272538Sesaxe# the data may be employed. 6282538Sesaxe# 6292538Sesaxetext_sections = [ '.text', '.init', '.fini' ] 6302538Sesaxedef diff_elf_section(f1, f2, section, sh_type) : 6312538Sesaxe 6322538Sesaxe if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA 6332538Sesaxe cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1 6342538Sesaxe cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2 6352538Sesaxe elif (section == ".group") : 6362538Sesaxe cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1 6372538Sesaxe cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2 6382538Sesaxe elif (section == ".hash") : 6392538Sesaxe cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1 6402538Sesaxe cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2 6412538Sesaxe elif (section == ".dynamic") : 6422538Sesaxe cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1 6432538Sesaxe cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2 6442538Sesaxe elif (section == ".got") : 6452538Sesaxe cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1 6462538Sesaxe cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2 6472538Sesaxe elif (section == ".SUNW_cap") : 6482538Sesaxe cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1 6492538Sesaxe cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2 6502538Sesaxe elif (section == ".interp") : 6512538Sesaxe cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1 6522538Sesaxe cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2 6532538Sesaxe elif (section == ".symtab" or section == ".dynsym") : 6542538Sesaxe cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1 6552538Sesaxe cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2 6562538Sesaxe elif (section in text_sections) : 6572538Sesaxe # dis sometimes complains when it hits something it doesn't 6582538Sesaxe # know how to disassemble. Just ignore it, as the output 6592538Sesaxe # being generated here is human readable, and we've already 6602538Sesaxe # correctly flagged the difference. 6612538Sesaxe cmd1 = dis_cmd + " -t " + section + " " + f1 + \ 6622538Sesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile1 6632538Sesaxe cmd2 = dis_cmd + " -t " + section + " " + f2 + \ 6642538Sesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile2 6652538Sesaxe else : 6662538Sesaxe cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \ 6672538Sesaxe section + " " + f1 6682538Sesaxe cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \ 6692538Sesaxe section + " " + f2 6702538Sesaxe 6712538Sesaxe os.system(cmd1) 6722538Sesaxe os.system(cmd2) 6732538Sesaxe 6742538Sesaxe data = diffFileData(tmpFile1, tmpFile2) 675*11300Srichlowe@richlowe.net 6762538Sesaxe return (data) 6772538Sesaxe 6782538Sesaxe# 6792538Sesaxe# compare the relevant sections of two ELF binaries 6802538Sesaxe# and report any differences 6812538Sesaxe# 6822538Sesaxe# Returns: 1 if any differenes found 6832538Sesaxe# 0 if no differences found 6842538Sesaxe# -1 on error 6852538Sesaxe# 6862538Sesaxe 6872538Sesaxe# Sections deliberately not considered when comparing two ELF 6882538Sesaxe# binaries. Differences observed in these sections are not considered 6892538Sesaxe# significant where patch deliverable identification is concerned. 6902538Sesaxesections_to_skip = [ ".SUNW_signature", 6912538Sesaxe ".comment", 6922538Sesaxe ".SUNW_ctf", 6932538Sesaxe ".debug", 6942538Sesaxe ".plt", 6952538Sesaxe ".rela.bss", 6962538Sesaxe ".rela.plt", 6972538Sesaxe ".line", 6982538Sesaxe ".note", 6993002Sesaxe ".compcom", 7002538Sesaxe ] 7012538Sesaxe 7022538Sesaxesections_preferred = [ ".rodata.str1.8", 7032538Sesaxe ".rodata.str1.1", 7042538Sesaxe ".rodata", 7052538Sesaxe ".data1", 7062538Sesaxe ".data", 7072538Sesaxe ".text", 7082538Sesaxe ] 7092538Sesaxe 7102538Sesaxedef compareElfs(base, ptch, quiet) : 7112538Sesaxe 7122538Sesaxe global logging 7132538Sesaxe 7142538Sesaxe base_header = get_elfheader(base) 7152538Sesaxe sections = base_header.keys() 7162538Sesaxe 7172538Sesaxe ptch_header = get_elfheader(ptch) 7182538Sesaxe e2_only_sections = ptch_header.keys() 7192538Sesaxe 7202538Sesaxe e1_only_sections = [] 7212538Sesaxe 7222538Sesaxe fileName = fnFormat(base) 7232538Sesaxe 7242538Sesaxe # Derive the list of ELF sections found only in 7252538Sesaxe # either e1 or e2. 7262538Sesaxe for sect in sections : 7272538Sesaxe if not sect in e2_only_sections : 7282538Sesaxe e1_only_sections.append(sect) 7292538Sesaxe else : 7302538Sesaxe e2_only_sections.remove(sect) 7312538Sesaxe 7322538Sesaxe if len(e1_only_sections) > 0 : 7332538Sesaxe if quiet : 7342538Sesaxe return 1 7352538Sesaxe info(fileName); 7362538Sesaxe if not logging : 7372538Sesaxe return 1 7382538Sesaxe 7392538Sesaxe slist = "" 7402538Sesaxe for sect in e1_only_sections : 7412538Sesaxe slist = slist + sect + "\t" 7422538Sesaxe v_info("\nELF sections found in " + \ 7432538Sesaxe base + " but not in " + ptch) 7442538Sesaxe v_info("\n" + slist) 7452538Sesaxe return 1 746*11300Srichlowe@richlowe.net 7472538Sesaxe if len(e2_only_sections) > 0 : 7482538Sesaxe if quiet : 7492538Sesaxe return 1 750*11300Srichlowe@richlowe.net 7512538Sesaxe info(fileName); 7522538Sesaxe if not logging : 7532538Sesaxe return 1 7542538Sesaxe 7552538Sesaxe slist = "" 7562538Sesaxe for sect in e2_only_sections : 7572538Sesaxe slist = slist + sect + "\t" 7582538Sesaxe v_info("\nELF sections found in " + \ 7592538Sesaxe ptch + " but not in " + base) 7602538Sesaxe v_info("\n" + slist) 7612538Sesaxe return 1 7622538Sesaxe 7632538Sesaxe # Look for preferred sections, and put those at the 7642538Sesaxe # top of the list of sections to compare 7652538Sesaxe for psect in sections_preferred : 7662538Sesaxe if psect in sections : 7672538Sesaxe sections.remove(psect) 7682538Sesaxe sections.insert(0, psect) 7692538Sesaxe 7702538Sesaxe # Compare ELF sections 7712538Sesaxe first_section = True 7722538Sesaxe for sect in sections : 7732538Sesaxe 7742538Sesaxe if sect in sections_to_skip : 7752538Sesaxe continue 7762538Sesaxe 7772538Sesaxe s1 = extract_elf_section(base, sect); 7782538Sesaxe s2 = extract_elf_section(ptch, sect); 7792538Sesaxe 7802538Sesaxe if len(s1) != len (s2) or s1 != s2: 7812538Sesaxe if not quiet: 7822538Sesaxe sh_type = base_header[sect] 7832538Sesaxe data = diff_elf_section(base, ptch, sect, \ 7842538Sesaxe sh_type) 7852538Sesaxe 7862538Sesaxe # If all ELF sections are being reported, then 7872538Sesaxe # invoke difference() to flag the file name to 7882538Sesaxe # stdout only once. Any other section differences 7892538Sesaxe # should be logged to the results file directly 7902538Sesaxe if not first_section : 7912538Sesaxe log_difference(fileName, "ELF " + sect, data) 7922538Sesaxe else : 7932538Sesaxe difference(fileName, "ELF " + sect, data) 794*11300Srichlowe@richlowe.net 7952538Sesaxe if not reportAllSects : 7962538Sesaxe return 1 7972538Sesaxe first_section = False 7982538Sesaxe return 0 799*11300Srichlowe@richlowe.net 8002538Sesaxe##### 8012538Sesaxe# Archive object comparison 8022538Sesaxe# 8032538Sesaxe# Returns 1 if difference detected 8042538Sesaxe# 0 if no difference detected 8052538Sesaxe# -1 on error 8062538Sesaxe# 8072538Sesaxedef compareArchives(base, ptch, fileType) : 8082538Sesaxe 8092538Sesaxe fileName = fnFormat(base) 8102538Sesaxe 8112538Sesaxe # clear the temp directories 8122538Sesaxe baseCmd = "rm -rf " + tmpDir1 + "*" 8132538Sesaxe status, output = commands.getstatusoutput(baseCmd) 8142538Sesaxe if status != 0 : 8152538Sesaxe error(baseCmd + " failed: " + output) 8162538Sesaxe return -1 8172538Sesaxe 8182538Sesaxe ptchCmd = "rm -rf " + tmpDir2 + "*" 8192538Sesaxe status, output = commands.getstatusoutput(ptchCmd) 8202538Sesaxe if status != 0 : 8212538Sesaxe error(ptchCmd + " failed: " + output) 8222538Sesaxe return -1 8232538Sesaxe 8242538Sesaxe # 8252538Sesaxe # Be optimistic and first try a straight file compare 8262538Sesaxe # as it will allow us to finish up quickly. 8272538Sesaxe if compareBasic(base, ptch, True, fileType) == 0 : 8282538Sesaxe return 0 8292538Sesaxe 8302538Sesaxe # copy over the objects to the temp areas, and 8312538Sesaxe # unpack them 8322538Sesaxe baseCmd = "cp -fp " + base + " " + tmpDir1 8332538Sesaxe status, output = commands.getstatusoutput(baseCmd) 8342538Sesaxe if status != 0 : 8352538Sesaxe error(baseCmd + " failed: " + output) 8362538Sesaxe return -1 8372538Sesaxe 8382538Sesaxe ptchCmd = "cp -fp " + ptch + " " + tmpDir2 8392538Sesaxe status, output = commands.getstatusoutput(ptchCmd) 8402538Sesaxe if status != 0 : 8412538Sesaxe error(ptchCmd + " failed: " + output) 8422538Sesaxe return -1 8432538Sesaxe 8442538Sesaxe bname = string.split(fileName, '/')[-1] 8452538Sesaxe if fileType == "Java Archive" : 8462538Sesaxe baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \ 8472538Sesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 8482538Sesaxe ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \ 8492538Sesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 8502538Sesaxe elif fileType == "ELF Object Archive" : 8512538Sesaxe baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \ 8522538Sesaxe bname + "; rm -f " + bname 8532538Sesaxe ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \ 8542538Sesaxe bname + "; rm -f " + bname 8552538Sesaxe else : 8562538Sesaxe error("unexpected file type: " + fileType) 8572538Sesaxe return -1 8582538Sesaxe 8592538Sesaxe os.system(baseCmd) 8602538Sesaxe os.system(ptchCmd) 8612538Sesaxe 8622538Sesaxe baseFlist = list(findFiles(tmpDir1)) 8632538Sesaxe ptchFlist = list(findFiles(tmpDir2)) 8642538Sesaxe 8652538Sesaxe # Trim leading path off base/ptch file lists 8662538Sesaxe flist = [] 8672538Sesaxe for fn in baseFlist : 8682538Sesaxe flist.append(str_prefix_trunc(fn, tmpDir1)) 8692538Sesaxe baseFlist = flist 8702538Sesaxe 8712538Sesaxe flist = [] 8722538Sesaxe for fn in ptchFlist : 8732538Sesaxe flist.append(str_prefix_trunc(fn, tmpDir2)) 8742538Sesaxe ptchFlist = flist 8752538Sesaxe 8762538Sesaxe for fn in ptchFlist : 8772538Sesaxe if not fn in baseFlist : 8782538Sesaxe difference(fileName, fileType, \ 8792538Sesaxe fn + " added to " + fileName) 8802538Sesaxe return 1 8812538Sesaxe 8822538Sesaxe for fn in baseFlist : 8832538Sesaxe if not fn in ptchFlist : 8842538Sesaxe difference(fileName, fileType, \ 8852538Sesaxe fn + " removed from " + fileName) 8862538Sesaxe return 1 8872538Sesaxe 8882538Sesaxe differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True) 8892538Sesaxe if differs : 8902538Sesaxe difference(fileName, fileType, \ 8912538Sesaxe fn + " in " + fileName + " differs") 8922538Sesaxe return 1 8932538Sesaxe return 0 8942538Sesaxe 8952538Sesaxe##### 8962538Sesaxe# (Basic) file comparison 8972538Sesaxe# 8982538Sesaxe# There's some special case code here for Javadoc HTML files 899*11300Srichlowe@richlowe.net# 9002538Sesaxe# Returns 1 if difference detected 9012538Sesaxe# 0 if no difference detected 9022538Sesaxe# -1 on error 9032538Sesaxe# 9042538Sesaxedef compareBasic(base, ptch, quiet, fileType) : 9052538Sesaxe 9062538Sesaxe fileName = fnFormat(base); 9072538Sesaxe 9082538Sesaxe if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] : 9092538Sesaxe return 1 9102538Sesaxe 9112538Sesaxe try: 9122538Sesaxe baseFile = open(base) 9132538Sesaxe except: 9142538Sesaxe error("could not open " + base) 9152538Sesaxe return -1 9162538Sesaxe try: 9172538Sesaxe ptchFile = open(ptch) 9182538Sesaxe except: 9192538Sesaxe error("could not open " + ptch) 9202538Sesaxe return -1 9212538Sesaxe 9222538Sesaxe baseData = baseFile.read() 9232538Sesaxe ptchData = ptchFile.read() 9242538Sesaxe 9252538Sesaxe baseFile.close() 9262538Sesaxe ptchFile.close() 9272538Sesaxe 9282538Sesaxe needToSnip = False 9292538Sesaxe if fileType == "HTML" : 9302538Sesaxe needToSnip = True 9312538Sesaxe toSnipBeginStr = "<!-- Generated by javadoc" 9322538Sesaxe toSnipEndStr = "-->\n" 9332538Sesaxe 9342538Sesaxe if needToSnip : 9352538Sesaxe toSnipBegin = string.find(baseData, toSnipBeginStr) 9362538Sesaxe if toSnipBegin != -1 : 9372538Sesaxe toSnipEnd = string.find(baseData[toSnipBegin:], \ 9382538Sesaxe toSnipEndStr) + \ 9392538Sesaxe len(toSnipEndStr) 9402538Sesaxe baseData = baseData[:toSnipBegin] + \ 9412538Sesaxe baseData[toSnipBegin + toSnipEnd:] 9422538Sesaxe ptchData = ptchData[:toSnipBegin] + \ 9432538Sesaxe ptchData[toSnipBegin + toSnipEnd:] 9442538Sesaxe 9452538Sesaxe if quiet : 9462538Sesaxe if baseData != ptchData : 9472538Sesaxe return 1 9482538Sesaxe else : 9492538Sesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 9502538Sesaxe diffs = diffData(baseData, ptchData) 9512538Sesaxe difference(fileName, fileType, diffs) 9522538Sesaxe return 1 9532538Sesaxe return 0 9542538Sesaxe 9552538Sesaxe 9562538Sesaxe##### 9572538Sesaxe# Compare two objects by producing a data dump from 9582538Sesaxe# each object, and then comparing the dump data 9592538Sesaxe# 9602538Sesaxe# Returns: 1 if a difference is detected 9612538Sesaxe# 0 if no difference detected 9622538Sesaxe# -1 upon error 9632538Sesaxe# 9642538Sesaxedef compareByDumping(base, ptch, quiet, fileType) : 9652538Sesaxe 9662538Sesaxe fileName = fnFormat(base); 9672538Sesaxe 9682538Sesaxe if fileType == "Lint Library" : 9692538Sesaxe baseCmd = lintdump_cmd + " -ir " + base + \ 9706362Smeem " | egrep -v '(LINTOBJ|LINTMOD):'" + " > " + tmpFile1 9712538Sesaxe ptchCmd = lintdump_cmd + " -ir " + ptch + \ 9726362Smeem " | egrep -v '(LINTOBJ|LINTMOD):'" + " > " + tmpFile2 9732538Sesaxe elif fileType == "Sqlite Database" : 9742538Sesaxe baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \ 9752538Sesaxe tmpFile1 9762538Sesaxe ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \ 9772538Sesaxe tmpFile2 978*11300Srichlowe@richlowe.net 9792538Sesaxe os.system(baseCmd) 9802538Sesaxe os.system(ptchCmd) 9812538Sesaxe 9822538Sesaxe try: 9832538Sesaxe baseFile = open(tmpFile1) 9842538Sesaxe except: 9852538Sesaxe error("could not open: " + tmpFile1) 9862538Sesaxe try: 9872538Sesaxe ptchFile = open(tmpFile2) 9882538Sesaxe except: 9892538Sesaxe error("could not open: " + tmpFile2) 9902538Sesaxe 9912538Sesaxe baseData = baseFile.read() 9922538Sesaxe ptchData = ptchFile.read() 9932538Sesaxe 9942538Sesaxe baseFile.close() 9952538Sesaxe ptchFile.close() 9962538Sesaxe 9972538Sesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 9982538Sesaxe if not quiet : 9992538Sesaxe data = diffFileData(tmpFile1, tmpFile2); 10002538Sesaxe difference(fileName, fileType, data) 10012538Sesaxe return 1 10022538Sesaxe return 0 10032538Sesaxe 10042538Sesaxe##### 10055298Srotondo# Compare two elfsign activation files. This ignores the activation 10065298Srotondo# files themselves and reports a difference if and only if the 10075298Srotondo# corresponding base files are different. 10085298Srotondo# 10095298Srotondo# Returns 1 if difference detected 10105298Srotondo# 0 if no difference detected 10115298Srotondo# -1 on error 10125298Srotondo# 10135298Srotondodef compareActivation(base, ptch, quiet, fileType) : 10145298Srotondo 10155298Srotondo fileName = fnFormat(base) 10165298Srotondo 10175298Srotondo # Drop the .esa suffix from both filenames. 10185298Srotondo base = base[0:base.rfind('.esa')] 10195298Srotondo ptch = ptch[0:ptch.rfind('.esa')] 10205298Srotondo 10215298Srotondo result = compareOneFile(base, ptch, True) 10225298Srotondo if result == -1 : 10235298Srotondo error("unable to compare " + fileName) 10245298Srotondo elif result == 1 : 10255298Srotondo if not quiet : 10265298Srotondo difference(fileName, fileType, \ 10275298Srotondo "change in corresponding ELF file") 10285298Srotondo 10295298Srotondo return result 10305298Srotondo 10315298Srotondo##### 10322538Sesaxe# Compare two objects. Detect type changes. 10332538Sesaxe# Vector off to the appropriate type specific 10342538Sesaxe# compare routine based on the type. 1035*11300Srichlowe@richlowe.net# 10362538Sesaxedef compareOneFile(base, ptch, quiet) : 10372538Sesaxe 10382538Sesaxe # Verify the file types. 10392538Sesaxe # If they are different, indicate this and move on 10402538Sesaxe btype = getTheFileType(base) 10412538Sesaxe ptype = getTheFileType(ptch) 10422538Sesaxe 10435298Srotondo if btype == 'Error' or ptype == 'Error' : 10445298Srotondo return -1 10455298Srotondo 10462538Sesaxe fileName = fnFormat(base) 10472538Sesaxe 10482538Sesaxe if (btype != ptype) : 10495298Srotondo if not quiet : 10505298Srotondo difference(fileName, "file type", btype + " to " + ptype) 10512538Sesaxe return 1 10522538Sesaxe else : 10532538Sesaxe fileType = btype 10542538Sesaxe 10552538Sesaxe if (fileType == 'ELF') : 10562538Sesaxe return compareElfs(base, ptch, quiet) 10572538Sesaxe 10582538Sesaxe elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') : 10592538Sesaxe return compareArchives(base, ptch, fileType) 10602538Sesaxe 10612538Sesaxe elif (fileType == 'HTML') : 10622538Sesaxe return compareBasic(base, ptch, quiet, fileType) 10632538Sesaxe 10642538Sesaxe elif ( fileType == 'Lint Library' ) : 10652538Sesaxe return compareByDumping(base, ptch, quiet, fileType) 10662538Sesaxe 10672538Sesaxe elif ( fileType == 'Sqlite Database' ) : 10682538Sesaxe return compareByDumping(base, ptch, quiet, fileType) 10695298Srotondo 10705298Srotondo elif ( fileType == 'Elfsign Activation' ) : 10715298Srotondo return compareActivation(base, ptch, quiet, fileType) 10725298Srotondo 10732538Sesaxe else : 10742538Sesaxe # it has to be some variety of text file 10752538Sesaxe return compareBasic(base, ptch, quiet, fileType) 10762538Sesaxe 10772538Sesaxe# Cleanup and self-terminate 10782538Sesaxedef cleanup(ret) : 10792538Sesaxe 10802538Sesaxe if len(tmpDir1) > 0 and len(tmpDir2) > 0 : 10812538Sesaxe 10822538Sesaxe baseCmd = "rm -rf " + tmpDir1 10832538Sesaxe ptchCmd = "rm -rf " + tmpDir2 10842538Sesaxe 10852538Sesaxe os.system(baseCmd) 10862538Sesaxe os.system(ptchCmd) 1087*11300Srichlowe@richlowe.net 10882538Sesaxe if logging : 10892538Sesaxe log.close() 1090*11300Srichlowe@richlowe.net 10912538Sesaxe sys.exit(ret) 10922538Sesaxe 10932538Sesaxedef main() : 10942538Sesaxe 10952538Sesaxe # Log file handle 10962538Sesaxe global log 10972538Sesaxe 10982538Sesaxe # Globals relating to command line options 10992538Sesaxe global logging, vdiffs, reportAllSects 11002538Sesaxe 11012538Sesaxe # Named temporary files / directories 11022538Sesaxe global tmpDir1, tmpDir2, tmpFile1, tmpFile2 11032538Sesaxe 11042538Sesaxe # Command paths 11052538Sesaxe global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd 11062538Sesaxe 11072538Sesaxe # Default search path 11082538Sesaxe global wsdiff_path 11092538Sesaxe 11102538Sesaxe # Essentially "uname -p" 11112538Sesaxe global arch 11122538Sesaxe 11132538Sesaxe # Some globals need to be initialized 11142538Sesaxe logging = vdiffs = reportAllSects = False 11152538Sesaxe 11162538Sesaxe 11172538Sesaxe # Process command line arguments 11182538Sesaxe # Return values are returned from args() in alpha order 11192538Sesaxe # (Yes, python functions can return multiple values (ewww)) 11202538Sesaxe # Note that args() also set the globals: 11212538Sesaxe # logging to True if verbose logging (to a file) was enabled 11222538Sesaxe # vdiffs to True if logged differences aren't to be truncated 11232538Sesaxe # reportAllSects to True if all ELF section differences are to be reported 11242538Sesaxe # 11252538Sesaxe baseRoot, fileNamesFile, localTools, ptchRoot, results = args() 11262538Sesaxe 11272538Sesaxe # 11282538Sesaxe # Set up the results/log file 11292538Sesaxe # 11302538Sesaxe if logging : 11312538Sesaxe try: 11322538Sesaxe log = open(results, "w") 11332538Sesaxe except: 11342538Sesaxe logging = False 11352538Sesaxe error("failed to open log file: " + log) 11362538Sesaxe sys.exit(1) 11372538Sesaxe 11382538Sesaxe dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6] 11392538Sesaxe v_info("# This file was produced by wsdiff") 11402538Sesaxe v_info(dateTimeStr) 11412538Sesaxe 1142*11300Srichlowe@richlowe.net # 11432538Sesaxe # Build paths to the tools required tools 11442538Sesaxe # 11452538Sesaxe # Try to look for tools in $SRC/tools if the "-t" option 11462538Sesaxe # was specified 11472538Sesaxe # 11482538Sesaxe arch = commands.getoutput("uname -p") 11492538Sesaxe if localTools : 11502538Sesaxe try: 11512538Sesaxe src = os.environ['SRC'] 11522538Sesaxe except: 11532538Sesaxe error("-t specified, but $SRC not set. Cannot find $SRC/tools") 11542538Sesaxe src = "" 11552538Sesaxe if len(src) > 0 : 11562538Sesaxe wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin") 11572538Sesaxe 11582538Sesaxe lintdump_cmd = find_tool("lintdump") 11592538Sesaxe elfdump_cmd = find_tool("elfdump") 11602538Sesaxe dump_cmd = find_tool("dump") 11612538Sesaxe od_cmd = find_tool("od") 11622538Sesaxe dis_cmd = find_tool("dis") 11632538Sesaxe diff_cmd = find_tool("diff") 11642538Sesaxe sqlite_cmd = find_tool("sqlite") 11652538Sesaxe 11662538Sesaxe # 11672538Sesaxe # validate the base and patch paths 11682538Sesaxe # 11692538Sesaxe if baseRoot[-1] != '/' : 11702538Sesaxe baseRoot += '/' 11712538Sesaxe 11722538Sesaxe if ptchRoot[-1] != '/' : 11732538Sesaxe ptchRoot += '/' 11742538Sesaxe 11752538Sesaxe if not os.path.exists(baseRoot) : 11762538Sesaxe error("old proto area: " + baseRoot + " does not exist") 11772538Sesaxe sys.exit(1) 11782538Sesaxe 11792538Sesaxe if not os.path.exists(ptchRoot) : 11802538Sesaxe error("new proto area: " + ptchRoot + \ 11812538Sesaxe " does not exist") 11822538Sesaxe sys.exit(1) 11832538Sesaxe 11842538Sesaxe # 11852538Sesaxe # log some information identifying the run 11862538Sesaxe # 11872538Sesaxe v_info("Old proto area: " + baseRoot) 11882538Sesaxe v_info("New proto area: " + ptchRoot) 11892538Sesaxe v_info("Results file: " + results + "\n") 11902538Sesaxe 1191*11300Srichlowe@richlowe.net # 11922538Sesaxe # Set up the temporary directories / files 11932538Sesaxe # Could use python's tmpdir routines, but these should 11942538Sesaxe # be easier to identify / keep around for debugging 11952538Sesaxe pid = os.getpid() 11962538Sesaxe tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/" 11972538Sesaxe tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/" 11982538Sesaxe if not os.path.exists(tmpDir1) : 11992538Sesaxe os.makedirs(tmpDir1) 12002538Sesaxe if not os.path.exists(tmpDir2) : 12012538Sesaxe os.makedirs(tmpDir2) 12022538Sesaxe 12032538Sesaxe tmpFile1 = tmpDir1 + "f1" 12042538Sesaxe tmpFile2 = tmpDir2 + "f2" 12052538Sesaxe 12062538Sesaxe # Derive a catalog of new, deleted, and to-be-compared objects 12072538Sesaxe # either from the specified base and patch proto areas, or from 12082538Sesaxe # from an input file list 12092538Sesaxe newOrDeleted = False 12102538Sesaxe 12112538Sesaxe if fileNamesFile != "" : 12122538Sesaxe changedFiles, newFiles, deletedFiles = \ 12132538Sesaxe flistCatalog(baseRoot, ptchRoot, fileNamesFile) 12142538Sesaxe else : 12152538Sesaxe changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot) 12162538Sesaxe 12172538Sesaxe if len(newFiles) > 0 : 12182538Sesaxe newOrDeleted = True 12192538Sesaxe info("\nNew objects found: ") 12202538Sesaxe 12212538Sesaxe for fn in newFiles : 12222538Sesaxe info(fnFormat(fn)) 12232538Sesaxe 12242538Sesaxe if len(deletedFiles) > 0 : 12252538Sesaxe newOrDeleted = True 12262538Sesaxe info("\nObjects removed: ") 12272538Sesaxe 12282538Sesaxe for fn in deletedFiles : 12292538Sesaxe info(fnFormat(fn)) 12302538Sesaxe 12312538Sesaxe if newOrDeleted : 12322538Sesaxe info("\nChanged objects: "); 12332538Sesaxe 12342538Sesaxe 12352538Sesaxe # Here's where all the heavy lifting happens 12362538Sesaxe # Perform a comparison on each object appearing in 12372538Sesaxe # both proto areas. compareOneFile will examine the 12382538Sesaxe # file types of each object, and will vector off to 12392538Sesaxe # the appropriate comparison routine, where the compare 12402538Sesaxe # will happen, and any differences will be reported / logged 12412538Sesaxe for fn in changedFiles : 12422538Sesaxe base = baseRoot + fn 12432538Sesaxe ptch = ptchRoot + fn 1244*11300Srichlowe@richlowe.net 12452538Sesaxe compareOneFile(base, ptch, False) 12462538Sesaxe 12472538Sesaxe # We're done, cleanup. 12482538Sesaxe cleanup(0) 12492538Sesaxe 12502538Sesaxeif __name__ == '__main__' : 12512538Sesaxe try: 12522538Sesaxe main() 12532538Sesaxe except KeyboardInterrupt : 12542538Sesaxe cleanup(1); 12552538Sesaxe 12562538Sesaxe 1257