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