import version class and make-version script from pyutil -- fixes win32 build, improves error handling, and eliminates unused features

This commit is contained in:
Zooko O'Whielacronx 2007-08-16 14:09:30 -07:00
parent 6c4fb6fd93
commit 094b687d6e
6 changed files with 205 additions and 198 deletions

View File

@ -59,7 +59,7 @@ PP=PYTHONPATH=$(PYTHONPATH)
.PHONY: make-version build .PHONY: make-version build
make-version: make-version:
$(PYTHON) misc/make-version.py $(PYTHON) misc/make-version.py "allmydata-tahoe" "src/allmydata/_version.py"
build: make-version build-zfec build-Crypto build-foolscap build-simplejson build: make-version build-zfec build-Crypto build-foolscap build-simplejson
$(PP) $(PYTHON) ./setup.py $(EXTRA_SETUP_ARGS) install --prefix="$(INSTDIR)" --install-lib="$(INSTDIR)/lib" --install-scripts="$(INSTDIR)/bin" $(PP) $(PYTHON) ./setup.py $(EXTRA_SETUP_ARGS) install --prefix="$(INSTDIR)" --install-lib="$(INSTDIR)/lib" --install-scripts="$(INSTDIR)/bin"

View File

@ -1,35 +1,26 @@
#! /usr/bin/env python #! /usr/bin/env python
import os, sys
""" """
Create src/allmydata/version.py, based upon the latest darcs release tag. Create _version.py, based upon the latest darcs release tag.
If your source tree is coming from darcs (i.e. there exists a _darcs If your source tree is coming from darcs (i.e. it is in a darcs repository),
directory), this tool will determine the most recent release tag, count the this tool will determine the most recent release tag, count the patches that
patches that have been applied since then, and compute a version number to be have been applied since then, and compute a version number to be written into
written into version.py . This version number will be available by doing: _version.py . This version number will be available by doing:
from allmydata import __version__ from your_package_name import __version__
Source trees that do not come from darcs (release tarballs, nightly tarballs) Source trees that do not come from darcs (e.g. release tarballs, nightly
do not have a _darcs directory. Instead, they should have a version.py that tarballs) and are not within a darcs repository should instead, come with a
was generated before the tarball was produced. In this case, this script will _version.py that was generated before the tarball was produced. In this case,
quietly exit without modifying the existing version.py . this script will quietly exit without modifying the existing _version.py .
FYI, src/allmydata/__init__.py will attempt to import version.py and use the 'release tags' are tags in the source repository that match the following
version number therein. If it cannot, it will announce a version of
'UNKNOWN'. This should only happen if someone manages to get hold of a
non-_darcs/ source tree.
'release tags' are tags in the tahoe source tree that match the following
regexp: regexp:
^allmydata-tahoe-\d+\.\d+\.\d+\w*$ ^your_package_name-\d+\.\d+(\.\d+)?((a|b|c)(\d+)?)?\w*$
This excludes zfec tags (which start with 'zfec '). It also excludes
'developer convenience tags', which look like 'hoping to fix bug -warner'.
(the original goal was to use release tags that lacked the 'allmydata-tahoe-'
prefix, but it turns out to be more efficient to keep it in, because I can't
get 'darcs changes --from-tag=' to accept real regexps).
""" """
@ -37,6 +28,16 @@ import os, sys, re
import xml.dom.minidom import xml.dom.minidom
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
try:
# If we can import allmydata.util.version_class then use its regex.
from allmydata.util import version_class
VERSION_BASE_RE_STR = version_class.VERSION_BASE_RE_STR
except ImportError:
# Else (perhaps a bootstrapping problem),then we'll use this
# regex, which was copied from the pyutil source code on
# 2007-08-11.
VERSION_BASE_RE_STR="(\d+)\.(\d+)(\.(\d+))?((a|b|c)(\d+))?"
def get_text(nodelist): def get_text(nodelist):
rc = "" rc = ""
for node in nodelist: for node in nodelist:
@ -45,55 +46,53 @@ def get_text(nodelist):
return rc return rc
VERSION_BODY = ''' VERSION_BODY = '''
from util.version import Version from allmydata.util.version_class import Version
# This is the version of this tree, as created by misc/make-version.py from # This is the version of this tree, as created by scripts/make-version.py from
# the Darcs patch information: the main version number is taken from the most # the Darcs patch information: the main version number is taken from the most
# recent release tag. If some patches have been added since the last release, # recent release tag. If some patches have been added since the last release,
# this will have a -NN "build number" suffix. Please see # this will have a -NN "build number" suffix. Please see
# allmydata.util.version for a description of what the different fields mean. # allmydata.util.version_class for a description of what the different fields
# mean.
verstr = "%s" verstr = "%s"
__version__ = Version(verstr) __version__ = Version(verstr)
''' '''
def write_version_py(verstr): def write_version_py(verstr, outfname):
f = open("src/allmydata/version.py", "wt") f = open(outfname, "wt+")
f.write(VERSION_BODY % (verstr,)) f.write(VERSION_BODY % (verstr,))
f.close() f.close()
def update(): def update(pkgname, verfilename):
if not os.path.exists("_darcs") or not os.path.isdir("_darcs"): rc = -1
if os.path.exists("src/allmydata/version.py"): cmd = ["darcs", "changes", "--from-tag=^%s" % (pkgname,), "--xml-output"]
print "no _darcs/ and version.py exists, leaving it alone"
return 0
print "no _darcs/ but no version.py either: how did you get this tree?"
return 0
cmd = ["darcs", "changes", "--from-tag=^allmydata-tahoe", "--xml-output"]
try: try:
p = Popen(cmd, stdout=PIPE) p = Popen(cmd, stdout=PIPE)
except:
pass
else:
output = p.communicate()[0]
rc = p.returncode
if rc != 0:
cmd = ["realdarcs.exe", "changes", "--from-tag=^%s" % (pkgname,), "--xml-output"]
p = Popen(cmd, stdout=PIPE)
output = p.communicate()[0] output = p.communicate()[0]
rc = p.returncode rc = p.returncode
except EnvironmentError, le: if rc != 0:
output = "There was an environment error: %s" % (le,) if os.path.exists(verfilename):
rc = -1 print "Failure from attempt to find version tags with 'darcs changes', and %s already exists, so leaving it alone." % (verfilename,)
return 0
else:
print "Failure from attempt to find version tags with 'darcs changes', and %s doesn't exist." % (verfilename,)
return rc
if rc != 0: doc = xml.dom.minidom.parseString(output)
print "unable to run 'darcs changes':"
print output
print "so I'm leaving version.py alone"
return 0
try:
doc = xml.dom.minidom.parseString(output)
except xml.parsers.expat.ExpatError:
print "unable to parse darcs XML output:"
print output
raise
changelog = doc.getElementsByTagName("changelog")[0] changelog = doc.getElementsByTagName("changelog")[0]
patches = changelog.getElementsByTagName("patch") patches = changelog.getElementsByTagName("patch")
count = 0 count = 0
version_re = re.compile("^TAG allmydata-tahoe-(\d+\.\d+\.\d+\w*)$") regexstr = "^TAG %s-(%s)" % (pkgname, VERSION_BASE_RE_STR,)
version_re = re.compile(regexstr)
for patch in patches: for patch in patches:
name = get_text(patch.getElementsByTagName("name")[0].childNodes) name = get_text(patch.getElementsByTagName("name")[0].childNodes)
m = version_re.match(name) m = version_re.match(name)
@ -103,23 +102,33 @@ def update():
break break
count += 1 count += 1
else: else:
print "unable to find a matching tag" print "I'm unable to find a tag in the darcs history matching \"%s\", so I'm leaving %s alone." % (regexstr, verfilename,)
print output
print "so I'm leaving version.py alone"
return 0 return 0
if count: if count:
# this is an interim version # this is an interim version
verstr = "%s-%d" % (last_tag, count) verstr = "%s-dev-r%d" % (last_tag, count)
else: else:
# this is a release # this is a release
verstr = last_tag verstr = last_tag
write_version_py(verstr) write_version_py(verstr, verfilename)
print "wrote '%s' into src/allmydata/version.py" % (verstr,) print "wrote '%s' into %s" % (verstr, verfilename,)
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
rc = update() if len(sys.argv) >= 2:
pkgname = sys.argv[1]
else:
pkgname = os.path.basename(os.getcwd())
print "You didn't pass a pkg-name on the command-line, so I'm going to take the name of the current working directory: \"%s\"" % (pkgname,)
if len(sys.argv) >= 3:
verfilename = sys.argv[2]
else:
verfilename = os.path.join(pkgname, "_version.py")
print "You didn't pass a verfilename on the command-line, so I'm going to build one from the name of the package: \"%s\"" % (verfilename,)
rc = update(pkgname=pkgname, verfilename=verfilename)
sys.exit(rc) sys.exit(rc)

View File

@ -9,7 +9,7 @@ community web site: U{http://allmydata.org/}
__version__ = "unknown" __version__ = "unknown"
try: try:
from allmydata.version import __version__ from _version import __version__
except ImportError: except ImportError:
# we're running in a tree that hasn't run misc/make-version.py, so we # we're running in a tree that hasn't run misc/make-version.py, so we
# don't know what our version is. This should not happen very often. # don't know what our version is. This should not happen very often.

View File

@ -6,7 +6,7 @@ from twisted.internet import reactor, defer
import allmydata import allmydata
from allmydata import client, introducer from allmydata import client, introducer
from allmydata.util import version from allmydata.util import version_class
from foolscap.eventual import flushEventualQueue from foolscap.eventual import flushEventualQueue
class MyIntroducerClient(introducer.IntroducerClient): class MyIntroducerClient(introducer.IntroducerClient):
@ -107,7 +107,7 @@ class Basic(unittest.TestCase):
open(os.path.join(basedir, "vdrive.furl"), "w").write("") open(os.path.join(basedir, "vdrive.furl"), "w").write("")
c = client.Client(basedir) c = client.Client(basedir)
mine, oldest = c.remote_get_versions() mine, oldest = c.remote_get_versions()
self.failUnlessEqual(version.Version(mine), allmydata.__version__) self.failUnlessEqual(version_class.Version(mine), allmydata.__version__)
def flush_but_dont_ignore(res): def flush_but_dont_ignore(res):
d = flushEventualQueue() d = flushEventualQueue()

View File

@ -1,135 +0,0 @@
# Copyright (c) 2004-2007 Bryce "Zooko" Wilcox-O'Hearn
# mailto:zooko@zooko.com
# http://zooko.com/repos/pyutil
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this work to deal in this work without restriction (including the rights
# to use, modify, distribute, sublicense, and/or sell copies).
"""
extended version number class
"""
from distutils import version
# End users see version strings like this:
# "1.0.0"
# ^ ^ ^
# | | |
# | | '- micro version number
# | '- minor version number
# '- major version number
# The first number is "major version number". The second number is the "minor
# version number" -- it gets bumped whenever we make a new release that adds or
# changes functionality. The third version is the "micro version number" -- it
# gets bumped whenever we make a new release that doesn't add or change
# functionality, but just fixes bugs (including performance issues).
# Early-adopter end users see version strings like this:
# "1.0.0a1"
# ^ ^ ^^^
# | | |||
# | | ||'- release number
# | | |'- alpha or beta (or none)
# | | '- micro version number
# | '- minor version number
# '- major version number
# The optional "a" or "b" stands for "alpha release" or "beta release"
# respectively. The number after "a" or "b" gets bumped every time we
# make a new alpha or beta release. This has the same form and the same
# meaning as version numbers of releases of Python.
# Developers see "full version strings", like this:
# "1.0.0a1-55"
# ^ ^ ^^^ ^
# | | ||| |
# | | ||| |
# | | ||| '- nano version number
# | | ||'- release number
# | | |'- alpha or beta (or none)
# | | '- micro version number
# | '- minor version number
# '- major version number
# The next number is the "nano version number". It is meaningful only to
# developers. It gets bumped whenever a developer changes anything that another
# developer might care about.
class Tag(str):
def __cmp__(t1, t2):
if t1 == t2:
return 0
if t1 == "UNSTABLE" and t2 == "STABLE":
return 1
if t1 == "STABLE" and t2 == "UNSTABLE":
return -1
return -2 # who knows
class Version:
def __init__(self, vstring=None):
self.major = None
self.minor = None
self.micro = None
self.prereleasetag = None
self.nano = None
self.tags = None
if vstring:
self.parse(vstring)
def parse(self, vstring):
i = vstring.find('-')
if i != -1:
svstring = vstring[:i]
estring = vstring[i+1:]
else:
svstring = vstring
estring = None
self.strictversion = version.StrictVersion(svstring)
self.nanovernum = None
self.tags = []
if estring:
if '-' in estring:
(self.nano, tags,) = estring.split('-')
else:
self.nano = estring
self.fullstr = str(self.strictversion)
if self.nano is not None:
self.fullstr += "-" + str(self.nano)
if self.tags:
self.fullstr += '_'.join(self.tags)
def tags(self):
return self.tags
def user_str(self):
return self.strictversion.__str__()
def full_str(self):
return self.fullstr
def __str__(self):
return self.full_str()
def __repr__(self):
return self.__str__()
def __cmp__ (self, other):
if isinstance(other, basestring):
other = Version(other)
res = cmp(self.strictversion, other.strictversion)
if res != 0:
return res
res = cmp(self.nano, other.nano)
if res != 0:
return res
return cmp(self.tags, other.tags)

View File

@ -0,0 +1,133 @@
# Copyright (c) 2004-2007 Bryce "Zooko" Wilcox-O'Hearn
# mailto:zooko@zooko.com
# http://zooko.com/repos/pyutil
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this work to deal in this work without restriction (including the rights
# to use, modify, distribute, sublicense, and/or sell copies).
"""
extended version number class
"""
# from setuptools, but intended to be included in future version of Python Standard Library (PEP 365)
import pkg_resources
# bindann, by Nathan Wilcox (needed only for debugging)
try:
import bindann
bindann.install_exception_handler()
except ImportError:
pass
# Python Standard Library
import re
# End users see version strings like this:
# "1.0.0"
# ^ ^ ^
# | | |
# | | '- micro version number
# | '- minor version number
# '- major version number
# The first number is "major version number". The second number is the "minor
# version number" -- it gets bumped whenever we make a new release that adds or
# changes functionality. The third version is the "micro version number" -- it
# gets bumped whenever we make a new release that doesn't add or change
# functionality, but just fixes bugs (including performance issues).
# Early-adopter end users see version strings like this:
# "1.0.0a1"
# ^ ^ ^^^
# | | |||
# | | ||'- release number
# | | |'- a=alpha, b=beta, c=release candidate, or none
# | | '- micro version number
# | '- minor version number
# '- major version number
# The optional "a" or "b" stands for "alpha release" or "beta release"
# respectively. The number after "a" or "b" gets bumped every time we
# make a new alpha or beta release. This has the same form and the same
# meaning as version numbers of releases of Python.
# Developers see "full version strings", like this:
# "1.0.0a1-dev-r55"
# ^ ^ ^^^ ^ ^
# | | ||| | |
# | | ||| | '- nano version number
# | | ||| '- "dev" if this is a development version (not a release version)
# | | ||'- release number
# | | |'- a=alpha, b=beta, c=release candidate or none
# | | '- micro version number
# | '- minor version number
# '- major version number
# The presence of "-dev" means that this is a development version. There are
# no guarantees about compatibility, etc. This version is considered to be
# more recent than the version without this field (e.g. "1.0.0a1").
# The next number is the "nano version number". It is meaningful only to
# developers. It gets generated automatically from darcs revision control
# history by "make-version-from-darcs-history.py". It is the count of patches
# that have been applied since the last version number tag was applied.
VERSION_BASE_RE_STR="(\d+)\.(\d+)(\.(\d+))?((a|b|c)(\d+))?"
VERSION_RE_STR=VERSION_BASE_RE_STR + "(-dev-r(\d+))?"
VERSION_RE=re.compile("^" + VERSION_RE_STR + "$")
class Version(object):
def __init__(self, vstring=None):
self.major = None
self.minor = None
self.micro = None
self.prereleasetag = None
self.prerelease = None
self.nano = None
self.leftovers = ''
if vstring:
try:
self.parse(vstring)
except ValueError, le:
le.args = tuple(le.args + ('vstring:', vstring,))
raise
def parse(self, vstring):
mo = VERSION_RE.search(vstring)
if not mo:
raise ValueError, "Not a valid version string for allmydata.util.version_class.Version(): %r" % (vstring,)
self.major = int(mo.group(1))
self.minor = int(mo.group(2))
self.micro = int(mo.group(4))
reltag = mo.group(5)
if reltag:
reltagnum = int(mo.group(6))
self.prereleasetag = reltag
self.prerelease = reltagnum
if mo.group(7):
self.nano = int(mo.group(8))
self.fullstr = "%d.%d.%d%s%s" % (self.major, self.minor, self.micro, self.prereleasetag and "%s%d" % (self.prereleasetag, self.prerelease,) or "", self.nano and "-dev-r%d" % (self.nano,) or "",)
def user_str(self):
return self.strictversion.__str__()
def full_str(self):
if hasattr(self, 'fullstr'):
return self.fullstr
else:
return 'None'
def __str__(self):
return self.full_str()
def __repr__(self):
return self.__str__()
def __cmp__ (self, other):
return cmp(pkg_resources.parse_version(str(self)), pkg_resources.parse_version(str(other)))