Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
numba
GitHub Repository: numba/llvmlite
Path: blob/main/versioneer.py
1154 views
1
2
# Version: 0.14
3
4
"""
5
The Versioneer
6
==============
7
8
* like a rocketeer, but for versions!
9
* https://github.com/warner/python-versioneer
10
* Brian Warner
11
* License: Public Domain
12
* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, and pypy
13
* [![Latest Version]
14
(https://pypip.in/version/versioneer/badge.svg?style=flat)
15
](https://pypi.python.org/pypi/versioneer/)
16
* [![Build Status]
17
(https://travis-ci.org/warner/python-versioneer.png?branch=master)
18
](https://travis-ci.org/warner/python-versioneer)
19
20
This is a tool for managing a recorded version number in distutils-based
21
python projects. The goal is to remove the tedious and error-prone "update
22
the embedded version string" step from your release process. Making a new
23
release should be as easy as recording a new tag in your version-control
24
system, and maybe making new tarballs.
25
26
27
## Quick Install
28
29
* `pip install versioneer` to somewhere to your $PATH
30
* run `versioneer-installer` in your source tree: this installs `versioneer.py`
31
* follow the instructions below (also in the `versioneer.py` docstring)
32
33
## Version Identifiers
34
35
Source trees come from a variety of places:
36
37
* a version-control system checkout (mostly used by developers)
38
* a nightly tarball, produced by build automation
39
* a snapshot tarball, produced by a web-based VCS browser, like github's
40
"tarball from tag" feature
41
* a release tarball, produced by "setup.py sdist", distributed through PyPI
42
43
Within each source tree, the version identifier (either a string or a number,
44
this tool is format-agnostic) can come from a variety of places:
45
46
* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows
47
about recent "tags" and an absolute revision-id
48
* the name of the directory into which the tarball was unpacked
49
* an expanded VCS keyword ($Id$, etc)
50
* a `_version.py` created by some earlier build step
51
52
For released software, the version identifier is closely related to a VCS
53
tag. Some projects use tag names that include more than just the version
54
string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool
55
needs to strip the tag prefix to extract the version identifier. For
56
unreleased software (between tags), the version identifier should provide
57
enough information to help developers recreate the same tree, while also
58
giving them an idea of roughly how old the tree is (after version 1.2, before
59
version 1.3). Many VCS systems can report a description that captures this,
60
for example 'git describe --tags --dirty --always' reports things like
61
"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the
62
0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has
63
uncommitted changes.
64
65
The version identifier is used for multiple purposes:
66
67
* to allow the module to self-identify its version: `myproject.__version__`
68
* to choose a name and prefix for a 'setup.py sdist' tarball
69
70
## Theory of Operation
71
72
Versioneer works by adding a special `_version.py` file into your source
73
tree, where your `__init__.py` can import it. This `_version.py` knows how to
74
dynamically ask the VCS tool for version information at import time. However,
75
when you use "setup.py build" or "setup.py sdist", `_version.py` in the new
76
copy is replaced by a small static file that contains just the generated
77
version data.
78
79
`_version.py` also contains `$Revision$` markers, and the installation
80
process marks `_version.py` to have this marker rewritten with a tag name
81
during the "git archive" command. As a result, generated tarballs will
82
contain enough information to get the proper version.
83
84
85
## Installation
86
87
First, decide on values for the following configuration variables:
88
89
* `VCS`: the version control system you use. Currently accepts "git".
90
91
* `versionfile_source`:
92
93
A project-relative pathname into which the generated version strings should
94
be written. This is usually a `_version.py` next to your project's main
95
`__init__.py` file, so it can be imported at runtime. If your project uses
96
`src/myproject/__init__.py`, this should be `src/myproject/_version.py`.
97
This file should be checked in to your VCS as usual: the copy created below
98
by `setup.py versioneer` will include code that parses expanded VCS
99
keywords in generated tarballs. The 'build' and 'sdist' commands will
100
replace it with a copy that has just the calculated version string.
101
102
This must be set even if your project does not have any modules (and will
103
therefore never import `_version.py`), since "setup.py sdist" -based trees
104
still need somewhere to record the pre-calculated version strings. Anywhere
105
in the source tree should do. If there is a `__init__.py` next to your
106
`_version.py`, the `setup.py versioneer` command (described below) will
107
append some `__version__`-setting assignments, if they aren't already
108
present.
109
110
* `versionfile_build`:
111
112
Like `versionfile_source`, but relative to the build directory instead of
113
the source directory. These will differ when your setup.py uses
114
'package_dir='. If you have `package_dir={'myproject': 'src/myproject'}`,
115
then you will probably have `versionfile_build='myproject/_version.py'` and
116
`versionfile_source='src/myproject/_version.py'`.
117
118
If this is set to None, then `setup.py build` will not attempt to rewrite
119
any `_version.py` in the built tree. If your project does not have any
120
libraries (e.g. if it only builds a script), then you should use
121
`versionfile_build = None` and override `distutils.command.build_scripts`
122
to explicitly insert a copy of `versioneer.get_version()` into your
123
generated script.
124
125
* `tag_prefix`:
126
127
a string, like 'PROJECTNAME-', which appears at the start of all VCS tags.
128
If your tags look like 'myproject-1.2.0', then you should use
129
tag_prefix='myproject-'. If you use unprefixed tags like '1.2.0', this
130
should be an empty string.
131
132
* `parentdir_prefix`:
133
134
a string, frequently the same as tag_prefix, which appears at the start of
135
all unpacked tarball filenames. If your tarball unpacks into
136
'myproject-1.2.0', this should be 'myproject-'.
137
138
This tool provides one script, named `versioneer-installer`. That script does
139
one thing: write a copy of `versioneer.py` into the current directory.
140
141
To versioneer-enable your project:
142
143
* 1: Run `versioneer-installer` to copy `versioneer.py` into the top of your
144
source tree.
145
146
* 2: add the following lines to the top of your `setup.py`, with the
147
configuration values you decided earlier:
148
149
````
150
import versioneer
151
versioneer.VCS = 'git'
152
versioneer.versionfile_source = 'src/myproject/_version.py'
153
versioneer.versionfile_build = 'myproject/_version.py'
154
versioneer.tag_prefix = '' # tags are like 1.2.0
155
versioneer.parentdir_prefix = 'myproject-' # dirname like 'myproject-1.2.0'
156
````
157
158
* 3: add the following arguments to the setup() call in your setup.py:
159
160
version=versioneer.get_version(),
161
cmdclass=versioneer.get_cmdclass(),
162
163
* 4: now run `setup.py versioneer`, which will create `_version.py`, and will
164
modify your `__init__.py` (if one exists next to `_version.py`) to define
165
`__version__` (by calling a function from `_version.py`). It will also
166
modify your `MANIFEST.in` to include both `versioneer.py` and the generated
167
`_version.py` in sdist tarballs.
168
169
* 5: commit these changes to your VCS. To make sure you won't forget,
170
`setup.py versioneer` will mark everything it touched for addition.
171
172
## Post-Installation Usage
173
174
Once established, all uses of your tree from a VCS checkout should get the
175
current version string. All generated tarballs should include an embedded
176
version string (so users who unpack them will not need a VCS tool installed).
177
178
If you distribute your project through PyPI, then the release process should
179
boil down to two steps:
180
181
* 1: git tag 1.0
182
* 2: python setup.py register sdist upload
183
184
If you distribute it through github (i.e. users use github to generate
185
tarballs with `git archive`), the process is:
186
187
* 1: git tag 1.0
188
* 2: git push; git push --tags
189
190
Currently, all version strings must be based upon a tag. Versioneer will
191
report "unknown" until your tree has at least one tag in its history. This
192
restriction will be fixed eventually (see issue #12).
193
194
## Version-String Flavors
195
196
Code which uses Versioneer can learn about its version string at runtime by
197
importing `_version` from your main `__init__.py` file and running the
198
`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can
199
import the top-level `versioneer.py` and run `get_versions()`.
200
201
Both functions return a dictionary with different keys for different flavors
202
of the version string:
203
204
* `['version']`: A condensed PEP440-compliant string, equal to the
205
un-prefixed tag name for actual releases, and containing an additional
206
"local version" section with more detail for in-between builds. For Git,
207
this is TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe
208
--tags --dirty --always`. For example "0.11+2.g1076c97.dirty" indicates
209
that the tree is like the "1076c97" commit but has uncommitted changes
210
(".dirty"), and that this commit is two revisions ("+2") beyond the "0.11"
211
tag. For released software (exactly equal to a known tag), the identifier
212
will only contain the stripped tag, e.g. "0.11".
213
214
* `['full']`: detailed revision identifier. For Git, this is the full SHA1
215
commit id, followed by ".dirty" if the tree contains uncommitted changes,
216
e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac.dirty".
217
218
Some variants are more useful than others. Including `full` in a bug report
219
should allow developers to reconstruct the exact code being tested (or
220
indicate the presence of local changes that should be shared with the
221
developers). `version` is suitable for display in an "about" box or a CLI
222
`--version` output: it can be easily compared against release notes and lists
223
of bugs fixed in various releases.
224
225
The `setup.py versioneer` command adds the following text to your
226
`__init__.py` to place a basic version in `YOURPROJECT.__version__`:
227
228
from ._version import get_versions
229
__version__ = get_versions()['version']
230
del get_versions
231
232
## Updating Versioneer
233
234
To upgrade your project to a new release of Versioneer, do the following:
235
236
* install the new Versioneer (`pip install -U versioneer` or equivalent)
237
* re-run `versioneer-installer` in your source tree to replace your copy of
238
`versioneer.py`
239
* edit `setup.py`, if necessary, to include any new configuration settings
240
indicated by the release notes
241
* re-run `setup.py versioneer` to replace `SRC/_version.py`
242
* commit any changed files
243
244
### Upgrading from 0.10 to 0.11
245
246
You must add a `versioneer.VCS = "git"` to your `setup.py` before re-running
247
`setup.py versioneer`. This will enable the use of additional version-control
248
systems (SVN, etc) in the future.
249
250
### Upgrading from 0.11 to 0.12
251
252
Nothing special.
253
254
## Upgrading to 0.14
255
256
0.14 changes the format of the version string. 0.13 and earlier used
257
hyphen-separated strings like "0.11-2-g1076c97-dirty". 0.14 and beyond use a
258
plus-separated "local version" section strings, with dot-separated
259
components, like "0.11+2.g1076c97". PEP440-strict tools did not like the old
260
format, but should be ok with the new one.
261
262
## Future Directions
263
264
This tool is designed to make it easily extended to other version-control
265
systems: all VCS-specific components are in separate directories like
266
src/git/ . The top-level `versioneer.py` script is assembled from these
267
components by running make-versioneer.py . In the future, make-versioneer.py
268
will take a VCS name as an argument, and will construct a version of
269
`versioneer.py` that is specific to the given VCS. It might also take the
270
configuration arguments that are currently provided manually during
271
installation by editing setup.py . Alternatively, it might go the other
272
direction and include code from all supported VCS systems, reducing the
273
number of intermediate scripts.
274
275
276
## License
277
278
To make Versioneer easier to embed, all its code is hereby released into the
279
public domain. The `_version.py` that it creates is also in the public
280
domain.
281
282
"""
283
284
import errno
285
import os
286
import re
287
import subprocess
288
import sys
289
from distutils.command.build import build as _build
290
from distutils.command.sdist import sdist as _sdist
291
from distutils.core import Command
292
293
# these configuration settings will be overridden by setup.py after it
294
# imports us
295
versionfile_source = None
296
versionfile_build = None
297
tag_prefix = None
298
parentdir_prefix = None
299
VCS = None
300
301
# these dictionaries contain VCS-specific tools
302
LONG_VERSION_PY = {}
303
304
305
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
306
assert isinstance(commands, list)
307
p = None
308
for c in commands:
309
try:
310
# remember shell=False, so use git.cmd on windows, not just git
311
p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
312
stderr=(subprocess.PIPE if hide_stderr
313
else None))
314
break
315
except EnvironmentError:
316
e = sys.exc_info()[1]
317
if e.errno == errno.ENOENT:
318
continue
319
if verbose:
320
print("unable to run %s" % args[0])
321
print(e)
322
return None
323
else:
324
if verbose:
325
print("unable to find command, tried %s" % (commands,))
326
return None
327
stdout = p.communicate()[0].strip()
328
if sys.version_info[0] >= 3:
329
stdout = stdout.decode()
330
if p.returncode != 0:
331
if verbose:
332
print("unable to run %s (error)" % args[0])
333
return None
334
return stdout
335
LONG_VERSION_PY['git'] = '''
336
# This file helps to compute a version number in source trees obtained from
337
# git-archive tarball (such as those provided by githubs download-from-tag
338
# feature). Distribution tarballs (built by setup.py sdist) and build
339
# directories (produced by setup.py build) will contain a much shorter file
340
# that just contains the computed version number.
341
342
# This file is released into the public domain. Generated by
343
# versioneer-0.14 (https://github.com/warner/python-versioneer)
344
345
import errno
346
import os
347
import re
348
import subprocess
349
import sys
350
351
# these strings will be replaced by git during git-archive
352
git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s"
353
git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s"
354
355
# these strings are filled in when 'setup.py versioneer' creates _version.py
356
tag_prefix = "%(TAG_PREFIX)s"
357
parentdir_prefix = "%(PARENTDIR_PREFIX)s"
358
versionfile_source = "%(VERSIONFILE_SOURCE)s"
359
360
361
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
362
assert isinstance(commands, list)
363
p = None
364
for c in commands:
365
try:
366
# remember shell=False, so use git.cmd on windows, not just git
367
p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
368
stderr=(subprocess.PIPE if hide_stderr
369
else None))
370
break
371
except EnvironmentError:
372
e = sys.exc_info()[1]
373
if e.errno == errno.ENOENT:
374
continue
375
if verbose:
376
print("unable to run %%s" %% args[0])
377
print(e)
378
return None
379
else:
380
if verbose:
381
print("unable to find command, tried %%s" %% (commands,))
382
return None
383
stdout = p.communicate()[0].strip()
384
if sys.version_info[0] >= 3:
385
stdout = stdout.decode()
386
if p.returncode != 0:
387
if verbose:
388
print("unable to run %%s (error)" %% args[0])
389
return None
390
return stdout
391
392
393
def versions_from_parentdir(parentdir_prefix, root, verbose=False):
394
# Source tarballs conventionally unpack into a directory that includes
395
# both the project name and a version string.
396
dirname = os.path.basename(root)
397
if not dirname.startswith(parentdir_prefix):
398
if verbose:
399
print("guessing rootdir is '%%s', but '%%s' doesn't start with "
400
"prefix '%%s'" %% (root, dirname, parentdir_prefix))
401
return None
402
return {"version": dirname[len(parentdir_prefix):], "full": ""}
403
404
405
def git_get_keywords(versionfile_abs):
406
# the code embedded in _version.py can just fetch the value of these
407
# keywords. When used from setup.py, we don't want to import _version.py,
408
# so we do it with a regexp instead. This function is not used from
409
# _version.py.
410
keywords = {}
411
try:
412
f = open(versionfile_abs, "r")
413
for line in f.readlines():
414
if line.strip().startswith("git_refnames ="):
415
mo = re.search(r'=\s*"(.*)"', line)
416
if mo:
417
keywords["refnames"] = mo.group(1)
418
if line.strip().startswith("git_full ="):
419
mo = re.search(r'=\s*"(.*)"', line)
420
if mo:
421
keywords["full"] = mo.group(1)
422
f.close()
423
except EnvironmentError:
424
pass
425
return keywords
426
427
428
def git_versions_from_keywords(keywords, tag_prefix, verbose=False):
429
if not keywords:
430
return {} # keyword-finding function failed to find keywords
431
refnames = keywords["refnames"].strip()
432
if refnames.startswith("$Format"):
433
if verbose:
434
print("keywords are unexpanded, not using")
435
return {} # unexpanded, so not in an unpacked git-archive tarball
436
refs = set([r.strip() for r in refnames.strip("()").split(",")])
437
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
438
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
439
TAG = "tag: "
440
tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
441
if not tags:
442
# Either we're using git < 1.8.3, or there really are no tags. We use
443
# a heuristic: assume all version tags have a digit. The old git %%d
444
# expansion behaves like git log --decorate=short and strips out the
445
# refs/heads/ and refs/tags/ prefixes that would let us distinguish
446
# between branches and tags. By ignoring refnames without digits, we
447
# filter out many common branch names like "release" and
448
# "stabilization", as well as "HEAD" and "master".
449
tags = set([r for r in refs if re.search(r'\d', r)])
450
if verbose:
451
print("discarding '%%s', no digits" %% ",".join(refs-tags))
452
if verbose:
453
print("likely tags: %%s" %% ",".join(sorted(tags)))
454
for ref in sorted(tags):
455
# sorting will prefer e.g. "2.0" over "2.0rc1"
456
if ref.startswith(tag_prefix):
457
r = ref[len(tag_prefix):]
458
if verbose:
459
print("picking %%s" %% r)
460
return {"version": r,
461
"full": keywords["full"].strip()}
462
# no suitable tags, so version is "0+unknown", but full hex is still there
463
if verbose:
464
print("no suitable tags, using unknown + full revision id")
465
return {"version": "0+unknown",
466
"full": keywords["full"].strip()}
467
468
469
def git_parse_vcs_describe(git_describe, tag_prefix, verbose=False):
470
# TAG-NUM-gHEX[-dirty] or HEX[-dirty] . TAG might have hyphens.
471
472
# dirty
473
dirty = git_describe.endswith("-dirty")
474
if dirty:
475
git_describe = git_describe[:git_describe.rindex("-dirty")]
476
dirty_suffix = ".dirty" if dirty else ""
477
478
# now we have TAG-NUM-gHEX or HEX
479
480
if "-" not in git_describe: # just HEX
481
return "0+untagged.g"+git_describe+dirty_suffix, dirty
482
483
# just TAG-NUM-gHEX
484
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
485
if not mo:
486
# unparseable. Maybe git-describe is misbehaving?
487
return "0+unparseable"+dirty_suffix, dirty
488
489
# tag
490
full_tag = mo.group(1)
491
if not full_tag.startswith(tag_prefix):
492
if verbose:
493
fmt = "tag '%%s' doesn't start with prefix '%%s'"
494
print(fmt %% (full_tag, tag_prefix))
495
return None, dirty
496
tag = full_tag[len(tag_prefix):]
497
498
# distance: number of commits since tag
499
distance = int(mo.group(2))
500
501
# commit: short hex revision ID
502
commit = mo.group(3)
503
504
# now build up version string, with post-release "local version
505
# identifier". Our goal: TAG[+NUM.gHEX[.dirty]] . Note that if you get a
506
# tagged build and then dirty it, you'll get TAG+0.gHEX.dirty . So you
507
# can always test version.endswith(".dirty").
508
version = tag
509
if distance or dirty:
510
version += "+%%d.g%%s" %% (distance, commit) + dirty_suffix
511
512
return version, dirty
513
514
515
def git_versions_from_vcs(tag_prefix, root, verbose=False):
516
# this runs 'git' from the root of the source tree. This only gets called
517
# if the git-archive 'subst' keywords were *not* expanded, and
518
# _version.py hasn't already been rewritten with a short version string,
519
# meaning we're inside a checked out source tree.
520
521
if not os.path.exists(os.path.join(root, ".git")):
522
if verbose:
523
print("no .git in %%s" %% root)
524
return {} # get_versions() will try next method
525
526
GITS = ["git"]
527
if sys.platform == "win32":
528
GITS = ["git.cmd", "git.exe"]
529
# if there is a tag, this yields TAG-NUM-gHEX[-dirty]
530
# if there are no tags, this yields HEX[-dirty] (no NUM)
531
stdout = run_command(GITS, ["describe", "--tags", "--dirty",
532
"--always", "--long"],
533
cwd=root)
534
# --long was added in git-1.5.5
535
if stdout is None:
536
return {} # try next method
537
version, dirty = git_parse_vcs_describe(stdout, tag_prefix, verbose)
538
539
# build "full", which is FULLHEX[.dirty]
540
stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
541
if stdout is None:
542
return {}
543
full = stdout.strip()
544
if dirty:
545
full += ".dirty"
546
547
return {"version": version, "full": full}
548
549
550
def get_versions(default={"version": "0+unknown", "full": ""}, verbose=False):
551
# I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
552
# __file__, we can work backwards from there to the root. Some
553
# py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
554
# case we can only use expanded keywords.
555
556
keywords = {"refnames": git_refnames, "full": git_full}
557
ver = git_versions_from_keywords(keywords, tag_prefix, verbose)
558
if ver:
559
return ver
560
561
try:
562
root = os.path.realpath(__file__)
563
# versionfile_source is the relative path from the top of the source
564
# tree (where the .git directory might live) to this file. Invert
565
# this to find the root from __file__.
566
for i in versionfile_source.split('/'):
567
root = os.path.dirname(root)
568
except NameError:
569
return default
570
571
return (git_versions_from_vcs(tag_prefix, root, verbose)
572
or versions_from_parentdir(parentdir_prefix, root, verbose)
573
or default)
574
'''
575
576
577
def git_get_keywords(versionfile_abs):
578
# the code embedded in _version.py can just fetch the value of these
579
# keywords. When used from setup.py, we don't want to import _version.py,
580
# so we do it with a regexp instead. This function is not used from
581
# _version.py.
582
keywords = {}
583
try:
584
f = open(versionfile_abs, "r")
585
for line in f.readlines():
586
if line.strip().startswith("git_refnames ="):
587
mo = re.search(r'=\s*"(.*)"', line)
588
if mo:
589
keywords["refnames"] = mo.group(1)
590
if line.strip().startswith("git_full ="):
591
mo = re.search(r'=\s*"(.*)"', line)
592
if mo:
593
keywords["full"] = mo.group(1)
594
f.close()
595
except EnvironmentError:
596
pass
597
return keywords
598
599
600
def git_versions_from_keywords(keywords, tag_prefix, verbose=False):
601
if not keywords:
602
return {} # keyword-finding function failed to find keywords
603
refnames = keywords["refnames"].strip()
604
if refnames.startswith("$Format"):
605
if verbose:
606
print("keywords are unexpanded, not using")
607
return {} # unexpanded, so not in an unpacked git-archive tarball
608
refs = set([r.strip() for r in refnames.strip("()").split(",")])
609
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
610
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
611
TAG = "tag: "
612
tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
613
if not tags:
614
# Either we're using git < 1.8.3, or there really are no tags. We use
615
# a heuristic: assume all version tags have a digit. The old git %d
616
# expansion behaves like git log --decorate=short and strips out the
617
# refs/heads/ and refs/tags/ prefixes that would let us distinguish
618
# between branches and tags. By ignoring refnames without digits, we
619
# filter out many common branch names like "release" and
620
# "stabilization", as well as "HEAD" and "master".
621
tags = set([r for r in refs if re.search(r'\d', r)])
622
if verbose:
623
print("discarding '%s', no digits" % ",".join(refs-tags))
624
if verbose:
625
print("likely tags: %s" % ",".join(sorted(tags)))
626
for ref in sorted(tags):
627
# sorting will prefer e.g. "2.0" over "2.0rc1"
628
if ref.startswith(tag_prefix):
629
r = ref[len(tag_prefix):]
630
if verbose:
631
print("picking %s" % r)
632
return {"version": r,
633
"full": keywords["full"].strip()}
634
# no suitable tags, so version is "0+unknown", but full hex is still there
635
if verbose:
636
print("no suitable tags, using unknown + full revision id")
637
return {"version": "0+unknown",
638
"full": keywords["full"].strip()}
639
640
641
def git_parse_vcs_describe(git_describe, tag_prefix, verbose=False):
642
# TAG-NUM-gHEX[-dirty] or HEX[-dirty] . TAG might have hyphens.
643
644
# dirty
645
dirty = git_describe.endswith("-dirty")
646
if dirty:
647
git_describe = git_describe[:git_describe.rindex("-dirty")]
648
dirty_suffix = ".dirty" if dirty else ""
649
650
# now we have TAG-NUM-gHEX or HEX
651
652
if "-" not in git_describe: # just HEX
653
return "0+untagged.g"+git_describe+dirty_suffix, dirty
654
655
# just TAG-NUM-gHEX
656
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
657
if not mo:
658
# unparseable. Maybe git-describe is misbehaving?
659
return "0+unparseable"+dirty_suffix, dirty
660
661
# tag
662
full_tag = mo.group(1)
663
if not full_tag.startswith(tag_prefix):
664
if verbose:
665
fmt = "tag '%s' doesn't start with prefix '%s'"
666
print(fmt % (full_tag, tag_prefix))
667
return None, dirty
668
tag = full_tag[len(tag_prefix):]
669
670
# distance: number of commits since tag
671
distance = int(mo.group(2))
672
673
# commit: short hex revision ID
674
commit = mo.group(3)
675
676
# now build up version string, with post-release "local version
677
# identifier". Our goal: TAG[+NUM.gHEX[.dirty]] . Note that if you get a
678
# tagged build and then dirty it, you'll get TAG+0.gHEX.dirty . So you
679
# can always test version.endswith(".dirty").
680
version = tag
681
if distance or dirty:
682
version += "+%d.g%s" % (distance, commit) + dirty_suffix
683
684
return version, dirty
685
686
687
def git_versions_from_vcs(tag_prefix, root, verbose=False):
688
# this runs 'git' from the root of the source tree. This only gets called
689
# if the git-archive 'subst' keywords were *not* expanded, and
690
# _version.py hasn't already been rewritten with a short version string,
691
# meaning we're inside a checked out source tree.
692
693
if not os.path.exists(os.path.join(root, ".git")):
694
if verbose:
695
print("no .git in %s" % root)
696
return {} # get_versions() will try next method
697
698
GITS = ["git"]
699
if sys.platform == "win32":
700
GITS = ["git.cmd", "git.exe"]
701
# if there is a tag, this yields TAG-NUM-gHEX[-dirty]
702
# if there are no tags, this yields HEX[-dirty] (no NUM)
703
stdout = run_command(GITS, ["describe", "--tags", "--dirty",
704
"--always", "--long"],
705
cwd=root)
706
# --long was added in git-1.5.5
707
if stdout is None:
708
return {} # try next method
709
version, dirty = git_parse_vcs_describe(stdout, tag_prefix, verbose)
710
711
# build "full", which is FULLHEX[.dirty]
712
stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
713
if stdout is None:
714
return {}
715
full = stdout.strip()
716
if dirty:
717
full += ".dirty"
718
719
return {"version": version, "full": full}
720
721
722
def do_vcs_install(manifest_in, versionfile_source, ipy):
723
GITS = ["git"]
724
if sys.platform == "win32":
725
GITS = ["git.cmd", "git.exe"]
726
files = [manifest_in, versionfile_source]
727
if ipy:
728
files.append(ipy)
729
try:
730
me = __file__
731
if me.endswith(".pyc") or me.endswith(".pyo"):
732
me = os.path.splitext(me)[0] + ".py"
733
versioneer_file = os.path.relpath(me)
734
except NameError:
735
versioneer_file = "versioneer.py"
736
files.append(versioneer_file)
737
present = False
738
try:
739
f = open(".gitattributes", "r")
740
for line in f.readlines():
741
if line.strip().startswith(versionfile_source):
742
if "export-subst" in line.strip().split()[1:]:
743
present = True
744
f.close()
745
except EnvironmentError:
746
pass
747
if not present:
748
f = open(".gitattributes", "a+")
749
f.write("%s export-subst\n" % versionfile_source)
750
f.close()
751
files.append(".gitattributes")
752
run_command(GITS, ["add", "--"] + files)
753
754
755
def versions_from_parentdir(parentdir_prefix, root, verbose=False):
756
# Source tarballs conventionally unpack into a directory that includes
757
# both the project name and a version string.
758
dirname = os.path.basename(root)
759
if not dirname.startswith(parentdir_prefix):
760
if verbose:
761
print("guessing rootdir is '%s', but '%s' doesn't start with "
762
"prefix '%s'" % (root, dirname, parentdir_prefix))
763
return None
764
return {"version": dirname[len(parentdir_prefix):], "full": ""}
765
766
SHORT_VERSION_PY = """
767
# This file was generated by 'versioneer.py' (0.14) from
768
# revision-control system data, or from the parent directory name of an
769
# unpacked source archive. Distribution tarballs contain a pre-generated copy
770
# of this file.
771
772
version_version = '%(version)s'
773
version_full = '%(full)s'
774
def get_versions(default={}, verbose=False):
775
return {'version': version_version, 'full': version_full}
776
777
"""
778
779
DEFAULT = {"version": "0+unknown", "full": "unknown"}
780
781
782
def versions_from_file(filename):
783
versions = {}
784
try:
785
with open(filename) as f:
786
for line in f.readlines():
787
mo = re.match("version_version = '([^']+)'", line)
788
if mo:
789
versions["version"] = mo.group(1)
790
mo = re.match("version_full = '([^']+)'", line)
791
if mo:
792
versions["full"] = mo.group(1)
793
except EnvironmentError:
794
return {}
795
796
return versions
797
798
799
def write_to_version_file(filename, versions):
800
with open(filename, "w") as f:
801
f.write(SHORT_VERSION_PY % versions)
802
803
print("set %s to '%s'" % (filename, versions["version"]))
804
805
806
def get_root():
807
try:
808
return os.path.dirname(os.path.abspath(__file__))
809
except NameError:
810
return os.path.dirname(os.path.abspath(sys.argv[0]))
811
812
813
def vcs_function(vcs, suffix):
814
return getattr(sys.modules[__name__], '%s_%s' % (vcs, suffix), None)
815
816
817
def get_versions(default=DEFAULT, verbose=False):
818
# returns dict with two keys: 'version' and 'full'
819
assert versionfile_source is not None, \
820
"please set versioneer.versionfile_source"
821
assert tag_prefix is not None, "please set versioneer.tag_prefix"
822
assert parentdir_prefix is not None, \
823
"please set versioneer.parentdir_prefix"
824
assert VCS is not None, "please set versioneer.VCS"
825
826
# I am in versioneer.py, which must live at the top of the source tree,
827
# which we use to compute the root directory. py2exe/bbfreeze/non-CPython
828
# don't have __file__, in which case we fall back to sys.argv[0] (which
829
# ought to be the setup.py script). We prefer __file__ since that's more
830
# robust in cases where setup.py was invoked in some weird way (e.g. pip)
831
root = get_root()
832
versionfile_abs = os.path.join(root, versionfile_source)
833
834
# extract version from first of _version.py, VCS command (e.g. 'git
835
# describe'), parentdir. This is meant to work for developers using a
836
# source checkout, for users of a tarball created by 'setup.py sdist',
837
# and for users of a tarball/zipball created by 'git archive' or github's
838
# download-from-tag feature or the equivalent in other VCSes.
839
840
get_keywords_f = vcs_function(VCS, "get_keywords")
841
versions_from_keywords_f = vcs_function(VCS, "versions_from_keywords")
842
if get_keywords_f and versions_from_keywords_f:
843
vcs_keywords = get_keywords_f(versionfile_abs)
844
ver = versions_from_keywords_f(vcs_keywords, tag_prefix)
845
if ver:
846
if verbose:
847
print("got version from expanded keyword %s" % ver)
848
return ver
849
850
ver = versions_from_file(versionfile_abs)
851
if ver:
852
if verbose:
853
print("got version from file %s %s" % (versionfile_abs, ver))
854
return ver
855
856
versions_from_vcs_f = vcs_function(VCS, "versions_from_vcs")
857
if versions_from_vcs_f:
858
ver = versions_from_vcs_f(tag_prefix, root, verbose)
859
if ver:
860
if verbose:
861
print("got version from VCS %s" % ver)
862
return ver
863
864
ver = versions_from_parentdir(parentdir_prefix, root, verbose)
865
if ver:
866
if verbose:
867
print("got version from parentdir %s" % ver)
868
return ver
869
870
if verbose:
871
print("got version from default %s" % default)
872
return default
873
874
875
def get_version(verbose=False):
876
return get_versions(verbose=verbose)["version"]
877
878
879
class cmd_version(Command):
880
description = "report generated version string"
881
user_options = []
882
boolean_options = []
883
884
def initialize_options(self):
885
pass
886
887
def finalize_options(self):
888
pass
889
890
def run(self):
891
ver = get_version(verbose=True)
892
print("Version is currently: %s" % ver)
893
894
895
class cmd_build(_build):
896
def run(self):
897
versions = get_versions(verbose=True)
898
_build.run(self)
899
# now locate _version.py in the new build/ directory and replace it
900
# with an updated value
901
if versionfile_build:
902
target_versionfile = os.path.join(self.build_lib,
903
versionfile_build)
904
print("UPDATING %s" % target_versionfile)
905
os.unlink(target_versionfile)
906
with open(target_versionfile, "w") as f:
907
f.write(SHORT_VERSION_PY % versions)
908
909
if 'cx_Freeze' in sys.modules: # cx_freeze enabled?
910
from cx_Freeze.dist import build_exe as _build_exe
911
912
class cmd_build_exe(_build_exe):
913
def run(self):
914
versions = get_versions(verbose=True)
915
target_versionfile = versionfile_source
916
print("UPDATING %s" % target_versionfile)
917
os.unlink(target_versionfile)
918
with open(target_versionfile, "w") as f:
919
f.write(SHORT_VERSION_PY % versions)
920
921
_build_exe.run(self)
922
os.unlink(target_versionfile)
923
with open(versionfile_source, "w") as f:
924
assert VCS is not None, "please set versioneer.VCS"
925
LONG = LONG_VERSION_PY[VCS]
926
f.write(LONG % {"DOLLAR": "$",
927
"TAG_PREFIX": tag_prefix,
928
"PARENTDIR_PREFIX": parentdir_prefix,
929
"VERSIONFILE_SOURCE": versionfile_source,
930
})
931
932
933
class cmd_sdist(_sdist):
934
def run(self):
935
versions = get_versions(verbose=True)
936
self._versioneer_generated_versions = versions
937
# unless we update this, the command will keep using the old version
938
self.distribution.metadata.version = versions["version"]
939
return _sdist.run(self)
940
941
def make_release_tree(self, base_dir, files):
942
_sdist.make_release_tree(self, base_dir, files)
943
# now locate _version.py in the new base_dir directory (remembering
944
# that it may be a hardlink) and replace it with an updated value
945
target_versionfile = os.path.join(base_dir, versionfile_source)
946
print("UPDATING %s" % target_versionfile)
947
os.unlink(target_versionfile)
948
with open(target_versionfile, "w") as f:
949
f.write(SHORT_VERSION_PY % self._versioneer_generated_versions)
950
951
INIT_PY_SNIPPET = """
952
from ._version import get_versions
953
__version__ = get_versions()['version']
954
del get_versions
955
"""
956
957
958
class cmd_update_files(Command):
959
description = ("install/upgrade Versioneer files: "
960
"__init__.py SRC/_version.py")
961
user_options = []
962
boolean_options = []
963
964
def initialize_options(self):
965
pass
966
967
def finalize_options(self):
968
pass
969
970
def run(self):
971
print(" creating %s" % versionfile_source)
972
with open(versionfile_source, "w") as f:
973
assert VCS is not None, "please set versioneer.VCS"
974
LONG = LONG_VERSION_PY[VCS]
975
f.write(LONG % {"DOLLAR": "$",
976
"TAG_PREFIX": tag_prefix,
977
"PARENTDIR_PREFIX": parentdir_prefix,
978
"VERSIONFILE_SOURCE": versionfile_source,
979
})
980
981
ipy = os.path.join(os.path.dirname(versionfile_source), "__init__.py")
982
if os.path.exists(ipy):
983
try:
984
with open(ipy, "r") as f:
985
old = f.read()
986
except EnvironmentError:
987
old = ""
988
if INIT_PY_SNIPPET not in old:
989
print(" appending to %s" % ipy)
990
with open(ipy, "a") as f:
991
f.write(INIT_PY_SNIPPET)
992
else:
993
print(" %s unmodified" % ipy)
994
else:
995
print(" %s doesn't exist, ok" % ipy)
996
ipy = None
997
998
# Make sure both the top-level "versioneer.py" and versionfile_source
999
# (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
1000
# they'll be copied into source distributions. Pip won't be able to
1001
# install the package without this.
1002
manifest_in = os.path.join(get_root(), "MANIFEST.in")
1003
simple_includes = set()
1004
try:
1005
with open(manifest_in, "r") as f:
1006
for line in f:
1007
if line.startswith("include "):
1008
for include in line.split()[1:]:
1009
simple_includes.add(include)
1010
except EnvironmentError:
1011
pass
1012
# That doesn't cover everything MANIFEST.in can do
1013
# (http://docs.python.org/2/distutils/sourcedist.html#commands), so
1014
# it might give some false negatives. Appending redundant 'include'
1015
# lines is safe, though.
1016
if "versioneer.py" not in simple_includes:
1017
print(" appending 'versioneer.py' to MANIFEST.in")
1018
with open(manifest_in, "a") as f:
1019
f.write("include versioneer.py\n")
1020
else:
1021
print(" 'versioneer.py' already in MANIFEST.in")
1022
if versionfile_source not in simple_includes:
1023
print(" appending versionfile_source ('%s') to MANIFEST.in" %
1024
versionfile_source)
1025
with open(manifest_in, "a") as f:
1026
f.write("include %s\n" % versionfile_source)
1027
else:
1028
print(" versionfile_source already in MANIFEST.in")
1029
1030
# Make VCS-specific changes. For git, this means creating/changing
1031
# .gitattributes to mark _version.py for export-time keyword
1032
# substitution.
1033
do_vcs_install(manifest_in, versionfile_source, ipy)
1034
1035
1036
def get_cmdclass():
1037
cmds = {'version': cmd_version,
1038
'versioneer': cmd_update_files,
1039
'build': cmd_build,
1040
'sdist': cmd_sdist,
1041
}
1042
if 'cx_Freeze' in sys.modules: # cx_freeze enabled?
1043
cmds['build_exe'] = cmd_build_exe
1044
del cmds['build']
1045
1046
return cmds
1047
1048