| #!/usr/bin/python |
| |
| """ |
| After building can be used to replace documentation, |
| and jars with the newly built versions in SVN. |
| """ |
| |
| import filecmp |
| import os |
| import pipes |
| import sys |
| |
| FILE = 'f' |
| DIR = 'd' |
| NO_EXIST = 'n' |
| |
| MIME_TYPES_BY_EXTENSION = { |
| 'html': 'text/html;charset=UTF-8', |
| 'txt': 'text/plain;charset=UTF-8', |
| 'css': 'text/css;charset=UTF-8', |
| 'js': 'text/javascript;charset=UTF-8', |
| 'jar': 'application/x-java-archive', |
| 'xsl': 'text/xml;charset=UTF-8', |
| 'gif': 'image/gif', |
| 'png': 'image/png' |
| } |
| |
| def sync(src_to_dest): |
| """ |
| Syncrhonize the destination file tree with the source file tree |
| in both the current client and in subversion. |
| """ |
| |
| def classify(path): |
| if not os.path.exists(path): return NO_EXIST |
| if os.path.isdir(path): return DIR |
| return FILE |
| |
| # If we see a case where (conflict) is present, then we need to be |
| # sure to do svn deletes in a separate commit before svn adds. |
| conflict = False |
| # Keep track of changes to make in subversion |
| svn_adds = [] |
| svn_deletes = [] |
| svn_propsets = {} |
| |
| # A bunch of actions that can be taken to synchronize one aspect |
| # of a source file and a destination file |
| def run(argv): |
| """ |
| Prints out a command line that needs to be run. |
| """ |
| print ' '.join([pipes.quote(arg) for arg in argv]) |
| |
| def svn(verb_and_flags, args): |
| cmd = ['svn'] |
| cmd.extend(verb_and_flags) |
| cmd.extend(args) |
| run(cmd) |
| |
| def remove(src, dst): run(['rm', dst]) |
| |
| def svn_delete(src, dst): svn_deletes.append(dst) |
| |
| def recurse(src, dst): |
| children = set() |
| if os.path.isdir(src): children.update(os.listdir(src)) |
| if os.path.isdir(dst): |
| children.update(os.listdir(dst)) |
| children.discard('.svn') |
| for child in children: |
| handle(os.path.join(src, child), os.path.join(dst, child)) |
| |
| def copy(src, dst): run(['cp', '-f', src, dst]) |
| |
| def copy_if_different(src, dst): |
| if not filecmp.cmp(src, dst, shallow=0): copy(src, dst) |
| |
| def svn_add(src, dst): |
| svn_adds.append(dst) |
| dot = dst.rfind('.') |
| if dot >= 0: |
| mime_type = MIME_TYPES_BY_EXTENSION.get(dst[dot+1:]) |
| if mime_type is not None: |
| key = ('svn:mime-type', mime_type) |
| if key not in svn_propsets: |
| svn_propsets[key] = [] |
| svn_propsets[key].append(dst) |
| |
| def cnf(src, dst): conflict = True |
| |
| def mkdir(src, dst): run(['mkdir', dst]) |
| |
| # The below table contains the actions to take for each possible |
| # scenario. |
| actions = { |
| # src dst actions |
| (NO_EXIST, NO_EXIST): (), |
| (NO_EXIST, FILE) : (remove, svn_delete,), |
| (NO_EXIST, DIR) : (recurse, remove, svn_delete,), |
| (FILE, NO_EXIST): (copy, svn_add,), |
| (FILE, FILE) : (copy_if_different,), |
| (FILE, DIR) : (recurse, remove, svn_delete, copy, svn_add, cnf), |
| (DIR, NO_EXIST): (mkdir, svn_add, recurse,), |
| (DIR, FILE) : (remove, svn_delete, mkdir, svn_add, recurse, cnf), |
| (DIR, DIR) : (recurse,), |
| } |
| |
| # Walk the file tree (see recurse action above) and synchronize it at |
| # each step. |
| def handle(src, dst): |
| src_t = classify(src) |
| dst_t = classify(dst) |
| for action in actions[(src_t, dst_t)]: action(src, dst) |
| |
| for (src, dst) in src_to_dest: |
| handle(src, dst) |
| |
| if len(svn_deletes): |
| svn(['delete'], svn_deletes) |
| if conflict: |
| svn(['commit', '-m', 'remove obsolete files from the snapshot tree'], |
| commit_args) |
| if len(svn_adds): |
| svn(['add', '--depth=empty'], svn_adds) |
| for ((propname, propvalue), files) in svn_propsets.items(): |
| svn(['propset', propname, propvalue], files) |
| |
| if '__main__' == __name__: |
| sync([(sys.argv[1], sys.argv[2])]) |