/ versioneer.py
versioneer.py
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