|
1 # |
|
2 # Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # This component and the accompanying materials are made available |
|
5 # under the terms of "Eclipse Public License v1.0" |
|
6 # which accompanies this distribution, and is available |
|
7 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 # |
|
9 # Initial Contributors: |
|
10 # Nokia Corporation - initial contribution. |
|
11 # |
|
12 # Contributors: |
|
13 # |
|
14 # Description: |
|
15 # |
|
16 # Script for comparing contents of two directories. |
|
17 # |
|
18 # Usage: |
|
19 # python -u comparedirs.py <source_path> <target_path> |
|
20 # |
|
21 |
|
22 import datetime, filecmp, os, sys, traceback, zlib |
|
23 from optparse import OptionParser |
|
24 |
|
25 class ResultCounter: |
|
26 def __init__(self): |
|
27 self.source_only_dirs = 0 |
|
28 self.source_only_files = 0 |
|
29 self.target_only_dirs = 0 |
|
30 self.target_only_files = 0 |
|
31 self.diff_case_dirs = 0 |
|
32 self.diff_case_files = 0 |
|
33 self.diff_files = 0 |
|
34 self.identical_files = 0 |
|
35 self.start_time = datetime.datetime.now() |
|
36 self.stop_time = None |
|
37 def __str__(self): |
|
38 if self.stop_time is None: |
|
39 self.stop_time = datetime.datetime.now() |
|
40 msg = "Execution time " + str(self.stop_time - self.start_time) + "\n" |
|
41 msg += "Summary of differences:\n" |
|
42 if self.source_only_dirs > 0: |
|
43 msg += "Source only dirs: " + str(self.source_only_dirs) + "\n" |
|
44 if self.source_only_files > 0: |
|
45 msg += "Source only files: " + str(self.source_only_files) + "\n" |
|
46 if self.target_only_dirs > 0: |
|
47 msg += "Target only dirs: " + str(self.target_only_dirs) + "\n" |
|
48 if self.target_only_files > 0: |
|
49 msg += "Target only files: " + str(self.target_only_files) + "\n" |
|
50 if self.diff_case_dirs > 0: |
|
51 msg += "Different case dirs: " + str(self.diff_case_dirs) + "\n" |
|
52 if self.diff_case_files > 0: |
|
53 msg += "Different case files: " + str(self.diff_case_files) + "\n" |
|
54 if self.diff_files > 0: |
|
55 msg += "Different files: " + str(self.diff_files) + "\n" |
|
56 msg += "Identical files: " + str(self.identical_files) |
|
57 return msg |
|
58 def differences_found(self): |
|
59 if self.source_only_dirs > 0 or \ |
|
60 self.source_only_files > 0 or \ |
|
61 self.target_only_dirs > 0 or \ |
|
62 self.target_only_files > 0 or \ |
|
63 self.diff_case_dirs > 0 or \ |
|
64 self.diff_case_files > 0 or \ |
|
65 self.diff_files > 0: |
|
66 return True |
|
67 else: |
|
68 return False |
|
69 |
|
70 # Counter for differences. |
|
71 result_counter = ResultCounter() |
|
72 |
|
73 # Command line options. |
|
74 opts = None |
|
75 |
|
76 def main(): |
|
77 global opts, result_counter |
|
78 # Parse command line options and arguments. |
|
79 parser = OptionParser( |
|
80 usage = "python -u %prog [options] <source_path> <target_path>") |
|
81 parser.add_option("-i", "--ignore", dest="ignore", action="append", |
|
82 help="dir or file name to be ignored in the root directory") |
|
83 parser.add_option("--ignore_all", dest="ignore_all", action="append", |
|
84 help="dir or file name to be ignored in all directories") |
|
85 parser.add_option("-v", "--verbose", dest="verbose", |
|
86 action="store_true", default=False, |
|
87 help="verbose output, lists also identical files") |
|
88 (opts, args) = parser.parse_args() |
|
89 if opts.ignore is None: |
|
90 opts.ignore = [] |
|
91 if opts.ignore_all is None: |
|
92 opts.ignore_all = [] |
|
93 source_path = args[0] |
|
94 target_path = args[1] |
|
95 if len(args) > 2: |
|
96 print "ERROR: Too many arguments" |
|
97 sys.exit(1) |
|
98 |
|
99 try: |
|
100 # Compare the source and target directories. |
|
101 print "%s: Comparing source %s and target %s" % \ |
|
102 (str(datetime.datetime.now()), source_path, target_path) |
|
103 if len(opts.ignore) > 0: |
|
104 print "Ignoring from root directory: " + ", ".join(opts.ignore) |
|
105 if len(opts.ignore_all) > 0: |
|
106 print "Ignoring from all directories: " + ", ".join(opts.ignore_all) |
|
107 compare_dirs(source_path, target_path, opts.ignore, opts.ignore_all + [".svn", "_ccmwaid.inf"]) |
|
108 |
|
109 # Comparison finished, display results. |
|
110 result_counter.stop_time = datetime.datetime.now() |
|
111 print "%s: Finished" % str(result_counter.stop_time) |
|
112 print str(result_counter) |
|
113 except: |
|
114 print "ERROR: Unexpected exception", datetime.datetime.now() |
|
115 traceback.print_exc() |
|
116 print str(result_counter) |
|
117 sys.exit(1) |
|
118 |
|
119 # if differences were found, exit with error status. |
|
120 if result_counter.differences_found(): |
|
121 print "Directories differ." |
|
122 sys.exit(1) |
|
123 else: |
|
124 print "Directories are identical." |
|
125 |
|
126 def compare_dirs(source, target, ignore, ignore_all): |
|
127 global opts, result_counter |
|
128 dircmp = filecmp.dircmp(source, target, ignore + ignore_all) |
|
129 #dircmp.report() |
|
130 filelist = get_filelist(target) |
|
131 # Loop the common files. |
|
132 for p in dircmp.common: |
|
133 sp = os.path.join(source, p) |
|
134 tp = os.path.join(target, p) |
|
135 tp_old = os.path.join(target, get_filename_case(p, filelist)) |
|
136 # Check if source has a file and target has a dir or vice versa, |
|
137 # or if the dir/file name case is different. |
|
138 if os.path.isdir(sp) and os.path.isfile(tp_old): |
|
139 print "Source dir, target file: %s, %s" % (sp, tp_old) |
|
140 result_counter.source_only_dirs += 1 |
|
141 result_counter.target_only_files += 1 |
|
142 elif os.path.isfile(sp) and os.path.isdir(tp_old): |
|
143 print "Source file, target dir: %s, %s" % (sp, tp_old) |
|
144 result_counter.source_only_files += 1 |
|
145 result_counter.target_only_dirs += 1 |
|
146 elif not tp == tp_old: |
|
147 if os.path.isdir(tp_old): |
|
148 print "Dir cases differ: %s, %s" % (sp, tp_old) |
|
149 result_counter.diff_case_dirs += 1 |
|
150 else: |
|
151 print "File cases differ: %s, %s" % (sp, tp_old) |
|
152 result_counter.diff_case_files += 1 |
|
153 # Compare existing dir or count existing file. |
|
154 elif os.path.isdir(sp) and os.path.isdir(tp): |
|
155 compare_dirs(sp, tp, [], ignore_all) |
|
156 else: |
|
157 if not files_equal(sp, tp): |
|
158 # Files are different. |
|
159 print "Files differ: %s, %s" % (sp, tp_old) |
|
160 result_counter.diff_files += 1 |
|
161 else: |
|
162 # Files are identical. |
|
163 if opts.verbose: |
|
164 print "Identical files: %s, %s" % (sp, tp_old) |
|
165 result_counter.identical_files += 1 |
|
166 # Loop the files which only exist in source. |
|
167 for p in dircmp.left_only: |
|
168 sp = os.path.join(source, p) |
|
169 if os.path.isdir(sp): |
|
170 print "Source only dir: %s" % sp |
|
171 result_counter.source_only_dirs += 1 |
|
172 else: |
|
173 print "Source only file: %s" % sp |
|
174 result_counter.source_only_files += 1 |
|
175 # Loop the files which only exist in target. |
|
176 for p in dircmp.right_only: |
|
177 tp = os.path.join(target, p) |
|
178 if os.path.isdir(tp): |
|
179 print "Target only dir: %s" % tp |
|
180 result_counter.target_only_dirs += 1 |
|
181 else: |
|
182 print "Target only file: %s" % tp |
|
183 result_counter.target_only_files += 1 |
|
184 |
|
185 def get_filelist(path): |
|
186 if os.path.isdir(path): |
|
187 return os.listdir(path) |
|
188 else: |
|
189 return None |
|
190 |
|
191 def get_filename_case(filename, filelist): |
|
192 filename = os.path.basename(filename) |
|
193 if filelist is None: |
|
194 result = [filename] |
|
195 else: |
|
196 result = [f for f in filelist if f.lower() == filename.lower()] |
|
197 return result[0] |
|
198 |
|
199 def files_equal(source, target): |
|
200 if digest(source) == digest(target): |
|
201 return True |
|
202 else: |
|
203 return False |
|
204 |
|
205 def digest(file): |
|
206 f = open(file, "rb") |
|
207 try: |
|
208 h = zlib.crc32(f.read()) |
|
209 finally: |
|
210 f.close() |
|
211 return h |
|
212 |
|
213 if __name__ == "__main__": |
|
214 main() |