Example #1
0
 def _add_it(s, d, actual_s, force=False, dryRun=False):
     if not exists(d):
         if dryRun:
             log.info("add '%s' to '%s' (dry run)", actual_s, d)
         else:
             log.info("add '%s' to '%s'", actual_s, d)
             sh.copy(s, d)
     elif not _diffPaths(s, d):
         log.info("skip add of '%s' to '%s': "
                  "no changes", actual_s, d)
     else:
         if dryRun:
             log.info("replace '%s' to '%s' (dry run)", actual_s, d)
         else:
             log.info("replace '%s' to '%s'", actual_s, d)
             sh.copy(s, d)
Example #2
0
 def _add_it(s, d, actual_s, force=False, dryRun=False):
     if not exists(d):
         if dryRun:
             log.info("add '%s' to '%s' (dry run)",
                      actual_s, d)
         else:
             log.info("add '%s' to '%s'", actual_s, d)
             sh.copy(s, d)
     elif not _diffPaths(s, d):
         log.info("skip add of '%s' to '%s': "
                  "no changes", actual_s, d)
     else:
         if dryRun:
             log.info("replace '%s' to '%s' (dry run)",
                      actual_s, d)
         else:
             log.info("replace '%s' to '%s'",
                      actual_s, d)
             sh.copy(s, d)
Example #3
0
def patch(patchesDir,
          sourceDir,
          config=None,
          logDir=None,
          dryRun=0,
          patchExe=None,
          logFilename=None):
    """Patch the given source tree with the given patches.
    
        "patchesDir" is a directory tree of patches to apply or a single
            patch file to apply. Alternatively it may be _list of_ patch
            directories and/or files.
        "sourceDir" is the base directory of the source tree to patch.
        "config" is a configuration object to pass to __patchinfo__.py
            special control functions. Typically it is a configuration
            module object but can be any object expected by the given
            patch directories.
        "logDir" (optional, default None) is a directory in which applied
            patches are logged. By default this is not created, however some
            projects want to create a package including all differences
            applied to a base source tree. If the package dir already exists
            it is deleted and re-created. As well, using a logDir is required
            to be able to unpatch().
        "dryRun" (optional, default false) is a boolean indicating that
            everything except actually applying patches should be done.
        "patchExe" (optional) can be used to specify a particular patch
            executable to use. Otherwise one is automatically found from
            the environment.
    
    The given patch tree (or patch trees) is processed as described in the
    module docstring above. First, the set of patches that should be applied
    is determined. Then is it checked that all patches _can_ be applied.
    Then all patches are applied. "Error" is raised if all patches cannot
    be applied. There is no return value. An error is NOT raised if it looks
    like all patches have already been applied.
    """
    log.debug(
        "patch(patchesDir=%r, sourceDir=%r, config=%r, logDir=%r, "
        "dryRun=%r)", patchesDir, sourceDir, config, logDir, dryRun)

    # Determine what patch actions should be carried out.
    # "actions" is a list of the following possible actions:
    #   ("apply",              <patches-basedir>, <patchfile-relpath>, <patch-args>)
    #   ("preprocess & apply", <patches-basedir>, <patchfile-relpath>, <patch-args>)
    #   ("add",                <patches-basedir>, <src-relpath>, <dst-relpath>, <force>)
    #   ("remove",             <patches-basedir>, <dst-relpath>)
    # Some notes:
    # - The "add" and "remove" paths can be directories.
    # - When add'ing a file, if its basename is of the form
    #   "BASE.p.EXT", then it will be preprocessed to "BASE.EXT".
    actions = []
    for patchSpec in patchesDir:
        if os.path.isfile(patchSpec):
            actions.append(("apply", os.path.dirname(patchSpec),
                            os.path.basename(patchSpec)))
        elif os.path.isdir(patchSpec):
            # Always skip SCC control dirs.
            if basename(patchSpec) in ("CVS", ".svn", ".hg"):
                continue
            os.path.walk(patchSpec, _shouldBeApplied,
                         (patchSpec, actions, config))
        else:
            raise Error("patches directory or file does not exist: '%s'" %
                        patchSpec)
    log.debug("patch set: %s" % pprint.pformat(actions))

    # Create a clean working directory.
    workDir = _createTempDir()
    if sys.platform.startswith("win"):
        # Windows patching leaves around temp files, so we work around this
        # problem by setting a different temp directory, which is later removed
        # at the end of this patching.
        oldTmpDir = os.environ.get("TMP")
        tmpDir = _createTempDir()
        os.environ["TMP"] = tmpDir
    log.debug("created patch working dir: '%s'" % workDir)
    try:
        # Create a patch image in the working dir (i.e. copy over patches and
        # files to add and preprocessed versions of those that need it.)
        defines = None  # preprocessor defines are lazily calculated
        for action in actions:
            if action[0] in ("apply", "add"):
                src = os.path.join(action[1], action[2])
                dst = os.path.join(workDir, action[2])
                if action[0] == "apply" and os.path.isfile(dst):
                    raise Error(
                        "conflicting patch file '%s': you have a "
                        "patch of the same name in more than one "
                        "patches tree", action[2])
                if os.path.isfile(src):
                    preprocess_me, new_filename \
                        = _shouldPreprocess(basename(src))
                    if preprocess_me:
                        if defines is None:
                            defines = _getPreprocessorDefines(config)
                        d = join(dirname(dst), new_filename)
                        log.debug("preprocess '%s' to '%s'", src, d)
                        if not exists(dirname(d)):
                            os.makedirs(dirname(d))
                        preprocess.preprocess(src,
                                              d,
                                              defines=defines,
                                              substitute=1)
                    else:
                        log.debug("cp '%s' to '%s'", src, dst)
                        sh.copy(src, dst)
                elif os.path.isdir(src):
                    for dirpath, dirnames, filenames in os.walk(src):
                        subpath = (dirpath == src and os.curdir
                                   or dirpath[len(src) + 1:])
                        for exclude_dir in (".svn", "CVS", ".hg"):
                            if exclude_dir in dirnames:
                                dirnames.remove(exclude_dir)
                        for filename in filenames:
                            s = join(dirpath, filename)
                            preprocess_me, new_filename \
                                = _shouldPreprocess(filename)
                            if preprocess_me:
                                d = normpath(join(dst, subpath, new_filename))
                                if defines is None:
                                    defines = _getPreprocessorDefines(config)
                                log.debug("preprocess '%s' to '%s'", s, d)
                                if not exists(dirname(d)):
                                    os.makedirs(dirname(d))
                                preprocess.preprocess(s,
                                                      d,
                                                      defines=defines,
                                                      substitute=1)
                            else:
                                d = normpath(join(dst, subpath, filename))
                                log.debug("cp '%s' to '%s'", s, d)
                                sh.copy(s, d)
                else:
                    raise Error("unknown file type for `%s'" % src)
            elif action[0] == "preprocess & apply":
                src = os.path.join(action[1], action[2])
                dst = os.path.join(workDir, action[2])
                if os.path.isfile(dst):
                    raise Error(
                        "conflicting patch file '%s': you have a "
                        "patch of the same name in more than one "
                        "patches tree", action[2])
                if defines is None:
                    defines = _getPreprocessorDefines(config)
                    #log.debug("defines: %s", pprint.pformat(defines))
                log.debug("preprocess '%s' to '%s'", src, dst)
                if not os.path.exists(os.path.dirname(dst)):
                    os.makedirs(os.path.dirname(dst))
                preprocess.preprocess(src, dst, defines=defines, substitute=1)
            elif action[0] == "remove":
                pass
            else:
                raise Error("unknown patch action '%s': %r" %
                            (action[0], action))

        # Ensure that each patch action can be carried out.
        patchExe = _getPatchExe(patchExe)
        for action in actions:
            if action[0] in ("apply", "preprocess & apply"):
                _assertCanApplyPatch(patchExe,
                                     os.path.join(workDir, action[2]),
                                     sourceDir,
                                     patchSrcFile=os.path.join(
                                         action[1], action[2]),
                                     patchArgs=action[3])
            elif action[0] == "add":
                # ("add", <patches-basedir>, <src-relpath>, <dst-relpath>, <force>)
                # e.g. ("add", "patches", "hpux_cpp_ext\\thingy.s", "python", False)
                #
                # Ensure that we won't clobber a target file that
                # differs.
                src = os.path.join(workDir, action[2])
                dst = os.path.join(sourceDir, action[3])
                if os.path.isdir(src):
                    for dirpath, dirnames, filenames in os.walk(src):
                        subpath = (dirpath == src and os.curdir
                                   or dirpath[len(src) + 1:])
                        for filename in filenames:
                            s = join(dirpath, filename)
                            d = normpath(join(dst, subpath, filename))
                            # 'actual_s' might actually have a '.p' in there.
                            actual_s = join(action[1], action[2], subpath,
                                            filename)
                            if not action[4]:
                                _assertCanAddFile(s, d, actual_s)
                else:
                    if not action[4]:
                        _assertCanAddFile(src, dst,
                                          os.path.join(action[1], action[2]))
            elif action[0] == "remove":
                pass
            else:
                raise Error("unknown patch action '%s': %r" %
                            (action[0], action))

        if logDir:
            # Log actions.
            if logFilename is None:
                logFilename = "__patchlog__.py"
            patchLogFile = os.path.join(workDir, logFilename)
            patchLog = open(patchLogFile, "w")
            try:
                patchLog.write("""\
# Patch log (%s)
#
# WARNING: This file is automatically generated by patchtree.py. Any
#          Changes you make will be lost.

sourceDir = %r
actions = %s
""" % (time.asctime(), sourceDir, pprint.pformat(actions)))
            finally:
                patchLog.close()

            # Write files scheduled for removal to the attic for possible
            # retrieval during unpatch().
            atticDir = os.path.join(workDir, "__attic__")
            oldAtticDir = os.path.join(logDir, "__attic__")
            for action in actions:
                if action[0] == "remove":
                    # ("remove", <patches-basedir>, <dst-relpath>)
                    origLoc = os.path.join(sourceDir, action[2])
                    oldAtticLoc = os.path.join(oldAtticDir, action[2])
                    atticLoc = os.path.join(atticDir, action[2])
                    possibleLocs = [origLoc, oldAtticLoc]
                    for location in possibleLocs:
                        if os.path.exists(location):
                            log.debug("copy '%s' to attic", origLoc)
                            sh.copy(location, atticLoc)

        # A little helper for doing the 'add' action with appropriate
        # logging.
        def _add_it(s, d, actual_s, force=False, dryRun=False):
            if not exists(d):
                if dryRun:
                    log.info("add '%s' to '%s' (dry run)", actual_s, d)
                else:
                    log.info("add '%s' to '%s'", actual_s, d)
                    sh.copy(s, d)
            elif not _diffPaths(s, d):
                log.info("skip add of '%s' to '%s': "
                         "no changes", actual_s, d)
            else:
                if dryRun:
                    log.info("replace '%s' to '%s' (dry run)", actual_s, d)
                else:
                    log.info("replace '%s' to '%s'", actual_s, d)
                    sh.copy(s, d)

        # Carry out each patch action.
        for action in actions:
            if action[0] in ("apply", "preprocess & apply"):
                _applyPatch(patchExe,
                            workDir,
                            action[2],
                            sourceDir,
                            dryRun=dryRun,
                            patchArgs=action[3])
            elif action[0] == "add":
                # ("add", <patches-basedir>, <src-relpath>, <dst-relpath>, <force>)
                # e.g. ("add", "patches", "hpux_cpp_ext\\thingy.s", "python", False)
                #
                #XXX Could improve logging here to only log one message
                #    in certain circumstances: no changes, all files
                #    were added.
                src = os.path.join(workDir, action[2])
                dst = os.path.join(sourceDir, action[3])
                if isdir(src):
                    for dirpath, dirnames, filenames in os.walk(src):
                        subpath = (dirpath == src and os.curdir
                                   or dirpath[len(src) + 1:])
                        for filename in filenames:
                            s = join(dirpath, filename)
                            d = normpath(join(dst, subpath, filename))
                            # 'actual_s' might actually have a '.p' in there.
                            actual_s = join(action[1], action[2], subpath,
                                            filename)
                            _add_it(s,
                                    d,
                                    actual_s,
                                    force=action[4],
                                    dryRun=dryRun)
                else:
                    d = isfile(dst) and dst or join(dst, basename(src))
                    actual_s = join(action[1], action[2])
                    _add_it(src, d, actual_s, force=action[4], dryRun=dryRun)
            elif action[0] == "remove":
                # ("remove", <patches-basedir>, <dst-relpath>)
                dst = os.path.join(sourceDir, action[2])
                if not os.path.exists(dst):
                    log.info("skip removal of '%s': already removed",
                             action[2])
                elif dryRun:
                    log.info("remove '%s' (dry run)", action[2])
                else:
                    log.info("remove '%s'", action[2])
                    sh.rm(dst)
            else:
                raise Error("unknown patch action '%s': %r" %
                            (action[0], action))

        # If a log dir was specified then copy working dir there.
        if logDir:
            if dryRun:
                log.info("creating patch log in '%s' (dry run)", logDir)
            else:
                if os.path.exists(logDir):
                    sh.rm(logDir)
                log.info("creating patch log in '%s'", logDir)
                sh.copy(workDir, logDir)
    finally:
        log.debug("removing temporary working dir '%s'", workDir)
        try:
            sh.rm(workDir)
        except EnvironmentError, ex:
            log.warn("could not remove temp working dir '%s': %s", workDir, ex)
        if sys.platform.startswith("win"):
            if oldTmpDir is not None:
                os.environ["TMP"] = oldTmpDir
            try:
                sh.rm(tmpDir)
            except EnvironmentError, ex:
                log.warn("could not remove temp patch dir '%s': %s", tmpDir,
                         ex)
Example #4
0
                if os.path.exists(sourceFile):
                    fin = open(sourceFile, 'rb')
                    try:
                        sourcemd5 = md5(fin.read()).hexdigest()
                    finally:
                        fin.close()
                    if atticmd5 == sourcemd5:
                        log.info(
                            "skip restoration of '%s' from attic: "
                            "already restored", action[2])
                        continue
            if dryRun:
                log.info("restore '%s' from attic (dry run)", action[2])
            else:
                log.info("restore '%s' from attic", action[2])
                sh.copy(atticLoc, sourceLoc)
        else:
            raise Error("unknown action, '%s', in patch log: %s" %
                        (action[0], action))


def patch(patchesDir,
          sourceDir,
          config=None,
          logDir=None,
          dryRun=0,
          patchExe=None,
          logFilename=None):
    """Patch the given source tree with the given patches.
    
        "patchesDir" is a directory tree of patches to apply or a single
Example #5
0
def patch(patchesDir, sourceDir, config=None, logDir=None, dryRun=0,
          patchExe=None, logFilename=None):
    """Patch the given source tree with the given patches.
    
        "patchesDir" is a directory tree of patches to apply or a single
            patch file to apply. Alternatively it may be _list of_ patch
            directories and/or files.
        "sourceDir" is the base directory of the source tree to patch.
        "config" is a configuration object to pass to __patchinfo__.py
            special control functions. Typically it is a configuration
            module object but can be any object expected by the given
            patch directories.
        "logDir" (optional, default None) is a directory in which applied
            patches are logged. By default this is not created, however some
            projects want to create a package including all differences
            applied to a base source tree. If the package dir already exists
            it is deleted and re-created. As well, using a logDir is required
            to be able to unpatch().
        "dryRun" (optional, default false) is a boolean indicating that
            everything except actually applying patches should be done.
        "patchExe" (optional) can be used to specify a particular patch
            executable to use. Otherwise one is automatically found from
            the environment.
    
    The given patch tree (or patch trees) is processed as described in the
    module docstring above. First, the set of patches that should be applied
    is determined. Then is it checked that all patches _can_ be applied.
    Then all patches are applied. "Error" is raised if all patches cannot
    be applied. There is no return value. An error is NOT raised if it looks
    like all patches have already been applied.
    """
    log.debug("patch(patchesDir=%r, sourceDir=%r, config=%r, logDir=%r, "
              "dryRun=%r)", patchesDir, sourceDir, config, logDir, dryRun)

    # Determine what patch actions should be carried out.
    # "actions" is a list of the following possible actions:
    #   ("apply",              <patches-basedir>, <patchfile-relpath>, <patch-args>)
    #   ("preprocess & apply", <patches-basedir>, <patchfile-relpath>, <patch-args>)
    #   ("add",                <patches-basedir>, <src-relpath>, <dst-relpath>, <force>)
    #   ("remove",             <patches-basedir>, <dst-relpath>)
    # Some notes:
    # - The "add" and "remove" paths can be directories.
    # - When add'ing a file, if its basename is of the form
    #   "BASE.p.EXT", then it will be preprocessed to "BASE.EXT".
    actions = []
    for patchSpec in patchesDir:
        if os.path.isfile(patchSpec):
            actions.append( ("apply", os.path.dirname(patchSpec),
                             os.path.basename(patchSpec)) )
        elif os.path.isdir(patchSpec):
            # Always skip SCC control dirs.
            if basename(patchSpec) in ("CVS", ".svn", ".hg"):
                continue
            os.path.walk(patchSpec, _shouldBeApplied,
                         (patchSpec, actions, config))
        else:
            raise Error("patches directory or file does not exist: '%s'"
                        % patchSpec)
    log.debug("patch set: %s" % pprint.pformat(actions))

    # Create a clean working directory.
    workDir = _createTempDir()
    if sys.platform.startswith("win"):
        # Windows patching leaves around temp files, so we work around this
        # problem by setting a different temp directory, which is later removed
        # at the end of this patching.
        oldTmpDir = os.environ.get("TMP")
        tmpDir = _createTempDir()
        os.environ["TMP"] = tmpDir
    log.debug("created patch working dir: '%s'" % workDir)
    try:
        # Create a patch image in the working dir (i.e. copy over patches and
        # files to add and preprocessed versions of those that need it.)
        defines = None # preprocessor defines are lazily calculated
        for action in actions:
            if action[0] in ("apply", "add"):
                src = os.path.join(action[1], action[2])
                dst = os.path.join(workDir, action[2])
                if action[0] == "apply" and os.path.isfile(dst):
                    raise Error("conflicting patch file '%s': you have a "
                                "patch of the same name in more than one "
                                "patches tree", action[2])
                if os.path.isfile(src):
                    preprocess_me, new_filename \
                        = _shouldPreprocess(basename(src))
                    if preprocess_me:
                        if defines is None:
                            defines = _getPreprocessorDefines(config)
                        d = join(dirname(dst), new_filename)
                        log.debug("preprocess '%s' to '%s'", src, d)
                        if not exists(dirname(d)):
                            os.makedirs(dirname(d))
                        preprocess.preprocess(src, d, defines=defines,
                                              substitute=1)
                    else:
                        log.debug("cp '%s' to '%s'", src, dst)
                        sh.copy(src, dst)
                elif os.path.isdir(src):
                    for dirpath, dirnames, filenames in os.walk(src):
                        subpath = (dirpath == src
                                   and os.curdir
                                   or dirpath[len(src)+1:])
                        for exclude_dir in (".svn", "CVS", ".hg"):
                            if exclude_dir in dirnames:
                                dirnames.remove(exclude_dir)
                        for filename in filenames:
                            s = join(dirpath, filename)
                            preprocess_me, new_filename \
                                = _shouldPreprocess(filename)
                            if preprocess_me:
                                d = normpath(join(dst, subpath, new_filename))
                                if defines is None:
                                    defines = _getPreprocessorDefines(config)
                                log.debug("preprocess '%s' to '%s'", s, d)
                                if not exists(dirname(d)):
                                    os.makedirs(dirname(d))
                                preprocess.preprocess(s, d, defines=defines,
                                                      substitute=1)
                            else:
                                d = normpath(join(dst, subpath, filename))
                                log.debug("cp '%s' to '%s'", s, d)
                                sh.copy(s, d)
                else:
                    raise Error("unknown file type for `%s'" % src)
            elif action[0] == "preprocess & apply":
                src = os.path.join(action[1], action[2])
                dst = os.path.join(workDir, action[2])
                if os.path.isfile(dst):
                    raise Error("conflicting patch file '%s': you have a "
                                "patch of the same name in more than one "
                                "patches tree", action[2])
                if defines is None:
                    defines = _getPreprocessorDefines(config)
                    #log.debug("defines: %s", pprint.pformat(defines))
                log.debug("preprocess '%s' to '%s'", src, dst)
                if not os.path.exists(os.path.dirname(dst)):
                    os.makedirs(os.path.dirname(dst))
                preprocess.preprocess(src, dst, defines=defines,
                                      substitute=1)
            elif action[0] == "remove":
                pass
            else:
                raise Error("unknown patch action '%s': %r"
                            % (action[0], action))
    
        # Ensure that each patch action can be carried out.
        patchExe = _getPatchExe(patchExe)
        for action in actions:
            if action[0] in ("apply", "preprocess & apply"):
                _assertCanApplyPatch(patchExe,
                                     os.path.join(workDir, action[2]),
                                     sourceDir,
                                     patchSrcFile=os.path.join(action[1], action[2]),
                                     patchArgs=action[3])
            elif action[0] == "add":
                # ("add", <patches-basedir>, <src-relpath>, <dst-relpath>, <force>)
                # e.g. ("add", "patches", "hpux_cpp_ext\\thingy.s", "python", False)
                #
                # Ensure that we won't clobber a target file that
                # differs.
                src = os.path.join(workDir, action[2])
                dst = os.path.join(sourceDir, action[3])
                if os.path.isdir(src):
                    for dirpath, dirnames, filenames in os.walk(src):
                        subpath = (dirpath == src
                                   and os.curdir
                                   or dirpath[len(src)+1:])
                        for filename in filenames:
                            s = join(dirpath, filename)
                            d = normpath(join(dst, subpath, filename))
                            # 'actual_s' might actually have a '.p' in there.
                            actual_s = join(action[1], action[2], subpath,
                                            filename)
                            if not action[4]:
                                _assertCanAddFile(s, d, actual_s)
                else:
                    if not action[4]:
                        _assertCanAddFile(src, dst,
                                          os.path.join(action[1], action[2]))
            elif action[0] == "remove":
                pass
            else:
                raise Error("unknown patch action '%s': %r"
                            % (action[0], action))

        if logDir:
            # Log actions.
            if logFilename is None:
                logFilename = "__patchlog__.py"
            patchLogFile = os.path.join(workDir, logFilename)
            patchLog = open(patchLogFile, "w")
            try:
                patchLog.write("""\
# Patch log (%s)
#
# WARNING: This file is automatically generated by patchtree.py. Any
#          Changes you make will be lost.

sourceDir = %r
actions = %s
""" % (time.asctime(), sourceDir, pprint.pformat(actions)))
            finally:
                patchLog.close()

            # Write files scheduled for removal to the attic for possible
            # retrieval during unpatch().
            atticDir = os.path.join(workDir, "__attic__")
            oldAtticDir = os.path.join(logDir, "__attic__")
            for action in actions:
                if action[0] == "remove":
                    # ("remove", <patches-basedir>, <dst-relpath>)
                    origLoc = os.path.join(sourceDir, action[2])
                    oldAtticLoc = os.path.join(oldAtticDir, action[2])
                    atticLoc = os.path.join(atticDir, action[2])
                    possibleLocs = [origLoc, oldAtticLoc]
                    for location in possibleLocs:
                        if os.path.exists(location):
                            log.debug("copy '%s' to attic", origLoc)
                            sh.copy(location, atticLoc)

        # A little helper for doing the 'add' action with appropriate
        # logging.
        def _add_it(s, d, actual_s, force=False, dryRun=False):
            if not exists(d):
                if dryRun:
                    log.info("add '%s' to '%s' (dry run)",
                             actual_s, d)
                else:
                    log.info("add '%s' to '%s'", actual_s, d)
                    sh.copy(s, d)
            elif not _diffPaths(s, d):
                log.info("skip add of '%s' to '%s': "
                         "no changes", actual_s, d)
            else:
                if dryRun:
                    log.info("replace '%s' to '%s' (dry run)",
                             actual_s, d)
                else:
                    log.info("replace '%s' to '%s'",
                             actual_s, d)
                    sh.copy(s, d)

        # Carry out each patch action.
        for action in actions:
            if action[0] in ("apply", "preprocess & apply"):
                _applyPatch(patchExe, workDir, action[2], sourceDir,
                            dryRun=dryRun, patchArgs=action[3])
            elif action[0] == "add":
                # ("add", <patches-basedir>, <src-relpath>, <dst-relpath>, <force>)
                # e.g. ("add", "patches", "hpux_cpp_ext\\thingy.s", "python", False)
                #
                #XXX Could improve logging here to only log one message
                #    in certain circumstances: no changes, all files
                #    were added.
                src = os.path.join(workDir, action[2])
                dst = os.path.join(sourceDir, action[3])
                if isdir(src):
                    for dirpath, dirnames, filenames in os.walk(src):
                        subpath = (dirpath == src
                                   and os.curdir
                                   or dirpath[len(src)+1:])
                        for filename in filenames:
                            s = join(dirpath, filename)
                            d = normpath(join(dst, subpath, filename))
                            # 'actual_s' might actually have a '.p' in there.
                            actual_s = join(action[1], action[2], subpath,
                                            filename)
                            _add_it(s, d, actual_s, force=action[4],
                                    dryRun=dryRun)
                else:
                    d = isfile(dst) and dst or join(dst, basename(src))
                    actual_s = join(action[1], action[2])
                    _add_it(src, d, actual_s, force=action[4], dryRun=dryRun)
            elif action[0] == "remove":
                # ("remove", <patches-basedir>, <dst-relpath>)
                dst = os.path.join(sourceDir, action[2])
                if not os.path.exists(dst):
                    log.info("skip removal of '%s': already removed",
                             action[2])
                elif dryRun:
                    log.info("remove '%s' (dry run)", action[2])
                else:
                    log.info("remove '%s'", action[2])
                    sh.rm(dst)
            else:
                raise Error("unknown patch action '%s': %r"
                            % (action[0], action))

        # If a log dir was specified then copy working dir there.
        if logDir:
            if dryRun:
                log.info("creating patch log in '%s' (dry run)", logDir)
            else:
                if os.path.exists(logDir):
                    sh.rm(logDir)
                log.info("creating patch log in '%s'", logDir)
                sh.copy(workDir, logDir)
    finally:    
        log.debug("removing temporary working dir '%s'", workDir)
        try:
            sh.rm(workDir)
        except EnvironmentError, ex:
            log.warn("could not remove temp working dir '%s': %s",
                     workDir, ex)
        if sys.platform.startswith("win"):
            if oldTmpDir is not None:
                os.environ["TMP"] = oldTmpDir
            try:
                sh.rm(tmpDir)
            except EnvironmentError, ex:
                log.warn("could not remove temp patch dir '%s': %s",
                         tmpDir, ex)
Example #6
0
                    sourceFile = os.path.join(dst, os.path.basename(sourceLoc))
                if os.path.exists(sourceFile):
                    fin = open(sourceFile, 'rb')
                    try:
                        sourcemd5 = md5(fin.read()).hexdigest()
                    finally:
                        fin.close()
                    if atticmd5 == sourcemd5:
                        log.info("skip restoration of '%s' from attic: "
                                 "already restored", action[2])
                        continue
            if dryRun:
                log.info("restore '%s' from attic (dry run)", action[2])
            else:
                log.info("restore '%s' from attic", action[2])
                sh.copy(atticLoc, sourceLoc)
        else:
            raise Error("unknown action, '%s', in patch log: %s"
                        % (action[0], action))


def patch(patchesDir, sourceDir, config=None, logDir=None, dryRun=0,
          patchExe=None, logFilename=None):
    """Patch the given source tree with the given patches.
    
        "patchesDir" is a directory tree of patches to apply or a single
            patch file to apply. Alternatively it may be _list of_ patch
            directories and/or files.
        "sourceDir" is the base directory of the source tree to patch.
        "config" is a configuration object to pass to __patchinfo__.py
            special control functions. Typically it is a configuration