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# 2296ccc8cbSesaxe# Copyright 2006 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', 34796ccc8cbSesaxe 'db' : 'Sqlite Database' } 34896ccc8cbSesaxe 34996ccc8cbSesaxe if os.stat(f)[ST_SIZE] == 0 : 35096ccc8cbSesaxe return 'ASCII' 35196ccc8cbSesaxe 35296ccc8cbSesaxe if isELF(f) == 1 : 35396ccc8cbSesaxe return 'ELF' 35496ccc8cbSesaxe 35596ccc8cbSesaxe fnamelist = f.split('.') 35696ccc8cbSesaxe if len(fnamelist) > 1 : # Test the file extension 35796ccc8cbSesaxe extension = fnamelist[-1] 35896ccc8cbSesaxe if extension in extensions.keys(): 35996ccc8cbSesaxe return extensions[extension] 36096ccc8cbSesaxe 36196ccc8cbSesaxe return 'ASCII' 36296ccc8cbSesaxe 36396ccc8cbSesaxe# 36496ccc8cbSesaxe# Return non-zero if "f" is an ELF file 36596ccc8cbSesaxe# 36696ccc8cbSesaxeelfmagic = '\177ELF' 36796ccc8cbSesaxedef isELF(f) : 36896ccc8cbSesaxe try: 36996ccc8cbSesaxe fd = open(f) 37096ccc8cbSesaxe except: 37196ccc8cbSesaxe error("failed to open: " + f) 37296ccc8cbSesaxe return 0 37396ccc8cbSesaxe magic = fd.read(len(elfmagic)) 37496ccc8cbSesaxe fd.close() 37596ccc8cbSesaxe 37696ccc8cbSesaxe if magic == elfmagic : 37796ccc8cbSesaxe return 1 37896ccc8cbSesaxe return 0 37996ccc8cbSesaxe 38096ccc8cbSesaxe# 38196ccc8cbSesaxe# Return non-zero is "f" is binary. 38296ccc8cbSesaxe# Consider the file to be binary if it contains any null characters 38396ccc8cbSesaxe# 38496ccc8cbSesaxedef isBinary(f) : 38596ccc8cbSesaxe try: 38696ccc8cbSesaxe fd = open(f) 38796ccc8cbSesaxe except: 38896ccc8cbSesaxe error("failed to open: " + f) 38996ccc8cbSesaxe return 0 39096ccc8cbSesaxe s = fd.read() 39196ccc8cbSesaxe fd.close() 39296ccc8cbSesaxe 39396ccc8cbSesaxe if s.find('\0') == -1 : 39496ccc8cbSesaxe return 0 39596ccc8cbSesaxe else : 39696ccc8cbSesaxe return 1 39796ccc8cbSesaxe 39896ccc8cbSesaxe##### 39996ccc8cbSesaxe# Directory traversal and file finding 40096ccc8cbSesaxe# 40196ccc8cbSesaxe 40296ccc8cbSesaxe# 40396ccc8cbSesaxe# Return a sorted list of files found under the specified directory 40496ccc8cbSesaxe# 40596ccc8cbSesaxedef findFiles(d) : 40696ccc8cbSesaxe for path, subdirs, files in os.walk(d) : 40796ccc8cbSesaxe files.sort() 40896ccc8cbSesaxe for name in files : 40996ccc8cbSesaxe yield os.path.join(path, name) 41096ccc8cbSesaxe 41196ccc8cbSesaxe# 41296ccc8cbSesaxe# Examine all files in base, ptch 41396ccc8cbSesaxe# 41496ccc8cbSesaxe# Return a list of files appearing in both proto areas, 41596ccc8cbSesaxe# a list of new files (files found only in ptch) and 41696ccc8cbSesaxe# a list of deleted files (files found only in base) 41796ccc8cbSesaxe# 41896ccc8cbSesaxedef protoCatalog(base, ptch) : 41996ccc8cbSesaxe compFiles = [] # List of files in both proto areas 42096ccc8cbSesaxe ptchList = [] # List of file in patch proto area 42196ccc8cbSesaxe 42296ccc8cbSesaxe newFiles = [] # New files detected 42396ccc8cbSesaxe deletedFiles = [] # Deleted files 42496ccc8cbSesaxe 42596ccc8cbSesaxe baseFilesList = list(findFiles(base)) 42696ccc8cbSesaxe baseStringLength = len(base) 42796ccc8cbSesaxe 42896ccc8cbSesaxe ptchFilesList = list(findFiles(ptch)) 42996ccc8cbSesaxe ptchStringLength = len(ptch) 43096ccc8cbSesaxe 43196ccc8cbSesaxe # Inventory files in the base proto area 43296ccc8cbSesaxe for fn in baseFilesList : 43396ccc8cbSesaxe if os.path.islink(fn) : 43496ccc8cbSesaxe continue 43596ccc8cbSesaxe 43696ccc8cbSesaxe fileName = fn[baseStringLength:] 43796ccc8cbSesaxe compFiles.append(fileName) 43896ccc8cbSesaxe 43996ccc8cbSesaxe # Inventory files in the patch proto area 44096ccc8cbSesaxe for fn in ptchFilesList : 44196ccc8cbSesaxe if os.path.islink(fn) : 44296ccc8cbSesaxe continue 44396ccc8cbSesaxe 44496ccc8cbSesaxe fileName = fn[ptchStringLength:] 44596ccc8cbSesaxe ptchList.append(fileName) 44696ccc8cbSesaxe 44796ccc8cbSesaxe # Deleted files appear in the base area, but not the patch area 44896ccc8cbSesaxe for fileName in compFiles : 44996ccc8cbSesaxe if not fileName in ptchList : 45096ccc8cbSesaxe deletedFiles.append(fileName) 45196ccc8cbSesaxe 45296ccc8cbSesaxe # Eliminate "deleted" files from the list of objects appearing 45396ccc8cbSesaxe # in both the base and patch proto areas 45496ccc8cbSesaxe for fileName in deletedFiles : 45596ccc8cbSesaxe try: 45696ccc8cbSesaxe compFiles.remove(fileName) 45796ccc8cbSesaxe except: 45896ccc8cbSesaxe error("filelist.remove() failed") 45996ccc8cbSesaxe 46096ccc8cbSesaxe # New files appear in the patch area, but not the base 46196ccc8cbSesaxe for fileName in ptchList : 46296ccc8cbSesaxe if not fileName in compFiles : 46396ccc8cbSesaxe newFiles.append(fileName) 46496ccc8cbSesaxe 46596ccc8cbSesaxe return compFiles, newFiles, deletedFiles 46696ccc8cbSesaxe 46796ccc8cbSesaxe# 46896ccc8cbSesaxe# Examine the files listed in the input file list 46996ccc8cbSesaxe# 47096ccc8cbSesaxe# Return a list of files appearing in both proto areas, 47196ccc8cbSesaxe# a list of new files (files found only in ptch) and 47296ccc8cbSesaxe# a list of deleted files (files found only in base) 47396ccc8cbSesaxe# 47496ccc8cbSesaxedef flistCatalog(base, ptch, flist) : 47596ccc8cbSesaxe compFiles = [] # List of files in both proto areas 47696ccc8cbSesaxe newFiles = [] # New files detected 47796ccc8cbSesaxe deletedFiles = [] # Deleted files 47896ccc8cbSesaxe 47996ccc8cbSesaxe try: 48096ccc8cbSesaxe fd = open(flist, "r") 48196ccc8cbSesaxe except: 48296ccc8cbSesaxe error("could not open: " + flist) 48396ccc8cbSesaxe cleanup(1) 48496ccc8cbSesaxe 48596ccc8cbSesaxe files = [] 48696ccc8cbSesaxe files = fd.readlines() 48796ccc8cbSesaxe 48896ccc8cbSesaxe for f in files : 48996ccc8cbSesaxe ptch_present = True 49096ccc8cbSesaxe base_present = True 49196ccc8cbSesaxe 49296ccc8cbSesaxe if f == '\n' : 49396ccc8cbSesaxe continue 49496ccc8cbSesaxe 49596ccc8cbSesaxe # the fileNames have a trailing '\n' 49696ccc8cbSesaxe f = f.rstrip() 49796ccc8cbSesaxe 49896ccc8cbSesaxe # The objects in the file list have paths relative 49996ccc8cbSesaxe # to $ROOT or to the base/ptch directory specified on 50096ccc8cbSesaxe # the command line. 50196ccc8cbSesaxe # If it's relative to $ROOT, we'll need to add back the 50296ccc8cbSesaxe # root_`uname -p` goo we stripped off in fnFormat() 50396ccc8cbSesaxe if os.path.exists(base + f) : 50496ccc8cbSesaxe fn = f; 50596ccc8cbSesaxe elif os.path.exists(base + "root_" + arch + "/" + f) : 50696ccc8cbSesaxe fn = "root_" + arch + "/" + f 50796ccc8cbSesaxe else : 50896ccc8cbSesaxe base_present = False 50996ccc8cbSesaxe 51096ccc8cbSesaxe if base_present : 51196ccc8cbSesaxe if not os.path.exists(ptch + fn) : 51296ccc8cbSesaxe ptch_present = False 51396ccc8cbSesaxe else : 51496ccc8cbSesaxe if os.path.exists(ptch + f) : 51596ccc8cbSesaxe fn = f 51696ccc8cbSesaxe elif os.path.exists(ptch + "root_" + arch + "/" + f) : 51796ccc8cbSesaxe fn = "root_" + arch + "/" + f 51896ccc8cbSesaxe else : 51996ccc8cbSesaxe ptch_present = False 52096ccc8cbSesaxe 52196ccc8cbSesaxe if os.path.islink(base + fn) : # ignore links 52296ccc8cbSesaxe base_present = False 52396ccc8cbSesaxe if os.path.islink(ptch + fn) : 52496ccc8cbSesaxe ptch_present = False 52596ccc8cbSesaxe 52696ccc8cbSesaxe if base_present and ptch_present : 52796ccc8cbSesaxe compFiles.append(fn) 52896ccc8cbSesaxe elif base_present : 52996ccc8cbSesaxe deletedFiles.append(fn) 53096ccc8cbSesaxe elif ptch_present : 53196ccc8cbSesaxe newFiles.append(fn) 53296ccc8cbSesaxe else : 53396ccc8cbSesaxe if os.path.islink(base + fn) and os.path.islink(ptch + fn) : 53496ccc8cbSesaxe continue 53596ccc8cbSesaxe error(f + " in file list, but not in either tree. Skipping...") 53696ccc8cbSesaxe 53796ccc8cbSesaxe return compFiles, newFiles, deletedFiles 53896ccc8cbSesaxe 53996ccc8cbSesaxe 54096ccc8cbSesaxe# 54196ccc8cbSesaxe# Build a fully qualified path to an external tool/utility. 54296ccc8cbSesaxe# Consider the default system locations. For onbld tools, if 54396ccc8cbSesaxe# the -t option was specified, we'll try to use built tools in $SRC tools, 54496ccc8cbSesaxe# and otherwise, we'll fall back on /opt/onbld/ 54596ccc8cbSesaxe# 54696ccc8cbSesaxedef find_tool(tool) : 54796ccc8cbSesaxe 54896ccc8cbSesaxe # First, check what was passed 54996ccc8cbSesaxe if os.path.exists(tool) : 55096ccc8cbSesaxe return tool 55196ccc8cbSesaxe 55296ccc8cbSesaxe # Next try in wsdiff path 55396ccc8cbSesaxe for pdir in wsdiff_path : 55496ccc8cbSesaxe location = pdir + "/" + tool 55596ccc8cbSesaxe if os.path.exists(location) : 55696ccc8cbSesaxe return location + " " 55796ccc8cbSesaxe 55896ccc8cbSesaxe location = pdir + "/" + arch + "/" + tool 55996ccc8cbSesaxe if os.path.exists(location) : 56096ccc8cbSesaxe return location + " " 56196ccc8cbSesaxe 56296ccc8cbSesaxe error("Could not find path to: " + tool); 56396ccc8cbSesaxe sys.exit(1); 56496ccc8cbSesaxe 56596ccc8cbSesaxe 56696ccc8cbSesaxe##### 56796ccc8cbSesaxe# ELF file comparison helper routines 56896ccc8cbSesaxe# 56996ccc8cbSesaxe 57096ccc8cbSesaxe# 57196ccc8cbSesaxe# Return a dictionary of ELF section types keyed by section name 57296ccc8cbSesaxe# 57396ccc8cbSesaxedef get_elfheader(f) : 57496ccc8cbSesaxe 57596ccc8cbSesaxe header = {} 57696ccc8cbSesaxe 57796ccc8cbSesaxe hstring = commands.getoutput(elfdump_cmd + " -c " + f) 57896ccc8cbSesaxe 57996ccc8cbSesaxe if len(hstring) == 0 : 58096ccc8cbSesaxe error("Failed to dump ELF header for " + f) 58196ccc8cbSesaxe return 58296ccc8cbSesaxe 58396ccc8cbSesaxe # elfdump(1) dumps the section headers with the section name 58496ccc8cbSesaxe # following "sh_name:", and the section type following "sh_type:" 58596ccc8cbSesaxe sections = hstring.split("Section Header") 58696ccc8cbSesaxe for sect in sections : 58796ccc8cbSesaxe datap = sect.find("sh_name:"); 58896ccc8cbSesaxe if datap == -1 : 58996ccc8cbSesaxe continue 59096ccc8cbSesaxe section = sect[datap:].split()[1] 59196ccc8cbSesaxe datap = sect.find("sh_type:"); 59296ccc8cbSesaxe if datap == -1 : 59396ccc8cbSesaxe error("Could not get type for sect: " + section + \ 59496ccc8cbSesaxe " in " + f) 59596ccc8cbSesaxe sh_type = sect[datap:].split()[2] 59696ccc8cbSesaxe header[section] = sh_type 59796ccc8cbSesaxe 59896ccc8cbSesaxe return header 59996ccc8cbSesaxe 60096ccc8cbSesaxe# 60196ccc8cbSesaxe# Extract data in the specified ELF section from the given file 60296ccc8cbSesaxe# 60396ccc8cbSesaxedef extract_elf_section(f, section) : 60496ccc8cbSesaxe 60596ccc8cbSesaxe data = commands.getoutput(dump_cmd + " -sn " + section + " " + f) 60696ccc8cbSesaxe 60796ccc8cbSesaxe if len(data) == 0 : 60896ccc8cbSesaxe error(cmd + " yielded no data") 60996ccc8cbSesaxe return 61096ccc8cbSesaxe 61196ccc8cbSesaxe # dump(1) displays the file name to start... 61296ccc8cbSesaxe # get past it to the data itself 61396ccc8cbSesaxe dbegin = data.find(":") + 1 61496ccc8cbSesaxe data = data[dbegin:]; 61596ccc8cbSesaxe 61696ccc8cbSesaxe return (data) 61796ccc8cbSesaxe 61896ccc8cbSesaxe# 61996ccc8cbSesaxe# Return a (hopefully meaningful) human readable set of diffs 62096ccc8cbSesaxe# for the specified ELF section between f1 and f2 62196ccc8cbSesaxe# 62296ccc8cbSesaxe# Depending on the section, various means for dumping and diffing 62396ccc8cbSesaxe# the data may be employed. 62496ccc8cbSesaxe# 62596ccc8cbSesaxetext_sections = [ '.text', '.init', '.fini' ] 62696ccc8cbSesaxedef diff_elf_section(f1, f2, section, sh_type) : 62796ccc8cbSesaxe 62896ccc8cbSesaxe if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA 62996ccc8cbSesaxe cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1 63096ccc8cbSesaxe cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2 63196ccc8cbSesaxe elif (section == ".group") : 63296ccc8cbSesaxe cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1 63396ccc8cbSesaxe cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2 63496ccc8cbSesaxe elif (section == ".hash") : 63596ccc8cbSesaxe cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1 63696ccc8cbSesaxe cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2 63796ccc8cbSesaxe elif (section == ".dynamic") : 63896ccc8cbSesaxe cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1 63996ccc8cbSesaxe cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2 64096ccc8cbSesaxe elif (section == ".got") : 64196ccc8cbSesaxe cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1 64296ccc8cbSesaxe cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2 64396ccc8cbSesaxe elif (section == ".SUNW_cap") : 64496ccc8cbSesaxe cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1 64596ccc8cbSesaxe cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2 64696ccc8cbSesaxe elif (section == ".interp") : 64796ccc8cbSesaxe cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1 64896ccc8cbSesaxe cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2 64996ccc8cbSesaxe elif (section == ".symtab" or section == ".dynsym") : 65096ccc8cbSesaxe cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1 65196ccc8cbSesaxe cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2 65296ccc8cbSesaxe elif (section in text_sections) : 65396ccc8cbSesaxe # dis sometimes complains when it hits something it doesn't 65496ccc8cbSesaxe # know how to disassemble. Just ignore it, as the output 65596ccc8cbSesaxe # being generated here is human readable, and we've already 65696ccc8cbSesaxe # correctly flagged the difference. 65796ccc8cbSesaxe cmd1 = dis_cmd + " -t " + section + " " + f1 + \ 65896ccc8cbSesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile1 65996ccc8cbSesaxe cmd2 = dis_cmd + " -t " + section + " " + f2 + \ 66096ccc8cbSesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile2 66196ccc8cbSesaxe else : 66296ccc8cbSesaxe cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \ 66396ccc8cbSesaxe section + " " + f1 66496ccc8cbSesaxe cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \ 66596ccc8cbSesaxe section + " " + f2 66696ccc8cbSesaxe 66796ccc8cbSesaxe os.system(cmd1) 66896ccc8cbSesaxe os.system(cmd2) 66996ccc8cbSesaxe 67096ccc8cbSesaxe data = diffFileData(tmpFile1, tmpFile2) 67196ccc8cbSesaxe 67296ccc8cbSesaxe return (data) 67396ccc8cbSesaxe 67496ccc8cbSesaxe# 67596ccc8cbSesaxe# compare the relevant sections of two ELF binaries 67696ccc8cbSesaxe# and report any differences 67796ccc8cbSesaxe# 67896ccc8cbSesaxe# Returns: 1 if any differenes found 67996ccc8cbSesaxe# 0 if no differences found 68096ccc8cbSesaxe# -1 on error 68196ccc8cbSesaxe# 68296ccc8cbSesaxe 68396ccc8cbSesaxe# Sections deliberately not considered when comparing two ELF 68496ccc8cbSesaxe# binaries. Differences observed in these sections are not considered 68596ccc8cbSesaxe# significant where patch deliverable identification is concerned. 68696ccc8cbSesaxesections_to_skip = [ ".SUNW_signature", 68796ccc8cbSesaxe ".comment", 68896ccc8cbSesaxe ".SUNW_ctf", 68996ccc8cbSesaxe ".debug", 69096ccc8cbSesaxe ".plt", 69196ccc8cbSesaxe ".rela.bss", 69296ccc8cbSesaxe ".rela.plt", 69396ccc8cbSesaxe ".line", 69496ccc8cbSesaxe ".note", 695*f6a1d796Sesaxe ".compcom", 69696ccc8cbSesaxe ] 69796ccc8cbSesaxe 69896ccc8cbSesaxesections_preferred = [ ".rodata.str1.8", 69996ccc8cbSesaxe ".rodata.str1.1", 70096ccc8cbSesaxe ".rodata", 70196ccc8cbSesaxe ".data1", 70296ccc8cbSesaxe ".data", 70396ccc8cbSesaxe ".text", 70496ccc8cbSesaxe ] 70596ccc8cbSesaxe 70696ccc8cbSesaxedef compareElfs(base, ptch, quiet) : 70796ccc8cbSesaxe 70896ccc8cbSesaxe global logging 70996ccc8cbSesaxe 71096ccc8cbSesaxe base_header = get_elfheader(base) 71196ccc8cbSesaxe sections = base_header.keys() 71296ccc8cbSesaxe 71396ccc8cbSesaxe ptch_header = get_elfheader(ptch) 71496ccc8cbSesaxe e2_only_sections = ptch_header.keys() 71596ccc8cbSesaxe 71696ccc8cbSesaxe e1_only_sections = [] 71796ccc8cbSesaxe 71896ccc8cbSesaxe fileName = fnFormat(base) 71996ccc8cbSesaxe 72096ccc8cbSesaxe # Derive the list of ELF sections found only in 72196ccc8cbSesaxe # either e1 or e2. 72296ccc8cbSesaxe for sect in sections : 72396ccc8cbSesaxe if not sect in e2_only_sections : 72496ccc8cbSesaxe e1_only_sections.append(sect) 72596ccc8cbSesaxe else : 72696ccc8cbSesaxe e2_only_sections.remove(sect) 72796ccc8cbSesaxe 72896ccc8cbSesaxe if len(e1_only_sections) > 0 : 72996ccc8cbSesaxe if quiet : 73096ccc8cbSesaxe return 1 73196ccc8cbSesaxe info(fileName); 73296ccc8cbSesaxe if not logging : 73396ccc8cbSesaxe return 1 73496ccc8cbSesaxe 73596ccc8cbSesaxe slist = "" 73696ccc8cbSesaxe for sect in e1_only_sections : 73796ccc8cbSesaxe slist = slist + sect + "\t" 73896ccc8cbSesaxe v_info("\nELF sections found in " + \ 73996ccc8cbSesaxe base + " but not in " + ptch) 74096ccc8cbSesaxe v_info("\n" + slist) 74196ccc8cbSesaxe return 1 74296ccc8cbSesaxe 74396ccc8cbSesaxe if len(e2_only_sections) > 0 : 74496ccc8cbSesaxe if quiet : 74596ccc8cbSesaxe return 1 74696ccc8cbSesaxe 74796ccc8cbSesaxe info(fileName); 74896ccc8cbSesaxe if not logging : 74996ccc8cbSesaxe return 1 75096ccc8cbSesaxe 75196ccc8cbSesaxe slist = "" 75296ccc8cbSesaxe for sect in e2_only_sections : 75396ccc8cbSesaxe slist = slist + sect + "\t" 75496ccc8cbSesaxe v_info("\nELF sections found in " + \ 75596ccc8cbSesaxe ptch + " but not in " + base) 75696ccc8cbSesaxe v_info("\n" + slist) 75796ccc8cbSesaxe return 1 75896ccc8cbSesaxe 75996ccc8cbSesaxe # Look for preferred sections, and put those at the 76096ccc8cbSesaxe # top of the list of sections to compare 76196ccc8cbSesaxe for psect in sections_preferred : 76296ccc8cbSesaxe if psect in sections : 76396ccc8cbSesaxe sections.remove(psect) 76496ccc8cbSesaxe sections.insert(0, psect) 76596ccc8cbSesaxe 76696ccc8cbSesaxe # Compare ELF sections 76796ccc8cbSesaxe first_section = True 76896ccc8cbSesaxe for sect in sections : 76996ccc8cbSesaxe 77096ccc8cbSesaxe if sect in sections_to_skip : 77196ccc8cbSesaxe continue 77296ccc8cbSesaxe 77396ccc8cbSesaxe s1 = extract_elf_section(base, sect); 77496ccc8cbSesaxe s2 = extract_elf_section(ptch, sect); 77596ccc8cbSesaxe 77696ccc8cbSesaxe if len(s1) != len (s2) or s1 != s2: 77796ccc8cbSesaxe if not quiet: 77896ccc8cbSesaxe sh_type = base_header[sect] 77996ccc8cbSesaxe data = diff_elf_section(base, ptch, sect, \ 78096ccc8cbSesaxe sh_type) 78196ccc8cbSesaxe 78296ccc8cbSesaxe # If all ELF sections are being reported, then 78396ccc8cbSesaxe # invoke difference() to flag the file name to 78496ccc8cbSesaxe # stdout only once. Any other section differences 78596ccc8cbSesaxe # should be logged to the results file directly 78696ccc8cbSesaxe if not first_section : 78796ccc8cbSesaxe log_difference(fileName, "ELF " + sect, data) 78896ccc8cbSesaxe else : 78996ccc8cbSesaxe difference(fileName, "ELF " + sect, data) 79096ccc8cbSesaxe 79196ccc8cbSesaxe if not reportAllSects : 79296ccc8cbSesaxe return 1 79396ccc8cbSesaxe first_section = False 79496ccc8cbSesaxe return 0 79596ccc8cbSesaxe 79696ccc8cbSesaxe##### 79796ccc8cbSesaxe# Archive object comparison 79896ccc8cbSesaxe# 79996ccc8cbSesaxe# Returns 1 if difference detected 80096ccc8cbSesaxe# 0 if no difference detected 80196ccc8cbSesaxe# -1 on error 80296ccc8cbSesaxe# 80396ccc8cbSesaxedef compareArchives(base, ptch, fileType) : 80496ccc8cbSesaxe 80596ccc8cbSesaxe fileName = fnFormat(base) 80696ccc8cbSesaxe 80796ccc8cbSesaxe # clear the temp directories 80896ccc8cbSesaxe baseCmd = "rm -rf " + tmpDir1 + "*" 80996ccc8cbSesaxe status, output = commands.getstatusoutput(baseCmd) 81096ccc8cbSesaxe if status != 0 : 81196ccc8cbSesaxe error(baseCmd + " failed: " + output) 81296ccc8cbSesaxe return -1 81396ccc8cbSesaxe 81496ccc8cbSesaxe ptchCmd = "rm -rf " + tmpDir2 + "*" 81596ccc8cbSesaxe status, output = commands.getstatusoutput(ptchCmd) 81696ccc8cbSesaxe if status != 0 : 81796ccc8cbSesaxe error(ptchCmd + " failed: " + output) 81896ccc8cbSesaxe return -1 81996ccc8cbSesaxe 82096ccc8cbSesaxe # 82196ccc8cbSesaxe # Be optimistic and first try a straight file compare 82296ccc8cbSesaxe # as it will allow us to finish up quickly. 82396ccc8cbSesaxe if compareBasic(base, ptch, True, fileType) == 0 : 82496ccc8cbSesaxe return 0 82596ccc8cbSesaxe 82696ccc8cbSesaxe # copy over the objects to the temp areas, and 82796ccc8cbSesaxe # unpack them 82896ccc8cbSesaxe baseCmd = "cp -fp " + base + " " + tmpDir1 82996ccc8cbSesaxe status, output = commands.getstatusoutput(baseCmd) 83096ccc8cbSesaxe if status != 0 : 83196ccc8cbSesaxe error(baseCmd + " failed: " + output) 83296ccc8cbSesaxe return -1 83396ccc8cbSesaxe 83496ccc8cbSesaxe ptchCmd = "cp -fp " + ptch + " " + tmpDir2 83596ccc8cbSesaxe status, output = commands.getstatusoutput(ptchCmd) 83696ccc8cbSesaxe if status != 0 : 83796ccc8cbSesaxe error(ptchCmd + " failed: " + output) 83896ccc8cbSesaxe return -1 83996ccc8cbSesaxe 84096ccc8cbSesaxe bname = string.split(fileName, '/')[-1] 84196ccc8cbSesaxe if fileType == "Java Archive" : 84296ccc8cbSesaxe baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \ 84396ccc8cbSesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 84496ccc8cbSesaxe ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \ 84596ccc8cbSesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 84696ccc8cbSesaxe elif fileType == "ELF Object Archive" : 84796ccc8cbSesaxe baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \ 84896ccc8cbSesaxe bname + "; rm -f " + bname 84996ccc8cbSesaxe ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \ 85096ccc8cbSesaxe bname + "; rm -f " + bname 85196ccc8cbSesaxe else : 85296ccc8cbSesaxe error("unexpected file type: " + fileType) 85396ccc8cbSesaxe return -1 85496ccc8cbSesaxe 85596ccc8cbSesaxe os.system(baseCmd) 85696ccc8cbSesaxe os.system(ptchCmd) 85796ccc8cbSesaxe 85896ccc8cbSesaxe baseFlist = list(findFiles(tmpDir1)) 85996ccc8cbSesaxe ptchFlist = list(findFiles(tmpDir2)) 86096ccc8cbSesaxe 86196ccc8cbSesaxe # Trim leading path off base/ptch file lists 86296ccc8cbSesaxe flist = [] 86396ccc8cbSesaxe for fn in baseFlist : 86496ccc8cbSesaxe flist.append(str_prefix_trunc(fn, tmpDir1)) 86596ccc8cbSesaxe baseFlist = flist 86696ccc8cbSesaxe 86796ccc8cbSesaxe flist = [] 86896ccc8cbSesaxe for fn in ptchFlist : 86996ccc8cbSesaxe flist.append(str_prefix_trunc(fn, tmpDir2)) 87096ccc8cbSesaxe ptchFlist = flist 87196ccc8cbSesaxe 87296ccc8cbSesaxe for fn in ptchFlist : 87396ccc8cbSesaxe if not fn in baseFlist : 87496ccc8cbSesaxe difference(fileName, fileType, \ 87596ccc8cbSesaxe fn + " added to " + fileName) 87696ccc8cbSesaxe return 1 87796ccc8cbSesaxe 87896ccc8cbSesaxe for fn in baseFlist : 87996ccc8cbSesaxe if not fn in ptchFlist : 88096ccc8cbSesaxe difference(fileName, fileType, \ 88196ccc8cbSesaxe fn + " removed from " + fileName) 88296ccc8cbSesaxe return 1 88396ccc8cbSesaxe 88496ccc8cbSesaxe differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True) 88596ccc8cbSesaxe if differs : 88696ccc8cbSesaxe difference(fileName, fileType, \ 88796ccc8cbSesaxe fn + " in " + fileName + " differs") 88896ccc8cbSesaxe return 1 88996ccc8cbSesaxe return 0 89096ccc8cbSesaxe 89196ccc8cbSesaxe##### 89296ccc8cbSesaxe# (Basic) file comparison 89396ccc8cbSesaxe# 89496ccc8cbSesaxe# There's some special case code here for Javadoc HTML files 89596ccc8cbSesaxe# 89696ccc8cbSesaxe# Returns 1 if difference detected 89796ccc8cbSesaxe# 0 if no difference detected 89896ccc8cbSesaxe# -1 on error 89996ccc8cbSesaxe# 90096ccc8cbSesaxedef compareBasic(base, ptch, quiet, fileType) : 90196ccc8cbSesaxe 90296ccc8cbSesaxe fileName = fnFormat(base); 90396ccc8cbSesaxe 90496ccc8cbSesaxe if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] : 90596ccc8cbSesaxe return 1 90696ccc8cbSesaxe 90796ccc8cbSesaxe try: 90896ccc8cbSesaxe baseFile = open(base) 90996ccc8cbSesaxe except: 91096ccc8cbSesaxe error("could not open " + base) 91196ccc8cbSesaxe return -1 91296ccc8cbSesaxe try: 91396ccc8cbSesaxe ptchFile = open(ptch) 91496ccc8cbSesaxe except: 91596ccc8cbSesaxe error("could not open " + ptch) 91696ccc8cbSesaxe return -1 91796ccc8cbSesaxe 91896ccc8cbSesaxe baseData = baseFile.read() 91996ccc8cbSesaxe ptchData = ptchFile.read() 92096ccc8cbSesaxe 92196ccc8cbSesaxe baseFile.close() 92296ccc8cbSesaxe ptchFile.close() 92396ccc8cbSesaxe 92496ccc8cbSesaxe needToSnip = False 92596ccc8cbSesaxe if fileType == "HTML" : 92696ccc8cbSesaxe needToSnip = True 92796ccc8cbSesaxe toSnipBeginStr = "<!-- Generated by javadoc" 92896ccc8cbSesaxe toSnipEndStr = "-->\n" 92996ccc8cbSesaxe 93096ccc8cbSesaxe if needToSnip : 93196ccc8cbSesaxe toSnipBegin = string.find(baseData, toSnipBeginStr) 93296ccc8cbSesaxe if toSnipBegin != -1 : 93396ccc8cbSesaxe toSnipEnd = string.find(baseData[toSnipBegin:], \ 93496ccc8cbSesaxe toSnipEndStr) + \ 93596ccc8cbSesaxe len(toSnipEndStr) 93696ccc8cbSesaxe baseData = baseData[:toSnipBegin] + \ 93796ccc8cbSesaxe baseData[toSnipBegin + toSnipEnd:] 93896ccc8cbSesaxe ptchData = ptchData[:toSnipBegin] + \ 93996ccc8cbSesaxe ptchData[toSnipBegin + toSnipEnd:] 94096ccc8cbSesaxe 94196ccc8cbSesaxe if quiet : 94296ccc8cbSesaxe if baseData != ptchData : 94396ccc8cbSesaxe return 1 94496ccc8cbSesaxe else : 94596ccc8cbSesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 94696ccc8cbSesaxe diffs = diffData(baseData, ptchData) 94796ccc8cbSesaxe difference(fileName, fileType, diffs) 94896ccc8cbSesaxe return 1 94996ccc8cbSesaxe return 0 95096ccc8cbSesaxe 95196ccc8cbSesaxe 95296ccc8cbSesaxe##### 95396ccc8cbSesaxe# Compare two objects by producing a data dump from 95496ccc8cbSesaxe# each object, and then comparing the dump data 95596ccc8cbSesaxe# 95696ccc8cbSesaxe# Returns: 1 if a difference is detected 95796ccc8cbSesaxe# 0 if no difference detected 95896ccc8cbSesaxe# -1 upon error 95996ccc8cbSesaxe# 96096ccc8cbSesaxedef compareByDumping(base, ptch, quiet, fileType) : 96196ccc8cbSesaxe 96296ccc8cbSesaxe fileName = fnFormat(base); 96396ccc8cbSesaxe 96496ccc8cbSesaxe if fileType == "Lint Library" : 96596ccc8cbSesaxe baseCmd = lintdump_cmd + " -ir " + base + \ 96696ccc8cbSesaxe " | grep -v LINTLIB:" + " > " + tmpFile1 96796ccc8cbSesaxe ptchCmd = lintdump_cmd + " -ir " + ptch + \ 96896ccc8cbSesaxe " | grep -v LINTLIB:" + " > " + tmpFile2 96996ccc8cbSesaxe elif fileType == "Sqlite Database" : 97096ccc8cbSesaxe baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \ 97196ccc8cbSesaxe tmpFile1 97296ccc8cbSesaxe ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \ 97396ccc8cbSesaxe tmpFile2 97496ccc8cbSesaxe 97596ccc8cbSesaxe os.system(baseCmd) 97696ccc8cbSesaxe os.system(ptchCmd) 97796ccc8cbSesaxe 97896ccc8cbSesaxe try: 97996ccc8cbSesaxe baseFile = open(tmpFile1) 98096ccc8cbSesaxe except: 98196ccc8cbSesaxe error("could not open: " + tmpFile1) 98296ccc8cbSesaxe try: 98396ccc8cbSesaxe ptchFile = open(tmpFile2) 98496ccc8cbSesaxe except: 98596ccc8cbSesaxe error("could not open: " + tmpFile2) 98696ccc8cbSesaxe 98796ccc8cbSesaxe baseData = baseFile.read() 98896ccc8cbSesaxe ptchData = ptchFile.read() 98996ccc8cbSesaxe 99096ccc8cbSesaxe baseFile.close() 99196ccc8cbSesaxe ptchFile.close() 99296ccc8cbSesaxe 99396ccc8cbSesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 99496ccc8cbSesaxe if not quiet : 99596ccc8cbSesaxe data = diffFileData(tmpFile1, tmpFile2); 99696ccc8cbSesaxe difference(fileName, fileType, data) 99796ccc8cbSesaxe return 1 99896ccc8cbSesaxe return 0 99996ccc8cbSesaxe 100096ccc8cbSesaxe##### 100196ccc8cbSesaxe# Compare two objects. Detect type changes. 100296ccc8cbSesaxe# Vector off to the appropriate type specific 100396ccc8cbSesaxe# compare routine based on the type. 100496ccc8cbSesaxe# 100596ccc8cbSesaxedef compareOneFile(base, ptch, quiet) : 100696ccc8cbSesaxe 100796ccc8cbSesaxe # Verify the file types. 100896ccc8cbSesaxe # If they are different, indicate this and move on 100996ccc8cbSesaxe btype = getTheFileType(base) 101096ccc8cbSesaxe ptype = getTheFileType(ptch) 101196ccc8cbSesaxe 101296ccc8cbSesaxe fileName = fnFormat(base) 101396ccc8cbSesaxe 101496ccc8cbSesaxe if (btype != ptype) : 101596ccc8cbSesaxe difference(fileName, "file type", btype + " to " + ptype) 101696ccc8cbSesaxe return 1 101796ccc8cbSesaxe else : 101896ccc8cbSesaxe fileType = btype 101996ccc8cbSesaxe 102096ccc8cbSesaxe if (fileType == 'ELF') : 102196ccc8cbSesaxe return compareElfs(base, ptch, quiet) 102296ccc8cbSesaxe 102396ccc8cbSesaxe elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') : 102496ccc8cbSesaxe return compareArchives(base, ptch, fileType) 102596ccc8cbSesaxe 102696ccc8cbSesaxe elif (fileType == 'HTML') : 102796ccc8cbSesaxe return compareBasic(base, ptch, quiet, fileType) 102896ccc8cbSesaxe 102996ccc8cbSesaxe elif ( fileType == 'Lint Library' ) : 103096ccc8cbSesaxe return compareByDumping(base, ptch, quiet, fileType) 103196ccc8cbSesaxe 103296ccc8cbSesaxe elif ( fileType == 'Sqlite Database' ) : 103396ccc8cbSesaxe return compareByDumping(base, ptch, quiet, fileType) 103496ccc8cbSesaxe else : 103596ccc8cbSesaxe # it has to be some variety of text file 103696ccc8cbSesaxe return compareBasic(base, ptch, quiet, fileType) 103796ccc8cbSesaxe 103896ccc8cbSesaxe# Cleanup and self-terminate 103996ccc8cbSesaxedef cleanup(ret) : 104096ccc8cbSesaxe 104196ccc8cbSesaxe if len(tmpDir1) > 0 and len(tmpDir2) > 0 : 104296ccc8cbSesaxe 104396ccc8cbSesaxe baseCmd = "rm -rf " + tmpDir1 104496ccc8cbSesaxe ptchCmd = "rm -rf " + tmpDir2 104596ccc8cbSesaxe 104696ccc8cbSesaxe os.system(baseCmd) 104796ccc8cbSesaxe os.system(ptchCmd) 104896ccc8cbSesaxe 104996ccc8cbSesaxe if logging : 105096ccc8cbSesaxe log.close() 105196ccc8cbSesaxe 105296ccc8cbSesaxe sys.exit(ret) 105396ccc8cbSesaxe 105496ccc8cbSesaxedef main() : 105596ccc8cbSesaxe 105696ccc8cbSesaxe # Log file handle 105796ccc8cbSesaxe global log 105896ccc8cbSesaxe 105996ccc8cbSesaxe # Globals relating to command line options 106096ccc8cbSesaxe global logging, vdiffs, reportAllSects 106196ccc8cbSesaxe 106296ccc8cbSesaxe # Named temporary files / directories 106396ccc8cbSesaxe global tmpDir1, tmpDir2, tmpFile1, tmpFile2 106496ccc8cbSesaxe 106596ccc8cbSesaxe # Command paths 106696ccc8cbSesaxe global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd 106796ccc8cbSesaxe 106896ccc8cbSesaxe # Default search path 106996ccc8cbSesaxe global wsdiff_path 107096ccc8cbSesaxe 107196ccc8cbSesaxe # Essentially "uname -p" 107296ccc8cbSesaxe global arch 107396ccc8cbSesaxe 107496ccc8cbSesaxe # Some globals need to be initialized 107596ccc8cbSesaxe logging = vdiffs = reportAllSects = False 107696ccc8cbSesaxe 107796ccc8cbSesaxe 107896ccc8cbSesaxe # Process command line arguments 107996ccc8cbSesaxe # Return values are returned from args() in alpha order 108096ccc8cbSesaxe # (Yes, python functions can return multiple values (ewww)) 108196ccc8cbSesaxe # Note that args() also set the globals: 108296ccc8cbSesaxe # logging to True if verbose logging (to a file) was enabled 108396ccc8cbSesaxe # vdiffs to True if logged differences aren't to be truncated 108496ccc8cbSesaxe # reportAllSects to True if all ELF section differences are to be reported 108596ccc8cbSesaxe # 108696ccc8cbSesaxe baseRoot, fileNamesFile, localTools, ptchRoot, results = args() 108796ccc8cbSesaxe 108896ccc8cbSesaxe # 108996ccc8cbSesaxe # Set up the results/log file 109096ccc8cbSesaxe # 109196ccc8cbSesaxe if logging : 109296ccc8cbSesaxe try: 109396ccc8cbSesaxe log = open(results, "w") 109496ccc8cbSesaxe except: 109596ccc8cbSesaxe logging = False 109696ccc8cbSesaxe error("failed to open log file: " + log) 109796ccc8cbSesaxe sys.exit(1) 109896ccc8cbSesaxe 109996ccc8cbSesaxe dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6] 110096ccc8cbSesaxe v_info("# This file was produced by wsdiff") 110196ccc8cbSesaxe v_info(dateTimeStr) 110296ccc8cbSesaxe 110396ccc8cbSesaxe # 110496ccc8cbSesaxe # Build paths to the tools required tools 110596ccc8cbSesaxe # 110696ccc8cbSesaxe # Try to look for tools in $SRC/tools if the "-t" option 110796ccc8cbSesaxe # was specified 110896ccc8cbSesaxe # 110996ccc8cbSesaxe arch = commands.getoutput("uname -p") 111096ccc8cbSesaxe if localTools : 111196ccc8cbSesaxe try: 111296ccc8cbSesaxe src = os.environ['SRC'] 111396ccc8cbSesaxe except: 111496ccc8cbSesaxe error("-t specified, but $SRC not set. Cannot find $SRC/tools") 111596ccc8cbSesaxe src = "" 111696ccc8cbSesaxe if len(src) > 0 : 111796ccc8cbSesaxe wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin") 111896ccc8cbSesaxe 111996ccc8cbSesaxe lintdump_cmd = find_tool("lintdump") 112096ccc8cbSesaxe elfdump_cmd = find_tool("elfdump") 112196ccc8cbSesaxe dump_cmd = find_tool("dump") 112296ccc8cbSesaxe od_cmd = find_tool("od") 112396ccc8cbSesaxe dis_cmd = find_tool("dis") 112496ccc8cbSesaxe diff_cmd = find_tool("diff") 112596ccc8cbSesaxe sqlite_cmd = find_tool("sqlite") 112696ccc8cbSesaxe 112796ccc8cbSesaxe # 112896ccc8cbSesaxe # validate the base and patch paths 112996ccc8cbSesaxe # 113096ccc8cbSesaxe if baseRoot[-1] != '/' : 113196ccc8cbSesaxe baseRoot += '/' 113296ccc8cbSesaxe 113396ccc8cbSesaxe if ptchRoot[-1] != '/' : 113496ccc8cbSesaxe ptchRoot += '/' 113596ccc8cbSesaxe 113696ccc8cbSesaxe if not os.path.exists(baseRoot) : 113796ccc8cbSesaxe error("old proto area: " + baseRoot + " does not exist") 113896ccc8cbSesaxe sys.exit(1) 113996ccc8cbSesaxe 114096ccc8cbSesaxe if not os.path.exists(ptchRoot) : 114196ccc8cbSesaxe error("new proto area: " + ptchRoot + \ 114296ccc8cbSesaxe " does not exist") 114396ccc8cbSesaxe sys.exit(1) 114496ccc8cbSesaxe 114596ccc8cbSesaxe # 114696ccc8cbSesaxe # log some information identifying the run 114796ccc8cbSesaxe # 114896ccc8cbSesaxe v_info("Old proto area: " + baseRoot) 114996ccc8cbSesaxe v_info("New proto area: " + ptchRoot) 115096ccc8cbSesaxe v_info("Results file: " + results + "\n") 115196ccc8cbSesaxe 115296ccc8cbSesaxe # 115396ccc8cbSesaxe # Set up the temporary directories / files 115496ccc8cbSesaxe # Could use python's tmpdir routines, but these should 115596ccc8cbSesaxe # be easier to identify / keep around for debugging 115696ccc8cbSesaxe pid = os.getpid() 115796ccc8cbSesaxe tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/" 115896ccc8cbSesaxe tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/" 115996ccc8cbSesaxe if not os.path.exists(tmpDir1) : 116096ccc8cbSesaxe os.makedirs(tmpDir1) 116196ccc8cbSesaxe if not os.path.exists(tmpDir2) : 116296ccc8cbSesaxe os.makedirs(tmpDir2) 116396ccc8cbSesaxe 116496ccc8cbSesaxe tmpFile1 = tmpDir1 + "f1" 116596ccc8cbSesaxe tmpFile2 = tmpDir2 + "f2" 116696ccc8cbSesaxe 116796ccc8cbSesaxe # Derive a catalog of new, deleted, and to-be-compared objects 116896ccc8cbSesaxe # either from the specified base and patch proto areas, or from 116996ccc8cbSesaxe # from an input file list 117096ccc8cbSesaxe newOrDeleted = False 117196ccc8cbSesaxe 117296ccc8cbSesaxe if fileNamesFile != "" : 117396ccc8cbSesaxe changedFiles, newFiles, deletedFiles = \ 117496ccc8cbSesaxe flistCatalog(baseRoot, ptchRoot, fileNamesFile) 117596ccc8cbSesaxe else : 117696ccc8cbSesaxe changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot) 117796ccc8cbSesaxe 117896ccc8cbSesaxe if len(newFiles) > 0 : 117996ccc8cbSesaxe newOrDeleted = True 118096ccc8cbSesaxe info("\nNew objects found: ") 118196ccc8cbSesaxe 118296ccc8cbSesaxe for fn in newFiles : 118396ccc8cbSesaxe info(fnFormat(fn)) 118496ccc8cbSesaxe 118596ccc8cbSesaxe if len(deletedFiles) > 0 : 118696ccc8cbSesaxe newOrDeleted = True 118796ccc8cbSesaxe info("\nObjects removed: ") 118896ccc8cbSesaxe 118996ccc8cbSesaxe for fn in deletedFiles : 119096ccc8cbSesaxe info(fnFormat(fn)) 119196ccc8cbSesaxe 119296ccc8cbSesaxe if newOrDeleted : 119396ccc8cbSesaxe info("\nChanged objects: "); 119496ccc8cbSesaxe 119596ccc8cbSesaxe 119696ccc8cbSesaxe # Here's where all the heavy lifting happens 119796ccc8cbSesaxe # Perform a comparison on each object appearing in 119896ccc8cbSesaxe # both proto areas. compareOneFile will examine the 119996ccc8cbSesaxe # file types of each object, and will vector off to 120096ccc8cbSesaxe # the appropriate comparison routine, where the compare 120196ccc8cbSesaxe # will happen, and any differences will be reported / logged 120296ccc8cbSesaxe for fn in changedFiles : 120396ccc8cbSesaxe base = baseRoot + fn 120496ccc8cbSesaxe ptch = ptchRoot + fn 120596ccc8cbSesaxe 120696ccc8cbSesaxe compareOneFile(base, ptch, False) 120796ccc8cbSesaxe 120896ccc8cbSesaxe # We're done, cleanup. 120996ccc8cbSesaxe cleanup(0) 121096ccc8cbSesaxe 121196ccc8cbSesaxeif __name__ == '__main__' : 121296ccc8cbSesaxe try: 121396ccc8cbSesaxe main() 121496ccc8cbSesaxe except KeyboardInterrupt : 121596ccc8cbSesaxe cleanup(1); 121696ccc8cbSesaxe 121796ccc8cbSesaxe 1218