blob: f2000a8cf2b81fc801cfbf322259460db068eeef [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2010 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Diff - Generate a unified diff of two sources or directories.
The diff module provides a mechanism for running a diff a pair of
sources or directories. If there are any differences they are
displayed as a unified diff and the module returns non zero. If
there are no differences the module returns zero.
diff.py <opts> <dir1/file1> <dir2/file2>
diff will load one source file, or multiple files if the sources are
directories. Only the top directory is used unless the recursive '-r'
option is used. Normally diff will quitely return zero if the files
are the same. Verbose '-v' will list all the files compared as well
and the status of the diff.
-a, --all : All sources of set 1 must be in set 2
-h, --help : Display usage.
-r, --recursive : Search subdirectories as well.
-v, --verbose : Dump verbose information.
"""
from __future__ import print_function
import getopt
import os
import sys
import difflib
class SourceInfo(object):
""" Contains one set of sources. """
def __init__(self, name, recursive):
self.files = []
name = os.path.normpath(name)
if not os.path.exists(name):
print("Can not find", name)
usage()
# If the source is a directory
if os.path.isdir(name):
self.path = name
self.dir = True
if recursive:
# and we are searching recursively, get a set of
# all files
self.files = GetFileList(name)
else:
# otherwise just a set of files in this directory
files = os.listdir(path)
for file in files:
if os.path.isfile(os.path.join(path, file)):
self.files.append(file)
else:
# If not a directory, then just add the one file
self.path = os.path.dirname(name)
self.files.append(os.path.basename(name))
self.dir = False
if recursive and not self.dir:
print("Source must be a directory to diff recusively.")
usage()
def GetFileList(path):
""" Generates a list of files at a given path """
files = []
# Determine the length of the leading path
skip = len(path.split(os.path.sep))
for dirname, dirnames, filenames in os.walk(path):
# Remove the leading path
dirname = os.path.sep.join(dirname.split(os.path.sep)[skip:])
for filename in filenames:
path = os.path.join(dirname, filename)
files.append(path)
return files
def SourceIntersect(src1, src2):
""" Returns the intersection of both source sets. """
files = []
src1set = set(src1.files)
src2set = set(src2.files)
for file1 in src1.files:
if file1 in src2set:
files.append(file1)
return files
def ReadLines(path):
""" Returns a list of lines of the file found at 'path'. """
try:
file = open(path, "r")
except IOError, e:
print(" ***I/O error({0}): {1} {2}".format(e[0], e[1], path))
print()
raise
lines = file.readlines()
file.close()
return lines
def Diff(file1, file2):
""" Print the unified returing non zero if not equal."""
foundDiff = 0
try:
lines1 = ReadLines(file1)
lines2 = ReadLines(file2)
diffs = difflib.unified_diff(lines1, lines2, fromfile=file1, tofile=file2)
for diff in diffs:
foundDiff = foundDiff + 1
sys.stdout.write(diff)
return foundDiff
except IOError:
return 1
def usage():
""" Print the usage information. """
print(__doc__)
sys.exit(1)
def main(argv):
verbose = False
recursive = False
all = False
# Parse command-line arguments
long_opts = ['all','help','recursive','verbose']
opts, args = getopt.getopt(argv[1:], 'ahrv', long_opts)
# Process options
for k,v in opts:
if k == '-h' or k == '--help':
usage()
if k == '-v' or k == '--verbose':
verbose = True
if k == '-r' or k == '--recursive':
recursive = True
if k == '-a' or k == '--all':
all = True
# Process sources
if len(args) != 2:
print("Expecting two sources (files or directories).")
usage()
src1 = SourceInfo(args[0], recursive)
src2 = SourceInfo(args[1], recursive)
if all:
# Diff all files in the first set
files = src1.files
else:
# Get a list of all matching files
files = SourceIntersect(src1, src2)
diffCnt = 0
for file in files:
path1 = os.path.join(src1.path, file)
path2 = os.path.join(src2.path, file)
if verbose:
print("Compare %s and %s" % (path1, path2))
diffCnt = diffCnt + Diff(path1, path2)
if verbose:
print("%d different file(s) or failure(s)." % diffCnt)
return diffCnt
if __name__ == '__main__':
sys.exit(main(sys.argv))