Merge pull request #27 from abusque/staging
[deliverable/lttng-analyses.git] / lttnganalyses / _version.py
CommitLineData
56936af2
MJ
1
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.
7
8# This file is released into the public domain. Generated by
9# versioneer-0.15 (https://github.com/warner/python-versioneer)
10
11import errno
12import os
13import re
14import subprocess
15import sys
16
17
18def get_keywords():
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
22 # get_keywords().
23 git_refnames = "$Format:%d$"
24 git_full = "$Format:%H$"
25 keywords = {"refnames": git_refnames, "full": git_full}
26 return keywords
27
28
29class VersioneerConfig:
30 pass
31
32
33def get_config():
34 # these strings are filled in when 'setup.py versioneer' creates
35 # _version.py
36 cfg = VersioneerConfig()
37 cfg.VCS = "git"
38 cfg.style = "pep440"
39 cfg.tag_prefix = "v"
40 cfg.parentdir_prefix = "lttnganalyses-"
41 cfg.versionfile_source = "lttnganalyses/_version.py"
42 cfg.verbose = False
43 return cfg
44
45
46class NotThisMethod(Exception):
47 pass
48
49
50LONG_VERSION_PY = {}
51HANDLERS = {}
52
53
54def register_vcs_handler(vcs, method): # decorator
55 def decorate(f):
56 if vcs not in HANDLERS:
57 HANDLERS[vcs] = {}
58 HANDLERS[vcs][method] = f
59 return f
60 return decorate
61
62
63def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
64 assert isinstance(commands, list)
65 p = None
66 for c in commands:
67 try:
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
72 else None))
73 break
74 except EnvironmentError:
75 e = sys.exc_info()[1]
76 if e.errno == errno.ENOENT:
77 continue
78 if verbose:
79 print("unable to run %s" % dispcmd)
80 print(e)
81 return None
82 else:
83 if verbose:
84 print("unable to find command, tried %s" % (commands,))
85 return None
86 stdout = p.communicate()[0].strip()
87 if sys.version_info[0] >= 3:
88 stdout = stdout.decode()
89 if p.returncode != 0:
90 if verbose:
91 print("unable to run %s (error)" % dispcmd)
92 return None
93 return stdout
94
95
96def 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):
101 if verbose:
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}
108
109
110@register_vcs_handler("git", "get_keywords")
111def 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
115 # _version.py.
116 keywords = {}
117 try:
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)
122 if mo:
123 keywords["refnames"] = mo.group(1)
124 if line.strip().startswith("git_full ="):
125 mo = re.search(r'=\s*"(.*)"', line)
126 if mo:
127 keywords["full"] = mo.group(1)
128 f.close()
129 except EnvironmentError:
130 pass
131 return keywords
132
133
134@register_vcs_handler("git", "keywords")
135def git_versions_from_keywords(keywords, tag_prefix, verbose):
136 if not keywords:
137 raise NotThisMethod("no keywords at all, weird")
138 refnames = keywords["refnames"].strip()
139 if refnames.startswith("$Format"):
140 if verbose:
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.
146 TAG = "tag: "
147 tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
148 if not tags:
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)])
157 if verbose:
158 print("discarding '%s', no digits" % ",".join(refs-tags))
159 if verbose:
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):]
165 if verbose:
166 print("picking %s" % r)
167 return {"version": r,
168 "full-revisionid": keywords["full"].strip(),
169 "dirty": False, "error": None
170 }
171 # no suitable tags, so version is "0+unknown", but full hex is still there
172 if verbose:
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"}
177
178
179@register_vcs_handler("git", "pieces_from_vcs")
180def 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.
185
186 if not os.path.exists(os.path.join(root, ".git")):
187 if verbose:
188 print("no .git in %s" % root)
189 raise NotThisMethod("no .git directory")
190
191 GITS = ["git"]
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"],
198 cwd=root)
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)
204 if full_out is None:
205 raise NotThisMethod("'git rev-parse' failed")
206 full_out = full_out.strip()
207
208 pieces = {}
209 pieces["long"] = full_out
210 pieces["short"] = full_out[:7] # maybe improved later
211 pieces["error"] = None
212
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
216
217 # look for -dirty suffix
218 dirty = git_describe.endswith("-dirty")
219 pieces["dirty"] = dirty
220 if dirty:
221 git_describe = git_describe[:git_describe.rindex("-dirty")]
222
223 # now we have TAG-NUM-gHEX or HEX
224
225 if "-" in git_describe:
226 # TAG-NUM-gHEX
227 mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
228 if not mo:
229 # unparseable. Maybe git-describe is misbehaving?
230 pieces["error"] = ("unable to parse git-describe output: '%s'"
231 % describe_out)
232 return pieces
233
234 # tag
235 full_tag = mo.group(1)
236 if not full_tag.startswith(tag_prefix):
237 if verbose:
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))
242 return pieces
243 pieces["closest-tag"] = full_tag[len(tag_prefix):]
244
245 # distance: number of commits since tag
246 pieces["distance"] = int(mo.group(2))
247
248 # commit: short hex revision ID
249 pieces["short"] = mo.group(3)
250
251 else:
252 # HEX: no tags
253 pieces["closest-tag"] = None
254 count_out = run_command(GITS, ["rev-list", "HEAD", "--count"],
255 cwd=root)
256 pieces["distance"] = int(count_out) # total number of commits
257
258 return pieces
259
260
261def plus_or_dot(pieces):
262 if "+" in pieces.get("closest-tag", ""):
263 return "."
264 return "+"
265
266
267def 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
271
272 # exceptions:
273 # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
274
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"])
280 if pieces["dirty"]:
281 rendered += ".dirty"
282 else:
283 # exception #1
284 rendered = "0+untagged.%d.g%s" % (pieces["distance"],
285 pieces["short"])
286 if pieces["dirty"]:
287 rendered += ".dirty"
288 return rendered
289
290
291def render_pep440_pre(pieces):
292 # TAG[.post.devDISTANCE] . No -dirty
293
294 # exceptions:
295 # 1: no tags. 0.post.devDISTANCE
296
297 if pieces["closest-tag"]:
298 rendered = pieces["closest-tag"]
299 if pieces["distance"]:
300 rendered += ".post.dev%d" % pieces["distance"]
301 else:
302 # exception #1
303 rendered = "0.post.dev%d" % pieces["distance"]
304 return rendered
305
306
307def 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
311 # -dirty anyways.
312
313 # exceptions:
314 # 1: no tags. 0.postDISTANCE[.dev0]
315
316 if pieces["closest-tag"]:
317 rendered = pieces["closest-tag"]
318 if pieces["distance"] or pieces["dirty"]:
319 rendered += ".post%d" % pieces["distance"]
320 if pieces["dirty"]:
321 rendered += ".dev0"
322 rendered += plus_or_dot(pieces)
323 rendered += "g%s" % pieces["short"]
324 else:
325 # exception #1
326 rendered = "0.post%d" % pieces["distance"]
327 if pieces["dirty"]:
328 rendered += ".dev0"
329 rendered += "+g%s" % pieces["short"]
330 return rendered
331
332
333def render_pep440_old(pieces):
334 # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty.
335
336 # exceptions:
337 # 1: no tags. 0.postDISTANCE[.dev0]
338
339 if pieces["closest-tag"]:
340 rendered = pieces["closest-tag"]
341 if pieces["distance"] or pieces["dirty"]:
342 rendered += ".post%d" % pieces["distance"]
343 if pieces["dirty"]:
344 rendered += ".dev0"
345 else:
346 # exception #1
347 rendered = "0.post%d" % pieces["distance"]
348 if pieces["dirty"]:
349 rendered += ".dev0"
350 return rendered
351
352
353def render_git_describe(pieces):
354 # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty
355 # --always'
356
357 # exceptions:
358 # 1: no tags. HEX[-dirty] (note: no 'g' prefix)
359
360 if pieces["closest-tag"]:
361 rendered = pieces["closest-tag"]
362 if pieces["distance"]:
363 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
364 else:
365 # exception #1
366 rendered = pieces["short"]
367 if pieces["dirty"]:
368 rendered += "-dirty"
369 return rendered
370
371
372def render_git_describe_long(pieces):
373 # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty
374 # --always -long'. The distance/hash is unconditional.
375
376 # exceptions:
377 # 1: no tags. HEX[-dirty] (note: no 'g' prefix)
378
379 if pieces["closest-tag"]:
380 rendered = pieces["closest-tag"]
381 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
382 else:
383 # exception #1
384 rendered = pieces["short"]
385 if pieces["dirty"]:
386 rendered += "-dirty"
387 return rendered
388
389
390def render(pieces, style):
391 if pieces["error"]:
392 return {"version": "unknown",
393 "full-revisionid": pieces.get("long"),
394 "dirty": None,
395 "error": pieces["error"]}
396
397 if not style or style == "default":
398 style = "pep440" # the default
399
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)
412 else:
413 raise ValueError("unknown style '%s'" % style)
414
415 return {"version": rendered, "full-revisionid": pieces["long"],
416 "dirty": pieces["dirty"], "error": None}
417
418
419def get_versions():
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.
424
425 cfg = get_config()
426 verbose = cfg.verbose
427
428 try:
429 return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
430 verbose)
431 except NotThisMethod:
432 pass
433
434 try:
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)
441 except NameError:
442 return {"version": "0+unknown", "full-revisionid": None,
443 "dirty": None,
444 "error": "unable to find root of source tree"}
445
446 try:
447 pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
448 return render(pieces, cfg.style)
449 except NotThisMethod:
450 pass
451
452 try:
453 if cfg.parentdir_prefix:
454 return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
455 except NotThisMethod:
456 pass
457
458 return {"version": "0+unknown", "full-revisionid": None,
459 "dirty": None,
460 "error": "unable to compute version"}
This page took 0.04455 seconds and 5 git commands to generate.