PrevUpHome

The Final Script


import os
import re
import shutil
import sys


def mapDirectory(dirmap, dname):
    """Return the new location of the input directory."""
    # Successively reduce the directory path until it
    # matches one of the keys in the directory map.
    mapped_dir = p = dname
    while p and not p in dirmap:
        p = os.path.dirname(p)
       
    if p:
        mapped_dir = os.path.join(dirmap[p], 
                                  dname[len(p) + 1:])
   
    return mapped_dir
   

def mapFiles(logfp, dirmap, dname, files):
    """Return a dictionary mapping files in dir to their new locations."""
    dname = os.path.normpath(dname)
    new_dir = mapDirectory(dirmap, dname)
    logfp.write("mapDirectory [%s] -> [%s]\n" % (dname, new_dir))
    fm = {}
    for f in files:
        src = os.path.join(dname, f)
        dst = os.path.join(new_dir, f)
        logfp.write("\t[%s] -> [%s]\n" % (src, dst))
        fm[src] = dst
    return fm
    

def isSourceFile(file):
    """Return True if the input file is a C/C++ source file.""" 
    src_re = re.compile(r'\.(c|h)(pp)?$',
                         re.IGNORECASE)
    return src_re.search(file) is not None


def swapSlashes(str):
    """Return the imput string with backslashes swapped to forward slashes."""
    back_to_fwd_re = re.compile(r'\\')
    return back_to_fwd_re.sub('/', str)


def mapIncludeFile(logfp, inc, srcdst, files_map):
    """Determine the remapped include file path."""
    # First, obtain a path to the include file relative to
    # the original source root
    if os.path.dirname(inc):
        pass # Assumption 1) - "inc" is our relative path
    else:
        # Assumption 2) The file must be located in the same
        # directory as the source file which includes it.
        inc = os.path.join(os.path.dirname(srcdst[0]), inc)
        inc = os.path.normpath(inc)
    # Look up the new home for the file
    try:
        mapped_inc = files_map[inc]
    except KeyError:
        err_msg = ('\nFatal error: Failed to locate [%s] '
            '(included by [%s]) '
            'relative to source root.' % (inc, srcdst[0]))
        logfp.write(err_msg)
        sys.stderr.write(err_msg)
        sys.exit(1)
      
    if (os.path.dirname(mapped_inc) ==
        os.path.dirname(srcdst[1])):
        mapped_inc = os.path.basename(mapped_inc)   
    
    return mapped_inc


def processSourceLine(logfp, line, srcdst, files_map):
    """Process a line from a source file, correcting included file paths."""
    include_re = re.compile(
        r'^\s*#\s*include\s*"'
        r'(?P<inc_file>\S+)'
        '"')
    match = include_re.match(line)
    if match:
        logfp.write('    [%s] -> ' % line.rstrip())
        mapped_inc_file = mapIncludeFile(
            logfp,
            match.group('inc_file'),
            srcdst,
            files_map
            )
        line = line[:match.start('inc_file')] + \
               mapped_inc_file + \
               line[match.end('inc_file'):]
        line = swapSlashes(line)
        logfp.write('[%s]\n' % line.rstrip())
    
    return line
   

def relocateSource(logfp, srcdst, dstfile, files_map):
    """Relocate a source file, correcting paths to included files."""
    infp  = open(srcdst[0], 'r')
    outfp = open(dstfile, 'w')
    logfp.write('Relocating source file [%s] -> [%s]\n' %
               (srcdst[0], dstfile))
    for line in infp:
        outfp.write(
            processSourceLine(
                logfp, line, srcdst, files_map)
            )
    infp.close()
    outfp.close()


def relocate(logfp, srcdst, dst_root, files_map):
    """Relocate a file, correcting internal file references."""
    dst_file = os.path.join(dst_root, srcdst[1])
    dst_dir = os.path.dirname(dst_file)
    if not os.path.isdir(dst_dir):
        os.makedirs(dst_dir) 
    if isSourceFile(dst_file):
        relocateSource(logfp, srcdst, dst_file, files_map)
    else:
        logfp.write('Copying [%s] -> [%s]\n' %
                   (srcdst[0], dst_file))
        shutil.copyfile(srcdst[0], dst_file)


def percent(num, denom):
    """Return num / denom expressed as an integer percentage."""
    return int(num * 100 / denom)


def printSettings(fp, dmap, src, dst):
    """Output script settings to the input file."""
    fp.write('Relocating source tree from [%s] to [%s]\n' %
             (src, dst))
    fp.write('Relocating directories:\n')
    for d in dirmap:
        fp.write('    [%s] -> [%s]\n' %
                 (d, dmap[d]))
    fp.write('\n')

    
# Main processing starts here...
# First, set up script data:
#   - a dictionary mapping existing dirs to their new locations,
#   - the source and destination roots for the source tree,
np = os.path.normpath
dirmap = {
    np('png')     : np('graphics/thirdparty/png'),
    np('jpeg')    : np('graphics/thirdparty/jpeg'),
    np('bitmap')  : np('graphics/common/bitmap'),
    np('UserIF')  : np('ui'),
    np('UserIF/Wgts') : np('ui/widgets'),
    np('os')      : np('platform/os'),
    np('os/hpux') : np('platform/os/hpux10')
}
from_root = '.'
to_root = '../relocate'

# Further initialisation.
logfp = open('relocate.log', 'w')
printSettings(logfp, dirmap, from_root, to_root)
printSettings(sys.stdout, dirmap, from_root, to_root)

# Initialise a dictionary to map current file path
# to new file path.
files_map = {}

# Fill the dictionary by remapping all files beneath
# the current working directory.
sys.stdout.write('Preprocessing files. Please wait.\n')

for (subdir, dirs, files) in os.walk(from_root):
    files_map.update(
        mapFiles(logfp, dirmap, subdir, files)
        )

# Create the new root directory for relocated files
os.makedirs(to_root)
   
# Now actually perform the relocation
count = len(files_map)
item = 0
sys.stdout.write('Relocating [%d] files.\n'
                 'Logfile at [%s]\n'
                 'Progress [%02d%%]' % 
       (count, logfp.name, percent(item, count))
    )
   
for srcdst in files_map.items():
    item += 1
    sys.stdout.write('\b\b\b\b%02d%%]' % 
                     percent(item, count))
    relocate(logfp, srcdst, to_root, files_map)

logfp.close()
sys.stdout.write('\nRelocation completed successfully.\n')
Copyright © 2004 Thomas Guest

PrevUpHome