2 # This file helps to compute a version number in source trees obtained from
3 # git-archive tarball (such as those provided by githubs download-from-tag
4 # feature). Distribution tarballs (built by setup.py sdist) and build
5 # directories (produced by setup.py build) will contain a much shorter file
6 # that just contains the computed version number.
8 # This file is released into the public domain. Generated by
9 # versioneer-0.15 (https://github.com/warner/python-versioneer)
19 # these strings will be replaced by git during git-archive.
20 # setup.py/versioneer.py will grep for the variable names, so they must
21 # each be defined on a line of their own. _version.py will just call
23 git_refnames
= "$Format:%d$"
24 git_full
= "$Format:%H$"
25 keywords
= {"refnames": git_refnames
, "full": git_full
}
29 class VersioneerConfig
:
34 # these strings are filled in when 'setup.py versioneer' creates
36 cfg
= VersioneerConfig()
39 cfg
.tag_prefix
= "bridgedb-"
40 cfg
.parentdir_prefix
= "bridgedb-"
41 cfg
.versionfile_source
= "bridgedb/_version.py"
46 class NotThisMethod(Exception):
54 def register_vcs_handler(vcs
, method
): # decorator
56 if vcs
not in HANDLERS
:
58 HANDLERS
[vcs
][method
] = f
63 def run_command(commands
, args
, cwd
=None, verbose
=False, hide_stderr
=False):
64 assert isinstance(commands
, list)
68 dispcmd
= str([c
] + args
)
69 # remember shell=False, so use git.cmd on windows, not just git
70 p
= subprocess
.Popen([c
] + args
, cwd
=cwd
, stdout
=subprocess
.PIPE
,
71 stderr
=(subprocess
.PIPE
if hide_stderr
74 except EnvironmentError:
76 if e
.errno
== errno
.ENOENT
:
79 print("unable to run %s" % dispcmd
)
84 print("unable to find command, tried %s" % (commands
,))
86 stdout
= p
.communicate()[0].strip()
87 if sys
.version_info
[0] >= 3:
88 stdout
= stdout
.decode()
91 print("unable to run %s (error)" % dispcmd
)
96 def versions_from_parentdir(parentdir_prefix
, root
, verbose
):
97 # Source tarballs conventionally unpack into a directory that includes
98 # both the project name and a version string.
99 dirname
= os
.path
.basename(root
)
100 if not dirname
.startswith(parentdir_prefix
):
102 print("guessing rootdir is '%s', but '%s' doesn't start with "
103 "prefix '%s'" % (root
, dirname
, parentdir_prefix
))
104 raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
105 return {"version": dirname
[len(parentdir_prefix
):],
106 "full-revisionid": None,
107 "dirty": False, "error": None}
110 @register_vcs_handler("git", "get_keywords")
111 def git_get_keywords(versionfile_abs
):
112 # the code embedded in _version.py can just fetch the value of these
113 # keywords. When used from setup.py, we don't want to import _version.py,
114 # so we do it with a regexp instead. This function is not used from
118 f
= open(versionfile_abs
, "r")
119 for line
in f
.readlines():
120 if line
.strip().startswith("git_refnames ="):
121 mo
= re
.search(r
'=\s*"(.*)"', line
)
123 keywords
["refnames"] = mo
.group(1)
124 if line
.strip().startswith("git_full ="):
125 mo
= re
.search(r
'=\s*"(.*)"', line
)
127 keywords
["full"] = mo
.group(1)
129 except EnvironmentError:
134 @register_vcs_handler("git", "keywords")
135 def git_versions_from_keywords(keywords
, tag_prefix
, verbose
):
137 raise NotThisMethod("no keywords at all, weird")
138 refnames
= keywords
["refnames"].strip()
139 if refnames
.startswith("$Format"):
141 print("keywords are unexpanded, not using")
142 raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
143 refs
= set([r
.strip() for r
in refnames
.strip("()").split(",")])
144 # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
145 # just "foo-1.0". If we see a "tag: " prefix, prefer those.
147 tags
= set([r
[len(TAG
):] for r
in refs
if r
.startswith(TAG
)])
149 # Either we're using git < 1.8.3, or there really are no tags. We use
150 # a heuristic: assume all version tags have a digit. The old git %d
151 # expansion behaves like git log --decorate=short and strips out the
152 # refs/heads/ and refs/tags/ prefixes that would let us distinguish
153 # between branches and tags. By ignoring refnames without digits, we
154 # filter out many common branch names like "release" and
155 # "stabilization", as well as "HEAD" and "master".
156 tags
= set([r
for r
in refs
if re
.search(r
'\d', r
)])
158 print("discarding '%s', no digits" % ",".join(refs
-tags
))
160 print("likely tags: %s" % ",".join(sorted(tags
)))
161 for ref
in sorted(tags
):
162 # sorting will prefer e.g. "2.0" over "2.0rc1"
163 if ref
.startswith(tag_prefix
):
164 r
= ref
[len(tag_prefix
):]
166 print("picking %s" % r
)
167 return {"version": r
,
168 "full-revisionid": keywords
["full"].strip(),
169 "dirty": False, "error": None
171 # no suitable tags, so version is "0+unknown", but full hex is still there
173 print("no suitable tags, using unknown + full revision id")
174 return {"version": "0+unknown",
175 "full-revisionid": keywords
["full"].strip(),
176 "dirty": False, "error": "no suitable tags"}
179 @register_vcs_handler("git", "pieces_from_vcs")
180 def git_pieces_from_vcs(tag_prefix
, root
, verbose
, run_command
=run_command
):
181 # this runs 'git' from the root of the source tree. This only gets called
182 # if the git-archive 'subst' keywords were *not* expanded, and
183 # _version.py hasn't already been rewritten with a short version string,
184 # meaning we're inside a checked out source tree.
186 if not os
.path
.exists(os
.path
.join(root
, ".git")):
188 print("no .git in %s" % root
)
189 raise NotThisMethod("no .git directory")
192 if sys
.platform
== "win32":
193 GITS
= ["git.cmd", "git.exe"]
194 # if there is a tag, this yields TAG-NUM-gHEX[-dirty]
195 # if there are no tags, this yields HEX[-dirty] (no NUM)
196 describe_out
= run_command(GITS
, ["describe", "--tags", "--dirty",
197 "--always", "--long"],
199 # --long was added in git-1.5.5
200 if describe_out
is None:
201 raise NotThisMethod("'git describe' failed")
202 describe_out
= describe_out
.strip()
203 full_out
= run_command(GITS
, ["rev-parse", "HEAD"], cwd
=root
)
205 raise NotThisMethod("'git rev-parse' failed")
206 full_out
= full_out
.strip()
209 pieces
["long"] = full_out
210 pieces
["short"] = full_out
[:7] # maybe improved later
211 pieces
["error"] = None
213 # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
214 # TAG might have hyphens.
215 git_describe
= describe_out
217 # look for -dirty suffix
218 dirty
= git_describe
.endswith("-dirty")
219 pieces
["dirty"] = dirty
221 git_describe
= git_describe
[:git_describe
.rindex("-dirty")]
223 # now we have TAG-NUM-gHEX or HEX
225 if "-" in git_describe
:
227 mo
= re
.search(r
'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe
)
229 # unparseable. Maybe git-describe is misbehaving?
230 pieces
["error"] = ("unable to parse git-describe output: '%s'"
235 full_tag
= mo
.group(1)
236 if not full_tag
.startswith(tag_prefix
):
238 fmt
= "tag '%s' doesn't start with prefix '%s'"
239 print(fmt
% (full_tag
, tag_prefix
))
240 pieces
["error"] = ("tag '%s' doesn't start with prefix '%s'"
241 % (full_tag
, tag_prefix
))
243 pieces
["closest-tag"] = full_tag
[len(tag_prefix
):]
245 # distance: number of commits since tag
246 pieces
["distance"] = int(mo
.group(2))
248 # commit: short hex revision ID
249 pieces
["short"] = mo
.group(3)
253 pieces
["closest-tag"] = None
254 count_out
= run_command(GITS
, ["rev-list", "HEAD", "--count"],
256 pieces
["distance"] = int(count_out
) # total number of commits
261 def plus_or_dot(pieces
):
262 if "+" in pieces
.get("closest-tag", ""):
267 def render_pep440(pieces
):
268 # now build up version string, with post-release "local version
269 # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
270 # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
273 # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
275 if pieces
["closest-tag"]:
276 rendered
= pieces
["closest-tag"]
277 if pieces
["distance"] or pieces
["dirty"]:
278 rendered
+= plus_or_dot(pieces
)
279 rendered
+= "%d.g%s" % (pieces
["distance"], pieces
["short"])
284 rendered
= "0+untagged.%d.g%s" % (pieces
["distance"],
291 def render_pep440_pre(pieces
):
292 # TAG[.post.devDISTANCE] . No -dirty
295 # 1: no tags. 0.post.devDISTANCE
297 if pieces
["closest-tag"]:
298 rendered
= pieces
["closest-tag"]
299 if pieces
["distance"]:
300 rendered
+= ".post.dev%d" % pieces
["distance"]
303 rendered
= "0.post.dev%d" % pieces
["distance"]
307 def render_pep440_post(pieces
):
308 # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that
309 # .dev0 sorts backwards (a dirty tree will appear "older" than the
310 # corresponding clean one), but you shouldn't be releasing software with
314 # 1: no tags. 0.postDISTANCE[.dev0]
316 if pieces
["closest-tag"]:
317 rendered
= pieces
["closest-tag"]
318 if pieces
["distance"] or pieces
["dirty"]:
319 rendered
+= ".post%d" % pieces
["distance"]
322 rendered
+= plus_or_dot(pieces
)
323 rendered
+= "g%s" % pieces
["short"]
326 rendered
= "0.post%d" % pieces
["distance"]
329 rendered
+= "+g%s" % pieces
["short"]
333 def render_pep440_old(pieces
):
334 # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty.
337 # 1: no tags. 0.postDISTANCE[.dev0]
339 if pieces
["closest-tag"]:
340 rendered
= pieces
["closest-tag"]
341 if pieces
["distance"] or pieces
["dirty"]:
342 rendered
+= ".post%d" % pieces
["distance"]
347 rendered
= "0.post%d" % pieces
["distance"]
353 def render_git_describe(pieces
):
354 # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty
358 # 1: no tags. HEX[-dirty] (note: no 'g' prefix)
360 if pieces
["closest-tag"]:
361 rendered
= pieces
["closest-tag"]
362 if pieces
["distance"]:
363 rendered
+= "-%d-g%s" % (pieces
["distance"], pieces
["short"])
366 rendered
= pieces
["short"]
372 def render_git_describe_long(pieces
):
373 # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty
374 # --always -long'. The distance/hash is unconditional.
377 # 1: no tags. HEX[-dirty] (note: no 'g' prefix)
379 if pieces
["closest-tag"]:
380 rendered
= pieces
["closest-tag"]
381 rendered
+= "-%d-g%s" % (pieces
["distance"], pieces
["short"])
384 rendered
= pieces
["short"]
390 def render(pieces
, style
):
392 return {"version": "unknown",
393 "full-revisionid": pieces
.get("long"),
395 "error": pieces
["error"]}
397 if not style
or style
== "default":
398 style
= "pep440" # the default
400 if style
== "pep440":
401 rendered
= render_pep440(pieces
)
402 elif style
== "pep440-pre":
403 rendered
= render_pep440_pre(pieces
)
404 elif style
== "pep440-post":
405 rendered
= render_pep440_post(pieces
)
406 elif style
== "pep440-old":
407 rendered
= render_pep440_old(pieces
)
408 elif style
== "git-describe":
409 rendered
= render_git_describe(pieces
)
410 elif style
== "git-describe-long":
411 rendered
= render_git_describe_long(pieces
)
413 raise ValueError("unknown style '%s'" % style
)
415 return {"version": rendered
, "full-revisionid": pieces
["long"],
416 "dirty": pieces
["dirty"], "error": None}
420 # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
421 # __file__, we can work backwards from there to the root. Some
422 # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
423 # case we can only use expanded keywords.
426 verbose
= cfg
.verbose
429 return git_versions_from_keywords(get_keywords(), cfg
.tag_prefix
,
431 except NotThisMethod
:
435 root
= os
.path
.realpath(__file__
)
436 # versionfile_source is the relative path from the top of the source
437 # tree (where the .git directory might live) to this file. Invert
438 # this to find the root from __file__.
439 for i
in cfg
.versionfile_source
.split('/'):
440 root
= os
.path
.dirname(root
)
442 return {"version": "0+unknown", "full-revisionid": None,
444 "error": "unable to find root of source tree"}
447 pieces
= git_pieces_from_vcs(cfg
.tag_prefix
, root
, verbose
)
448 return render(pieces
, cfg
.style
)
449 except NotThisMethod
:
453 if cfg
.parentdir_prefix
:
454 return versions_from_parentdir(cfg
.parentdir_prefix
, root
, verbose
)
455 except NotThisMethod
:
458 return {"version": "0+unknown", "full-revisionid": None,
460 "error": "unable to compute version"}