Gaudi Framework, version v23r2

Home   Generated: Thu Jun 28 2012

make_patch.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 import os, sys, re
00004 from subprocess import Popen, PIPE, STDOUT
00005 from fnmatch import fnmatch
00006 import logging
00007 
00008 def command(cmd, *args, **kwargs):
00009     """
00010     Simple wrapper to execute a command and return standard output and standard error.
00011     """
00012     d = {"stdout": PIPE, "stderr": PIPE}
00013     d.update(kwargs)
00014     cmd = [cmd] + list(args)
00015     logging.debug("Execute command: %r %r", " ".join(cmd), kwargs)
00016     proc = apply(Popen, (cmd,), d)
00017     return proc.communicate()
00018 
00019 cmt = lambda *args, **kwargs: apply(command, ("cmt",) + args, kwargs)
00020 cvs = lambda *args, **kwargs: apply(command, ("cvs",) + args, kwargs)
00021 svn = lambda *args, **kwargs: apply(command, ("svn",) + args, kwargs)
00022 
00023 def broadcast_packages():
00024     """
00025     Find the local packages the current one depends on (using 'cmt broadcast').
00026     Returns a list of pairs ("package name","path to the cmt directory").
00027     """
00028     # make cmt print one line per package with python syntax
00029     if not sys.platform.startswith("win"):
00030         pkg_dirs = "[\n" + cmt("broadcast",r'echo "(\"<package>\", \"$PWD\"),"')[0] + ']'
00031     else:
00032         pkg_dirs = "[\n" + cmt("broadcast",r'echo ("<package>", r"%<package>root%\cmt"),')[0] + ']'
00033     # Clean up a bit the output (actually needed only on Windows because of the newlines)
00034     pkg_dirs = "\n".join([l.strip() for l in pkg_dirs.splitlines() if not l.startswith("#")])
00035     return eval(pkg_dirs)
00036 
00037 def matches(filename, patterns):
00038     """
00039     Returns True if any of the specified glob patterns (list of strings) matched
00040     the string 'filename'.
00041     """
00042     for p in patterns:
00043         if fnmatch(filename, p):
00044             logging.debug("Excluding file: %r", filename)
00045             return True
00046     return False
00047 
00048 def expand_dirs(files, basepath = ""):
00049     """
00050     Replace the entries in files that correspond to directories with the list of
00051     files in those directories.
00052     """
00053     if basepath:
00054         lb = len(basepath)+1
00055     else:
00056         lb = 0
00057     newlist = []
00058     for f in files:
00059         base = os.path.join(basepath, f)
00060         if os.path.isdir(base):
00061             for root, ds, fs in os.walk(base):
00062                 for ff in fs:
00063                     newlist.append(os.path.join(root,ff)[lb:])
00064         else:
00065             newlist.append(f)
00066     return newlist
00067 
00068 def revision_diff_cmd(cwd):
00069     if os.path.isdir(os.path.join(cwd, "CVS")):
00070         return cvs("diff", "-upN", cwd = cwd)
00071     else:
00072         # special treatment to show new files in a way compatible with CVS
00073         out, err = svn("status", cwd = cwd)
00074 
00075         newfiles = [ l
00076                      for l in out.splitlines()
00077                      if l.startswith("? ") ]
00078         out, err = svn("diff", cwd = cwd)
00079         if newfiles:
00080             out = "\n".join(newfiles) + "\n" + out
00081         return out, err
00082 
00083 def diff_pkg(name, cmtdir, exclusions = []):
00084     """
00085     Return the patch data for a package.
00086     """
00087     rootdir = os.path.dirname(cmtdir)
00088     out, err = revision_diff_cmd(cwd = rootdir)
00089     # extract new files
00090     new_files = [ l.split(None,1)[1]
00091                   for l in out.splitlines()
00092                   if l.startswith("? ") ]
00093     new_files = expand_dirs(new_files, rootdir)
00094     new_files = [ f
00095                   for f in new_files
00096                   if not matches(f, exclusions) ]
00097     # make diff segments for added files
00098     for f in new_files:
00099         logging.info("Added file %r", f)
00100         #out += "diff -u -p -N %s\n" % os.path.basename(f)
00101         #out += command("diff", "-upN", "/dev/null", f,
00102         #               cwd = rootdir)[0]
00103         out += "Index: %s\n" % f
00104         out += "===================================================================\n"
00105         out += command("diff", "-upN", "/dev/null", f,
00106                        cwd = rootdir)[0]
00107     # extract removed files
00108     removed_files = [ l.split()[-1]
00109                       for l in err.splitlines()
00110                       if "cannot find" in l ]
00111     removed_files = [ f
00112                       for f in removed_files
00113                       if not matches(f, exclusions) ]
00114     # make diff segments for removed files (more tricky)
00115     for f in removed_files:
00116         logging.info("Removed file %r", f)
00117         # retrieve the original content from CVS
00118         orig = cvs("up", "-p", f,
00119                    cwd = rootdir)[0]
00120         out += "diff -u -p -N %s\n" % os.path.basename(f)
00121         out += "--- %s\t1 Jan 1970 00:00:00 -0000\n" % f
00122         out += "+++ /dev/null\t1 Jan 1970 00:00:00 -0000\n"
00123         lines = orig.splitlines()
00124         out += "@@ -1,%d +0,0 @@\n" % len(lines)
00125         for l in lines:
00126             out += '-%s\n' % l
00127     # Fix the paths to have the package names
00128     rex = re.compile(r"^(Index: |\? |\+\+\+ |--- (?!/dev/null))", re.MULTILINE)
00129     out = rex.sub(r"\1%s/" % name, out)
00130     return out
00131 
00132 def main():
00133     from optparse import OptionParser
00134     parser = OptionParser(description = "Produce a patch file from a CMT project. "
00135                                         "The patch contains the changes with respect "
00136                                         "to the CVS repository, including new files "
00137                                         "that are present only locally. Run the script "
00138                                         "from the cmt directory of a package." )
00139     parser.add_option("-x", "--exclude", action="append", type="string",
00140                       metavar="PATTERN", dest="exclusions",
00141                       help="Pattern to exclude new files from the patch")
00142     parser.add_option("-o", "--output", action="store", type="string",
00143                       help="Name of the file to send the output to. Standard "
00144                            "output is used if not specified")
00145     parser.add_option("-v", "--verbose", action="store_true",
00146                       help="Print some progress information on standard error")
00147     parser.add_option("--debug", action="store_true",
00148                       help="Print debug information on standard error")
00149     parser.set_defaults(exclusions = [])
00150 
00151     opts, args = parser.parse_args()
00152 
00153     if opts.debug:
00154         logging.basicConfig(level = logging.DEBUG)
00155     elif opts.verbose:
00156         logging.basicConfig(level = logging.INFO)
00157 
00158     # default exclusions
00159     opts.exclusions += [ "*.py[co]",
00160                          "*.patch",
00161                          "cmt/cleanup.*",
00162                          "cmt/setup.*",
00163                          "cmt/*.make",
00164                          "cmt/Makefile",
00165                          "cmt/*.nmake",
00166                          "cmt/*.nmakesav",
00167                          "cmt/NMake",
00168                          "cmt/install*.history",
00169                          "cmt/build.*.log",
00170                          "cmt/version.cmt",
00171                          "genConf",
00172                          "slc3_ia32_gcc323*",
00173                          "slc4_ia32_gcc34*",
00174                          "slc4_amd64_gcc34*",
00175                          "slc4_amd64_gcc43*",
00176                          "win32_vc71*",
00177                          "i686-slc3-gcc323*",
00178                          "i686-slc4-gcc34*",
00179                          "i686-slc4-gcc41*",
00180                          "x86_64-slc4-gcc34*",
00181                          "x86_64-slc4-gcc41*",
00182                          "i686-slc5-gcc43*",
00183                          "x86_64-slc5-gcc43*",
00184                          "x86_64-slc5-icc*",
00185                          ]
00186     if "CMTCONFIG" in os.environ:
00187         opts.exclusions.append(os.environ["CMTCONFIG"])
00188 
00189     # check if we are in the cmt directory before broadcasting
00190     if not (os.path.basename(os.getcwd()) == "cmt" and os.path.exists("requirements")):
00191         logging.error("This script must be executed from the cmt directory of a package.")
00192         return 1
00193 
00194     pkgs = broadcast_packages()
00195     num_pkgs = len(pkgs)
00196     count = 0
00197 
00198     patch = ""
00199     for name, path in pkgs:
00200         count += 1
00201         logging.info("Processing %s from %s (%d/%d)",
00202                      name, os.path.dirname(path), count, num_pkgs)
00203         patch += diff_pkg(name, path, opts.exclusions)
00204 
00205     if sys.platform.startswith("win"):
00206         # fix newlines chars
00207         patch = patch.replace("\r","")
00208 
00209     if opts.output:
00210         logging.info("Writing patch file %r", opts.output)
00211         open(opts.output,"w").write(patch)
00212     else:
00213         sys.stdout.write(patch)
00214     return 0
00215 
00216 if __name__ == "__main__":
00217     sys.exit(main())
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated at Thu Jun 28 2012 23:27:24 for Gaudi Framework, version v23r2 by Doxygen version 1.7.2 written by Dimitri van Heesch, © 1997-2004