1 #!/usr/sfw/bin/python 2 # 3 # CDDL HEADER START 4 # 5 # The contents of this file are subject to the terms of the 6 # Common Development and Distribution License (the "License"). 7 # You may not use this file except in compliance with the License. 8 # 9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 # or http://www.opensolaris.org/os/licensing. 11 # See the License for the specific language governing permissions 12 # and limitations under the License. 13 # 14 # When distributing Covered Code, include this CDDL HEADER in each 15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 # If applicable, add the following below this CDDL HEADER, with the 17 # fields enclosed by brackets "[]" replaced with your own identifying 18 # information: Portions Copyright [yyyy] [name of copyright owner] 19 # 20 # CDDL HEADER END 21 # 22 # Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 # Use is subject to license terms. 24 # 25 #ident "%Z%%M% %I% %E% SMI" 26 27 # 28 # wsdiff(1) is a tool that can be used to determine which compiled objects 29 # have changed as a result of a given source change. Developers backporting 30 # new features, RFEs and bug fixes need to be able to identify the set of 31 # patch deliverables necessary for feature/fix realization on a patched system. 32 # 33 # The tool works by comparing objects in two trees/proto areas (one build with, 34 # and without the source changes. 35 # 36 # Using wsdiff(1) is fairly simple: 37 # - Bringover to a fresh workspace 38 # - Perform a full non-debug build (clobber if workspace isn't fresh) 39 # - Move the proto area aside, call it proto.old, or something. 40 # - Integrate your changes to the workspace 41 # - Perform another full non-debug clobber build. 42 # - Use wsdiff(1) to see what changed: 43 # $ wsdiff proto.old proto 44 # 45 # By default, wsdiff will print the list of changed objects / deliverables to 46 # stdout. If a results file is specified via -r, the list of differing objects, 47 # and details about why wsdiff(1) thinks they are different will be logged to 48 # the results file. 49 # 50 # By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use 51 # wsdiff(1) to report on what objects changed since the last build. 52 # 53 # For patch deliverable purposes, it's advised to have nightly do a clobber, 54 # non-debug build. 55 # 56 # Think about the results. Was something flagged that you don't expect? Go look 57 # at the results file to see details about the differences. 58 # 59 # Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1) 60 # report with more verbosity. 61 # 62 # Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 63 # 64 # Where "old" is the path to the proto area build without the changes, and 65 # "new" is the path to the proto area built with the changes. The following 66 # options are supported: 67 # 68 # -v Do not truncate observed diffs in results 69 # -V Log *all* ELF sect diffs vs. logging the first diff found 70 # -t Use onbld tools in $SRC/tools 71 # -r Log results and observed differences 72 # -i Tell wsdiff which objects to compare via an input file list 73 74 import datetime, fnmatch, getopt, profile, os, popen2, commands 75 import re, select, string, struct, sys, tempfile, time 76 from stat import * 77 78 # Human readable diffs truncated by default if longer than this 79 # Specifying -v on the command line will override 80 diffs_sz_thresh = 4096 81 82 # Default search path for wsdiff 83 wsdiff_path = [ "/usr/bin", 84 "/usr/ccs/bin", 85 "/lib/svc/bin", 86 "/opt/onbld/bin" ] 87 88 # These are objects that wsdiff will notice look different, but will not report. 89 # Existence of an exceptions list, and adding things here is *dangerous*, 90 # and therefore the *only* reasons why anything would be listed here is because 91 # the objects do not build deterministically, yet we *cannot* fix this. 92 # 93 # These perl libraries use __DATE__ and therefore always look different. 94 # Ideally, we would purge use the use of __DATE__ from the source, but because 95 # this is source we wish to distribute with Solaris "unchanged", we cannot modify. 96 # 97 wsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1", 98 "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 99 "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1", 100 "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1" 101 ] 102 103 ##### 104 # Logging routines 105 # 106 107 # Informational message to be printed to the screen, and the log file 108 def info(msg) : 109 110 print >> sys.stdout, msg 111 if logging : 112 print >> log, msg 113 sys.stdout.flush() 114 115 # Error message to be printed to the screen, and the log file 116 def error(msg) : 117 118 print >> sys.stderr, "ERROR:", msg 119 sys.stderr.flush() 120 if logging : 121 print >> log, "ERROR:", msg 122 log.flush() 123 124 # Informational message to be printed only to the log, if there is one. 125 def v_info(msg) : 126 127 if logging : 128 print >> log, msg 129 log.flush() 130 131 # 132 # Flag a detected file difference 133 # Display the fileName to stdout, and log the difference 134 # 135 def difference(f, dtype, diffs) : 136 137 if f in wsdiff_exceptions : 138 return 139 140 print >> sys.stdout, f 141 sys.stdout.flush() 142 143 log_difference(f, dtype, diffs) 144 145 # 146 # Do the actual logging of the difference to the results file 147 # 148 def log_difference(f, dtype, diffs) : 149 if logging : 150 print >> log, f 151 print >> log, "NOTE:", dtype, "difference detected." 152 153 difflen = len(diffs) 154 if difflen > 0 : 155 print >> log 156 157 if not vdiffs and difflen > diffs_sz_thresh : 158 print >> log, diffs[:diffs_sz_thresh] 159 print >> log, \ 160 "... truncated due to length: " \ 161 "use -v to override ..." 162 else : 163 print >> log, diffs 164 print >> log, "\n" 165 log.flush() 166 167 168 ##### 169 # diff generating routines 170 # 171 172 # 173 # Return human readable diffs from two temporary files 174 # 175 def diffFileData(tmpf1, tmpf2) : 176 177 # Filter the data through od(1) if the data is detected 178 # as being binary 179 if isBinary(tmpf1) or isBinary(tmpf2) : 180 tmp_od1 = tmpf1 + ".od" 181 tmp_od2 = tmpf2 + ".od" 182 183 cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1 184 os.system(cmd) 185 cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2 186 os.system(cmd) 187 188 tmpf1 = tmp_od1 189 tmpf2 = tmp_od2 190 191 data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2) 192 193 return data 194 195 # 196 # Return human readable diffs betweeen two datasets 197 # 198 def diffData(d1, d2) : 199 200 global tmpFile1 201 global tmpFile2 202 203 try: 204 fd1 = open(tmpFile1, "w") 205 except: 206 error("failed to open: " + tmpFile1) 207 cleanup(1) 208 try: 209 fd2 = open(tmpFile2, "w") 210 except: 211 error("failed to open: " + tmpFile2) 212 cleanup(1) 213 214 fd1.write(d1) 215 fd2.write(d2) 216 fd1.close() 217 fd2.close() 218 219 return diffFileData(tmpFile1, tmpFile2) 220 221 ##### 222 # Misc utility functions 223 # 224 225 # Prune off the leading prefix from string s 226 def str_prefix_trunc(s, prefix) : 227 snipLen = len(prefix) 228 return s[snipLen:] 229 230 # 231 # Prune off leading proto path goo (if there is one) to yield 232 # the deliverable's eventual path relative to root 233 # e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat 234 # 235 def fnFormat(fn) : 236 root_arch_str = "root_" + arch 237 238 pos = fn.find(root_arch_str) 239 if pos == -1 : 240 return fn 241 242 pos = fn.find("/", pos) 243 if pos == -1 : 244 return fn 245 246 return fn[pos + 1:] 247 248 ##### 249 # Usage / argument processing 250 # 251 252 # 253 # Display usage message 254 # 255 def usage() : 256 sys.stdout.flush() 257 print >> sys.stderr, """Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 258 -v Do not truncate observed diffs in results 259 -V Log *all* ELF sect diffs vs. logging the first diff found 260 -t Use onbld tools in $SRC/tools 261 -r Log results and observed differences 262 -i Tell wsdiff which objects to compare via an input file list""" 263 sys.exit(1) 264 265 # 266 # Process command line options 267 # 268 def args() : 269 270 global logging 271 global vdiffs 272 global reportAllSects 273 274 validOpts = 'i:r:vVt?' 275 276 baseRoot = "" 277 ptchRoot = "" 278 fileNamesFile = "" 279 results = "" 280 localTools = False 281 282 # getopt.getopt() returns: 283 # an option/value tuple 284 # a list of remaining non-option arguments 285 # 286 # A correct wsdiff invocation will have exactly two non option 287 # arguments, the paths to the base (old), ptch (new) proto areas 288 try: 289 optlist, args = getopt.getopt(sys.argv[1:], validOpts) 290 except getopt.error, val: 291 usage() 292 293 if len(args) != 2 : 294 usage(); 295 296 for opt,val in optlist : 297 if opt == '-i' : 298 fileNamesFile = val 299 elif opt == '-r' : 300 results = val 301 logging = True 302 elif opt == '-v' : 303 vdiffs = True 304 elif opt == '-V' : 305 reportAllSects = True 306 elif opt == '-t': 307 localTools = True 308 else: 309 usage() 310 311 baseRoot = args[0] 312 ptchRoot = args[1] 313 314 if len(baseRoot) == 0 or len(ptchRoot) == 0 : 315 usage() 316 317 if logging and len(results) == 0 : 318 usage() 319 320 if vdiffs and not logging : 321 error("The -v option requires a results file (-r)") 322 sys.exit(1) 323 324 if reportAllSects and not logging : 325 error("The -V option requires a results file (-r)") 326 sys.exit(1) 327 328 # alphabetical order 329 return baseRoot, fileNamesFile, localTools, ptchRoot, results 330 331 ##### 332 # File identification 333 # 334 335 # 336 # Identify the file type. 337 # If it's not ELF, use the file extension to identify 338 # certain file types that require special handling to 339 # compare. Otherwise just return a basic "ASCII" type. 340 # 341 def getTheFileType(f) : 342 343 extensions = { 'a' : 'ELF Object Archive', 344 'jar' : 'Java Archive', 345 'html' : 'HTML', 346 'ln' : 'Lint Library', 347 'esa' : 'Elfsign Activation', 348 'db' : 'Sqlite Database' } 349 350 try: 351 if os.stat(f)[ST_SIZE] == 0 : 352 return 'ASCII' 353 except: 354 error("failed to stat " + f) 355 return 'Error' 356 357 if isELF(f) == 1 : 358 return 'ELF' 359 360 fnamelist = f.split('.') 361 if len(fnamelist) > 1 : # Test the file extension 362 extension = fnamelist[-1] 363 if extension in extensions.keys(): 364 return extensions[extension] 365 366 return 'ASCII' 367 368 # 369 # Return non-zero if "f" is an ELF file 370 # 371 elfmagic = '\177ELF' 372 def isELF(f) : 373 try: 374 fd = open(f) 375 except: 376 error("failed to open: " + f) 377 return 0 378 magic = fd.read(len(elfmagic)) 379 fd.close() 380 381 if magic == elfmagic : 382 return 1 383 return 0 384 385 # 386 # Return non-zero is "f" is binary. 387 # Consider the file to be binary if it contains any null characters 388 # 389 def isBinary(f) : 390 try: 391 fd = open(f) 392 except: 393 error("failed to open: " + f) 394 return 0 395 s = fd.read() 396 fd.close() 397 398 if s.find('\0') == -1 : 399 return 0 400 else : 401 return 1 402 403 ##### 404 # Directory traversal and file finding 405 # 406 407 # 408 # Return a sorted list of files found under the specified directory 409 # 410 def findFiles(d) : 411 for path, subdirs, files in os.walk(d) : 412 files.sort() 413 for name in files : 414 yield os.path.join(path, name) 415 416 # 417 # Examine all files in base, ptch 418 # 419 # Return a list of files appearing in both proto areas, 420 # a list of new files (files found only in ptch) and 421 # a list of deleted files (files found only in base) 422 # 423 def protoCatalog(base, ptch) : 424 compFiles = [] # List of files in both proto areas 425 ptchList = [] # List of file in patch proto area 426 427 newFiles = [] # New files detected 428 deletedFiles = [] # Deleted files 429 430 baseFilesList = list(findFiles(base)) 431 baseStringLength = len(base) 432 433 ptchFilesList = list(findFiles(ptch)) 434 ptchStringLength = len(ptch) 435 436 # Inventory files in the base proto area 437 for fn in baseFilesList : 438 if os.path.islink(fn) : 439 continue 440 441 fileName = fn[baseStringLength:] 442 compFiles.append(fileName) 443 444 # Inventory files in the patch proto area 445 for fn in ptchFilesList : 446 if os.path.islink(fn) : 447 continue 448 449 fileName = fn[ptchStringLength:] 450 ptchList.append(fileName) 451 452 # Deleted files appear in the base area, but not the patch area 453 for fileName in compFiles : 454 if not fileName in ptchList : 455 deletedFiles.append(fileName) 456 457 # Eliminate "deleted" files from the list of objects appearing 458 # in both the base and patch proto areas 459 for fileName in deletedFiles : 460 try: 461 compFiles.remove(fileName) 462 except: 463 error("filelist.remove() failed") 464 465 # New files appear in the patch area, but not the base 466 for fileName in ptchList : 467 if not fileName in compFiles : 468 newFiles.append(fileName) 469 470 return compFiles, newFiles, deletedFiles 471 472 # 473 # Examine the files listed in the input file list 474 # 475 # Return a list of files appearing in both proto areas, 476 # a list of new files (files found only in ptch) and 477 # a list of deleted files (files found only in base) 478 # 479 def flistCatalog(base, ptch, flist) : 480 compFiles = [] # List of files in both proto areas 481 newFiles = [] # New files detected 482 deletedFiles = [] # Deleted files 483 484 try: 485 fd = open(flist, "r") 486 except: 487 error("could not open: " + flist) 488 cleanup(1) 489 490 files = [] 491 files = fd.readlines() 492 493 for f in files : 494 ptch_present = True 495 base_present = True 496 497 if f == '\n' : 498 continue 499 500 # the fileNames have a trailing '\n' 501 f = f.rstrip() 502 503 # The objects in the file list have paths relative 504 # to $ROOT or to the base/ptch directory specified on 505 # the command line. 506 # If it's relative to $ROOT, we'll need to add back the 507 # root_`uname -p` goo we stripped off in fnFormat() 508 if os.path.exists(base + f) : 509 fn = f; 510 elif os.path.exists(base + "root_" + arch + "/" + f) : 511 fn = "root_" + arch + "/" + f 512 else : 513 base_present = False 514 515 if base_present : 516 if not os.path.exists(ptch + fn) : 517 ptch_present = False 518 else : 519 if os.path.exists(ptch + f) : 520 fn = f 521 elif os.path.exists(ptch + "root_" + arch + "/" + f) : 522 fn = "root_" + arch + "/" + f 523 else : 524 ptch_present = False 525 526 if os.path.islink(base + fn) : # ignore links 527 base_present = False 528 if os.path.islink(ptch + fn) : 529 ptch_present = False 530 531 if base_present and ptch_present : 532 compFiles.append(fn) 533 elif base_present : 534 deletedFiles.append(fn) 535 elif ptch_present : 536 newFiles.append(fn) 537 else : 538 if os.path.islink(base + fn) and os.path.islink(ptch + fn) : 539 continue 540 error(f + " in file list, but not in either tree. Skipping...") 541 542 return compFiles, newFiles, deletedFiles 543 544 545 # 546 # Build a fully qualified path to an external tool/utility. 547 # Consider the default system locations. For onbld tools, if 548 # the -t option was specified, we'll try to use built tools in $SRC tools, 549 # and otherwise, we'll fall back on /opt/onbld/ 550 # 551 def find_tool(tool) : 552 553 # First, check what was passed 554 if os.path.exists(tool) : 555 return tool 556 557 # Next try in wsdiff path 558 for pdir in wsdiff_path : 559 location = pdir + "/" + tool 560 if os.path.exists(location) : 561 return location + " " 562 563 location = pdir + "/" + arch + "/" + tool 564 if os.path.exists(location) : 565 return location + " " 566 567 error("Could not find path to: " + tool); 568 sys.exit(1); 569 570 571 ##### 572 # ELF file comparison helper routines 573 # 574 575 # 576 # Return a dictionary of ELF section types keyed by section name 577 # 578 def get_elfheader(f) : 579 580 header = {} 581 582 hstring = commands.getoutput(elfdump_cmd + " -c " + f) 583 584 if len(hstring) == 0 : 585 error("Failed to dump ELF header for " + f) 586 return 587 588 # elfdump(1) dumps the section headers with the section name 589 # following "sh_name:", and the section type following "sh_type:" 590 sections = hstring.split("Section Header") 591 for sect in sections : 592 datap = sect.find("sh_name:"); 593 if datap == -1 : 594 continue 595 section = sect[datap:].split()[1] 596 datap = sect.find("sh_type:"); 597 if datap == -1 : 598 error("Could not get type for sect: " + section + \ 599 " in " + f) 600 sh_type = sect[datap:].split()[2] 601 header[section] = sh_type 602 603 return header 604 605 # 606 # Extract data in the specified ELF section from the given file 607 # 608 def extract_elf_section(f, section) : 609 610 data = commands.getoutput(dump_cmd + " -sn " + section + " " + f) 611 612 if len(data) == 0 : 613 error(cmd + " yielded no data") 614 return 615 616 # dump(1) displays the file name to start... 617 # get past it to the data itself 618 dbegin = data.find(":") + 1 619 data = data[dbegin:]; 620 621 return (data) 622 623 # 624 # Return a (hopefully meaningful) human readable set of diffs 625 # for the specified ELF section between f1 and f2 626 # 627 # Depending on the section, various means for dumping and diffing 628 # the data may be employed. 629 # 630 text_sections = [ '.text', '.init', '.fini' ] 631 def diff_elf_section(f1, f2, section, sh_type) : 632 633 if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA 634 cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1 635 cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2 636 elif (section == ".group") : 637 cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1 638 cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2 639 elif (section == ".hash") : 640 cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1 641 cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2 642 elif (section == ".dynamic") : 643 cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1 644 cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2 645 elif (section == ".got") : 646 cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1 647 cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2 648 elif (section == ".SUNW_cap") : 649 cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1 650 cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2 651 elif (section == ".interp") : 652 cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1 653 cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2 654 elif (section == ".symtab" or section == ".dynsym") : 655 cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1 656 cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2 657 elif (section in text_sections) : 658 # dis sometimes complains when it hits something it doesn't 659 # know how to disassemble. Just ignore it, as the output 660 # being generated here is human readable, and we've already 661 # correctly flagged the difference. 662 cmd1 = dis_cmd + " -t " + section + " " + f1 + \ 663 " 2>/dev/null | grep -v disassembly > " + tmpFile1 664 cmd2 = dis_cmd + " -t " + section + " " + f2 + \ 665 " 2>/dev/null | grep -v disassembly > " + tmpFile2 666 else : 667 cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \ 668 section + " " + f1 669 cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \ 670 section + " " + f2 671 672 os.system(cmd1) 673 os.system(cmd2) 674 675 data = diffFileData(tmpFile1, tmpFile2) 676 677 return (data) 678 679 # 680 # compare the relevant sections of two ELF binaries 681 # and report any differences 682 # 683 # Returns: 1 if any differenes found 684 # 0 if no differences found 685 # -1 on error 686 # 687 688 # Sections deliberately not considered when comparing two ELF 689 # binaries. Differences observed in these sections are not considered 690 # significant where patch deliverable identification is concerned. 691 sections_to_skip = [ ".SUNW_signature", 692 ".comment", 693 ".SUNW_ctf", 694 ".debug", 695 ".plt", 696 ".rela.bss", 697 ".rela.plt", 698 ".line", 699 ".note", 700 ".compcom", 701 ] 702 703 sections_preferred = [ ".rodata.str1.8", 704 ".rodata.str1.1", 705 ".rodata", 706 ".data1", 707 ".data", 708 ".text", 709 ] 710 711 def compareElfs(base, ptch, quiet) : 712 713 global logging 714 715 base_header = get_elfheader(base) 716 sections = base_header.keys() 717 718 ptch_header = get_elfheader(ptch) 719 e2_only_sections = ptch_header.keys() 720 721 e1_only_sections = [] 722 723 fileName = fnFormat(base) 724 725 # Derive the list of ELF sections found only in 726 # either e1 or e2. 727 for sect in sections : 728 if not sect in e2_only_sections : 729 e1_only_sections.append(sect) 730 else : 731 e2_only_sections.remove(sect) 732 733 if len(e1_only_sections) > 0 : 734 if quiet : 735 return 1 736 info(fileName); 737 if not logging : 738 return 1 739 740 slist = "" 741 for sect in e1_only_sections : 742 slist = slist + sect + "\t" 743 v_info("\nELF sections found in " + \ 744 base + " but not in " + ptch) 745 v_info("\n" + slist) 746 return 1 747 748 if len(e2_only_sections) > 0 : 749 if quiet : 750 return 1 751 752 info(fileName); 753 if not logging : 754 return 1 755 756 slist = "" 757 for sect in e2_only_sections : 758 slist = slist + sect + "\t" 759 v_info("\nELF sections found in " + \ 760 ptch + " but not in " + base) 761 v_info("\n" + slist) 762 return 1 763 764 # Look for preferred sections, and put those at the 765 # top of the list of sections to compare 766 for psect in sections_preferred : 767 if psect in sections : 768 sections.remove(psect) 769 sections.insert(0, psect) 770 771 # Compare ELF sections 772 first_section = True 773 for sect in sections : 774 775 if sect in sections_to_skip : 776 continue 777 778 s1 = extract_elf_section(base, sect); 779 s2 = extract_elf_section(ptch, sect); 780 781 if len(s1) != len (s2) or s1 != s2: 782 if not quiet: 783 sh_type = base_header[sect] 784 data = diff_elf_section(base, ptch, sect, \ 785 sh_type) 786 787 # If all ELF sections are being reported, then 788 # invoke difference() to flag the file name to 789 # stdout only once. Any other section differences 790 # should be logged to the results file directly 791 if not first_section : 792 log_difference(fileName, "ELF " + sect, data) 793 else : 794 difference(fileName, "ELF " + sect, data) 795 796 if not reportAllSects : 797 return 1 798 first_section = False 799 return 0 800 801 ##### 802 # Archive object comparison 803 # 804 # Returns 1 if difference detected 805 # 0 if no difference detected 806 # -1 on error 807 # 808 def compareArchives(base, ptch, fileType) : 809 810 fileName = fnFormat(base) 811 812 # clear the temp directories 813 baseCmd = "rm -rf " + tmpDir1 + "*" 814 status, output = commands.getstatusoutput(baseCmd) 815 if status != 0 : 816 error(baseCmd + " failed: " + output) 817 return -1 818 819 ptchCmd = "rm -rf " + tmpDir2 + "*" 820 status, output = commands.getstatusoutput(ptchCmd) 821 if status != 0 : 822 error(ptchCmd + " failed: " + output) 823 return -1 824 825 # 826 # Be optimistic and first try a straight file compare 827 # as it will allow us to finish up quickly. 828 if compareBasic(base, ptch, True, fileType) == 0 : 829 return 0 830 831 # copy over the objects to the temp areas, and 832 # unpack them 833 baseCmd = "cp -fp " + base + " " + tmpDir1 834 status, output = commands.getstatusoutput(baseCmd) 835 if status != 0 : 836 error(baseCmd + " failed: " + output) 837 return -1 838 839 ptchCmd = "cp -fp " + ptch + " " + tmpDir2 840 status, output = commands.getstatusoutput(ptchCmd) 841 if status != 0 : 842 error(ptchCmd + " failed: " + output) 843 return -1 844 845 bname = string.split(fileName, '/')[-1] 846 if fileType == "Java Archive" : 847 baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \ 848 "; rm -f " + bname + " META-INF/MANIFEST.MF" 849 ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \ 850 "; rm -f " + bname + " META-INF/MANIFEST.MF" 851 elif fileType == "ELF Object Archive" : 852 baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \ 853 bname + "; rm -f " + bname 854 ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \ 855 bname + "; rm -f " + bname 856 else : 857 error("unexpected file type: " + fileType) 858 return -1 859 860 os.system(baseCmd) 861 os.system(ptchCmd) 862 863 baseFlist = list(findFiles(tmpDir1)) 864 ptchFlist = list(findFiles(tmpDir2)) 865 866 # Trim leading path off base/ptch file lists 867 flist = [] 868 for fn in baseFlist : 869 flist.append(str_prefix_trunc(fn, tmpDir1)) 870 baseFlist = flist 871 872 flist = [] 873 for fn in ptchFlist : 874 flist.append(str_prefix_trunc(fn, tmpDir2)) 875 ptchFlist = flist 876 877 for fn in ptchFlist : 878 if not fn in baseFlist : 879 difference(fileName, fileType, \ 880 fn + " added to " + fileName) 881 return 1 882 883 for fn in baseFlist : 884 if not fn in ptchFlist : 885 difference(fileName, fileType, \ 886 fn + " removed from " + fileName) 887 return 1 888 889 differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True) 890 if differs : 891 difference(fileName, fileType, \ 892 fn + " in " + fileName + " differs") 893 return 1 894 return 0 895 896 ##### 897 # (Basic) file comparison 898 # 899 # There's some special case code here for Javadoc HTML files 900 # 901 # Returns 1 if difference detected 902 # 0 if no difference detected 903 # -1 on error 904 # 905 def compareBasic(base,