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