196ccc8cbSesaxe#!/usr/sfw/bin/python 296ccc8cbSesaxe# 396ccc8cbSesaxe# CDDL HEADER START 496ccc8cbSesaxe# 596ccc8cbSesaxe# The contents of this file are subject to the terms of the 696ccc8cbSesaxe# Common Development and Distribution License (the "License"). 796ccc8cbSesaxe# You may not use this file except in compliance with the License. 896ccc8cbSesaxe# 996ccc8cbSesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 1096ccc8cbSesaxe# or http://www.opensolaris.org/os/licensing. 1196ccc8cbSesaxe# See the License for the specific language governing permissions 1296ccc8cbSesaxe# and limitations under the License. 1396ccc8cbSesaxe# 1496ccc8cbSesaxe# When distributing Covered Code, include this CDDL HEADER in each 1596ccc8cbSesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1696ccc8cbSesaxe# If applicable, add the following below this CDDL HEADER, with the 1796ccc8cbSesaxe# fields enclosed by brackets "[]" replaced with your own identifying 1896ccc8cbSesaxe# information: Portions Copyright [yyyy] [name of copyright owner] 1996ccc8cbSesaxe# 2096ccc8cbSesaxe# CDDL HEADER END 2196ccc8cbSesaxe# 22*619b4598Srotondo# Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2396ccc8cbSesaxe# Use is subject to license terms. 2496ccc8cbSesaxe# 2596ccc8cbSesaxe#ident "%Z%%M% %I% %E% SMI" 2696ccc8cbSesaxe 2796ccc8cbSesaxe# 2896ccc8cbSesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects 2996ccc8cbSesaxe# have changed as a result of a given source change. Developers backporting 3096ccc8cbSesaxe# new features, RFEs and bug fixes need to be able to identify the set of 3196ccc8cbSesaxe# patch deliverables necessary for feature/fix realization on a patched system. 3296ccc8cbSesaxe# 3396ccc8cbSesaxe# The tool works by comparing objects in two trees/proto areas (one build with, 3496ccc8cbSesaxe# and without the source changes. 3596ccc8cbSesaxe# 3696ccc8cbSesaxe# Using wsdiff(1) is fairly simple: 3796ccc8cbSesaxe# - Bringover to a fresh workspace 3896ccc8cbSesaxe# - Perform a full non-debug build (clobber if workspace isn't fresh) 3996ccc8cbSesaxe# - Move the proto area aside, call it proto.old, or something. 4096ccc8cbSesaxe# - Integrate your changes to the workspace 4196ccc8cbSesaxe# - Perform another full non-debug clobber build. 4296ccc8cbSesaxe# - Use wsdiff(1) to see what changed: 4396ccc8cbSesaxe# $ wsdiff proto.old proto 4496ccc8cbSesaxe# 4596ccc8cbSesaxe# By default, wsdiff will print the list of changed objects / deliverables to 4696ccc8cbSesaxe# stdout. If a results file is specified via -r, the list of differing objects, 4796ccc8cbSesaxe# and details about why wsdiff(1) thinks they are different will be logged to 4896ccc8cbSesaxe# the results file. 4996ccc8cbSesaxe# 5096ccc8cbSesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use 5196ccc8cbSesaxe# wsdiff(1) to report on what objects changed since the last build. 5296ccc8cbSesaxe# 5396ccc8cbSesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber, 5496ccc8cbSesaxe# non-debug build. 5596ccc8cbSesaxe# 5696ccc8cbSesaxe# Think about the results. Was something flagged that you don't expect? Go look 5796ccc8cbSesaxe# at the results file to see details about the differences. 5896ccc8cbSesaxe# 5996ccc8cbSesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1) 6096ccc8cbSesaxe# report with more verbosity. 6196ccc8cbSesaxe# 6296ccc8cbSesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 6396ccc8cbSesaxe# 6496ccc8cbSesaxe# Where "old" is the path to the proto area build without the changes, and 6596ccc8cbSesaxe# "new" is the path to the proto area built with the changes. The following 6696ccc8cbSesaxe# options are supported: 6796ccc8cbSesaxe# 6896ccc8cbSesaxe# -v Do not truncate observed diffs in results 6996ccc8cbSesaxe# -V Log *all* ELF sect diffs vs. logging the first diff found 7096ccc8cbSesaxe# -t Use onbld tools in $SRC/tools 7196ccc8cbSesaxe# -r Log results and observed differences 7296ccc8cbSesaxe# -i Tell wsdiff which objects to compare via an input file list 7396ccc8cbSesaxe 7496ccc8cbSesaxeimport datetime, fnmatch, getopt, profile, os, popen2, commands 7596ccc8cbSesaxeimport re, select, string, struct, sys, tempfile, time 7696ccc8cbSesaxefrom stat import * 7796ccc8cbSesaxe 7896ccc8cbSesaxe# Human readable diffs truncated by default if longer than this 7996ccc8cbSesaxe# Specifying -v on the command line will override 8096ccc8cbSesaxediffs_sz_thresh = 4096 8196ccc8cbSesaxe 8296ccc8cbSesaxe# Default search path for wsdiff 8396ccc8cbSesaxewsdiff_path = [ "/usr/bin", 8496ccc8cbSesaxe "/usr/ccs/bin", 8596ccc8cbSesaxe "/lib/svc/bin", 8696ccc8cbSesaxe "/opt/onbld/bin" ] 8796ccc8cbSesaxe 8896ccc8cbSesaxe# These are objects that wsdiff will notice look different, but will not report. 8996ccc8cbSesaxe# Existence of an exceptions list, and adding things here is *dangerous*, 9096ccc8cbSesaxe# and therefore the *only* reasons why anything would be listed here is because 9196ccc8cbSesaxe# the objects do not build deterministically, yet we *cannot* fix this. 9296ccc8cbSesaxe# 9396ccc8cbSesaxe# These perl libraries use __DATE__ and therefore always look different. 9496ccc8cbSesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because 9596ccc8cbSesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify. 9696ccc8cbSesaxe# 9796ccc8cbSesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1", 9896ccc8cbSesaxe "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 9996ccc8cbSesaxe "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1", 10096ccc8cbSesaxe "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1" 10196ccc8cbSesaxe ] 10296ccc8cbSesaxe 10396ccc8cbSesaxe##### 10496ccc8cbSesaxe# Logging routines 10596ccc8cbSesaxe# 10696ccc8cbSesaxe 10796ccc8cbSesaxe# Informational message to be printed to the screen, and the log file 10896ccc8cbSesaxedef info(msg) : 10996ccc8cbSesaxe 11096ccc8cbSesaxe print >> sys.stdout, msg 11196ccc8cbSesaxe if logging : 11296ccc8cbSesaxe print >> log, msg 11396ccc8cbSesaxe sys.stdout.flush() 11496ccc8cbSesaxe 11596ccc8cbSesaxe# Error message to be printed to the screen, and the log file 11696ccc8cbSesaxedef error(msg) : 11796ccc8cbSesaxe 11896ccc8cbSesaxe print >> sys.stderr, "ERROR:", msg 11996ccc8cbSesaxe sys.stderr.flush() 12096ccc8cbSesaxe if logging : 12196ccc8cbSesaxe print >> log, "ERROR:", msg 12296ccc8cbSesaxe log.flush() 12396ccc8cbSesaxe 12496ccc8cbSesaxe# Informational message to be printed only to the log, if there is one. 12596ccc8cbSesaxedef v_info(msg) : 12696ccc8cbSesaxe 12796ccc8cbSesaxe if logging : 12896ccc8cbSesaxe print >> log, msg 12996ccc8cbSesaxe log.flush() 13096ccc8cbSesaxe 13196ccc8cbSesaxe# 13296ccc8cbSesaxe# Flag a detected file difference 13396ccc8cbSesaxe# Display the fileName to stdout, and log the difference 13496ccc8cbSesaxe# 13596ccc8cbSesaxedef difference(f, dtype, diffs) : 13696ccc8cbSesaxe 13796ccc8cbSesaxe if f in wsdiff_exceptions : 13896ccc8cbSesaxe return 13996ccc8cbSesaxe 14096ccc8cbSesaxe print >> sys.stdout, f 14196ccc8cbSesaxe sys.stdout.flush() 14296ccc8cbSesaxe 14396ccc8cbSesaxe log_difference(f, dtype, diffs) 14496ccc8cbSesaxe 14596ccc8cbSesaxe# 14696ccc8cbSesaxe# Do the actual logging of the difference to the results file 14796ccc8cbSesaxe# 14896ccc8cbSesaxedef log_difference(f, dtype, diffs) : 14996ccc8cbSesaxe if logging : 15096ccc8cbSesaxe print >> log, f 15196ccc8cbSesaxe print >> log, "NOTE:", dtype, "difference detected." 15296ccc8cbSesaxe 15396ccc8cbSesaxe difflen = len(diffs) 15496ccc8cbSesaxe if difflen > 0 : 15596ccc8cbSesaxe print >> log 15696ccc8cbSesaxe 15796ccc8cbSesaxe if not vdiffs and difflen > diffs_sz_thresh : 15896ccc8cbSesaxe print >> log, diffs[:diffs_sz_thresh] 15996ccc8cbSesaxe print >> log, \ 16096ccc8cbSesaxe "... truncated due to length: " \ 16196ccc8cbSesaxe "use -v to override ..." 16296ccc8cbSesaxe else : 16396ccc8cbSesaxe print >> log, diffs 16496ccc8cbSesaxe print >> log, "\n" 16596ccc8cbSesaxe log.flush() 16696ccc8cbSesaxe 16796ccc8cbSesaxe 16896ccc8cbSesaxe##### 16996ccc8cbSesaxe# diff generating routines 17096ccc8cbSesaxe# 17196ccc8cbSesaxe 17296ccc8cbSesaxe# 17396ccc8cbSesaxe# Return human readable diffs from two temporary files 17496ccc8cbSesaxe# 17596ccc8cbSesaxedef diffFileData(tmpf1, tmpf2) : 17696ccc8cbSesaxe 17796ccc8cbSesaxe # Filter the data through od(1) if the data is detected 17896ccc8cbSesaxe # as being binary 17996ccc8cbSesaxe if isBinary(tmpf1) or isBinary(tmpf2) : 18096ccc8cbSesaxe tmp_od1 = tmpf1 + ".od" 18196ccc8cbSesaxe tmp_od2 = tmpf2 + ".od" 18296ccc8cbSesaxe 18396ccc8cbSesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1 18496ccc8cbSesaxe os.system(cmd) 18596ccc8cbSesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2 18696ccc8cbSesaxe os.system(cmd) 18796ccc8cbSesaxe 18896ccc8cbSesaxe tmpf1 = tmp_od1 18996ccc8cbSesaxe tmpf2 = tmp_od2 19096ccc8cbSesaxe 19196ccc8cbSesaxe data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2) 19296ccc8cbSesaxe 19396ccc8cbSesaxe return data 19496ccc8cbSesaxe 19596ccc8cbSesaxe# 19696ccc8cbSesaxe# Return human readable diffs betweeen two datasets 19796ccc8cbSesaxe# 19896ccc8cbSesaxedef diffData(d1, d2) : 19996ccc8cbSesaxe 20096ccc8cbSesaxe global tmpFile1 20196ccc8cbSesaxe global tmpFile2 20296ccc8cbSesaxe 20396ccc8cbSesaxe try: 20496ccc8cbSesaxe fd1 = open(tmpFile1, "w") 20596ccc8cbSesaxe except: 20696ccc8cbSesaxe error("failed to open: " + tmpFile1) 20796ccc8cbSesaxe cleanup(1) 20896ccc8cbSesaxe try: 20996ccc8cbSesaxe fd2 = open(tmpFile2, "w") 21096ccc8cbSesaxe except: 21196ccc8cbSesaxe error("failed to open: " + tmpFile2) 21296ccc8cbSesaxe cleanup(1) 21396ccc8cbSesaxe 21496ccc8cbSesaxe fd1.write(d1) 21596ccc8cbSesaxe fd2.write(d2) 21696ccc8cbSesaxe fd1.close() 21796ccc8cbSesaxe fd2.close() 21896ccc8cbSesaxe 21996ccc8cbSesaxe return diffFileData(tmpFile1, tmpFile2) 22096ccc8cbSesaxe 22196ccc8cbSesaxe##### 22296ccc8cbSesaxe# Misc utility functions 22396ccc8cbSesaxe# 22496ccc8cbSesaxe 22596ccc8cbSesaxe# Prune off the leading prefix from string s 22696ccc8cbSesaxedef str_prefix_trunc(s, prefix) : 22796ccc8cbSesaxe snipLen = len(prefix) 22896ccc8cbSesaxe return s[snipLen:] 22996ccc8cbSesaxe 23096ccc8cbSesaxe# 23196ccc8cbSesaxe# Prune off leading proto path goo (if there is one) to yield 23296ccc8cbSesaxe# the deliverable's eventual path relative to root 23396ccc8cbSesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat 23496ccc8cbSesaxe# 23596ccc8cbSesaxedef fnFormat(fn) : 23696ccc8cbSesaxe root_arch_str = "root_" + arch 23796ccc8cbSesaxe 23896ccc8cbSesaxe pos = fn.find(root_arch_str) 23996ccc8cbSesaxe if pos == -1 : 24096ccc8cbSesaxe return fn 24196ccc8cbSesaxe 24296ccc8cbSesaxe pos = fn.find("/", pos) 24396ccc8cbSesaxe if pos == -1 : 24496ccc8cbSesaxe return fn 24596ccc8cbSesaxe 24696ccc8cbSesaxe return fn[pos + 1:] 24796ccc8cbSesaxe 24896ccc8cbSesaxe##### 24996ccc8cbSesaxe# Usage / argument processing 25096ccc8cbSesaxe# 25196ccc8cbSesaxe 25296ccc8cbSesaxe# 25396ccc8cbSesaxe# Display usage message 25496ccc8cbSesaxe# 25596ccc8cbSesaxedef usage() : 25696ccc8cbSesaxe sys.stdout.flush() 25796ccc8cbSesaxe print >> sys.stderr, """Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 25896ccc8cbSesaxe -v Do not truncate observed diffs in results 25996ccc8cbSesaxe -V Log *all* ELF sect diffs vs. logging the first diff found 26096ccc8cbSesaxe -t Use onbld tools in $SRC/tools 26196ccc8cbSesaxe -r Log results and observed differences 26296ccc8cbSesaxe -i Tell wsdiff which objects to compare via an input file list""" 26396ccc8cbSesaxe sys.exit(1) 26496ccc8cbSesaxe 26596ccc8cbSesaxe# 26696ccc8cbSesaxe# Process command line options 26796ccc8cbSesaxe# 26896ccc8cbSesaxedef args() : 26996ccc8cbSesaxe 27096ccc8cbSesaxe global logging 27196ccc8cbSesaxe global vdiffs 27296ccc8cbSesaxe global reportAllSects 27396ccc8cbSesaxe 27496ccc8cbSesaxe validOpts = 'i:r:vVt?' 27596ccc8cbSesaxe 27696ccc8cbSesaxe baseRoot = "" 27796ccc8cbSesaxe ptchRoot = "" 27896ccc8cbSesaxe fileNamesFile = "" 27996ccc8cbSesaxe results = "" 28096ccc8cbSesaxe localTools = False 28196ccc8cbSesaxe 28296ccc8cbSesaxe # getopt.getopt() returns: 28396ccc8cbSesaxe # an option/value tuple 28496ccc8cbSesaxe # a list of remaining non-option arguments 28596ccc8cbSesaxe # 28696ccc8cbSesaxe # A correct wsdiff invocation will have exactly two non option 28796ccc8cbSesaxe # arguments, the paths to the base (old), ptch (new) proto areas 28896ccc8cbSesaxe try: 28996ccc8cbSesaxe optlist, args = getopt.getopt(sys.argv[1:], validOpts) 29096ccc8cbSesaxe except getopt.error, val: 29196ccc8cbSesaxe usage() 29296ccc8cbSesaxe 29396ccc8cbSesaxe if len(args) != 2 : 29496ccc8cbSesaxe usage(); 29596ccc8cbSesaxe 29696ccc8cbSesaxe for opt,val in optlist : 29796ccc8cbSesaxe if opt == '-i' : 29896ccc8cbSesaxe fileNamesFile = val 29996ccc8cbSesaxe elif opt == '-r' : 30096ccc8cbSesaxe results = val 30196ccc8cbSesaxe logging = True 30296ccc8cbSesaxe elif opt == '-v' : 30396ccc8cbSesaxe vdiffs = True 30496ccc8cbSesaxe elif opt == '-V' : 30596ccc8cbSesaxe reportAllSects = True 30696ccc8cbSesaxe elif opt == '-t': 30796ccc8cbSesaxe localTools = True 30896ccc8cbSesaxe else: 30996ccc8cbSesaxe usage() 31096ccc8cbSesaxe 31196ccc8cbSesaxe baseRoot = args[0] 31296ccc8cbSesaxe ptchRoot = args[1] 31396ccc8cbSesaxe 31496ccc8cbSesaxe if len(baseRoot) == 0 or len(ptchRoot) == 0 : 31596ccc8cbSesaxe usage() 31696ccc8cbSesaxe 31796ccc8cbSesaxe if logging and len(results) == 0 : 31896ccc8cbSesaxe usage() 31996ccc8cbSesaxe 32096ccc8cbSesaxe if vdiffs and not logging : 32196ccc8cbSesaxe error("The -v option requires a results file (-r)") 32296ccc8cbSesaxe sys.exit(1) 32396ccc8cbSesaxe 32496ccc8cbSesaxe if reportAllSects and not logging : 32596ccc8cbSesaxe error("The -V option requires a results file (-r)") 32696ccc8cbSesaxe sys.exit(1) 32796ccc8cbSesaxe 32896ccc8cbSesaxe # alphabetical order 32996ccc8cbSesaxe return baseRoot, fileNamesFile, localTools, ptchRoot, results 33096ccc8cbSesaxe 33196ccc8cbSesaxe##### 33296ccc8cbSesaxe# File identification 33396ccc8cbSesaxe# 33496ccc8cbSesaxe 33596ccc8cbSesaxe# 33696ccc8cbSesaxe# Identify the file type. 33796ccc8cbSesaxe# If it's not ELF, use the file extension to identify 33896ccc8cbSesaxe# certain file types that require special handling to 33996ccc8cbSesaxe# compare. Otherwise just return a basic "ASCII" type. 34096ccc8cbSesaxe# 34196ccc8cbSesaxedef getTheFileType(f) : 34296ccc8cbSesaxe 34396ccc8cbSesaxe extensions = { 'a' : 'ELF Object Archive', 34496ccc8cbSesaxe 'jar' : 'Java Archive', 34596ccc8cbSesaxe 'html' : 'HTML', 34696ccc8cbSesaxe 'ln' : 'Lint Library', 347*619b4598Srotondo 'esa' : 'Elfsign Activation', 34896ccc8cbSesaxe 'db' : 'Sqlite Database' } 34996ccc8cbSesaxe 350*619b4598Srotondo try: 35196ccc8cbSesaxe if os.stat(f)[ST_SIZE] == 0 : 35296ccc8cbSesaxe return 'ASCII' 353*619b4598Srotondo except: 354*619b4598Srotondo error("failed to stat " + f) 355*619b4598Srotondo return 'Error' 35696ccc8cbSesaxe 35796ccc8cbSesaxe if isELF(f) == 1 : 35896ccc8cbSesaxe return 'ELF' 35996ccc8cbSesaxe 36096ccc8cbSesaxe fnamelist = f.split('.') 36196ccc8cbSesaxe if len(fnamelist) > 1 : # Test the file extension 36296ccc8cbSesaxe extension = fnamelist[-1] 36396ccc8cbSesaxe if extension in extensions.keys(): 36496ccc8cbSesaxe return extensions[extension] 36596ccc8cbSesaxe 36696ccc8cbSesaxe return 'ASCII' 36796ccc8cbSesaxe 36896ccc8cbSesaxe# 36996ccc8cbSesaxe# Return non-zero if "f" is an ELF file 37096ccc8cbSesaxe# 37196ccc8cbSesaxeelfmagic = '\177ELF' 37296ccc8cbSesaxedef isELF(f) : 37396ccc8cbSesaxe try: 37496ccc8cbSesaxe fd = open(f) 37596ccc8cbSesaxe except: 37696ccc8cbSesaxe error("failed to open: " + f) 37796ccc8cbSesaxe return 0 37896ccc8cbSesaxe magic = fd.read(len(elfmagic)) 37996ccc8cbSesaxe fd.close() 38096ccc8cbSesaxe 38196ccc8cbSesaxe if magic == elfmagic : 38296ccc8cbSesaxe return 1 38396ccc8cbSesaxe return 0 38496ccc8cbSesaxe 38596ccc8cbSesaxe# 38696ccc8cbSesaxe# Return non-zero is "f" is binary. 38796ccc8cbSesaxe# Consider the file to be binary if it contains any null characters 38896ccc8cbSesaxe# 38996ccc8cbSesaxedef isBinary(f) : 39096ccc8cbSesaxe try: 39196ccc8cbSesaxe fd = open(f) 39296ccc8cbSesaxe except: 39396ccc8cbSesaxe error("failed to open: " + f) 39496ccc8cbSesaxe return 0 39596ccc8cbSesaxe s = fd.read() 39696ccc8cbSesaxe fd.close() 39796ccc8cbSesaxe 39896ccc8cbSesaxe if s.find('\0') == -1 : 39996ccc8cbSesaxe return 0 40096ccc8cbSesaxe else : 40196ccc8cbSesaxe return 1 40296ccc8cbSesaxe 40396ccc8cbSesaxe##### 40496ccc8cbSesaxe# Directory traversal and file finding 40596ccc8cbSesaxe# 40696ccc8cbSesaxe 40796ccc8cbSesaxe# 40896ccc8cbSesaxe# Return a sorted list of files found under the specified directory 40996ccc8cbSesaxe# 41096ccc8cbSesaxedef findFiles(d) : 41196ccc8cbSesaxe for path, subdirs, files in os.walk(d) : 41296ccc8cbSesaxe files.sort() 41396ccc8cbSesaxe for name in files : 41496ccc8cbSesaxe yield os.path.join(path, name) 41596ccc8cbSesaxe 41696ccc8cbSesaxe# 41796ccc8cbSesaxe# Examine all files in base, ptch 41896ccc8cbSesaxe# 41996ccc8cbSesaxe# Return a list of files appearing in both proto areas, 42096ccc8cbSesaxe# a list of new files (files found only in ptch) and 42196ccc8cbSesaxe# a list of deleted files (files found only in base) 42296ccc8cbSesaxe# 42396ccc8cbSesaxedef protoCatalog(base, ptch) : 42496ccc8cbSesaxe compFiles = [] # List of files in both proto areas 42596ccc8cbSesaxe ptchList = [] # List of file in patch proto area 42696ccc8cbSesaxe 42796ccc8cbSesaxe newFiles = [] # New files detected 42896ccc8cbSesaxe deletedFiles = [] # Deleted files 42996ccc8cbSesaxe 43096ccc8cbSesaxe baseFilesList = list(findFiles(base)) 43196ccc8cbSesaxe baseStringLength = len(base) 43296ccc8cbSesaxe 43396ccc8cbSesaxe ptchFilesList = list(findFiles(ptch)) 43496ccc8cbSesaxe ptchStringLength = len(ptch) 43596ccc8cbSesaxe 43696ccc8cbSesaxe # Inventory files in the base proto area 43796ccc8cbSesaxe for fn in baseFilesList : 43896ccc8cbSesaxe if os.path.islink(fn) : 43996ccc8cbSesaxe continue 44096ccc8cbSesaxe 44196ccc8cbSesaxe fileName = fn[baseStringLength:] 44296ccc8cbSesaxe compFiles.append(fileName) 44396ccc8cbSesaxe 44496ccc8cbSesaxe # Inventory files in the patch proto area 44596ccc8cbSesaxe for fn in ptchFilesList : 44696ccc8cbSesaxe if os.path.islink(fn) : 44796ccc8cbSesaxe continue 44896ccc8cbSesaxe 44996ccc8cbSesaxe fileName = fn[ptchStringLength:] 45096ccc8cbSesaxe ptchList.append(fileName) 45196ccc8cbSesaxe 45296ccc8cbSesaxe # Deleted files appear in the base area, but not the patch area 45396ccc8cbSesaxe for fileName in compFiles : 45496ccc8cbSesaxe if not fileName in ptchList : 45596ccc8cbSesaxe deletedFiles.append(fileName) 45696ccc8cbSesaxe 45796ccc8cbSesaxe # Eliminate "deleted" files from the list of objects appearing 45896ccc8cbSesaxe # in both the base and patch proto areas 45996ccc8cbSesaxe for fileName in deletedFiles : 46096ccc8cbSesaxe try: 46196ccc8cbSesaxe compFiles.remove(fileName) 46296ccc8cbSesaxe except: 46396ccc8cbSesaxe error("filelist.remove() failed") 46496ccc8cbSesaxe 46596ccc8cbSesaxe # New files appear in the patch area, but not the base 46696ccc8cbSesaxe for fileName in ptchList : 46796ccc8cbSesaxe if not fileName in compFiles : 46896ccc8cbSesaxe newFiles.append(fileName) 46996ccc8cbSesaxe 47096ccc8cbSesaxe return compFiles, newFiles, deletedFiles 47196ccc8cbSesaxe 47296ccc8cbSesaxe# 47396ccc8cbSesaxe# Examine the files listed in the input file list 47496ccc8cbSesaxe# 47596ccc8cbSesaxe# Return a list of files appearing in both proto areas, 47696ccc8cbSesaxe# a list of new files (files found only in ptch) and 47796ccc8cbSesaxe# a list of deleted files (files found only in base) 47896ccc8cbSesaxe# 47996ccc8cbSesaxedef flistCatalog(base, ptch, flist) : 48096ccc8cbSesaxe compFiles = [] # List of files in both proto areas 48196ccc8cbSesaxe newFiles = [] # New files detected 48296ccc8cbSesaxe deletedFiles = [] # Deleted files 48396ccc8cbSesaxe 48496ccc8cbSesaxe try: 48596ccc8cbSesaxe fd = open(flist, "r") 48696ccc8cbSesaxe except: 48796ccc8cbSesaxe error("could not open: " + flist) 48896ccc8cbSesaxe cleanup(1) 48996ccc8cbSesaxe 49096ccc8cbSesaxe files = [] 49196ccc8cbSesaxe files = fd.readlines() 49296ccc8cbSesaxe 49396ccc8cbSesaxe for f in files : 49496ccc8cbSesaxe ptch_present = True 49596ccc8cbSesaxe base_present = True 49696ccc8cbSesaxe 49796ccc8cbSesaxe if f == '\n' : 49896ccc8cbSesaxe continue 49996ccc8cbSesaxe 50096ccc8cbSesaxe # the fileNames have a trailing '\n' 50196ccc8cbSesaxe f = f.rstrip() 50296ccc8cbSesaxe 50396ccc8cbSesaxe # The objects in the file list have paths relative 50496ccc8cbSesaxe # to $ROOT or to the base/ptch directory specified on 50596ccc8cbSesaxe # the command line. 50696ccc8cbSesaxe # If it's relative to $ROOT, we'll need to add back the 50796ccc8cbSesaxe # root_`uname -p` goo we stripped off in fnFormat() 50896ccc8cbSesaxe if os.path.exists(base + f) : 50996ccc8cbSesaxe fn = f; 51096ccc8cbSesaxe elif os.path.exists(base + "root_" + arch + "/" + f) : 51196ccc8cbSesaxe fn = "root_" + arch + "/" + f 51296ccc8cbSesaxe else : 51396ccc8cbSesaxe base_present = False 51496ccc8cbSesaxe 51596ccc8cbSesaxe if base_present : 51696ccc8cbSesaxe if not os.path.exists(ptch + fn) : 51796ccc8cbSesaxe ptch_present = False 51896ccc8cbSesaxe else : 51996ccc8cbSesaxe if os.path.exists(ptch + f) : 52096ccc8cbSesaxe fn = f 52196ccc8cbSesaxe elif os.path.exists(ptch + "root_" + arch + "/" + f) : 52296ccc8cbSesaxe fn = "root_" + arch + "/" + f 52396ccc8cbSesaxe else : 52496ccc8cbSesaxe ptch_present = False 52596ccc8cbSesaxe 52696ccc8cbSesaxe if os.path.islink(base + fn) : # ignore links 52796ccc8cbSesaxe base_present = False 52896ccc8cbSesaxe if os.path.islink(ptch + fn) : 52996ccc8cbSesaxe ptch_present = False 53096ccc8cbSesaxe 53196ccc8cbSesaxe if base_present and ptch_present : 53296ccc8cbSesaxe compFiles.append(fn) 53396ccc8cbSesaxe elif base_present : 53496ccc8cbSesaxe deletedFiles.append(fn) 53596ccc8cbSesaxe elif ptch_present : 53696ccc8cbSesaxe newFiles.append(fn) 53796ccc8cbSesaxe else : 53896ccc8cbSesaxe if os.path.islink(base + fn) and os.path.islink(ptch + fn) : 53996ccc8cbSesaxe continue 54096ccc8cbSesaxe error(f + " in file list, but not in either tree. Skipping...") 54196ccc8cbSesaxe 54296ccc8cbSesaxe return compFiles, newFiles, deletedFiles 54396ccc8cbSesaxe 54496ccc8cbSesaxe 54596ccc8cbSesaxe# 54696ccc8cbSesaxe# Build a fully qualified path to an external tool/utility. 54796ccc8cbSesaxe# Consider the default system locations. For onbld tools, if 54896ccc8cbSesaxe# the -t option was specified, we'll try to use built tools in $SRC tools, 54996ccc8cbSesaxe# and otherwise, we'll fall back on /opt/onbld/ 55096ccc8cbSesaxe# 55196ccc8cbSesaxedef find_tool(tool) : 55296ccc8cbSesaxe 55396ccc8cbSesaxe # First, check what was passed 55496ccc8cbSesaxe if os.path.exists(tool) : 55596ccc8cbSesaxe return tool 55696ccc8cbSesaxe 55796ccc8cbSesaxe # Next try in wsdiff path 55896ccc8cbSesaxe for pdir in wsdiff_path : 55996ccc8cbSesaxe location = pdir + "/" + tool 56096ccc8cbSesaxe if os.path.exists(location) : 56196ccc8cbSesaxe return location + " " 56296ccc8cbSesaxe 56396ccc8cbSesaxe location = pdir + "/" + arch + "/" + tool 56496ccc8cbSesaxe if os.path.exists(location) : 56596ccc8cbSesaxe return location + " " 56696ccc8cbSesaxe 56796ccc8cbSesaxe error("Could not find path to: " + tool); 56896ccc8cbSesaxe sys.exit(1); 56996ccc8cbSesaxe 57096ccc8cbSesaxe 57196ccc8cbSesaxe##### 57296ccc8cbSesaxe# ELF file comparison helper routines 57396ccc8cbSesaxe# 57496ccc8cbSesaxe 57596ccc8cbSesaxe# 57696ccc8cbSesaxe# Return a dictionary of ELF section types keyed by section name 57796ccc8cbSesaxe# 57896ccc8cbSesaxedef get_elfheader(f) : 57996ccc8cbSesaxe 58096ccc8cbSesaxe header = {} 58196ccc8cbSesaxe 58296ccc8cbSesaxe hstring = commands.getoutput(elfdump_cmd + " -c " + f) 58396ccc8cbSesaxe 58496ccc8cbSesaxe if len(hstring) == 0 : 58596ccc8cbSesaxe error("Failed to dump ELF header for " + f) 58696ccc8cbSesaxe return 58796ccc8cbSesaxe 58896ccc8cbSesaxe # elfdump(1) dumps the section headers with the section name 58996ccc8cbSesaxe # following "sh_name:", and the section type following "sh_type:" 59096ccc8cbSesaxe sections = hstring.split("Section Header") 59196ccc8cbSesaxe for sect in sections : 59296ccc8cbSesaxe datap = sect.find("sh_name:"); 59396ccc8cbSesaxe if datap == -1 : 59496ccc8cbSesaxe continue 59596ccc8cbSesaxe section = sect[datap:].split()[1] 59696ccc8cbSesaxe datap = sect.find("sh_type:"); 59796ccc8cbSesaxe if datap == -1 : 59896ccc8cbSesaxe error("Could not get type for sect: " + section + \ 59996ccc8cbSesaxe " in " + f) 60096ccc8cbSesaxe sh_type = sect[datap:].split()[2] 60196ccc8cbSesaxe header[section] = sh_type 60296ccc8cbSesaxe 60396ccc8cbSesaxe return header 60496ccc8cbSesaxe 60596ccc8cbSesaxe# 60696ccc8cbSesaxe# Extract data in the specified ELF section from the given file 60796ccc8cbSesaxe# 60896ccc8cbSesaxedef extract_elf_section(f, section) : 60996ccc8cbSesaxe 61096ccc8cbSesaxe data = commands.getoutput(dump_cmd + " -sn " + section + " " + f) 61196ccc8cbSesaxe 61296ccc8cbSesaxe if len(data) == 0 : 61396ccc8cbSesaxe error(cmd + " yielded no data") 61496ccc8cbSesaxe return 61596ccc8cbSesaxe 61696ccc8cbSesaxe # dump(1) displays the file name to start... 61796ccc8cbSesaxe # get past it to the data itself 61896ccc8cbSesaxe dbegin = data.find(":") + 1 61996ccc8cbSesaxe data = data[dbegin:]; 62096ccc8cbSesaxe 62196ccc8cbSesaxe return (data) 62296ccc8cbSesaxe 62396ccc8cbSesaxe# 62496ccc8cbSesaxe# Return a (hopefully meaningful) human readable set of diffs 62596ccc8cbSesaxe# for the specified ELF section between f1 and f2 62696ccc8cbSesaxe# 62796ccc8cbSesaxe# Depending on the section, various means for dumping and diffing 62896ccc8cbSesaxe# the data may be employed. 62996ccc8cbSesaxe# 63096ccc8cbSesaxetext_sections = [ '.text', '.init', '.fini' ] 63196ccc8cbSesaxedef diff_elf_section(f1, f2, section, sh_type) : 63296ccc8cbSesaxe 63396ccc8cbSesaxe if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA 63496ccc8cbSesaxe cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1 63596ccc8cbSesaxe cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2 63696ccc8cbSesaxe elif (section == ".group") : 63796ccc8cbSesaxe cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1 63896ccc8cbSesaxe cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2 63996ccc8cbSesaxe elif (section == ".hash") : 64096ccc8cbSesaxe cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1 64196ccc8cbSesaxe cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2 64296ccc8cbSesaxe elif (section == ".dynamic") : 64396ccc8cbSesaxe cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1 64496ccc8cbSesaxe cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2 64596ccc8cbSesaxe elif (section == ".got") : 64696ccc8cbSesaxe cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1 64796ccc8cbSesaxe cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2 64896ccc8cbSesaxe elif (section == ".SUNW_cap") : 64996ccc8cbSesaxe cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1 65096ccc8cbSesaxe cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2 65196ccc8cbSesaxe elif (section == ".interp") : 65296ccc8cbSesaxe cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1 65396ccc8cbSesaxe cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2 65496ccc8cbSesaxe elif (section == ".symtab" or section == ".dynsym") : 65596ccc8cbSesaxe cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1 65696ccc8cbSesaxe cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2 65796ccc8cbSesaxe elif (section in text_sections) : 65896ccc8cbSesaxe # dis sometimes complains when it hits something it doesn't 65996ccc8cbSesaxe # know how to disassemble. Just ignore it, as the output 66096ccc8cbSesaxe # being generated here is human readable, and we've already 66196ccc8cbSesaxe # correctly flagged the difference. 66296ccc8cbSesaxe cmd1 = dis_cmd + " -t " + section + " " + f1 + \ 66396ccc8cbSesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile1 66496ccc8cbSesaxe cmd2 = dis_cmd + " -t " + section + " " + f2 + \ 66596ccc8cbSesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile2 66696ccc8cbSesaxe else : 66796ccc8cbSesaxe cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \ 66896ccc8cbSesaxe section + " " + f1 66996ccc8cbSesaxe cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \ 67096ccc8cbSesaxe section + " " + f2 67196ccc8cbSesaxe 67296ccc8cbSesaxe os.system(cmd1) 67396ccc8cbSesaxe os.system(cmd2) 67496ccc8cbSesaxe 67596ccc8cbSesaxe data = diffFileData(tmpFile1, tmpFile2) 67696ccc8cbSesaxe 67796ccc8cbSesaxe return (data) 67896ccc8cbSesaxe 67996ccc8cbSesaxe# 68096ccc8cbSesaxe# compare the relevant sections of two ELF binaries 68196ccc8cbSesaxe# and report any differences 68296ccc8cbSesaxe# 68396ccc8cbSesaxe# Returns: 1 if any differenes found 68496ccc8cbSesaxe# 0 if no differences found 68596ccc8cbSesaxe# -1 on error 68696ccc8cbSesaxe# 68796ccc8cbSesaxe 68896ccc8cbSesaxe# Sections deliberately not considered when comparing two ELF 68996ccc8cbSesaxe# binaries. Differences observed in these sections are not considered 69096ccc8cbSesaxe# significant where patch deliverable identification is concerned. 69196ccc8cbSesaxesections_to_skip = [ ".SUNW_signature", 69296ccc8cbSesaxe ".comment", 69396ccc8cbSesaxe ".SUNW_ctf", 69496ccc8cbSesaxe ".debug", 69596ccc8cbSesaxe ".plt", 69696ccc8cbSesaxe ".rela.bss", 69796ccc8cbSesaxe ".rela.plt", 69896ccc8cbSesaxe ".line", 69996ccc8cbSesaxe ".note", 700f6a1d796Sesaxe ".compcom", 70196ccc8cbSesaxe ] 70296ccc8cbSesaxe 70396ccc8cbSesaxesections_preferred = [ ".rodata.str1.8", 70496ccc8cbSesaxe ".rodata.str1.1", 70596ccc8cbSesaxe ".rodata", 70696ccc8cbSesaxe ".data1", 70796ccc8cbSesaxe ".data", 70896ccc8cbSesaxe ".text", 70996ccc8cbSesaxe ] 71096ccc8cbSesaxe 71196ccc8cbSesaxedef compareElfs(base, ptch, quiet) : 71296ccc8cbSesaxe 71396ccc8cbSesaxe global logging 71496ccc8cbSesaxe 71596ccc8cbSesaxe base_header = get_elfheader(base) 71696ccc8cbSesaxe sections = base_header.keys() 71796ccc8cbSesaxe 71896ccc8cbSesaxe ptch_header = get_elfheader(ptch) 71996ccc8cbSesaxe e2_only_sections = ptch_header.keys() 72096ccc8cbSesaxe 72196ccc8cbSesaxe e1_only_sections = [] 72296ccc8cbSesaxe 72396ccc8cbSesaxe fileName = fnFormat(base) 72496ccc8cbSesaxe 72596ccc8cbSesaxe # Derive the list of ELF sections found only in 72696ccc8cbSesaxe # either e1 or e2. 72796ccc8cbSesaxe for sect in sections : 72896ccc8cbSesaxe if not sect in e2_only_sections : 72996ccc8cbSesaxe e1_only_sections.append(sect) 73096ccc8cbSesaxe else : 73196ccc8cbSesaxe e2_only_sections.remove(sect) 73296ccc8cbSesaxe 73396ccc8cbSesaxe if len(e1_only_sections) > 0 : 73496ccc8cbSesaxe if quiet : 73596ccc8cbSesaxe return 1 73696ccc8cbSesaxe info(fileName); 73796ccc8cbSesaxe if not logging : 73896ccc8cbSesaxe return 1 73996ccc8cbSesaxe 74096ccc8cbSesaxe slist = "" 74196ccc8cbSesaxe for sect in e1_only_sections : 74296ccc8cbSesaxe slist = slist + sect + "\t" 74396ccc8cbSesaxe v_info("\nELF sections found in " + \ 74496ccc8cbSesaxe base + " but not in " + ptch) 74596ccc8cbSesaxe v_info("\n" + slist) 74696ccc8cbSesaxe return 1 74796ccc8cbSesaxe 74896ccc8cbSesaxe if len(e2_only_sections) > 0 : 74996ccc8cbSesaxe if quiet : 75096ccc8cbSesaxe return 1 75196ccc8cbSesaxe 75296ccc8cbSesaxe info(fileName); 75396ccc8cbSesaxe if not logging : 75496ccc8cbSesaxe return 1 75596ccc8cbSesaxe 75696ccc8cbSesaxe slist = "" 75796ccc8cbSesaxe for sect in e2_only_sections : 75896ccc8cbSesaxe slist = slist + sect + "\t" 75996ccc8cbSesaxe v_info("\nELF sections found in " + \ 76096ccc8cbSesaxe ptch + " but not in " + base) 76196ccc8cbSesaxe v_info("\n" + slist) 76296ccc8cbSesaxe return 1 76396ccc8cbSesaxe 76496ccc8cbSesaxe # Look for preferred sections, and put those at the 76596ccc8cbSesaxe # top of the list of sections to compare 76696ccc8cbSesaxe for psect in sections_preferred : 76796ccc8cbSesaxe if psect in sections : 76896ccc8cbSesaxe sections.remove(psect) 76996ccc8cbSesaxe sections.insert(0, psect) 77096ccc8cbSesaxe 77196ccc8cbSesaxe # Compare ELF sections 77296ccc8cbSesaxe first_section = True 77396ccc8cbSesaxe for sect in sections : 77496ccc8cbSesaxe 77596ccc8cbSesaxe if sect in sections_to_skip : 77696ccc8cbSesaxe continue 77796ccc8cbSesaxe 77896ccc8cbSesaxe s1 = extract_elf_section(base, sect); 77996ccc8cbSesaxe s2 = extract_elf_section(ptch, sect); 78096ccc8cbSesaxe 78196ccc8cbSesaxe if len(s1) != len (s2) or s1 != s2: 78296ccc8cbSesaxe if not quiet: 78396ccc8cbSesaxe sh_type = base_header[sect] 78496ccc8cbSesaxe data = diff_elf_section(base, ptch, sect, \ 78596ccc8cbSesaxe sh_type) 78696ccc8cbSesaxe 78796ccc8cbSesaxe # If all ELF sections are being reported, then 78896ccc8cbSesaxe # invoke difference() to flag the file name to 78996ccc8cbSesaxe # stdout only once. Any other section differences 79096ccc8cbSesaxe # should be logged to the results file directly 79196ccc8cbSesaxe if not first_section : 79296ccc8cbSesaxe log_difference(fileName, "ELF " + sect, data) 79396ccc8cbSesaxe else : 79496ccc8cbSesaxe difference(fileName, "ELF " + sect, data) 79596ccc8cbSesaxe 79696ccc8cbSesaxe if not reportAllSects : 79796ccc8cbSesaxe return 1 79896ccc8cbSesaxe first_section = False 79996ccc8cbSesaxe return 0 80096ccc8cbSesaxe 80196ccc8cbSesaxe##### 80296ccc8cbSesaxe# Archive object comparison 80396ccc8cbSesaxe# 80496ccc8cbSesaxe# Returns 1 if difference detected 80596ccc8cbSesaxe# 0 if no difference detected 80696ccc8cbSesaxe# -1 on error 80796ccc8cbSesaxe# 80896ccc8cbSesaxedef compareArchives(base, ptch, fileType) : 80996ccc8cbSesaxe 81096ccc8cbSesaxe fileName = fnFormat(base) 81196ccc8cbSesaxe 81296ccc8cbSesaxe # clear the temp directories 81396ccc8cbSesaxe baseCmd = "rm -rf " + tmpDir1 + "*" 81496ccc8cbSesaxe status, output = commands.getstatusoutput(baseCmd) 81596ccc8cbSesaxe if status != 0 : 81696ccc8cbSesaxe error(baseCmd + " failed: " + output) 81796ccc8cbSesaxe return -1 81896ccc8cbSesaxe 81996ccc8cbSesaxe ptchCmd = "rm -rf " + tmpDir2 + "*" 82096ccc8cbSesaxe status, output = commands.getstatusoutput(ptchCmd) 82196ccc8cbSesaxe if status != 0 : 82296ccc8cbSesaxe error(ptchCmd + " failed: " + output) 82396ccc8cbSesaxe return -1 82496ccc8cbSesaxe 82596ccc8cbSesaxe # 82696ccc8cbSesaxe # Be optimistic and first try a straight file compare 82796ccc8cbSesaxe # as it will allow us to finish up quickly. 82896ccc8cbSesaxe if compareBasic(base, ptch, True, fileType) == 0 : 82996ccc8cbSesaxe return 0 83096ccc8cbSesaxe 83196ccc8cbSesaxe # copy over the objects to the temp areas, and 83296ccc8cbSesaxe # unpack them 83396ccc8cbSesaxe baseCmd = "cp -fp " + base + " " + tmpDir1 83496ccc8cbSesaxe status, output = commands.getstatusoutput(baseCmd) 83596ccc8cbSesaxe if status != 0 : 83696ccc8cbSesaxe error(baseCmd + " failed: " + output) 83796ccc8cbSesaxe return -1 83896ccc8cbSesaxe 83996ccc8cbSesaxe ptchCmd = "cp -fp " + ptch + " " + tmpDir2 84096ccc8cbSesaxe status, output = commands.getstatusoutput(ptchCmd) 84196ccc8cbSesaxe if status != 0 : 84296ccc8cbSesaxe error(ptchCmd + " failed: " + output) 84396ccc8cbSesaxe return -1 84496ccc8cbSesaxe 84596ccc8cbSesaxe bname = string.split(fileName, '/')[-1] 84696ccc8cbSesaxe if fileType == "Java Archive" : 84796ccc8cbSesaxe baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \ 84896ccc8cbSesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 84996ccc8cbSesaxe ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \ 85096ccc8cbSesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 85196ccc8cbSesaxe elif fileType == "ELF Object Archive" : 85296ccc8cbSesaxe baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \ 85396ccc8cbSesaxe bname + "; rm -f " + bname 85496ccc8cbSesaxe ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \ 85596ccc8cbSesaxe bname + "; rm -f " + bname 85696ccc8cbSesaxe else : 85796ccc8cbSesaxe error("unexpected file type: " + fileType) 85896ccc8cbSesaxe return -1 85996ccc8cbSesaxe 86096ccc8cbSesaxe os.system(baseCmd) 86196ccc8cbSesaxe os.system(ptchCmd) 86296ccc8cbSesaxe 86396ccc8cbSesaxe baseFlist = list(findFiles(tmpDir1)) 86496ccc8cbSesaxe ptchFlist = list(findFiles(tmpDir2)) 86596ccc8cbSesaxe 86696ccc8cbSesaxe # Trim leading path off base/ptch file lists 86796ccc8cbSesaxe flist = [] 86896ccc8cbSesaxe for fn in baseFlist : 86996ccc8cbSesaxe flist.append(str_prefix_trunc(fn, tmpDir1)) 87096ccc8cbSesaxe baseFlist = flist 87196ccc8cbSesaxe 87296ccc8cbSesaxe flist = [] 87396ccc8cbSesaxe for fn in ptchFlist : 87496ccc8cbSesaxe flist.append(str_prefix_trunc(fn, tmpDir2)) 87596ccc8cbSesaxe ptchFlist = flist 87696ccc8cbSesaxe 87796ccc8cbSesaxe for fn in ptchFlist : 87896ccc8cbSesaxe if not fn in baseFlist : 87996ccc8cbSesaxe difference(fileName, fileType, \ 88096ccc8cbSesaxe fn + " added to " + fileName) 88196ccc8cbSesaxe return 1 88296ccc8cbSesaxe 88396ccc8cbSesaxe for fn in baseFlist : 88496ccc8cbSesaxe if not fn in ptchFlist : 88596ccc8cbSesaxe difference(fileName, fileType, \ 88696ccc8cbSesaxe fn + " removed from " + fileName) 88796ccc8cbSesaxe return 1 88896ccc8cbSesaxe 88996ccc8cbSesaxe differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True) 89096ccc8cbSesaxe if differs : 89196ccc8cbSesaxe difference(fileName, fileType, \ 89296ccc8cbSesaxe fn + " in " + fileName + " differs") 89396ccc8cbSesaxe return 1 89496ccc8cbSesaxe return 0 89596ccc8cbSesaxe 89696ccc8cbSesaxe##### 89796ccc8cbSesaxe# (Basic) file comparison 89896ccc8cbSesaxe# 89996ccc8cbSesaxe# There's some special case code here for Javadoc HTML files 90096ccc8cbSesaxe# 90196ccc8cbSesaxe# Returns 1 if difference detected 90296ccc8cbSesaxe# 0 if no difference detected 90396ccc8cbSesaxe# -1 on error 90496ccc8cbSesaxe# 90596ccc8cbSesaxedef compareBasic(base, ptch, quiet, fileType) : 90696ccc8cbSesaxe 90796ccc8cbSesaxe fileName = fnFormat(base); 90896ccc8cbSesaxe 90996ccc8cbSesaxe if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] : 91096ccc8cbSesaxe return 1 91196ccc8cbSesaxe 91296ccc8cbSesaxe try: 91396ccc8cbSesaxe baseFile = open(base) 91496ccc8cbSesaxe except: 91596ccc8cbSesaxe error("could not open " + base) 91696ccc8cbSesaxe return -1 91796ccc8cbSesaxe try: 91896ccc8cbSesaxe ptchFile = open(ptch) 91996ccc8cbSesaxe except: 92096ccc8cbSesaxe error("could not open " + ptch) 92196ccc8cbSesaxe return -1 92296ccc8cbSesaxe 92396ccc8cbSesaxe baseData = baseFile.read() 92496ccc8cbSesaxe ptchData = ptchFile.read() 92596ccc8cbSesaxe 92696ccc8cbSesaxe baseFile.close() 92796ccc8cbSesaxe ptchFile.close() 92896ccc8cbSesaxe 92996ccc8cbSesaxe needToSnip = False 93096ccc8cbSesaxe if fileType == "HTML" : 93196ccc8cbSesaxe needToSnip = True 93296ccc8cbSesaxe toSnipBeginStr = "<!-- Generated by javadoc" 93396ccc8cbSesaxe toSnipEndStr = "-->\n" 93496ccc8cbSesaxe 93596ccc8cbSesaxe if needToSnip : 93696ccc8cbSesaxe toSnipBegin = string.find(baseData, toSnipBeginStr) 93796ccc8cbSesaxe if toSnipBegin != -1 : 93896ccc8cbSesaxe toSnipEnd = string.find(baseData[toSnipBegin:], \ 93996ccc8cbSesaxe toSnipEndStr) + \ 94096ccc8cbSesaxe len(toSnipEndStr) 94196ccc8cbSesaxe baseData = baseData[:toSnipBegin] + \ 94296ccc8cbSesaxe baseData[toSnipBegin + toSnipEnd:] 94396ccc8cbSesaxe ptchData = ptchData[:toSnipBegin] + \ 94496ccc8cbSesaxe ptchData[toSnipBegin + toSnipEnd:] 94596ccc8cbSesaxe 94696ccc8cbSesaxe if quiet : 94796ccc8cbSesaxe if baseData != ptchData : 94896ccc8cbSesaxe return 1 94996ccc8cbSesaxe else : 95096ccc8cbSesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 95196ccc8cbSesaxe diffs = diffData(baseData, ptchData) 95296ccc8cbSesaxe difference(fileName, fileType, diffs) 95396ccc8cbSesaxe return 1 95496ccc8cbSesaxe return 0 95596ccc8cbSesaxe 95696ccc8cbSesaxe 95796ccc8cbSesaxe##### 95896ccc8cbSesaxe# Compare two objects by producing a data dump from 95996ccc8cbSesaxe# each object, and then comparing the dump data 96096ccc8cbSesaxe# 96196ccc8cbSesaxe# Returns: 1 if a difference is detected 96296ccc8cbSesaxe# 0 if no difference detected 96396ccc8cbSesaxe# -1 upon error 96496ccc8cbSesaxe# 96596ccc8cbSesaxedef compareByDumping(base, ptch, quiet, fileType) : 96696ccc8cbSesaxe 96796ccc8cbSesaxe fileName = fnFormat(base); 96896ccc8cbSesaxe 96996ccc8cbSesaxe if fileType == "Lint Library" : 97096ccc8cbSesaxe baseCmd = lintdump_cmd + " -ir " + base + \ 97196ccc8cbSesaxe " | grep -v LINTLIB:" + " > " + tmpFile1 97296ccc8cbSesaxe ptchCmd = lintdump_cmd + " -ir " + ptch + \ 97396ccc8cbSesaxe " | grep -v LINTLIB:" + " > " + tmpFile2 97496ccc8cbSesaxe elif fileType == "Sqlite Database" : 97596ccc8cbSesaxe baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \ 97696ccc8cbSesaxe tmpFile1 97796ccc8cbSesaxe ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \ 97896ccc8cbSesaxe tmpFile2 97996ccc8cbSesaxe 98096ccc8cbSesaxe os.system(baseCmd) 98196ccc8cbSesaxe os.system(ptchCmd) 98296ccc8cbSesaxe 98396ccc8cbSesaxe try: 98496ccc8cbSesaxe baseFile = open(tmpFile1) 98596ccc8cbSesaxe except: 98696ccc8cbSesaxe error("could not open: " + tmpFile1) 98796ccc8cbSesaxe try: 98896ccc8cbSesaxe ptchFile = open(tmpFile2) 98996ccc8cbSesaxe except: 99096ccc8cbSesaxe error("could not open: " + tmpFile2) 99196ccc8cbSesaxe 99296ccc8cbSesaxe baseData = baseFile.read() 99396ccc8cbSesaxe ptchData = ptchFile.read() 99496ccc8cbSesaxe 99596ccc8cbSesaxe baseFile.close() 99696ccc8cbSesaxe ptchFile.close() 99796ccc8cbSesaxe 99896ccc8cbSesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 99996ccc8cbSesaxe if not quiet : 100096ccc8cbSesaxe data = diffFileData(tmpFile1, tmpFile2); 100196ccc8cbSesaxe difference(fileName, fileType, data) 100296ccc8cbSesaxe return 1 100396ccc8cbSesaxe return 0 100496ccc8cbSesaxe 100596ccc8cbSesaxe##### 1006*619b4598Srotondo# Compare two elfsign activation files. This ignores the activation 1007*619b4598Srotondo# files themselves and reports a difference if and only if the 1008*619b4598Srotondo# corresponding base files are different. 1009*619b4598Srotondo# 1010*619b4598Srotondo# Returns 1 if difference detected 1011*619b4598Srotondo# 0 if no difference detected 1012*619b4598Srotondo# -1 on error 1013*619b4598Srotondo# 1014*619b4598Srotondodef compareActivation(base, ptch, quiet, fileType) : 1015*619b4598Srotondo 1016*619b4598Srotondo fileName = fnFormat(base) 1017*619b4598Srotondo 1018*619b4598Srotondo # Drop the .esa suffix from both filenames. 1019*619b4598Srotondo base = base[0:base.rfind('.esa')] 1020*619b4598Srotondo ptch = ptch[0:ptch.rfind('.esa')] 1021*619b4598Srotondo 1022*619b4598Srotondo result = compareOneFile(base, ptch, True) 1023*619b4598Srotondo if result == -1 : 1024*619b4598Srotondo error("unable to compare " + fileName) 1025*619b4598Srotondo elif result == 1 : 1026*619b4598Srotondo if not quiet : 1027*619b4598Srotondo difference(fileName, fileType, \ 1028*619b4598Srotondo "change in corresponding ELF file") 1029*619b4598Srotondo 1030*619b4598Srotondo return result 1031*619b4598Srotondo 1032*619b4598Srotondo##### 103396ccc8cbSesaxe# Compare two objects. Detect type changes. 103496ccc8cbSesaxe# Vector off to the appropriate type specific 103596ccc8cbSesaxe# compare routine based on the type. 103696ccc8cbSesaxe# 103796ccc8cbSesaxedef compareOneFile(base, ptch, quiet) : 103896ccc8cbSesaxe 103996ccc8cbSesaxe # Verify the file types. 104096ccc8cbSesaxe # If they are different, indicate this and move on 104196ccc8cbSesaxe btype = getTheFileType(base) 104296ccc8cbSesaxe ptype = getTheFileType(ptch) 104396ccc8cbSesaxe 1044*619b4598Srotondo if btype == 'Error' or ptype == 'Error' : 1045*619b4598Srotondo return -1 1046*619b4598Srotondo 104796ccc8cbSesaxe fileName = fnFormat(base) 104896ccc8cbSesaxe 104996ccc8cbSesaxe if (btype != ptype) : 1050*619b4598Srotondo if not quiet : 105196ccc8cbSesaxe difference(fileName, "file type", btype + " to " + ptype) 105296ccc8cbSesaxe return 1 105396ccc8cbSesaxe else : 105496ccc8cbSesaxe fileType = btype 105596ccc8cbSesaxe 105696ccc8cbSesaxe if (fileType == 'ELF') : 105796ccc8cbSesaxe return compareElfs(base, ptch, quiet) 105896ccc8cbSesaxe 105996ccc8cbSesaxe elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') : 106096ccc8cbSesaxe return compareArchives(base, ptch, fileType) 106196ccc8cbSesaxe 106296ccc8cbSesaxe elif (fileType == 'HTML') : 106396ccc8cbSesaxe return compareBasic(base, ptch, quiet, fileType) 106496ccc8cbSesaxe 106596ccc8cbSesaxe elif ( fileType == 'Lint Library' ) : 106696ccc8cbSesaxe return compareByDumping(base, ptch, quiet, fileType) 106796ccc8cbSesaxe 106896ccc8cbSesaxe elif ( fileType == 'Sqlite Database' ) : 106996ccc8cbSesaxe return compareByDumping(base, ptch, quiet, fileType) 1070*619b4598Srotondo 1071*619b4598Srotondo elif ( fileType == 'Elfsign Activation' ) : 1072*619b4598Srotondo return compareActivation(base, ptch, quiet, fileType) 1073*619b4598Srotondo 107496ccc8cbSesaxe else : 107596ccc8cbSesaxe # it has to be some variety of text file 107696ccc8cbSesaxe return compareBasic(base, ptch, quiet, fileType) 107796ccc8cbSesaxe 107896ccc8cbSesaxe# Cleanup and self-terminate 107996ccc8cbSesaxedef cleanup(ret) : 108096ccc8cbSesaxe 108196ccc8cbSesaxe if len(tmpDir1) > 0 and len(tmpDir2) > 0 : 108296ccc8cbSesaxe 108396ccc8cbSesaxe baseCmd = "rm -rf " + tmpDir1 108496ccc8cbSesaxe ptchCmd = "rm -rf " + tmpDir2 108596ccc8cbSesaxe 108696ccc8cbSesaxe os.system(baseCmd) 108796ccc8cbSesaxe os.system(ptchCmd) 108896ccc8cbSesaxe 108996ccc8cbSesaxe if logging : 109096ccc8cbSesaxe log.close() 109196ccc8cbSesaxe 109296ccc8cbSesaxe sys.exit(ret) 109396ccc8cbSesaxe 109496ccc8cbSesaxedef main() : 109596ccc8cbSesaxe 109696ccc8cbSesaxe # Log file handle 109796ccc8cbSesaxe global log 109896ccc8cbSesaxe 109996ccc8cbSesaxe # Globals relating to command line options 110096ccc8cbSesaxe global logging, vdiffs, reportAllSects 110196ccc8cbSesaxe 110296ccc8cbSesaxe # Named temporary files / directories 110396ccc8cbSesaxe global tmpDir1, tmpDir2, tmpFile1, tmpFile2 110496ccc8cbSesaxe 110596ccc8cbSesaxe # Command paths 110696ccc8cbSesaxe global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd 110796ccc8cbSesaxe 110896ccc8cbSesaxe # Default search path 110996ccc8cbSesaxe global wsdiff_path 111096ccc8cbSesaxe 111196ccc8cbSesaxe # Essentially "uname -p" 111296ccc8cbSesaxe global arch 111396ccc8cbSesaxe 111496ccc8cbSesaxe # Some globals need to be initialized 111596ccc8cbSesaxe logging = vdiffs = reportAllSects = False 111696ccc8cbSesaxe 111796ccc8cbSesaxe 111896ccc8cbSesaxe # Process command line arguments 111996ccc8cbSesaxe # Return values are returned from args() in alpha order 112096ccc8cbSesaxe # (Yes, python functions can return multiple values (ewww)) 112196ccc8cbSesaxe # Note that args() also set the globals: 112296ccc8cbSesaxe # logging to True if verbose logging (to a file) was enabled 112396ccc8cbSesaxe # vdiffs to True if logged differences aren't to be truncated 112496ccc8cbSesaxe # reportAllSects to True if all ELF section differences are to be reported 112596ccc8cbSesaxe # 112696ccc8cbSesaxe baseRoot, fileNamesFile, localTools, ptchRoot, results = args() 112796ccc8cbSesaxe 112896ccc8cbSesaxe # 112996ccc8cbSesaxe # Set up the results/log file 113096ccc8cbSesaxe # 113196ccc8cbSesaxe if logging : 113296ccc8cbSesaxe try: 113396ccc8cbSesaxe log = open(results, "w") 113496ccc8cbSesaxe except: 113596ccc8cbSesaxe logging = False 113696ccc8cbSesaxe error("failed to open log file: " + log) 113796ccc8cbSesaxe sys.exit(1) 113896ccc8cbSesaxe 113996ccc8cbSesaxe dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6] 114096ccc8cbSesaxe v_info("# This file was produced by wsdiff") 114196ccc8cbSesaxe v_info(dateTimeStr) 114296ccc8cbSesaxe 114396ccc8cbSesaxe # 114496ccc8cbSesaxe # Build paths to the tools required tools 114596ccc8cbSesaxe # 114696ccc8cbSesaxe # Try to look for tools in $SRC/tools if the "-t" option 114796ccc8cbSesaxe # was specified 114896ccc8cbSesaxe # 114996ccc8cbSesaxe arch = commands.getoutput("uname -p") 115096ccc8cbSesaxe if localTools : 115196ccc8cbSesaxe try: 115296ccc8cbSesaxe src = os.environ['SRC'] 115396ccc8cbSesaxe except: 115496ccc8cbSesaxe error("-t specified, but $SRC not set. Cannot find $SRC/tools") 115596ccc8cbSesaxe src = "" 115696ccc8cbSesaxe if len(src) > 0 : 115796ccc8cbSesaxe wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin") 115896ccc8cbSesaxe 115996ccc8cbSesaxe lintdump_cmd = find_tool("lintdump") 116096ccc8cbSesaxe elfdump_cmd = find_tool("elfdump") 116196ccc8cbSesaxe dump_cmd = find_tool("dump") 116296ccc8cbSesaxe od_cmd = find_tool("od") 116396ccc8cbSesaxe dis_cmd = find_tool("dis") 116496ccc8cbSesaxe diff_cmd = find_tool("diff") 116596ccc8cbSesaxe sqlite_cmd = find_tool("sqlite") 116696ccc8cbSesaxe 116796ccc8cbSesaxe # 116896ccc8cbSesaxe # validate the base and patch paths 116996ccc8cbSesaxe # 117096ccc8cbSesaxe if baseRoot[-1] != '/' : 117196ccc8cbSesaxe baseRoot += '/' 117296ccc8cbSesaxe 117396ccc8cbSesaxe if ptchRoot[-1] != '/' : 117496ccc8cbSesaxe ptchRoot += '/' 117596ccc8cbSesaxe 117696ccc8cbSesaxe if not os.path.exists(baseRoot) : 117796ccc8cbSesaxe error("old proto area: " + baseRoot + " does not exist") 117896ccc8cbSesaxe sys.exit(1) 117996ccc8cbSesaxe 118096ccc8cbSesaxe if not os.path.exists(ptchRoot) : 118196ccc8cbSesaxe error("new proto area: " + ptchRoot + \ 118296ccc8cbSesaxe " does not exist") 118396ccc8cbSesaxe sys.exit(1) 118496ccc8cbSesaxe 118596ccc8cbSesaxe # 118696ccc8cbSesaxe # log some information identifying the run 118796ccc8cbSesaxe # 118896ccc8cbSesaxe v_info("Old proto area: " + baseRoot) 118996ccc8cbSesaxe v_info("New proto area: " + ptchRoot) 119096ccc8cbSesaxe v_info("Results file: " + results + "\n") 119196ccc8cbSesaxe 119296ccc8cbSesaxe # 119396ccc8cbSesaxe # Set up the temporary directories / files 119496ccc8cbSesaxe # Could use python's tmpdir routines, but these should 119596ccc8cbSesaxe # be easier to identify / keep around for debugging 119696ccc8cbSesaxe pid = os.getpid() 119796ccc8cbSesaxe tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/" 119896ccc8cbSesaxe tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/" 119996ccc8cbSesaxe if not os.path.exists(tmpDir1) : 120096ccc8cbSesaxe os.makedirs(tmpDir1) 120196ccc8cbSesaxe if not os.path.exists(tmpDir2) : 120296ccc8cbSesaxe os.makedirs(tmpDir2) 120396ccc8cbSesaxe 120496ccc8cbSesaxe tmpFile1 = tmpDir1 + "f1" 120596ccc8cbSesaxe tmpFile2 = tmpDir2 + "f2" 120696ccc8cbSesaxe 120796ccc8cbSesaxe # Derive a catalog of new, deleted, and to-be-compared objects 120896ccc8cbSesaxe # either from the specified base and patch proto areas, or from 120996ccc8cbSesaxe # from an input file list 121096ccc8cbSesaxe newOrDeleted = False 121196ccc8cbSesaxe 121296ccc8cbSesaxe if fileNamesFile != "" : 121396ccc8cbSesaxe changedFiles, newFiles, deletedFiles = \ 121496ccc8cbSesaxe flistCatalog(baseRoot, ptchRoot, fileNamesFile) 121596ccc8cbSesaxe else : 121696ccc8cbSesaxe changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot) 121796ccc8cbSesaxe 121896ccc8cbSesaxe if len(newFiles) > 0 : 121996ccc8cbSesaxe newOrDeleted = True 122096ccc8cbSesaxe info("\nNew objects found: ") 122196ccc8cbSesaxe 122296ccc8cbSesaxe for fn in newFiles : 122396ccc8cbSesaxe info(fnFormat(fn)) 122496ccc8cbSesaxe 122596ccc8cbSesaxe if len(deletedFiles) > 0 : 122696ccc8cbSesaxe newOrDeleted = True 122796ccc8cbSesaxe info("\nObjects removed: ") 122896ccc8cbSesaxe 122996ccc8cbSesaxe for fn in deletedFiles : 123096ccc8cbSesaxe info(fnFormat(fn)) 123196ccc8cbSesaxe 123296ccc8cbSesaxe if newOrDeleted : 123396ccc8cbSesaxe info("\nChanged objects: "); 123496ccc8cbSesaxe 123596ccc8cbSesaxe 123696ccc8cbSesaxe # Here's where all the heavy lifting happens 123796ccc8cbSesaxe # Perform a comparison on each object appearing in 123896ccc8cbSesaxe # both proto areas. compareOneFile will examine the 123996ccc8cbSesaxe # file types of each object, and will vector off to 124096ccc8cbSesaxe # the appropriate comparison routine, where the compare 124196ccc8cbSesaxe # will happen, and any differences will be reported / logged 124296ccc8cbSesaxe for fn in changedFiles : 124396ccc8cbSesaxe base = baseRoot + fn 124496ccc8cbSesaxe ptch = ptchRoot + fn 124596ccc8cbSesaxe 124696ccc8cbSesaxe compareOneFile(base, ptch, False) 124796ccc8cbSesaxe 124896ccc8cbSesaxe # We're done, cleanup. 124996ccc8cbSesaxe cleanup(0) 125096ccc8cbSesaxe 125196ccc8cbSesaxeif __name__ == '__main__' : 125296ccc8cbSesaxe try: 125396ccc8cbSesaxe main() 125496ccc8cbSesaxe except KeyboardInterrupt : 125596ccc8cbSesaxe cleanup(1); 125696ccc8cbSesaxe 125796ccc8cbSesaxe 1258