Repository: numba/numba

Issue:
Missing dependencies in setup.py
We are in 2015 and you have not heard about specifying dependencies like

numpy
enum34
llvmlite

in your setup.py and to ensure that numba can be easily installed using pip/easy_install? So hard?



Discussion:
Yeah, our packaging story may be a bit imperfect. We welcome patches, though :)

The bigger question for me is whether llvmlite is installable with pip.  No point in specifying the dependencies until the dependencies can also be pip-installed.


Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -13,6 +13,7 @@
 import numpy.distutils.misc_util as np_misc
 import versioneer
 
+versioneer.VCS = 'git'
 versioneer.versionfile_source = 'numba/_version.py'
 versioneer.versionfile_build = 'numba/_version.py'
 versioneer.tag_prefix = ''
@@ -119,6 +120,12 @@
     "numba.runtime",
 ]
 
+install_requires = ['llvmlite', 'numpy']
+if sys.version_info < (3, 4):
+    install_requires.append('enum34')
+if sys.version_info < (3, 3):
+    install_requires.append('funcsigs')
+
 setup(name='numba',
       description="compiling Python code using LLVM",
       version=versioneer.get_version(),
@@ -148,6 +155,7 @@
       url="http://numba.github.com",
       ext_modules=ext_modules,
       packages=packages,
+      install_requires=install_requires,
       license="BSD",
       cmdclass=cmdclass,
       **setup_args)
diff --git a/versioneer.py b/versioneer.py
--- a/versioneer.py
+++ b/versioneer.py
@@ -1,136 +1,387 @@
-#! /usr/bin/python
-# -*- coding: utf-8 -*-
 
-"""versioneer.py
+# Version: 0.14
 
-(like a rocketeer, but for versions)
+"""
+The Versioneer
+==============
 
+* like a rocketeer, but for versions!
 * https://github.com/warner/python-versioneer
 * Brian Warner
 * License: Public Domain
-* Version: 0.7+
-
-This file helps distutils-based projects manage their version number by just
-creating version-control tags.
-
-For developers who work from a VCS-generated tree (e.g. 'git clone' etc),
-each 'setup.py version', 'setup.py build', 'setup.py sdist' will compute a
-version number by asking your version-control tool about the current
-checkout. The version number will be written into a generated _version.py
-file of your choosing, where it can be included by your __init__.py
-
-For users who work from a VCS-generated tarball (e.g. 'git archive'), it will
-compute a version number by looking at the name of the directory created when
-te tarball is unpacked. This conventionally includes both the name of the
-project and a version number.
-
-For users who work from a tarball built by 'setup.py sdist', it will get a
-version number from a previously-generated _version.py file.
-
-As a result, loading code directly from the source tree will not result in a
-real version. If you want real versions from VCS trees (where you frequently
-update from the upstream repository, or do new development), you will need to
-do a 'setup.py version' after each update, and load code from the build/
-directory.
-
-You need to provide this code with a few configuration values:
-
- versionfile_source:
-    A project-relative pathname into which the generated version strings
-    should be written. This is usually a _version.py next to your project's
-    main __init__.py file. If your project uses src/myproject/__init__.py,
-    this should be 'src/myproject/_version.py'. This file should be checked
-    in to your VCS as usual: the copy created below by 'setup.py
-    update_files' will include code that parses expanded VCS keywords in
-    generated tarballs. The 'build' and 'sdist' commands will replace it with
-    a copy that has just the calculated version string.
-
- versionfile_build:
-    Like versionfile_source, but relative to the build directory instead of
-    the source directory. These will differ when your setup.py uses
-    'package_dir='. If you have package_dir={'myproject': 'src/myproject'},
-    then you will probably have versionfile_build='myproject/_version.py' and
-    versionfile_source='src/myproject/_version.py'.
-
- tag_prefix: a string, like 'PROJECTNAME-', which appears at the start of all
-             VCS tags. If your tags look like 'myproject-1.2.0', then you
-             should use tag_prefix='myproject-'. If you use unprefixed tags
-             like '1.2.0', this should be an empty string.
-
- parentdir_prefix: a string, frequently the same as tag_prefix, which
-                   appears at the start of all unpacked tarball filenames. If
-                   your tarball unpacks into 'myproject-1.2.0', this should
-                   be 'myproject-'.
-
-To use it:
-
- 1: include this file in the top level of your project
- 2: make the following changes to the top of your setup.py:
-     import versioneer
-     versioneer.versionfile_source = 'src/myproject/_version.py'
-     versioneer.versionfile_build = 'myproject/_version.py'
-     versioneer.tag_prefix = '' # tags are like 1.2.0
-     versioneer.parentdir_prefix = 'myproject-' # dirname like 'myproject-1.2.0'
- 3: add the following arguments to the setup() call in your setup.py:
-     version=versioneer.get_version(),
-     cmdclass=versioneer.get_cmdclass(),
- 4: run 'setup.py update_files', which will create _version.py, and will
-    append the following to your __init__.py:
-     from _version import __version__
- 5: modify your MANIFEST.in to include versioneer.py
- 6: add both versioneer.py and the generated _version.py to your VCS
+* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, and pypy
+* [![Latest Version]
+(https://pypip.in/version/versioneer/badge.svg?style=flat)
+](https://pypi.python.org/pypi/versioneer/)
+* [![Build Status]
+(https://travis-ci.org/warner/python-versioneer.png?branch=master)
+](https://travis-ci.org/warner/python-versioneer)
+
+This is a tool for managing a recorded version number in distutils-based
+python projects. The goal is to remove the tedious and error-prone "update
+the embedded version string" step from your release process. Making a new
+release should be as easy as recording a new tag in your version-control
+system, and maybe making new tarballs.
+
+
+## Quick Install
+
+* `pip install versioneer` to somewhere to your $PATH
+* run `versioneer-installer` in your source tree: this installs `versioneer.py`
+* follow the instructions below (also in the `versioneer.py` docstring)
+
+## Version Identifiers
+
+Source trees come from a variety of places:
+
+* a version-control system checkout (mostly used by developers)
+* a nightly tarball, produced by build automation
+* a snapshot tarball, produced by a web-based VCS browser, like github's
+  "tarball from tag" feature
+* a release tarball, produced by "setup.py sdist", distributed through PyPI
+
+Within each source tree, the version identifier (either a string or a number,
+this tool is format-agnostic) can come from a variety of places:
+
+* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows
+  about recent "tags" and an absolute revision-id
+* the name of the directory into which the tarball was unpacked
+* an expanded VCS keyword ($Id$, etc)
+* a `_version.py` created by some earlier build step
+
+For released software, the version identifier is closely related to a VCS
+tag. Some projects use tag names that include more than just the version
+string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool
+needs to strip the tag prefix to extract the version identifier. For
+unreleased software (between tags), the version identifier should provide
+enough information to help developers recreate the same tree, while also
+giving them an idea of roughly how old the tree is (after version 1.2, before
+version 1.3). Many VCS systems can report a description that captures this,
+for example 'git describe --tags --dirty --always' reports things like
+"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the
+0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has
+uncommitted changes.
+
+The version identifier is used for multiple purposes:
+
+* to allow the module to self-identify its version: `myproject.__version__`
+* to choose a name and prefix for a 'setup.py sdist' tarball
+
+## Theory of Operation
+
+Versioneer works by adding a special `_version.py` file into your source
+tree, where your `__init__.py` can import it. This `_version.py` knows how to
+dynamically ask the VCS tool for version information at import time. However,
+when you use "setup.py build" or "setup.py sdist", `_version.py` in the new
+copy is replaced by a small static file that contains just the generated
+version data.
+
+`_version.py` also contains `$Revision$` markers, and the installation
+process marks `_version.py` to have this marker rewritten with a tag name
+during the "git archive" command. As a result, generated tarballs will
+contain enough information to get the proper version.
+
+
+## Installation
+
+First, decide on values for the following configuration variables:
+
+* `VCS`: the version control system you use. Currently accepts "git".
+
+* `versionfile_source`:
+
+  A project-relative pathname into which the generated version strings should
+  be written. This is usually a `_version.py` next to your project's main
+  `__init__.py` file, so it can be imported at runtime. If your project uses
+  `src/myproject/__init__.py`, this should be `src/myproject/_version.py`.
+  This file should be checked in to your VCS as usual: the copy created below
+  by `setup.py versioneer` will include code that parses expanded VCS
+  keywords in generated tarballs. The 'build' and 'sdist' commands will
+  replace it with a copy that has just the calculated version string.
+
+  This must be set even if your project does not have any modules (and will
+  therefore never import `_version.py`), since "setup.py sdist" -based trees
+  still need somewhere to record the pre-calculated version strings. Anywhere
+  in the source tree should do. If there is a `__init__.py` next to your
+  `_version.py`, the `setup.py versioneer` command (described below) will
+  append some `__version__`-setting assignments, if they aren't already
+  present.
+
+* `versionfile_build`:
+
+  Like `versionfile_source`, but relative to the build directory instead of
+  the source directory. These will differ when your setup.py uses
+  'package_dir='. If you have `package_dir={'myproject': 'src/myproject'}`,
+  then you will probably have `versionfile_build='myproject/_version.py'` and
+  `versionfile_source='src/myproject/_version.py'`.
+
+  If this is set to None, then `setup.py build` will not attempt to rewrite
+  any `_version.py` in the built tree. If your project does not have any
+  libraries (e.g. if it only builds a script), then you should use
+  `versionfile_build = None` and override `distutils.command.build_scripts`
+  to explicitly insert a copy of `versioneer.get_version()` into your
+  generated script.
+
+* `tag_prefix`:
+
+  a string, like 'PROJECTNAME-', which appears at the start of all VCS tags.
+  If your tags look like 'myproject-1.2.0', then you should use
+  tag_prefix='myproject-'. If you use unprefixed tags like '1.2.0', this
+  should be an empty string.
+
+* `parentdir_prefix`:
+
+  a string, frequently the same as tag_prefix, which appears at the start of
+  all unpacked tarball filenames. If your tarball unpacks into
+  'myproject-1.2.0', this should be 'myproject-'.
+
+This tool provides one script, named `versioneer-installer`. That script does
+one thing: write a copy of `versioneer.py` into the current directory.
+
+To versioneer-enable your project:
+
+* 1: Run `versioneer-installer` to copy `versioneer.py` into the top of your
+  source tree.
+
+* 2: add the following lines to the top of your `setup.py`, with the
+  configuration values you decided earlier:
+
+  ````
+  import versioneer
+  versioneer.VCS = 'git'
+  versioneer.versionfile_source = 'src/myproject/_version.py'
+  versioneer.versionfile_build = 'myproject/_version.py'
+  versioneer.tag_prefix = '' # tags are like 1.2.0
+  versioneer.parentdir_prefix = 'myproject-' # dirname like 'myproject-1.2.0'
+  ````
+
+* 3: add the following arguments to the setup() call in your setup.py:
+
+        version=versioneer.get_version(),
+        cmdclass=versioneer.get_cmdclass(),
+
+* 4: now run `setup.py versioneer`, which will create `_version.py`, and will
+  modify your `__init__.py` (if one exists next to `_version.py`) to define
+  `__version__` (by calling a function from `_version.py`). It will also
+  modify your `MANIFEST.in` to include both `versioneer.py` and the generated
+  `_version.py` in sdist tarballs.
+
+* 5: commit these changes to your VCS. To make sure you won't forget,
+  `setup.py versioneer` will mark everything it touched for addition.
+
+## Post-Installation Usage
+
+Once established, all uses of your tree from a VCS checkout should get the
+current version string. All generated tarballs should include an embedded
+version string (so users who unpack them will not need a VCS tool installed).
+
+If you distribute your project through PyPI, then the release process should
+boil down to two steps:
+
+* 1: git tag 1.0
+* 2: python setup.py register sdist upload
+
+If you distribute it through github (i.e. users use github to generate
+tarballs with `git archive`), the process is:
+
+* 1: git tag 1.0
+* 2: git push; git push --tags
+
+Currently, all version strings must be based upon a tag. Versioneer will
+report "unknown" until your tree has at least one tag in its history. This
+restriction will be fixed eventually (see issue #12).
+
+## Version-String Flavors
+
+Code which uses Versioneer can learn about its version string at runtime by
+importing `_version` from your main `__init__.py` file and running the
+`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can
+import the top-level `versioneer.py` and run `get_versions()`.
+
+Both functions return a dictionary with different keys for different flavors
+of the version string:
+
+* `['version']`: A condensed PEP440-compliant string, equal to the
+  un-prefixed tag name for actual releases, and containing an additional
+  "local version" section with more detail for in-between builds. For Git,
+  this is TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe
+  --tags --dirty --always`. For example "0.11+2.g1076c97.dirty" indicates
+  that the tree is like the "1076c97" commit but has uncommitted changes
+  (".dirty"), and that this commit is two revisions ("+2") beyond the "0.11"
+  tag. For released software (exactly equal to a known tag), the identifier
+  will only contain the stripped tag, e.g. "0.11".
+
+* `['full']`: detailed revision identifier. For Git, this is the full SHA1
+  commit id, followed by ".dirty" if the tree contains uncommitted changes,
+  e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac.dirty".
+
+Some variants are more useful than others. Including `full` in a bug report
+should allow developers to reconstruct the exact code being tested (or
+indicate the presence of local changes that should be shared with the
+developers). `version` is suitable for display in an "about" box or a CLI
+`--version` output: it can be easily compared against release notes and lists
+of bugs fixed in various releases.
+
+The `setup.py versioneer` command adds the following text to your
+`__init__.py` to place a basic version in `YOURPROJECT.__version__`:
+
+    from ._version import get_versions
+    __version__ = get_versions()['version']
+    del get_versions
+
+## Updating Versioneer
+
+To upgrade your project to a new release of Versioneer, do the following:
+
+* install the new Versioneer (`pip install -U versioneer` or equivalent)
+* re-run `versioneer-installer` in your source tree to replace your copy of
+  `versioneer.py`
+* edit `setup.py`, if necessary, to include any new configuration settings
+  indicated by the release notes
+* re-run `setup.py versioneer` to replace `SRC/_version.py`
+* commit any changed files
+
+### Upgrading from 0.10 to 0.11
+
+You must add a `versioneer.VCS = "git"` to your `setup.py` before re-running
+`setup.py versioneer`. This will enable the use of additional version-control
+systems (SVN, etc) in the future.
+
+### Upgrading from 0.11 to 0.12
+
+Nothing special.
+
+## Upgrading to 0.14
+
+0.14 changes the format of the version string. 0.13 and earlier used
+hyphen-separated strings like "0.11-2-g1076c97-dirty". 0.14 and beyond use a
+plus-separated "local version" section strings, with dot-separated
+components, like "0.11+2.g1076c97". PEP440-strict tools did not like the old
+format, but should be ok with the new one.
+
+## Future Directions
+
+This tool is designed to make it easily extended to other version-control
+systems: all VCS-specific components are in separate directories like
+src/git/ . The top-level `versioneer.py` script is assembled from these
+components by running make-versioneer.py . In the future, make-versioneer.py
+will take a VCS name as an argument, and will construct a version of
+`versioneer.py` that is specific to the given VCS. It might also take the
+configuration arguments that are currently provided manually during
+installation by editing setup.py . Alternatively, it might go the other
+direction and include code from all supported VCS systems, reducing the
+number of intermediate scripts.
+
+
+## License
+
+To make Versioneer easier to embed, all its code is hereby released into the
+public domain. The `_version.py` that it creates is also in the public
+domain.
+
 """
-from __future__ import print_function, division, absolute_import
 
+import errno
 import os
-import sys
 import re
 import subprocess
-from distutils.core import Command
-from distutils.command.sdist import sdist as _sdist
+import sys
 from distutils.command.build import build as _build
+from distutils.command.sdist import sdist as _sdist
+from distutils.core import Command
 
+# these configuration settings will be overridden by setup.py after it
+# imports us
 versionfile_source = None
 versionfile_build = None
 tag_prefix = None
 parentdir_prefix = None
+VCS = None
 
-VCS = "git"
-IN_LONG_VERSION_PY = False
-GIT = "git"
+# these dictionaries contain VCS-specific tools
+LONG_VERSION_PY = {}
 
 
-LONG_VERSION_PY = '''
-IN_LONG_VERSION_PY = True
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
+    assert isinstance(commands, list)
+    p = None
+    for c in commands:
+        try:
+            # remember shell=False, so use git.cmd on windows, not just git
+            p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
+                                 stderr=(subprocess.PIPE if hide_stderr
+                                         else None))
+            break
+        except EnvironmentError:
+            e = sys.exc_info()[1]
+            if e.errno == errno.ENOENT:
+                continue
+            if verbose:
+                print("unable to run %s" % args[0])
+                print(e)
+            return None
+    else:
+        if verbose:
+            print("unable to find command, tried %s" % (commands,))
+        return None
+    stdout = p.communicate()[0].strip()
+    if sys.version_info[0] >= 3:
+        stdout = stdout.decode()
+    if p.returncode != 0:
+        if verbose:
+            print("unable to run %s (error)" % args[0])
+        return None
+    return stdout
+LONG_VERSION_PY['git'] = '''
 # This file helps to compute a version number in source trees obtained from
-# git-archive tarball (such as those provided by github's download-from-tag
-# feature). Distribution tarballs (build by setup.py sdist) and build
+# git-archive tarball (such as those provided by githubs download-from-tag
+# feature). Distribution tarballs (built by setup.py sdist) and build
 # directories (produced by setup.py build) will contain a much shorter file
 # that just contains the computed version number.
 
 # This file is released into the public domain. Generated by
-# versioneer-0.7+ (https://github.com/warner/python-versioneer)
+# versioneer-0.14 (https://github.com/warner/python-versioneer)
+
+import errno
+import os
+import re
+import subprocess
+import sys
 
 # these strings will be replaced by git during git-archive
 git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s"
 git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s"
 
+# these strings are filled in when 'setup.py versioneer' creates _version.py
+tag_prefix = "%(TAG_PREFIX)s"
+parentdir_prefix = "%(PARENTDIR_PREFIX)s"
+versionfile_source = "%(VERSIONFILE_SOURCE)s"
 
-import subprocess
-import sys
 
-def run_command(args, cwd=None, verbose=False):
-    try:
-        # remember shell=False, so use git.cmd on windows, not just git
-        p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)
-    except EnvironmentError:
-        e = sys.exc_info()[1]
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
+    assert isinstance(commands, list)
+    p = None
+    for c in commands:
+        try:
+            # remember shell=False, so use git.cmd on windows, not just git
+            p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
+                                 stderr=(subprocess.PIPE if hide_stderr
+                                         else None))
+            break
+        except EnvironmentError:
+            e = sys.exc_info()[1]
+            if e.errno == errno.ENOENT:
+                continue
+            if verbose:
+                print("unable to run %%s" %% args[0])
+                print(e)
+            return None
+    else:
         if verbose:
-            print("unable to run %%s" %% args[0])
-            print(e)
+            print("unable to find command, tried %%s" %% (commands,))
         return None
     stdout = p.communicate()[0].strip()
-    if sys.version >= '3':
+    if sys.version_info[0] >= 3:
         stdout = stdout.decode()
     if p.returncode != 0:
         if verbose:
@@ -139,323 +390,350 @@ def run_command(args, cwd=None, verbose=False):
     return stdout
 
 
-import sys
-import re
-import os.path
+def versions_from_parentdir(parentdir_prefix, root, verbose=False):
+    # Source tarballs conventionally unpack into a directory that includes
+    # both the project name and a version string.
+    dirname = os.path.basename(root)
+    if not dirname.startswith(parentdir_prefix):
+        if verbose:
+            print("guessing rootdir is '%%s', but '%%s' doesn't start with "
+                  "prefix '%%s'" %% (root, dirname, parentdir_prefix))
+        return None
+    return {"version": dirname[len(parentdir_prefix):], "full": ""}
+
 
-def get_expanded_variables(versionfile_source):
+def git_get_keywords(versionfile_abs):
     # the code embedded in _version.py can just fetch the value of these
-    # variables. When used from setup.py, we don't want to import
-    # _version.py, so we do it with a regexp instead. This function is not
-    # used from _version.py.
-    variables = {}
+    # keywords. When used from setup.py, we don't want to import _version.py,
+    # so we do it with a regexp instead. This function is not used from
+    # _version.py.
+    keywords = {}
     try:
-        for line in open(versionfile_source,"r").readlines():
+        f = open(versionfile_abs, "r")
+        for line in f.readlines():
             if line.strip().startswith("git_refnames ="):
                 mo = re.search(r'=\s*"(.*)"', line)
                 if mo:
-                    variables["refnames"] = mo.group(1)
+                    keywords["refnames"] = mo.group(1)
             if line.strip().startswith("git_full ="):
                 mo = re.search(r'=\s*"(.*)"', line)
                 if mo:
-                    variables["full"] = mo.group(1)
+                    keywords["full"] = mo.group(1)
+        f.close()
     except EnvironmentError:
         pass
-    return variables
+    return keywords
 
-def versions_from_expanded_variables(variables, tag_prefix, verbose=False):
-    refnames = variables["refnames"].strip()
+
+def git_versions_from_keywords(keywords, tag_prefix, verbose=False):
+    if not keywords:
+        return {}  # keyword-finding function failed to find keywords
+    refnames = keywords["refnames"].strip()
     if refnames.startswith("$Format"):
         if verbose:
-            print("variables are unexpanded, not using")
-        return {} # unexpanded, so not in an unpacked git-archive tarball
+            print("keywords are unexpanded, not using")
+        return {}  # unexpanded, so not in an unpacked git-archive tarball
     refs = set([r.strip() for r in refnames.strip("()").split(",")])
-    for ref in list(refs):
-        if not re.search(r'\d', ref):
-            if verbose:
-                print("discarding '%%s', no digits" %% ref)
-            refs.discard(ref)
-            # Assume all version tags have a digit. git's %%d expansion
-            # behaves like git log --decorate=short and strips out the
-            # refs/heads/ and refs/tags/ prefixes that would let us
-            # distinguish between branches and tags. By ignoring refnames
-            # without digits, we filter out many common branch names like
-            # "release" and "stabilization", as well as "HEAD" and "master".
+    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
+    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
+    TAG = "tag: "
+    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    if not tags:
+        # Either we're using git < 1.8.3, or there really are no tags. We use
+        # a heuristic: assume all version tags have a digit. The old git %%d
+        # expansion behaves like git log --decorate=short and strips out the
+        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
+        # between branches and tags. By ignoring refnames without digits, we
+        # filter out many common branch names like "release" and
+        # "stabilization", as well as "HEAD" and "master".
+        tags = set([r for r in refs if re.search(r'\d', r)])
+        if verbose:
+            print("discarding '%%s', no digits" %% ",".join(refs-tags))
     if verbose:
-        print("remaining refs: %%s" %% ",".join(sorted(refs)))
-    for ref in sorted(refs):
+        print("likely tags: %%s" %% ",".join(sorted(tags)))
+    for ref in sorted(tags):
         # sorting will prefer e.g. "2.0" over "2.0rc1"
         if ref.startswith(tag_prefix):
             r = ref[len(tag_prefix):]
             if verbose:
                 print("picking %%s" %% r)
-            return { "version": r,
-                     "full": variables["full"].strip() }
-    # no suitable tags, so we use the full revision id
+            return {"version": r,
+                    "full": keywords["full"].strip()}
+    # no suitable tags, so version is "0+unknown", but full hex is still there
     if verbose:
-        print("no suitable tags, using full revision id")
-    return { "version": variables["full"].strip(),
-             "full": variables["full"].strip() }
-
-def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):
-    # this runs 'git' from the root of the source tree. That either means
-    # someone ran a setup.py command (and this code is in versioneer.py, so
-    # IN_LONG_VERSION_PY=False, thus the containing directory is the root of
-    # the source tree), or someone ran a project-specific entry point (and
-    # this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the
-    # containing directory is somewhere deeper in the source tree). This only
-    # gets called if the git-archive 'subst' variables were *not* expanded,
-    # and _version.py hasn't already been rewritten with a short version
-    # string, meaning we're inside a checked out source tree.
+        print("no suitable tags, using unknown + full revision id")
+    return {"version": "0+unknown",
+            "full": keywords["full"].strip()}
+
+
+def git_parse_vcs_describe(git_describe, tag_prefix, verbose=False):
+    # TAG-NUM-gHEX[-dirty] or HEX[-dirty] . TAG might have hyphens.
+
+    # dirty
+    dirty = git_describe.endswith("-dirty")
+    if dirty:
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
+    dirty_suffix = ".dirty" if dirty else ""
+
+    # now we have TAG-NUM-gHEX or HEX
+
+    if "-" not in git_describe:  # just HEX
+        return "0+untagged.g"+git_describe+dirty_suffix, dirty
+
+    # just TAG-NUM-gHEX
+    mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
+    if not mo:
+        # unparseable. Maybe git-describe is misbehaving?
+        return "0+unparseable"+dirty_suffix, dirty
+
+    # tag
+    full_tag = mo.group(1)
+    if not full_tag.startswith(tag_prefix):
+        if verbose:
+            fmt = "tag '%%s' doesn't start with prefix '%%s'"
+            print(fmt %% (full_tag, tag_prefix))
+        return None, dirty
+    tag = full_tag[len(tag_prefix):]
+
+    # distance: number of commits since tag
+    distance = int(mo.group(2))
+
+    # commit: short hex revision ID
+    commit = mo.group(3)
+
+    # now build up version string, with post-release "local version
+    # identifier". Our goal: TAG[+NUM.gHEX[.dirty]] . Note that if you get a
+    # tagged build and then dirty it, you'll get TAG+0.gHEX.dirty . So you
+    # can always test version.endswith(".dirty").
+    version = tag
+    if distance or dirty:
+        version += "+%%d.g%%s" %% (distance, commit) + dirty_suffix
+
+    return version, dirty
+
+
+def git_versions_from_vcs(tag_prefix, root, verbose=False):
+    # this runs 'git' from the root of the source tree. This only gets called
+    # if the git-archive 'subst' keywords were *not* expanded, and
+    # _version.py hasn't already been rewritten with a short version string,
+    # meaning we're inside a checked out source tree.
 
-    try:
-        here = os.path.abspath(__file__)
-    except NameError:
-        # some py2exe/bbfreeze/non-CPython implementations don't do __file__
-        return {} # not always correct
-
-    # versionfile_source is the relative path from the top of the source tree
-    # (where the .git directory might live) to this file. Invert this to find
-    # the root from __file__.
-    root = here
-    if IN_LONG_VERSION_PY:
-        for i in range(len(versionfile_source.split("/"))):
-            root = os.path.dirname(root)
-    else:
-        root = os.path.dirname(here)
     if not os.path.exists(os.path.join(root, ".git")):
         if verbose:
             print("no .git in %%s" %% root)
-        return {}
-
-    stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],
+        return {}  # get_versions() will try next method
+
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+    # if there is a tag, this yields TAG-NUM-gHEX[-dirty]
+    # if there are no tags, this yields HEX[-dirty] (no NUM)
+    stdout = run_command(GITS, ["describe", "--tags", "--dirty",
+                                "--always", "--long"],
                          cwd=root)
+    # --long was added in git-1.5.5
     if stdout is None:
-        return {}
-    if not stdout.startswith(tag_prefix):
-        if verbose:
-            print("tag '%%s' doesn't start with prefix '%%s'" %% (stdout, tag_prefix))
-        return {}
-    tag = stdout[len(tag_prefix):]
-    stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root)
+        return {}  # try next method
+    version, dirty = git_parse_vcs_describe(stdout, tag_prefix, verbose)
+
+    # build "full", which is FULLHEX[.dirty]
+    stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
     if stdout is None:
         return {}
     full = stdout.strip()
-    if tag.endswith("-dirty"):
-        full += "-dirty"
-    return {"version": tag, "full": full}
+    if dirty:
+        full += ".dirty"
 
+    return {"version": version, "full": full}
 
-def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False):
-    if IN_LONG_VERSION_PY:
-        # We're running from _version.py. If it's from a source tree
-        # (execute-in-place), we can work upwards to find the root of the
-        # tree, and then check the parent directory for a version string. If
-        # it's in an installed application, there's no hope.
-        try:
-            here = os.path.abspath(__file__)
-        except NameError:
-            # py2exe/bbfreeze/non-CPython don't have __file__
-            return {} # without __file__, we have no hope
-        # versionfile_source is the relative path from the top of the source
-        # tree to _version.py. Invert this to find the root from __file__.
-        root = here
-        for i in range(len(versionfile_source.split("/"))):
-            root = os.path.dirname(root)
-    else:
-        # we're running from versioneer.py, which means we're running from
-        # the setup.py in a source tree. sys.argv[0] is setup.py in the root.
-        here = os.path.abspath(sys.argv[0])
-        root = os.path.dirname(here)
 
-    # Source tarballs conventionally unpack into a directory that includes
-    # both the project name and a version string.
-    dirname = os.path.basename(root)
-    if not dirname.startswith(parentdir_prefix):
-        if verbose:
-            print("guessing rootdir is '%%s', but '%%s' doesn't start with prefix '%%s'" %%
-                  (root, dirname, parentdir_prefix))
-        return None
-    return {"version": dirname[len(parentdir_prefix):], "full": ""}
+def get_versions(default={"version": "0+unknown", "full": ""}, verbose=False):
+    # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
+    # __file__, we can work backwards from there to the root. Some
+    # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
+    # case we can only use expanded keywords.
 
-tag_prefix = "%(TAG_PREFIX)s"
-parentdir_prefix = "%(PARENTDIR_PREFIX)s"
-versionfile_source = "%(VERSIONFILE_SOURCE)s"
+    keywords = {"refnames": git_refnames, "full": git_full}
+    ver = git_versions_from_keywords(keywords, tag_prefix, verbose)
+    if ver:
+        return ver
 
-def get_versions(default={"version": "unknown", "full": ""}, verbose=False):
-    variables = { "refnames": git_refnames, "full": git_full }
-    ver = versions_from_expanded_variables(variables, tag_prefix, verbose)
-    if not ver:
-        ver = versions_from_vcs(tag_prefix, versionfile_source, verbose)
-    if not ver:
-        ver = versions_from_parentdir(parentdir_prefix, versionfile_source,
-                                      verbose)
-    if not ver:
-        ver = default
-    return ver
+    try:
+        root = os.path.realpath(__file__)
+        # versionfile_source is the relative path from the top of the source
+        # tree (where the .git directory might live) to this file. Invert
+        # this to find the root from __file__.
+        for i in versionfile_source.split('/'):
+            root = os.path.dirname(root)
+    except NameError:
+        return default
 
+    return (git_versions_from_vcs(tag_prefix, root, verbose)
+            or versions_from_parentdir(parentdir_prefix, root, verbose)
+            or default)
 '''
 
-def run_command(args, cwd=None, verbose=False):
-    try:
-        # remember shell=False, so use git.cmd on windows, not just git
-        p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)
-    except EnvironmentError:
-        e = sys.exc_info()[1]
-        if verbose:
-            print("unable to run %s" % args[0])
-            print(e)
-        return None
-    stdout = p.communicate()[0].strip()
-    if sys.version >= '3':
-        stdout = stdout.decode()
-    if p.returncode != 0:
-        if verbose:
-            print("unable to run %s (error)" % args[0])
-        return None
-    return stdout
-
 
-def get_expanded_variables(versionfile_source):
+def git_get_keywords(versionfile_abs):
     # the code embedded in _version.py can just fetch the value of these
-    # variables. When used from setup.py, we don't want to import
-    # _version.py, so we do it with a regexp instead. This function is not
-    # used from _version.py.
-    variables = {}
+    # keywords. When used from setup.py, we don't want to import _version.py,
+    # so we do it with a regexp instead. This function is not used from
+    # _version.py.
+    keywords = {}
     try:
-        for line in open(versionfile_source,"r").readlines():
+        f = open(versionfile_abs, "r")
+        for line in f.readlines():
             if line.strip().startswith("git_refnames ="):
                 mo = re.search(r'=\s*"(.*)"', line)
                 if mo:
-                    variables["refnames"] = mo.group(1)
+                    keywords["refnames"] = mo.group(1)
             if line.strip().startswith("git_full ="):
                 mo = re.search(r'=\s*"(.*)"', line)
                 if mo:
-                    variables["full"] = mo.group(1)
+                    keywords["full"] = mo.group(1)
+        f.close()
     except EnvironmentError:
         pass
-    return variables
+    return keywords
 
-def versions_from_expanded_variables(variables, tag_prefix, verbose=False):
-    refnames = variables["refnames"].strip()
+
+def git_versions_from_keywords(keywords, tag_prefix, verbose=False):
+    if not keywords:
+        return {}  # keyword-finding function failed to find keywords
+    refnames = keywords["refnames"].strip()
     if refnames.startswith("$Format"):
         if verbose:
-            print("variables are unexpanded, not using")
-        return {} # unexpanded, so not in an unpacked git-archive tarball
+            print("keywords are unexpanded, not using")
+        return {}  # unexpanded, so not in an unpacked git-archive tarball
     refs = set([r.strip() for r in refnames.strip("()").split(",")])
-    for ref in list(refs):
-        if not re.search(r'\d', ref):
-            if verbose:
-                print("discarding '%s', no digits" % ref)
-            refs.discard(ref)
-            # Assume all version tags have a digit. git's %d expansion
-            # behaves like git log --decorate=short and strips out the
-            # refs/heads/ and refs/tags/ prefixes that would let us
-            # distinguish between branches and tags. By ignoring refnames
-            # without digits, we filter out many common branch names like
-            # "release" and "stabilization", as well as "HEAD" and "master".
+    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
+    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
+    TAG = "tag: "
+    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    if not tags:
+        # Either we're using git < 1.8.3, or there really are no tags. We use
+        # a heuristic: assume all version tags have a digit. The old git %d
+        # expansion behaves like git log --decorate=short and strips out the
+        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
+        # between branches and tags. By ignoring refnames without digits, we
+        # filter out many common branch names like "release" and
+        # "stabilization", as well as "HEAD" and "master".
+        tags = set([r for r in refs if re.search(r'\d', r)])
+        if verbose:
+            print("discarding '%s', no digits" % ",".join(refs-tags))
     if verbose:
-        print("remaining refs: %s" % ",".join(sorted(refs)))
-    for ref in sorted(refs):
+        print("likely tags: %s" % ",".join(sorted(tags)))
+    for ref in sorted(tags):
         # sorting will prefer e.g. "2.0" over "2.0rc1"
         if ref.startswith(tag_prefix):
             r = ref[len(tag_prefix):]
             if verbose:
                 print("picking %s" % r)
-            return { "version": r,
-                     "full": variables["full"].strip() }
-    # no suitable tags, so we use the full revision id
+            return {"version": r,
+                    "full": keywords["full"].strip()}
+    # no suitable tags, so version is "0+unknown", but full hex is still there
     if verbose:
-        print("no suitable tags, using full revision id")
-    return { "version": variables["full"].strip(),
-             "full": variables["full"].strip() }
-
-def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):
-    # this runs 'git' from the root of the source tree. That either means
-    # someone ran a setup.py command (and this code is in versioneer.py, so
-    # IN_LONG_VERSION_PY=False, thus the containing directory is the root of
-    # the source tree), or someone ran a project-specific entry point (and
-    # this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the
-    # containing directory is somewhere deeper in the source tree). This only
-    # gets called if the git-archive 'subst' variables were *not* expanded,
-    # and _version.py hasn't already been rewritten with a short version
-    # string, meaning we're inside a checked out source tree.
+        print("no suitable tags, using unknown + full revision id")
+    return {"version": "0+unknown",
+            "full": keywords["full"].strip()}
+
+
+def git_parse_vcs_describe(git_describe, tag_prefix, verbose=False):
+    # TAG-NUM-gHEX[-dirty] or HEX[-dirty] . TAG might have hyphens.
+
+    # dirty
+    dirty = git_describe.endswith("-dirty")
+    if dirty:
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
+    dirty_suffix = ".dirty" if dirty else ""
+
+    # now we have TAG-NUM-gHEX or HEX
+
+    if "-" not in git_describe:  # just HEX
+        return "0+untagged.g"+git_describe+dirty_suffix, dirty
+
+    # just TAG-NUM-gHEX
+    mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
+    if not mo:
+        # unparseable. Maybe git-describe is misbehaving?
+        return "0+unparseable"+dirty_suffix, dirty
+
+    # tag
+    full_tag = mo.group(1)
+    if not full_tag.startswith(tag_prefix):
+        if verbose:
+            fmt = "tag '%s' doesn't start with prefix '%s'"
+            print(fmt % (full_tag, tag_prefix))
+        return None, dirty
+    tag = full_tag[len(tag_prefix):]
+
+    # distance: number of commits since tag
+    distance = int(mo.group(2))
+
+    # commit: short hex revision ID
+    commit = mo.group(3)
+
+    # now build up version string, with post-release "local version
+    # identifier". Our goal: TAG[+NUM.gHEX[.dirty]] . Note that if you get a
+    # tagged build and then dirty it, you'll get TAG+0.gHEX.dirty . So you
+    # can always test version.endswith(".dirty").
+    version = tag
+    if distance or dirty:
+        version += "+%d.g%s" % (distance, commit) + dirty_suffix
+
+    return version, dirty
+
+
+def git_versions_from_vcs(tag_prefix, root, verbose=False):
+    # this runs 'git' from the root of the source tree. This only gets called
+    # if the git-archive 'subst' keywords were *not* expanded, and
+    # _version.py hasn't already been rewritten with a short version string,
+    # meaning we're inside a checked out source tree.
 
-    try:
-        here = os.path.abspath(__file__)
-    except NameError:
-        # some py2exe/bbfreeze/non-CPython implementations don't do __file__
-        return {} # not always correct
-
-    # versionfile_source is the relative path from the top of the source tree
-    # (where the .git directory might live) to this file. Invert this to find
-    # the root from __file__.
-    root = here
-    if IN_LONG_VERSION_PY:
-        for i in range(len(versionfile_source.split("/"))):
-            root = os.path.dirname(root)
-    else:
-        root = os.path.dirname(here)
     if not os.path.exists(os.path.join(root, ".git")):
         if verbose:
             print("no .git in %s" % root)
-        return {}
-
-    stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],
+        return {}  # get_versions() will try next method
+
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+    # if there is a tag, this yields TAG-NUM-gHEX[-dirty]
+    # if there are no tags, this yields HEX[-dirty] (no NUM)
+    stdout = run_command(GITS, ["describe", "--tags", "--dirty",
+                                "--always", "--long"],
                          cwd=root)
+    # --long was added in git-1.5.5
     if stdout is None:
-        return {}
-    if not stdout.startswith(tag_prefix):
-        if verbose:
-            print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix))
-        return {}
-    tag = stdout[len(tag_prefix):]
-    stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root)
+        return {}  # try next method
+    version, dirty = git_parse_vcs_describe(stdout, tag_prefix, verbose)
+
+    # build "full", which is FULLHEX[.dirty]
+    stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
     if stdout is None:
         return {}
     full = stdout.strip()
-    if tag.endswith("-dirty"):
-        full += "-dirty"
-    return {"version": tag, "full": full}
-
+    if dirty:
+        full += ".dirty"
 
-def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False):
-    if IN_LONG_VERSION_PY:
-        # We're running from _version.py. If it's from a source tree
-        # (execute-in-place), we can work upwards to find the root of the
-        # tree, and then check the parent directory for a version string. If
-        # it's in an installed application, there's no hope.
-        try:
-            here = os.path.abspath(__file__)
-        except NameError:
-            # py2exe/bbfreeze/non-CPython don't have __file__
-            return {} # without __file__, we have no hope
-        # versionfile_source is the relative path from the top of the source
-        # tree to _version.py. Invert this to find the root from __file__.
-        root = here
-        for i in range(len(versionfile_source.split("/"))):
-            root = os.path.dirname(root)
-    else:
-        # we're running from versioneer.py, which means we're running from
-        # the setup.py in a source tree. sys.argv[0] is setup.py in the root.
-        here = os.path.abspath(sys.argv[0])
-        root = os.path.dirname(here)
-
-    # Source tarballs conventionally unpack into a directory that includes
-    # both the project name and a version string.
-    dirname = os.path.basename(root)
-    if not dirname.startswith(parentdir_prefix):
-        if verbose:
-            print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" %
-                  (root, dirname, parentdir_prefix))
-        return None
-    return {"version": dirname[len(parentdir_prefix):], "full": ""}
+    return {"version": version, "full": full}
 
 
-def do_vcs_install(versionfile_source, ipy):
-    run_command([GIT, "add", "versioneer.py"])
-    run_command([GIT, "add", versionfile_source])
-    run_command([GIT, "add", ipy])
+def do_vcs_install(manifest_in, versionfile_source, ipy):
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+    files = [manifest_in, versionfile_source]
+    if ipy:
+        files.append(ipy)
+    try:
+        me = __file__
+        if me.endswith(".pyc") or me.endswith(".pyo"):
+            me = os.path.splitext(me)[0] + ".py"
+        versioneer_file = os.path.relpath(me)
+    except NameError:
+        versioneer_file = "versioneer.py"
+    files.append(versioneer_file)
     present = False
     try:
         f = open(".gitattributes", "r")
@@ -470,11 +748,23 @@ def do_vcs_install(versionfile_source, ipy):
         f = open(".gitattributes", "a+")
         f.write("%s export-subst\n" % versionfile_source)
         f.close()
-        run_command([GIT, "add", ".gitattributes"])
+        files.append(".gitattributes")
+    run_command(GITS, ["add", "--"] + files)
 
 
+def versions_from_parentdir(parentdir_prefix, root, verbose=False):
+    # Source tarballs conventionally unpack into a directory that includes
+    # both the project name and a version string.
+    dirname = os.path.basename(root)
+    if not dirname.startswith(parentdir_prefix):
+        if verbose:
+            print("guessing rootdir is '%s', but '%s' doesn't start with "
+                  "prefix '%s'" % (root, dirname, parentdir_prefix))
+        return None
+    return {"version": dirname[len(parentdir_prefix):], "full": ""}
+
 SHORT_VERSION_PY = """
-# This file was generated by 'versioneer.py' (0.7+) from
+# This file was generated by 'versioneer.py' (0.14) from
 # revision-control system data, or from the parent directory name of an
 # unpacked source archive. Distribution tarballs contain a pre-generated copy
 # of this file.
@@ -486,82 +776,117 @@ def get_versions(default={}, verbose=False):
 
 """
 
-DEFAULT = {"version": "unknown", "full": "unknown"}
+DEFAULT = {"version": "0+unknown", "full": "unknown"}
+
 
 def versions_from_file(filename):
     versions = {}
     try:
-        f = open(filename)
+        with open(filename) as f:
+            for line in f.readlines():
+                mo = re.match("version_version = '([^']+)'", line)
+                if mo:
+                    versions["version"] = mo.group(1)
+                mo = re.match("version_full = '([^']+)'", line)
+                if mo:
+                    versions["full"] = mo.group(1)
     except EnvironmentError:
-        return versions
-    for line in f.readlines():
-        mo = re.match("version_version = '([^']+)'", line)
-        if mo:
-            versions["version"] = mo.group(1)
-        mo = re.match("version_full = '([^']+)'", line)
-        if mo:
-            versions["full"] = mo.group(1)
+        return {}
+
     return versions
 
+
 def write_to_version_file(filename, versions):
-    f = open(filename, "w")
-    f.write(SHORT_VERSION_PY % versions)
-    f.close()
+    with open(filename, "w") as f:
+        f.write(SHORT_VERSION_PY % versions)
+
     print("set %s to '%s'" % (filename, versions["version"]))
 
 
-def get_best_versions(versionfile, tag_prefix, parentdir_prefix,
-                      default=DEFAULT, verbose=False):
+def get_root():
+    try:
+        return os.path.dirname(os.path.abspath(__file__))
+    except NameError:
+        return os.path.dirname(os.path.abspath(sys.argv[0]))
+
+
+def vcs_function(vcs, suffix):
+    return getattr(sys.modules[__name__], '%s_%s' % (vcs, suffix), None)
+
+
+def get_versions(default=DEFAULT, verbose=False):
     # returns dict with two keys: 'version' and 'full'
-    #
-    # extract version from first of _version.py, 'git describe', parentdir.
-    # This is meant to work for developers using a source checkout, for users
-    # of a tarball created by 'setup.py sdist', and for users of a
-    # tarball/zipball created by 'git archive' or github's download-from-tag
-    # feature.
-
-    variables = get_expanded_variables(versionfile_source)
-    if variables:
-        ver = versions_from_expanded_variables(variables, tag_prefix)
+    assert versionfile_source is not None, \
+        "please set versioneer.versionfile_source"
+    assert tag_prefix is not None, "please set versioneer.tag_prefix"
+    assert parentdir_prefix is not None, \
+        "please set versioneer.parentdir_prefix"
+    assert VCS is not None, "please set versioneer.VCS"
+
+    # I am in versioneer.py, which must live at the top of the source tree,
+    # which we use to compute the root directory. py2exe/bbfreeze/non-CPython
+    # don't have __file__, in which case we fall back to sys.argv[0] (which
+    # ought to be the setup.py script). We prefer __file__ since that's more
+    # robust in cases where setup.py was invoked in some weird way (e.g. pip)
+    root = get_root()
+    versionfile_abs = os.path.join(root, versionfile_source)
+
+    # extract version from first of _version.py, VCS command (e.g. 'git
+    # describe'), parentdir. This is meant to work for developers using a
+    # source checkout, for users of a tarball created by 'setup.py sdist',
+    # and for users of a tarball/zipball created by 'git archive' or github's
+    # download-from-tag feature or the equivalent in other VCSes.
+
+    get_keywords_f = vcs_function(VCS, "get_keywords")
+    versions_from_keywords_f = vcs_function(VCS, "versions_from_keywords")
+    if get_keywords_f and versions_from_keywords_f:
+        vcs_keywords = get_keywords_f(versionfile_abs)
+        ver = versions_from_keywords_f(vcs_keywords, tag_prefix)
         if ver:
-            if verbose: print("got version from expanded variable %s" % ver)
+            if verbose:
+                print("got version from expanded keyword %s" % ver)
             return ver
 
-    ver = versions_from_file(versionfile)
+    ver = versions_from_file(versionfile_abs)
     if ver:
-        if verbose: print("got version from file %s %s" % (versionfile, ver))
+        if verbose:
+            print("got version from file %s %s" % (versionfile_abs, ver))
         return ver
 
-    ver = versions_from_vcs(tag_prefix, versionfile_source, verbose)
-    if ver:
-        if verbose: print("got version from git %s" % ver)
-        return ver
+    versions_from_vcs_f = vcs_function(VCS, "versions_from_vcs")
+    if versions_from_vcs_f:
+        ver = versions_from_vcs_f(tag_prefix, root, verbose)
+        if ver:
+            if verbose:
+                print("got version from VCS %s" % ver)
+            return ver
 
-    ver = versions_from_parentdir(parentdir_prefix, versionfile_source, verbose)
+    ver = versions_from_parentdir(parentdir_prefix, root, verbose)
     if ver:
-        if verbose: print("got version from parentdir %s" % ver)
+        if verbose:
+            print("got version from parentdir %s" % ver)
         return ver
 
-    if verbose: print("got version from default %s" % ver)
+    if verbose:
+        print("got version from default %s" % default)
     return default
 
-def get_versions(default=DEFAULT, verbose=False):
-    assert versionfile_source is not None, "please set versioneer.versionfile_source"
-    assert tag_prefix is not None, "please set versioneer.tag_prefix"
-    assert parentdir_prefix is not None, "please set versioneer.parentdir_prefix"
-    return get_best_versions(versionfile_source, tag_prefix, parentdir_prefix,
-                             default=default, verbose=verbose)
+
 def get_version(verbose=False):
     return get_versions(verbose=verbose)["version"]
 
+
 class cmd_version(Command):
     description = "report generated version string"
     user_options = []
     boolean_options = []
+
     def initialize_options(self):
         pass
+
     def finalize_options(self):
         pass
+
     def run(self):
         ver = get_version(verbose=True)
         print("Version is currently: %s" % ver)
@@ -573,12 +898,37 @@ def run(self):
         _build.run(self)
         # now locate _version.py in the new build/ directory and replace it
         # with an updated value
-        target_versionfile = os.path.join(self.build_lib, versionfile_build)
-        print("UPDATING %s" % target_versionfile)
-        os.unlink(target_versionfile)
-        f = open(target_versionfile, "w")
-        f.write(SHORT_VERSION_PY % versions)
-        f.close()
+        if versionfile_build:
+            target_versionfile = os.path.join(self.build_lib,
+                                              versionfile_build)
+            print("UPDATING %s" % target_versionfile)
+            os.unlink(target_versionfile)
+            with open(target_versionfile, "w") as f:
+                f.write(SHORT_VERSION_PY % versions)
+
+if 'cx_Freeze' in sys.modules:  # cx_freeze enabled?
+    from cx_Freeze.dist import build_exe as _build_exe
+
+    class cmd_build_exe(_build_exe):
+        def run(self):
+            versions = get_versions(verbose=True)
+            target_versionfile = versionfile_source
+            print("UPDATING %s" % target_versionfile)
+            os.unlink(target_versionfile)
+            with open(target_versionfile, "w") as f:
+                f.write(SHORT_VERSION_PY % versions)
+
+            _build_exe.run(self)
+            os.unlink(target_versionfile)
+            with open(versionfile_source, "w") as f:
+                assert VCS is not None, "please set versioneer.VCS"
+                LONG = LONG_VERSION_PY[VCS]
+                f.write(LONG % {"DOLLAR": "$",
+                                "TAG_PREFIX": tag_prefix,
+                                "PARENTDIR_PREFIX": parentdir_prefix,
+                                "VERSIONFILE_SOURCE": versionfile_source,
+                                })
+
 
 class cmd_sdist(_sdist):
     def run(self):
@@ -595,9 +945,8 @@ def make_release_tree(self, base_dir, files):
         target_versionfile = os.path.join(base_dir, versionfile_source)
         print("UPDATING %s" % target_versionfile)
         os.unlink(target_versionfile)
-        f = open(target_versionfile, "w")
-        f.write(SHORT_VERSION_PY % self._versioneer_generated_versions)
-        f.close()
+        with open(target_versionfile, "w") as f:
+            f.write(SHORT_VERSION_PY % self._versioneer_generated_versions)
 
 INIT_PY_SNIPPET = """
 from ._version import get_versions
@@ -605,40 +954,93 @@ def make_release_tree(self, base_dir, files):
 del get_versions
 """
 
+
 class cmd_update_files(Command):
-    description = "modify __init__.py and create _version.py"
+    description = ("install/upgrade Versioneer files: "
+                   "__init__.py SRC/_version.py")
     user_options = []
     boolean_options = []
+
     def initialize_options(self):
         pass
+
     def finalize_options(self):
         pass
+
     def run(self):
-        ipy = os.path.join(os.path.dirname(versionfile_source), "__init__.py")
         print(" creating %s" % versionfile_source)
-        f = open(versionfile_source, "w")
-        f.write(LONG_VERSION_PY % {"DOLLAR": "$",
-                                   "TAG_PREFIX": tag_prefix,
-                                   "PARENTDIR_PREFIX": parentdir_prefix,
-                                   "VERSIONFILE_SOURCE": versionfile_source,
-                                   })
-        f.close()
+        with open(versionfile_source, "w") as f:
+            assert VCS is not None, "please set versioneer.VCS"
+            LONG = LONG_VERSION_PY[VCS]
+            f.write(LONG % {"DOLLAR": "$",
+                            "TAG_PREFIX": tag_prefix,
+                            "PARENTDIR_PREFIX": parentdir_prefix,
+                            "VERSIONFILE_SOURCE": versionfile_source,
+                            })
+
+        ipy = os.path.join(os.path.dirname(versionfile_source), "__init__.py")
+        if os.path.exists(ipy):
+            try:
+                with open(ipy, "r") as f:
+                    old = f.read()
+            except EnvironmentError:
+                old = ""
+            if INIT_PY_SNIPPET not in old:
+                print(" appending to %s" % ipy)
+                with open(ipy, "a") as f:
+                    f.write(INIT_PY_SNIPPET)
+            else:
+                print(" %s unmodified" % ipy)
+        else:
+            print(" %s doesn't exist, ok" % ipy)
+            ipy = None
+
+        # Make sure both the top-level "versioneer.py" and versionfile_source
+        # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
+        # they'll be copied into source distributions. Pip won't be able to
+        # install the package without this.
+        manifest_in = os.path.join(get_root(), "MANIFEST.in")
+        simple_includes = set()
         try:
-            old = open(ipy, "r").read()
+            with open(manifest_in, "r") as f:
+                for line in f:
+                    if line.startswith("include "):
+                        for include in line.split()[1:]:
+                            simple_includes.add(include)
         except EnvironmentError:
-            old = ""
-        if INIT_PY_SNIPPET not in old:
-            print(" appending to %s" % ipy)
-            f = open(ipy, "a")
-            f.write(INIT_PY_SNIPPET)
-            f.close()
+            pass
+        # That doesn't cover everything MANIFEST.in can do
+        # (http://docs.python.org/2/distutils/sourcedist.html#commands), so
+        # it might give some false negatives. Appending redundant 'include'
+        # lines is safe, though.
+        if "versioneer.py" not in simple_includes:
+            print(" appending 'versioneer.py' to MANIFEST.in")
+            with open(manifest_in, "a") as f:
+                f.write("include versioneer.py\n")
         else:
-            print(" %s unmodified" % ipy)
-        do_vcs_install(versionfile_source, ipy)
+            print(" 'versioneer.py' already in MANIFEST.in")
+        if versionfile_source not in simple_includes:
+            print(" appending versionfile_source ('%s') to MANIFEST.in" %
+                  versionfile_source)
+            with open(manifest_in, "a") as f:
+                f.write("include %s\n" % versionfile_source)
+        else:
+            print(" versionfile_source already in MANIFEST.in")
+
+        # Make VCS-specific changes. For git, this means creating/changing
+        # .gitattributes to mark _version.py for export-time keyword
+        # substitution.
+        do_vcs_install(manifest_in, versionfile_source, ipy)
+
 
 def get_cmdclass():
-    return {'version': cmd_version,
-            'update_files': cmd_update_files,
+    cmds = {'version': cmd_version,
+            'versioneer': cmd_update_files,
             'build': cmd_build,
             'sdist': cmd_sdist,
             }
+    if 'cx_Freeze' in sys.modules:  # cx_freeze enabled?
+        cmds['build_exe'] = cmd_build_exe
+        del cmds['build']
+
+    return cmds


Test patch: