|
1 |
| -# This file is part of 'miniver': https://github.com/jbweston/miniver |
2 |
| -# |
3 |
| -from __future__ import annotations |
| 1 | +from pathlib import Path |
4 | 2 |
|
5 |
| -import os |
6 |
| -import subprocess |
7 |
| -from collections import namedtuple |
| 3 | +import versioningit |
8 | 4 |
|
9 |
| -from setuptools.command.build_py import build_py as build_py_orig |
10 |
| -from setuptools.command.sdist import sdist as sdist_orig |
11 |
| - |
12 |
| -Version = namedtuple("Version", ("release", "dev", "labels")) |
13 |
| - |
14 |
| -# No public API |
15 |
| -__all__: list[str] = [] |
16 |
| - |
17 |
| -package_root = os.path.dirname(os.path.realpath(__file__)) |
18 |
| -package_name = os.path.basename(package_root) |
19 |
| -distr_root = os.path.dirname(package_root) |
20 |
| -# If the package is inside a "src" directory the |
21 |
| -# distribution root is 1 level up. |
22 |
| -if os.path.split(distr_root)[1] == "src": |
23 |
| - _package_root_inside_src = True |
24 |
| - distr_root = os.path.dirname(distr_root) |
25 |
| -else: |
26 |
| - _package_root_inside_src = False |
27 |
| - |
28 |
| -STATIC_VERSION_FILE = "_static_version.py" |
29 |
| - |
30 |
| - |
31 |
| -def get_version(version_file=STATIC_VERSION_FILE): |
32 |
| - version_info = get_static_version_info(version_file) |
33 |
| - version = version_info["version"] |
34 |
| - if version == "__use_git__": |
35 |
| - version = get_version_from_git() |
36 |
| - if not version: |
37 |
| - version = get_version_from_git_archive(version_info) |
38 |
| - if not version: |
39 |
| - version = Version("unknown", None, None) |
40 |
| - return pep440_format(version) |
41 |
| - else: |
42 |
| - return version |
43 |
| - |
44 |
| - |
45 |
| -def get_static_version_info(version_file=STATIC_VERSION_FILE): |
46 |
| - version_info = {} |
47 |
| - with open(os.path.join(package_root, version_file), "rb") as f: |
48 |
| - exec(f.read(), {}, version_info) |
49 |
| - return version_info |
50 |
| - |
51 |
| - |
52 |
| -def version_is_from_git(version_file=STATIC_VERSION_FILE): |
53 |
| - return get_static_version_info(version_file)["version"] == "__use_git__" |
54 |
| - |
55 |
| - |
56 |
| -def pep440_format(version_info): |
57 |
| - release, dev, labels = version_info |
58 |
| - |
59 |
| - version_parts = [release] |
60 |
| - if dev: |
61 |
| - if release.endswith("-dev") or release.endswith(".dev"): |
62 |
| - version_parts.append(dev) |
63 |
| - else: # prefer PEP440 over strict adhesion to semver |
64 |
| - version_parts.append(f".dev{dev}") |
65 |
| - |
66 |
| - if labels: |
67 |
| - version_parts.append("+") |
68 |
| - version_parts.append(".".join(labels)) |
69 |
| - |
70 |
| - return "".join(version_parts) |
71 |
| - |
72 |
| - |
73 |
| -def get_version_from_git(): |
74 |
| - try: |
75 |
| - p = subprocess.Popen( |
76 |
| - ["git", "rev-parse", "--show-toplevel"], |
77 |
| - cwd=distr_root, |
78 |
| - stdout=subprocess.PIPE, |
79 |
| - stderr=subprocess.PIPE, |
80 |
| - ) |
81 |
| - except OSError: |
82 |
| - return |
83 |
| - if p.wait() != 0: |
84 |
| - return |
85 |
| - if not os.path.samefile(p.communicate()[0].decode().rstrip("\n"), distr_root): |
86 |
| - # The top-level directory of the current Git repository is not the same |
87 |
| - # as the root directory of the distribution: do not extract the |
88 |
| - # version from Git. |
89 |
| - return |
90 |
| - |
91 |
| - # git describe --first-parent does not take into account tags from branches |
92 |
| - # that were merged-in. The '--long' flag gets us the 'dev' version and |
93 |
| - # git hash, '--always' returns the git hash even if there are no tags. |
94 |
| - for opts in [["--first-parent"], []]: |
95 |
| - try: |
96 |
| - p = subprocess.Popen( |
97 |
| - ["git", "describe", "--long", "--always", "--tags"] + opts, |
98 |
| - cwd=distr_root, |
99 |
| - stdout=subprocess.PIPE, |
100 |
| - stderr=subprocess.PIPE, |
101 |
| - ) |
102 |
| - except OSError: |
103 |
| - return |
104 |
| - if p.wait() == 0: |
105 |
| - break |
106 |
| - else: |
107 |
| - return |
108 |
| - |
109 |
| - description = ( |
110 |
| - p.communicate()[0] |
111 |
| - .decode() |
112 |
| - .strip("v") # Tags can have a leading 'v', but the version should not |
113 |
| - .rstrip("\n") |
114 |
| - .rsplit("-", 2) # Split the latest tag, commits since tag, and hash |
115 |
| - ) |
116 |
| - |
117 |
| - try: |
118 |
| - release, dev, git = description |
119 |
| - except ValueError: # No tags, only the git hash |
120 |
| - # prepend 'g' to match with format returned by 'git describe' |
121 |
| - git = "g{}".format(*description) |
122 |
| - release = "unknown" |
123 |
| - dev = None |
124 |
| - |
125 |
| - labels = [] |
126 |
| - if dev == "0": |
127 |
| - dev = None |
128 |
| - else: |
129 |
| - labels.append(git) |
130 |
| - |
131 |
| - try: |
132 |
| - p = subprocess.Popen(["git", "diff", "--quiet"], cwd=distr_root) |
133 |
| - except OSError: |
134 |
| - labels.append("confused") # This should never happen. |
135 |
| - else: |
136 |
| - if p.wait() == 1: |
137 |
| - labels.append("dirty") |
138 |
| - |
139 |
| - return Version(release, dev, labels) |
140 |
| - |
141 |
| - |
142 |
| -# TODO: change this logic when there is a git pretty-format |
143 |
| -# that gives the same output as 'git describe'. |
144 |
| -# Currently we can only tell the tag the current commit is |
145 |
| -# pointing to, or its hash (with no version info) |
146 |
| -# if it is not tagged. |
147 |
| -def get_version_from_git_archive(version_info): |
148 |
| - try: |
149 |
| - refnames = version_info["refnames"] |
150 |
| - git_hash = version_info["git_hash"] |
151 |
| - except KeyError: |
152 |
| - # These fields are not present if we are running from an sdist. |
153 |
| - # Execution should never reach here, though |
154 |
| - return None |
155 |
| - |
156 |
| - if git_hash.startswith("$Format") or refnames.startswith("$Format"): |
157 |
| - # variables not expanded during 'git archive' |
158 |
| - return None |
159 |
| - |
160 |
| - VTAG = "tag: v" |
161 |
| - refs = {r.strip() for r in refnames.split(",")} |
162 |
| - version_tags = {r[len(VTAG) :] for r in refs if r.startswith(VTAG)} |
163 |
| - if version_tags: |
164 |
| - release, *_ = sorted(version_tags) # prefer e.g. "2.0" over "2.0rc1" |
165 |
| - return Version(release, dev=None, labels=None) |
166 |
| - else: |
167 |
| - return Version("unknown", dev=None, labels=[f"g{git_hash}"]) |
168 |
| - |
169 |
| - |
170 |
| -__version__ = get_version() |
171 |
| - |
172 |
| - |
173 |
| -# The following section defines a module global 'cmdclass', |
174 |
| -# which can be used from setup.py. The 'package_name' and |
175 |
| -# '__version__' module globals are used (but not modified). |
176 |
| - |
177 |
| - |
178 |
| -def _write_version(fname): |
179 |
| - # This could be a hard link, so try to delete it first. Is there any way |
180 |
| - # to do this atomically together with opening? |
181 |
| - try: |
182 |
| - os.remove(fname) |
183 |
| - except OSError: |
184 |
| - pass |
185 |
| - with open(fname, "w") as f: |
186 |
| - f.write( |
187 |
| - "# This file has been created by setup.py.\n" |
188 |
| - "version = '{}'\n".format(__version__) |
189 |
| - ) |
190 |
| - |
191 |
| - |
192 |
| -class _build_py(build_py_orig): |
193 |
| - def run(self): |
194 |
| - super().run() |
195 |
| - _write_version(os.path.join(self.build_lib, package_name, STATIC_VERSION_FILE)) |
196 |
| - |
197 |
| - |
198 |
| -class _sdist(sdist_orig): |
199 |
| - def make_release_tree(self, base_dir, files): |
200 |
| - super().make_release_tree(base_dir, files) |
201 |
| - if _package_root_inside_src: |
202 |
| - p = os.path.join("src", package_name) |
203 |
| - else: |
204 |
| - p = package_name |
205 |
| - _write_version(os.path.join(base_dir, p, STATIC_VERSION_FILE)) |
206 |
| - |
207 |
| - |
208 |
| -cmdclass = {"sdist": _sdist, "build_py": _build_py} |
| 5 | +REPO_ROOT = Path(__file__).parent.parent |
| 6 | +__version__ = versioningit.get_version(project_dir=REPO_ROOT) |
0 commit comments