1 |
# check-sync-dirs.py --- check that one directory is an exact subset of another |
2 |
# |
3 |
# Usage: python check-sync-dirs.py COPY ORIGINAL |
4 |
# |
5 |
# Check that the files present in the directory tree COPY are exact |
6 |
# copies of their counterparts in the directory tree ORIGINAL. COPY |
7 |
# need not have all the files in ORIGINAL, but COPY may not have files |
8 |
# absent from ORIGINAL. |
9 |
# |
10 |
# Each directory in COPY may have a file named |
11 |
# 'check-sync-exceptions', which lists files in COPY that need not be |
12 |
# the same as the corresponding file in ORIGINAL, or exist at all in |
13 |
# ORIGINAL. (The 'check-sync-exceptions' file itself is always |
14 |
# treated as exceptional.) Blank lines and '#' comments in the file |
15 |
# are ignored. |
16 |
|
17 |
import sys |
18 |
import os |
19 |
from os.path import join |
20 |
import filecmp |
21 |
import textwrap |
22 |
import fnmatch |
23 |
|
24 |
if len(sys.argv) != 3: |
25 |
print >> sys.stderr, 'TEST-UNEXPECTED-FAIL | check-sync-dirs.py | Usage: %s COPY ORIGINAL' % sys.argv[0] |
26 |
sys.exit(1) |
27 |
|
28 |
copy = os.path.abspath(sys.argv[1]) |
29 |
original = os.path.abspath(sys.argv[2]) |
30 |
|
31 |
# Ignore detritus left lying around by editing tools. |
32 |
ignored_patterns = ['*~', '.#*', '#*#', '*.orig', '*.rej'] |
33 |
|
34 |
# Return the contents of FILENAME, a 'check-sync-exceptions' file, as |
35 |
# a dictionary whose keys are exactly the list of filenames, along |
36 |
# with the basename of FILENAME itself. If FILENAME does not exist, |
37 |
# return the empty dictionary. |
38 |
def read_exceptions(filename): |
39 |
if (os.path.exists(filename)): |
40 |
f = file(filename) |
41 |
exceptions = {} |
42 |
for line in f: |
43 |
line = line.strip() |
44 |
if line != '' and line[0] != '#': |
45 |
exceptions[line] = None |
46 |
exceptions[os.path.basename (filename)] = None |
47 |
f.close() |
48 |
return exceptions |
49 |
else: |
50 |
return {} |
51 |
|
52 |
# Return true if FILENAME matches any pattern in the list of filename |
53 |
# patterns PATTERNS. |
54 |
def fnmatch_any(filename, patterns): |
55 |
for pattern in patterns: |
56 |
if fnmatch.fnmatch(filename, pattern): |
57 |
return True |
58 |
return False |
59 |
|
60 |
# Check the contents of the directory tree COPY against ORIGINAL. For each |
61 |
# file that differs, apply REPORT to COPY, ORIGINAL, and the file's |
62 |
# relative path. COPY and ORIGINAL should be absolute. Ignore files |
63 |
# that match patterns given in the list IGNORE. |
64 |
def check(copy, original, ignore): |
65 |
os.chdir(copy) |
66 |
for (dirpath, dirnames, filenames) in os.walk('.'): |
67 |
exceptions = read_exceptions(join(dirpath, 'check-sync-exceptions')) |
68 |
for filename in filenames: |
69 |
if (filename in exceptions) or fnmatch_any(filename, ignore): |
70 |
continue |
71 |
relative_name = join(dirpath, filename) |
72 |
original_name = join(original, relative_name) |
73 |
if (os.path.exists(original_name) |
74 |
and filecmp.cmp(relative_name, original_name)): |
75 |
continue |
76 |
report(copy, original, relative_name) |
77 |
|
78 |
differences_found = False |
79 |
|
80 |
# Print an error message for DIFFERING, which was found to differ |
81 |
# between COPY and ORIGINAL. Set the global variable differences_found. |
82 |
def report(copy, original, differing): |
83 |
global differences_found |
84 |
if not differences_found: |
85 |
print >> sys.stderr, 'TEST-UNEXPECTED-FAIL | check-sync-dirs.py | build file copies are not in sync\n' \ |
86 |
'TEST-INFO | check-sync-dirs.py | file(s) found in: %s\n' \ |
87 |
'TEST-INFO | check-sync-dirs.py | differ from their originals in: %s' \ |
88 |
% (copy, original) |
89 |
print >> sys.stderr, 'TEST-INFO | check-sync-dirs.py | differing file: %s' % differing |
90 |
differences_found = True |
91 |
|
92 |
check(copy, original, ignored_patterns) |
93 |
|
94 |
if differences_found: |
95 |
msg = '''In general, the files in '%s' should always be exact copies of |
96 |
originals in '%s'. A change made to one should also be made to the |
97 |
other. See 'check-sync-dirs.py' for more details.''' \ |
98 |
% (copy, original) |
99 |
print >> sys.stderr, textwrap.fill(msg, 75) |
100 |
sys.exit(1) |
101 |
|
102 |
print >> sys.stderr, 'TEST-PASS | check-sync-dirs.py | %s <= %s' % (copy, original) |
103 |
sys.exit(0) |