| # Gaudi Framework, version v25r0 |

Home |  | Generated: Mon Feb 17 2014

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
make_patch.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2
3 import os, sys, re
4 from subprocess import Popen, PIPE, STDOUT
5 from fnmatch import fnmatch
6 import logging
7
8 def command(cmd, *args, **kwargs):
9 """
10 Simple wrapper to execute a command and return standard output and standard error.
11 """
12 d = {"stdout": PIPE, "stderr": PIPE}
13 d.update(kwargs)
14 cmd = [cmd] + list(args)
15 logging.debug("Execute command: %r %r", " ".join(cmd), kwargs)
16 proc = apply(Popen, (cmd,), d)
17 return proc.communicate()
18
19 cmt = lambda *args, **kwargs: apply(command, ("cmt",) + args, kwargs)
20 cvs = lambda *args, **kwargs: apply(command, ("cvs",) + args, kwargs)
21 svn = lambda *args, **kwargs: apply(command, ("svn",) + args, kwargs)
22
23 def broadcast_packages():
24 """
25 Find the local packages the current one depends on (using 'cmt broadcast').
26 Returns a list of pairs ("package name","path to the cmt directory").
27 """
28 # make cmt print one line per package with python syntax
29 if not sys.platform.startswith("win"):
30 pkg_dirs = "[\n" + cmt("broadcast",r'echo "(\"<package>\", \"$PWD\"),"')[0] + ']'
31 else:
32 pkg_dirs = "[\n" + cmt("broadcast",r'echo ("<package>", r"%<package>root%\cmt"),')[0] + ']'
33 # Clean up a bit the output (actually needed only on Windows because of the newlines)
34 pkg_dirs = "\n".join([l.strip() for l in pkg_dirs.splitlines() if not l.startswith("#")])
35 return eval(pkg_dirs)
36
37 def matches(filename, patterns):
38 """
39 Returns True if any of the specified glob patterns (list of strings) matched
40 the string 'filename'.
41 """
42 for p in patterns:
43 if fnmatch(filename, p):
44 logging.debug("Excluding file: %r", filename)
45 return True
46 return False
47
48 def expand_dirs(files, basepath = ""):
49 """
50 Replace the entries in files that correspond to directories with the list of
51 files in those directories.
52 """
53 if basepath:
54 lb = len(basepath)+1
55 else:
56 lb = 0
57 newlist = []
58 for f in files:
59 base = os.path.join(basepath, f)
60 if os.path.isdir(base):
61 for root, ds, fs in os.walk(base):
62 for ff in fs:
63 newlist.append(os.path.join(root,ff)[lb:])
64 else:
65 newlist.append(f)
66 return newlist
67
68 def revision_diff_cmd(cwd):
69 if os.path.isdir(os.path.join(cwd, "CVS")):
70 return cvs("diff", "-upN", cwd = cwd)
71 else:
72 # special treatment to show new files in a way compatible with CVS
73 out, err = svn("status", cwd = cwd)
74
75 newfiles = [ l
76 for l in out.splitlines()
77 if l.startswith("? ") ]
78 out, err = svn("diff", cwd = cwd)
79 if newfiles:
80 out = "\n".join(newfiles) + "\n" + out
81 return out, err
82
83 def diff_pkg(name, cmtdir, exclusions = []):
84 """
85 Return the patch data for a package.
86 """
87 rootdir = os.path.dirname(cmtdir)
88 out, err = revision_diff_cmd(cwd = rootdir)
89 # extract new files
90 new_files = [ l.split(None,1)[1]
91 for l in out.splitlines()
92 if l.startswith("? ") ]
93 new_files = expand_dirs(new_files, rootdir)
94 new_files = [ f
95 for f in new_files
96 if not matches(f, exclusions) ]
97 # make diff segments for added files
98 for f in new_files:
99 logging.info("Added file %r", f)
100 #out += "diff -u -p -N %s\n" % os.path.basename(f)
101 #out += command("diff", "-upN", "/dev/null", f,
102 # cwd = rootdir)[0]
103 out += "Index: %s\n" % f
104 out += "===================================================================\n"
105 out += command("diff", "-upN", "/dev/null", f,
106 cwd = rootdir)[0]
107 # extract removed files
108 removed_files = [ l.split()[-1]
109 for l in err.splitlines()
110 if "cannot find" in l ]
111 removed_files = [ f
112 for f in removed_files
113 if not matches(f, exclusions) ]
114 # make diff segments for removed files (more tricky)
115 for f in removed_files:
116 logging.info("Removed file %r", f)
117 # retrieve the original content from CVS
118 orig = cvs("up", "-p", f,
119 cwd = rootdir)[0]
120 out += "diff -u -p -N %s\n" % os.path.basename(f)
121 out += "--- %s\t1 Jan 1970 00:00:00 -0000\n" % f
122 out += "+++ /dev/null\t1 Jan 1970 00:00:00 -0000\n"
123 lines = orig.splitlines()
124 out += "@@ -1,%d +0,0 @@\n" % len(lines)
125 for l in lines:
126 out += '-%s\n' % l
127 # Fix the paths to have the package names
128 rex = re.compile(r"^(Index: |\? |\+\+\+ |--- (?!/dev/null))", re.MULTILINE)
129 out = rex.sub(r"\1%s/" % name, out)
130 return out
131
132 def main():
133 from optparse import OptionParser
134 parser = OptionParser(description = "Produce a patch file from a CMT project. "
135 "The patch contains the changes with respect "
136 "to the CVS repository, including new files "
137 "that are present only locally. Run the script "
138 "from the cmt directory of a package." )
139 parser.add_option("-x", "--exclude", action="append", type="string",
140 metavar="PATTERN", dest="exclusions",
141 help="Pattern to exclude new files from the patch")
142 parser.add_option("-o", "--output", action="store", type="string",
143 help="Name of the file to send the output to. Standard "
144 "output is used if not specified")
145 parser.add_option("-v", "--verbose", action="store_true",
146 help="Print some progress information on standard error")
147 parser.add_option("--debug", action="store_true",
148 help="Print debug information on standard error")
149 parser.set_defaults(exclusions = [])
150
151 opts, args = parser.parse_args()
152
153 if opts.debug:
154 logging.basicConfig(level = logging.DEBUG)
155 elif opts.verbose:
156 logging.basicConfig(level = logging.INFO)
157
158 # default exclusions
159 opts.exclusions += [ "*.py[co]",
160 "*.patch",
161 "cmt/cleanup.*",
162 "cmt/setup.*",
163 "cmt/*.make",
164 "cmt/Makefile",
165 "cmt/*.nmake",
166 "cmt/*.nmakesav",
167 "cmt/NMake",
168 "cmt/install*.history",
169 "cmt/build.*.log",
170 "cmt/version.cmt",
171 "genConf",
172 "slc3_ia32_gcc323*",
173 "slc4_ia32_gcc34*",
174 "slc4_amd64_gcc34*",
175 "slc4_amd64_gcc43*",
176 "win32_vc71*",
177 "i686-slc[34567]-[ig]cc*",
178 "i686-slc[34567]-clang*",
179 "x86_64-slc[34567]-[ig]cc*",
180 "x86_64-slc[34567]-clang*",
181 ".eclipse",
182 ]
183 if "CMTCONFIG" in os.environ:
184 opts.exclusions.append(os.environ["CMTCONFIG"])
185
186 # check if we are in the cmt directory before broadcasting
187 if not (os.path.basename(os.getcwd()) == "cmt" and os.path.exists("requirements")):
188 logging.error("This script must be executed from the cmt directory of a package.")
189 return 1
190
191 pkgs = broadcast_packages()
192 num_pkgs = len(pkgs)
193 count = 0
194
195 patch = ""
196 for name, path in pkgs:
197 count += 1
198 logging.info("Processing %s from %s (%d/%d)",
199 name, os.path.dirname(path), count, num_pkgs)
200 patch += diff_pkg(name, path, opts.exclusions)
201
202 if sys.platform.startswith("win"):
203 # fix newlines chars
204 patch = patch.replace("\r","")
205
206 if opts.output:
207 logging.info("Writing patch file %r", opts.output)
208 open(opts.output,"w").write(patch)
209 else:
210 sys.stdout.write(patch)
211 return 0
212
213 if __name__ == "__main__":
214 sys.exit(main())