nord/_version.py
00152bee
 # -*- coding: utf-8 -*-
 # This file is part of 'miniver': https://github.com/jbweston/miniver
 #
 from collections import namedtuple
6f63c0a5
 import os
 import subprocess
 import sys
 
00152bee
 from distutils.command.build import build as build_orig
 from setuptools.command.sdist import sdist as sdist_orig
6f63c0a5
 
00152bee
 Version = namedtuple('Version', ('release', 'dev', 'labels'))
6f63c0a5
 
00152bee
 # No public API
 __all__ = []
6f63c0a5
 
00152bee
 package_root = os.path.dirname(os.path.realpath(__file__))
 package_name = os.path.basename(package_root)
 distr_root = os.path.dirname(package_root)
6f63c0a5
 
00152bee
 STATIC_VERSION_FILE = '_static_version.py'
6f63c0a5
 
 
00152bee
 def get_version(version_file=STATIC_VERSION_FILE):
     version_info = {}
     with open(os.path.join(package_root, version_file), 'rb') as f:
         exec(f.read(), {}, version_info)
     version = version_info['version']
     version_is_from_git = (version == "__use_git__")
     if version_is_from_git:
         version = get_version_from_git()
         if not version:
             version = get_version_from_git_archive(version_info)
         if not version:
             version = Version("unknown", None, None)
         return semver_format(version)
     else:
         return version
6f63c0a5
 
 
00152bee
 def semver_format(version_info):
     release, dev, labels = version_info
6f63c0a5
 
00152bee
     version_parts = [release]
     if dev:
         if release.endswith('-dev'):
             version_parts.append(dev)
         elif release.contains('-'):
             version_parts.append('.dev{}'.format(dev))
         else:
             version_parts.append('-dev{}'.format(dev))
6f63c0a5
 
00152bee
     if labels:
         version_parts.append('+')
         version_parts.append(".".join(labels))
6f63c0a5
 
00152bee
     return "".join(version_parts)
6f63c0a5
 
 
00152bee
 def get_version_from_git():
     try:
         p = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'],
                              cwd=distr_root,
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     except OSError:
         return
     if p.wait() != 0:
         return
     if not os.path.samefile(p.communicate()[0].decode().rstrip('\n'),
                             distr_root):
         # The top-level directory of the current Git repository is not the same
         # as the root directory of the Kwant distribution: do not extract the
         # version from Git.
         return
 
     # git describe --first-parent does not take into account tags from branches
     # that were merged-in.
     for opts in [['--first-parent'], []]:
6f63c0a5
         try:
00152bee
             p = subprocess.Popen(['git', 'describe', '--long'] + opts,
                                  cwd=distr_root,
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         except OSError:
             return
         if p.wait() == 0:
6f63c0a5
             break
     else:
00152bee
         return
     description = p.communicate()[0].decode().strip('v').rstrip('\n')
6f63c0a5
 
00152bee
     release, dev, git = description.rsplit('-', 2)
     labels = []
     if dev == "0":
         dev = None
6f63c0a5
     else:
00152bee
         labels.append(git)
6f63c0a5
 
00152bee
     try:
         p = subprocess.Popen(['git', 'diff', '--quiet'], cwd=distr_root)
     except OSError:
         labels.append('confused') # This should never happen.
6f63c0a5
     else:
00152bee
         if p.wait() == 1:
             labels.append('dirty')
6f63c0a5
 
00152bee
     return Version(release, dev, labels)
6f63c0a5
 
 
00152bee
 # TODO: change this logic when there is a git pretty-format
 #       that gives the same output as 'git describe'.
 #       Currently we can only tell the tag the current commit is
 #       pointing to, or its hash (with no version info)
 #       if it is not tagged.
 def get_version_from_git_archive(version_info):
     try:
         refnames = version_info['refnames']
         git_hash = version_info['git_hash']
     except KeyError:
         # These fields are not present if we are running from an sdist.
         # Execution should never reach here, though
         return None
 
     if git_hash.startswith('$Format') or refnames.startswith('$Format'):
         # variables not expanded during 'git archive'
         return None
 
     VTAG = 'tag: v'
     refs = set(r.strip() for r in refnames.split(","))
     version_tags = set(r[len(VTAG):] for r in refs if r.startswith(VTAG))
     if version_tags:
         release, *_ = sorted(version_tags)  # prefer e.g. "2.0" over "2.0rc1"
         return Version(release, dev=None, labels=None)
6f63c0a5
     else:
00152bee
         return Version('unknown', dev=None, labels=[f'g{git_hash}'])
6f63c0a5
 
 
00152bee
 __version__ = get_version()
6f63c0a5
 
00152bee
 # The following section defines a module global 'cmdclass',
 # which can be used from setup.py. The 'package_name' and
 # '__version__' module globals are used (but not modified).
6f63c0a5
 
00152bee
 def _write_version(fname):
     # This could be a hard link, so try to delete it first.  Is there any way
     # to do this atomically together with opening?
6f63c0a5
     try:
00152bee
         os.remove(fname)
     except OSError:
6f63c0a5
         pass
00152bee
     with open(fname, 'w') as f:
         f.write("# This file has been created by setup.py.\n"
                 "version = '{}'\n".format(__version__))
6f63c0a5
 
 
00152bee
 class _build(build_orig):
     def run(self):
         super().run()
         _write_version(os.path.join(self.build_lib, package_name,
                                     STATIC_VERSION_FILE))
 
 
 class _sdist(sdist_orig):
     def make_release_tree(self, base_dir, files):
         super().make_release_tree(base_dir, files)
         _write_version(os.path.join(base_dir, package_name,
                                     STATIC_VERSION_FILE))
6f63c0a5
 
 
00152bee
 cmdclass = dict(sdist=_sdist, build=_build)