| #!/usr/bin/python |
| |
| # This file is licensed under CC Zero |
| |
| import os |
| from os.path import join |
| import shutil |
| import filecmp |
| |
| # Files to not sync across support/ directories |
| fileExcludes = ('README') |
| dirExcludes = ('.svn', '.hg', 'CVS') |
| # To prevent support files from being propagated into a particular support/ |
| # directory, add a file named LOCK |
| |
| def propagate(source, dest, errors): |
| """Compare each file and copy from source to destination. |
| Do nothing and flag an error if the destination already exists |
| but is different. Recurse. |
| source and dest are both directory paths. |
| errors is a list of 2-element tuples, the first being a |
| source filepath and the second a destination filepath, |
| of file pairs where the destination isdifferent from |
| """ |
| |
| # This directory is locked; don't propagate |
| if os.path.exists(join(dest, 'LOCK')): return |
| |
| # If the source directory doesn't exist return |
| if not os.path.exists(source): return |
| |
| # Get the file and directory lists for source |
| children = os.listdir(source) |
| for name in children: |
| origin = join(source, name) |
| copy = join(dest, name) |
| if os.path.isfile(origin): |
| if name in fileExcludes: continue |
| # Copy over the file if it needs copying |
| if not os.path.exists(copy): # file missing |
| shutil.copy2(origin, copy) # copy it over |
| elif not filecmp.cmp(origin, copy): # files differ |
| if not filecmp.cmp(origin, copy, True): # contents equal, stats differ |
| shutil.copystat(origin, copy) # update stats so they match for next time |
| else: # contents differ |
| errors.append((origin, copy)) |
| elif os.path.isdir(origin): |
| if name in dirExcludes: continue |
| # Duplicate the directory structure and propagate the subtree |
| if not os.path.exists(copy): |
| os.makedirs(copy) |
| propagate(origin, copy, errors) |
| if len(children) == 0: |
| print "Warn: " + source + " is empty.\n" |
| |
| def waterfall(parentDir, childDir, errors): |
| """Copy down support files from parent support to child. |
| parentDir is the parent of the parent support directory. |
| childDir is the parent of the current support directory, |
| that we should copy into. |
| waterfall recurses into childDir's children.""" |
| assert os.path.exists(join(parentDir, 'support')), join(parentDir, 'support') + " doesn't exist\n" |
| if os.path.exists(join(childDir, 'support')): |
| propagate(join(parentDir, 'support'), join(childDir, 'support'), errors) |
| dirs = os.walk(childDir).next()[1] |
| for name in dirs: |
| if name == 'support': |
| pass |
| elif name not in dirExcludes: |
| waterfall(childDir, join(childDir, name), errors) |
| |
| def outline(source, dest, errors): |
| """Copy over directory structure and all files under any support/ directories |
| source and dest are both directory paths. |
| errors is a list of 2-element tuples, the first being a |
| source filepath and the second a destination filepath, |
| of support file pairs where the destination copy is |
| different from the source |
| """ |
| # Get the directory list for source |
| dirs = os.walk(source).next()[1] |
| # Copy directory structure |
| for name in dirs: |
| if name in dirExcludes: continue |
| origin = join(source, name) |
| copy = join(dest, name) |
| if not os.path.exists(copy): |
| os.mkdirs(copy) |
| if name == 'support': |
| # Copy support files |
| propagate(origin, copy, errors) |
| else: |
| outline(origin, copy, errors) |
| |
| def syncSupport(source, dest, errors): |
| """For each support directory in dest, propagate the corresponding support |
| files from source. |
| source and dest are both directory paths. |
| errors is a list of 2-element tuples, the first being a |
| source filepath and the second a destination filepath, |
| of support file pairs where the destination copy is |
| different from the source |
| """ |
| # Get the directory list for est |
| dirs = os.walk(dest).next()[1] |
| # Scan directory structure, building support dirs as necessary |
| for name in dirs: |
| if name in dirExcludes: continue |
| master = join(source, name) |
| slave = join(dest, name) |
| if name == 'support': |
| # Copy support files |
| propagate(master, slave, errors) |
| else: |
| syncSupport(master, slave, errors) |
| |
| def main(): |
| # Part I: Propagate support files through approved/ |
| |
| errors = [] |
| root, dirs, _ = os.walk('.').next() |
| if 'approved' in dirs: |
| root = join(root, 'approved') |
| suites = os.walk(root).next()[1] |
| suites = filter(lambda name: name not in dirExcludes, suites) |
| for suite in suites: |
| waterfall(root, join(root, suite, 'src'), errors) |
| else: |
| print "Failed to find approved/ directory.\n" |
| exit(); |
| |
| # Part II: Propagate test suite support files into contributors/ |
| |
| if 'contributors' in dirs: |
| _, contribs, _ = os.walk('contributors').next() |
| for contributor in contribs: |
| contribRoot = join('contributors', contributor, 'submitted') |
| if not os.path.exists(contribRoot): continue # missing submitted folder |
| dirs = os.walk(contribRoot).next()[1] |
| for dir in dirs: |
| # Check if contributor has a top-level directory name matching |
| # one of our suites; if so, sync any matching support directories |
| if dir in suites: |
| suiteRoot = join(root, dir, 'src') |
| if os.path.exists(suiteRoot): |
| syncSupport(suiteRoot, join(contribRoot, dir), errors) |
| else: |
| print "Failed to find contributors/ directory.\n" |
| |
| # Print all errors |
| |
| for error in errors: |
| print "Mismatch: " + error[0] + " vs " + error [1] + " Copy failed.\n" |
| |
| if __name__ == "__main__": |
| main() |