1 import os, string, sys, re
5 from distutils import log
13 OUR_VERSION_BASE_RE_STR="(\d+)(\.(\d+)(\.(\d+))?)?((a|b|c)(\d+))?(\.dev(\d+))?"
15 # If we can import pyutil.version_class then use its regex.
16 from pyutil import version_class
17 VERSION_BASE_RE_STR = version_class.VERSION_BASE_RE_STR
18 except (ImportError, AttributeError):
19 # Else (perhaps a bootstrapping problem),then we'll use this
20 # regex, which was copied from the pyutil source code on
22 VERSION_BASE_RE_STR=OUR_VERSION_BASE_RE_STR
24 def get_text(nodelist):
27 if node.nodeType == node.TEXT_NODE:
32 # This is the version of this tree, as created by %(versiontool)s from the darcs patch
33 # information: the main version number is taken from the most recent release
34 # tag. If some patches have been added since the last release, this will have a
35 # -NN "build number" suffix, or else a -rNN "revision number" suffix. Please see
36 # pyutil.version_class for a description of what the different fields mean.
38 __pkgname__ = "%(pkgname)s"
39 verstr = "%(pkgversion)s"
41 from pyutil.version_class import Version as pyutil_Version
42 __version__ = pyutil_Version(verstr)
43 except (ImportError, ValueError):
44 # Maybe there is no pyutil installed.
45 from distutils.version import LooseVersion as distutils_Version
46 __version__ = distutils_Version(verstr)
49 def write_version_py(verstr, outfname, EXE_NAME, version_body, pkgname):
50 f = open(outfname, "wb+")
51 f.write(version_body % {
52 'versiontool': EXE_NAME,
58 def read_version_py(infname):
60 verstrline = open(infname, "rt").read()
61 except EnvironmentError:
64 VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
65 mo = re.search(VSRE, verstrline, re.M)
69 def update(pkgname, verfilename, revision_number=False, loud=False, abort_if_snapshot=False, EXE_NAME="darcsver", version_body=VERSION_BODY):
71 @param revision_number If true, count the total number of patches in all
72 history. If false, count the total number of patches since the most recent
75 Returns a tuple of (exit code, new version string).
77 if isinstance(verfilename, basestring):
78 verfilenames = [verfilename]
80 verfilenames = verfilename
81 assert all([isinstance(vfn, basestring) for vfn in verfilenames]), [vfn for vfn in verfilenames if not isinstance(vfn, basestring)]
82 if isinstance(version_body, basestring):
83 verbodies = [version_body]
85 verbodies = version_body
88 # First we try "darcs query repo" because if that fails then we
89 # won't try "darcs changes" at all, because "darcs changes" emits
90 # an ugly error message when run in not-a-repo.
92 p = subprocess.Popen(["darcs", 'query', 'repo'], stdout=PIPE, stderr=PIPE, universal_newlines=True)
94 if ose.errno == 2 and '~' in os.environ['PATH']:
95 expanded_path = os.environ['PATH'].replace('~', os.path.expanduser('~'))
96 msg = ("WARNING: 'darcs' was not found. However '~' was found in your PATH. \n"
97 "Please note that bugs in python cause it to fail to traverse '~' in \n"
98 "the user's PATH. Please fix your path, e.g. \nPATH=%s" )
99 log.warn(msg % (expanded_path,))
102 (output, errput) = p.communicate()
106 cmd = ["changes", "--xml-output"]
107 if not revision_number:
108 cmd.append("--from-tag=^%s" % (pkgname,))
110 p = subprocess.Popen(["darcs"] + cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
114 (output, errput) = p.communicate()
116 if rc != 0 and errput:
117 log.info("%s: darcs wrote to stderr: '%s'" % (EXE_NAME, errput,))
120 if all([os.path.exists(vfn) for vfn in verfilenames]):
121 log.info("%s: using extant version file %s" % (EXE_NAME, verfilenames))
122 return (0, read_version_py(verfilenames[0]))
124 log.warn("%s: didn't find version tags with darcs, and %s don't exist." % (EXE_NAME, verfilenames))
127 # Filter out bad chars that can cause the XML parser to give up in despair.
128 # (Thanks to lelit of the tailor project and ndurner and warner for this hack.)
129 allbadchars = "".join([chr(i) for i in range(0x0a) + [0x0b, 0x0c] + range(0x0e, 0x20) + range(0x7f,0x100)])
130 tt = string.maketrans(allbadchars, "-"*len(allbadchars))
131 output = output.translate(tt)
132 regexstr = "^TAG %s-(%s)$" % (pkgname, VERSION_BASE_RE_STR)
135 # strip off trailing warning messages that darcs 2.3.1 writes to stdout
136 endi = output.find("</changelog>")+len("</changelog>")
138 output = output[:endi]
140 doc = xml.dom.minidom.parseString(output)
141 except xml.parsers.expat.ExpatError:
142 # Okay maybe this is an error message instead of an XML output.
145 changelog = doc.getElementsByTagName("changelog")[0]
146 patches = changelog.getElementsByTagName("patch")
147 version_re = re.compile(regexstr)
148 count_since_last_patch = 0
149 if abort_if_snapshot:
150 for patch in patches:
151 name = get_text(patch.getElementsByTagName("name")[0].childNodes)
152 m = version_re.match(name)
154 last_tag = m.group(1)
155 last_tag = last_tag.encode("utf-8")
158 sys.exit(0) # because abort_if_snapshot
160 for patch in patches:
161 name = get_text(patch.getElementsByTagName("name")[0].childNodes)
162 m = version_re.match(name)
164 last_tag = m.group(1)
165 last_tag = last_tag.encode("utf-8")
168 count_since_last_patch += 1
172 log.info("%s: darcs wrote to stderr: '%s'" % (EXE_NAME, errput,))
174 assert all([isinstance(vfn, basestring) for vfn in verfilenames]), [vfn for vfn in verfilenames if not isinstance(vfn, basestring)]
175 if all([os.path.exists(vfn) for vfn in verfilenames]):
176 log.warn("%s: I'm unable to find a tag in the darcs history matching \"%s\", so I'm leaving %s alone." % (EXE_NAME, regexstr, verfilenames,))
177 return (0, read_version_py(verfilenames[0]))
179 log.warn("%s: I'm unable to find a tag in the darcs history matching \"%s\", and %s don't exist." % (EXE_NAME, regexstr, verfilenames,))
183 if count_since_last_patch:
184 # this is an interim version
185 verstr = "%s-r%d" % (last_tag, len(patches))
190 if count_since_last_patch:
191 # this is an interim version
192 verstr = "%s-%d" % (last_tag, count_since_last_patch)
197 for verfn, verbod in zip(verfilenames, verbodies):
198 write_version_py(verstr, verfn, EXE_NAME, verbod, pkgname)
199 log.info("%s: wrote '%s' into %s" % (EXE_NAME, verstr, verfn,))