| # Gaudi Framework, version v21r7p1 |

Home |  | Generated: 15 Feb 2010

# validate_patch.py

Go to the documentation of this file.

    00001 #!/usr/bin/env python
    00002 """
    00003 Simple script for automatic validation of a patch.
    00004
    00005 Usage:
    00006    validate_patch.py <savannah patch id>
    00007    validate_patch.py file.patch
    00008 """
    00009
    00010 __author__ = "Marco Clemencic <marco.clemencic@cern.ch>"
    00011
    00012 import os, sys
    00013 import logging
    00014 from shutil import rmtree
    00015 from tempfile import mkdtemp
    00016 from subprocess import Popen, PIPE
    00017
    00018 from HTMLParser import HTMLParser
    00019 from urllib import urlopen
    00020
    00021 class PatchData(object):
    00022     def __init__(self, id = None, title = None, files = None):
    00023         self.id = None
    00024         self.title = None
    00025         if files is None:
    00026             self.files = []
    00027     def __repr__(self):
    00028         r = self.__class__.__name__ + "("
    00029         fields = []
    00030         if self.id is not None:
    00031             fields.append("id=%r" % self.id)
    00032         if self.title is not None:
    00033             fields.append("title=%r" % self.title)
    00034         if self.files:
    00035             fields.append("files=%r" % self.files)
    00036         return "%s(%s)" % (self.__class__.__name__, ",".join(fields))
    00037
    00038 ## parse the
    00039 class SavannahParser(HTMLParser):
    00040     __attachments_id__ = "hidsubpartcontentattached"
    00041     def __init__(self):
    00042         HTMLParser.__init__(self)
    00043         # data collected
    00044         self.patch = None
    00045
    00046         # parsing flags and temporary data
    00047         self._parsingAttachments = 0 # depth of span tag in the attachment block
    00048         self._currentFileId = None
    00049         self._currentFileData = ""
    00050
    00051         # temporary storage
    00052
    00053     def handle_starttag(self, tag, attrs):
    00054         attrs = dict(attrs)
    00055         if tag == "html":
    00056             # new file: new patch data
    00057             self.patch = PatchData()
    00058         elif tag == "span":
    00059             if attrs.get("id", None) == self.__attachments_id__:
    00060                 self._parsingAttachments = 1
    00061             elif self._parsingAttachments:
    00062                 self._parsingAttachments += 1
    00063         elif (self._parsingAttachments
    00064               and tag == "a"
    00065               and "file_id=" in attrs.get("href", "")):
    00066             self._currentFileId = attrs["href"].split("file_id=")[-1]
    00067     def handle_endtag(self, tag):
    00068         if tag == "span" and self._parsingAttachments:
    00069             self._parsingAttachments -= 1
    00070         elif tag == "a" and self._currentFileId:
    00071             #print self._currentFileData
    00072             filename = self._currentFileData.split(":")[-1].strip()
    00073             #print filename, self._currentFileId
    00074             self.patch.files.append((filename, int(self._currentFileId)))
    00075             self._currentFileId = None
    00076             self._currentFileData = ""
    00077
    00078     def handle_data(self, data):
    00079         if self._currentFileId:
    00080             data = data.replace("&nbsp;", " ")
    00081             self._currentFileData += data
    00082
    00083 def get_patch_info_x(patch):
    00084     patch = int(patch)
    00085     server = "savannah.cern.ch"
    00086     path =  "/patch/?%d" % patch
    00087     conn = httplib.HTTPSConnection(server)
    00088     conn.request("GET", path)
    00089     r = conn.getresponse()
    00090     if r.status == 200:
    00091         pass
    00092     else:
    00093         raise RuntimeError(r.status, r.reason, "https://" + server + path)
    00094     parser = SavannahParser()
    00095     parser.feed(r.read())
    00096     parser.close()
    00097     conn.close()
    00098     return parser.patch
    00099
    00100 def get_patch_info(patch):
    00101     patch = int(patch)
    00102     parser = SavannahParser()
    00103     parser.feed(urlopen("https://savannah.cern.ch/patch/?%d" % patch).read())
    00104     parser.close()
    00105     return parser.patch
    00106
    00107 def get_patch_data(file_id):
    00108     file_id = int(file_id)
    00109     return urlopen("https://savannah.cern.ch/patch/download.php?file_id=%d" % file_id).read()
    00110
    00111 class TempDir(object):
    00112     """Class to create a temporary directory."""
    00113     def __init__(self, suffix="", prefix="tmp", dir=None, keep_var="KEEPTEMPDIR"):
    00114         """Constructor.
    00115
    00116         'keep_var' is used to define which environment variable will prevent the
    00117         deletion of the directory.
    00118
    00119         The other arguments are the same as tempfile.mkdtemp.
    00120         """
    00121         self._keep_var = keep_var
    00122         self._name = mkdtemp(suffix, prefix, dir)
    00123
    00124     def getName(self):
    00125         """Returns the name of the temporary directory"""
    00126         return self._name
    00127
    00128     def __str__(self):
    00129         """Convert to string."""
    00130         return self.getName()
    00131
    00132     def __del__(self):
    00133         """Destructor.
    00134
    00135         Remove the temporary directory.
    00136         """
    00137         if self._name:
    00138             if self._keep_var in os.environ:
    00139                 logging.info("%s set: I do not remove the temporary directory '%s'",
    00140                              self._keep_var, self._name)
    00141                 return
    00142             rmtree(self._name)
    00143
    00144 def check_out_gaudi(path):
    00145     return Popen(["svn", "co", "http://svnweb.cern.ch/guest/gaudi/Gaudi/trunk", os.path.join(path, "Gaudi")]).wait()
    00146
    00147 def apply_patch(patch_data, path):
    00148     proc = Popen(["patch", "-p0", "--batch"], cwd = path, stdin = PIPE)
    00149     proc.communicate(patch_data)
    00150     return proc.returncode
    00151
    00152 def check(path):
    00153     return Popen(" ".join(["cmt", "show", "projects"]), shell = True, cwd = path).wait()
    00154
    00155 def build(path):
    00156     if "LBCONFIGURATIONROOT" in os.environ:
    00157         cmd = ["make",
    00158                "-f", os.path.join(os.environ["LBCONFIGURATIONROOT"], "data", "Makefile")]
    00159         if "use-distcc" in os.environ.get("CMTEXTRATAGS",""):
    00160                cmd += ["-j", "6"]
    00161     else:
    00162         cmd = ["cmt", "-pack=GaudiRelease", "broadcast", "cmt", "make", "all_groups"]
    00163     return Popen(" ".join(cmd),
    00164                  shell = True,
    00165                  cwd = path).wait()
    00166 def test(path):
    00167     cmd = ["cmt", "-pack=GaudiRelease", "TestProject"]
    00168     proc = Popen(" ".join(cmd),
    00169                  stdout = PIPE,
    00170                  shell = True,
    00171                  cwd = path)
    00172     output = []
    00173     while proc.poll() is None:
    00174         chunk = proc.stdout.read(256)
    00175         output.append(chunk)
    00176         sys.stdout.write(chunk)
    00177         sys.stdout.flush()
    00178     chunk = proc.stdout.read(256)
    00179     output.append(chunk)
    00180     sys.stdout.write(chunk)
    00181     sys.stdout.flush()
    00182     if proc.returncode:
    00183         # immediately return in case of failure
    00184         return proc.returncode
    00185     # look for failures in the output
    00186     output = ("".join(output)).splitlines()
    00187     for l in output:
    00188         l = l.strip()
    00189         if ": FAIL" in l or ": ERROR" in l:
    00190             return 1
    00191     return 0
    00192
    00193 def main():
    00194     logging.basicConfig()
    00195     if len(sys.argv) != 2:
    00196         print """Usage:
    00197    validate_patch.py <savannah patch id>
    00198    validate_patch.py file.patch
    00199 """
    00200         return 2
    00201     patch_id = sys.argv[1]
    00202     if os.path.isfile(patch_id):
    00203         patch_data = open(patch_id, "rb").read()
    00204     else:
    00205         patch = get_patch_info(patch_id)
    00206         patch_file_id = patch.files[0][1]
    00207         patch_data = get_patch_data(patch_file_id)
    00208
    00209     td = TempDir(prefix = patch_id + "-")
    00210     if check_out_gaudi(str(td)) != 0:
    00211         print "Sorry, problems checking out Gaudi. Try again."
    00212         return 0
    00213     top_dir = os.path.join(str(td), "Gaudi")
    00214     open(os.path.join(top_dir, patch_id) ,"wb").write(patch_data)
    00215
    00216     revision = -1
    00217     for l in Popen(["svn", "info", top_dir], stdout = PIPE).communicate()[0].splitlines():
    00218         if l.startswith("Revision:"):
    00219             revision = int(l.split()[-1])
    00220             break
    00221
    00222     actions = [(lambda path: apply_patch(patch_data, path), "application of the patch"),
    00223                (check, "check of the configuration"),
    00224                (build, "build"),
    00225                (test, "test"),
    00226                ]
    00227     failure = False
    00228     for action, title in actions:
    00229         if action(top_dir) != 0:
    00230             failure = title
    00231             break
    00232
    00233     if failure:
    00234         print "*** Patch %s failed during %s (using revision r%d) ***" % (patch_id, failure, revision)
    00235         return 1
    00236
    00237     print "*** Patch %s succeeded (using revision r%d) ***" % (patch_id, revision)
    00238     return 0
    00239
    00240 if __name__ == "__main__":
    00241     sys.exit(main())