1#!/usr/bin/python 2 3""" 4After building can be used to replace documentation, 5and jars with the newly built versions in SVN. 6""" 7 8import filecmp 9import os 10import pipes 11import sys 12 13FILE = 'f' 14DIR = 'd' 15NO_EXIST = 'n' 16 17MIME_TYPES_BY_EXTENSION = { 18 'html': 'text/html;charset=UTF-8', 19 'txt': 'text/plain;charset=UTF-8', 20 'css': 'text/css;charset=UTF-8', 21 'js': 'text/javascript;charset=UTF-8', 22 'jar': 'application/x-java-archive', 23 'xsl': 'text/xml;charset=UTF-8', 24 'gif': 'image/gif', 25 'png': 'image/png' 26 } 27 28def sync(src_to_dest): 29 """ 30 Syncrhonize the destination file tree with the source file tree 31 in both the current client and in subversion. 32 """ 33 34 def classify(path): 35 if not os.path.exists(path): return NO_EXIST 36 if os.path.isdir(path): return DIR 37 return FILE 38 39 # If we see a case where (conflict) is present, then we need to be 40 # sure to do svn deletes in a separate commit before svn adds. 41 conflict = False 42 # Keep track of changes to make in subversion 43 svn_adds = [] 44 svn_deletes = [] 45 svn_propsets = {} 46 47 # A bunch of actions that can be taken to synchronize one aspect 48 # of a source file and a destination file 49 def run(argv): 50 """ 51 Prints out a command line that needs to be run. 52 """ 53 print ' '.join([pipes.quote(arg) for arg in argv]) 54 55 def svn(verb_and_flags, args): 56 cmd = ['svn'] 57 cmd.extend(verb_and_flags) 58 cmd.extend(args) 59 run(cmd) 60 61 def remove(src, dst): run(['rm', dst]) 62 63 def svn_delete(src, dst): svn_deletes.append(dst) 64 65 def recurse(src, dst): 66 children = set() 67 if os.path.isdir(src): children.update(os.listdir(src)) 68 if os.path.isdir(dst): 69 children.update(os.listdir(dst)) 70 children.discard('.svn') 71 for child in children: 72 handle(os.path.join(src, child), os.path.join(dst, child)) 73 74 def copy(src, dst): run(['cp', '-f', src, dst]) 75 76 def copy_if_different(src, dst): 77 if not filecmp.cmp(src, dst, shallow=0): copy(src, dst) 78 79 def svn_add(src, dst): 80 svn_adds.append(dst) 81 dot = dst.rfind('.') 82 if dot >= 0: 83 mime_type = MIME_TYPES_BY_EXTENSION.get(dst[dot+1:]) 84 if mime_type is not None: 85 key = ('svn:mime-type', mime_type) 86 if key not in svn_propsets: 87 svn_propsets[key] = [] 88 svn_propsets[key].append(dst) 89 90 def cnf(src, dst): conflict = True 91 92 def mkdir(src, dst): run(['mkdir', dst]) 93 94 # The below table contains the actions to take for each possible 95 # scenario. 96 actions = { 97 # src dst actions 98 (NO_EXIST, NO_EXIST): (), 99 (NO_EXIST, FILE) : (remove, svn_delete,), 100 (NO_EXIST, DIR) : (recurse, remove, svn_delete,), 101 (FILE, NO_EXIST): (copy, svn_add,), 102 (FILE, FILE) : (copy_if_different,), 103 (FILE, DIR) : (recurse, remove, svn_delete, copy, svn_add, cnf), 104 (DIR, NO_EXIST): (mkdir, svn_add, recurse,), 105 (DIR, FILE) : (remove, svn_delete, mkdir, svn_add, recurse, cnf), 106 (DIR, DIR) : (recurse,), 107 } 108 109 # Walk the file tree (see recurse action above) and synchronize it at 110 # each step. 111 def handle(src, dst): 112 src_t = classify(src) 113 dst_t = classify(dst) 114 for action in actions[(src_t, dst_t)]: action(src, dst) 115 116 for (src, dst) in src_to_dest: 117 handle(src, dst) 118 119 if len(svn_deletes): 120 svn(['delete'], svn_deletes) 121 if conflict: 122 svn(['commit', '-m', 'remove obsolete files from the snapshot tree'], 123 commit_args) 124 if len(svn_adds): 125 svn(['add', '--depth=empty'], svn_adds) 126 for ((propname, propvalue), files) in svn_propsets.items(): 127 svn(['propset', propname, propvalue], files) 128 129if '__main__' == __name__: 130 sync([(sys.argv[1], sys.argv[2])]) 131