| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| """ |
| This is the global script that set the version information of TVM. |
| This script runs and update all the locations that related to versions |
| |
| List of affected files: |
| - tvm-root/python/tvm/_ffi/libinfo.py |
| - tvm-root/include/tvm/runtime/c_runtime_api.h |
| - tvm-root/conda/recipe/meta.yaml |
| - tvm-root/web/package.json |
| """ |
| import os |
| import re |
| import argparse |
| import logging |
| import subprocess |
| |
| # Modify the following value during release |
| # --------------------------------------------------- |
| # Current version: |
| # We use the version of the incoming release for code |
| # that is under development. |
| # |
| # It is also fallback version to be used when --git-describe |
| # is not invoked, or when the repository does not present the |
| # git tags in a format that this script can use. |
| # |
| # Two tag formats are supported: |
| # - vMAJ.MIN.PATCH (e.g. v0.8.0) or |
| # - vMAJ.MIN.devN (e.g. v0.8.dev0) |
| __version__ = "0.17.dev0" |
| |
| # --------------------------------------------------- |
| |
| PROJ_ROOT = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) |
| |
| |
| def py_str(cstr): |
| return cstr.decode("utf-8") |
| |
| |
| def git_describe_version(): |
| """Get PEP-440 compatible public and local version using git describe. |
| |
| Returns |
| ------- |
| pub_ver: str |
| Public version. |
| |
| local_ver: str |
| Local version (with additional label appended to pub_ver). |
| |
| Notes |
| ----- |
| - We follow PEP 440's convention of public version |
| and local versions. |
| - Only tags conforming to vMAJOR.MINOR.REV (e.g. "v0.7.0") |
| are considered in order to generate the version string. |
| See the use of `--match` in the `git` command below. |
| |
| Here are some examples: |
| |
| - pub_ver = '0.7.0', local_ver = '0.7.0': |
| We are at the 0.7.0 release. |
| - pub_ver = '0.8.dev94', local_ver = '0.8.dev94+g0d07a329e': |
| We are at the 0.8 development cycle. |
| The current source contains 94 additional commits |
| after the most recent tag(v0.7.0), |
| the git short hash tag of the current commit is 0d07a329e. |
| """ |
| cmd = [ |
| "git", |
| "describe", |
| "--tags", |
| "--match", |
| "v[0-9]*.[0-9]*.[0-9]*", |
| "--match", |
| "v[0-9]*.[0-9]*.dev[0-9]*", |
| ] |
| proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=PROJ_ROOT) |
| (out, _) = proc.communicate() |
| |
| if proc.returncode != 0: |
| msg = py_str(out) |
| if msg.find("not a git repository") != -1: |
| return __version__, __version__ |
| logging.warning("git describe: %s, use %s", msg, __version__) |
| return __version__, __version__ |
| describe = py_str(out).strip() |
| arr_info = describe.split("-") |
| |
| # Remove the v prefix, mainly to be robust |
| # to the case where v is not presented as well. |
| if arr_info[0].startswith("v"): |
| arr_info[0] = arr_info[0][1:] |
| |
| # hit the exact tag |
| if len(arr_info) == 1: |
| return arr_info[0], arr_info[0] |
| |
| if len(arr_info) != 3: |
| logging.warning("Invalid output from git describe %s", describe) |
| return __version__, __version__ |
| |
| dev_pos = arr_info[0].find(".dev") |
| |
| # Development versions: |
| # The code will reach this point in case it can't match a full release version, such as v0.7.0. |
| # |
| # 1. in case the last known label looks like vMAJ.MIN.devN e.g. v0.8.dev0, we use |
| # the current behaviour of just using vMAJ.MIN.devNNNN+gGIT_REV |
| if dev_pos != -1: |
| dev_version = arr_info[0][: arr_info[0].find(".dev")] |
| # 2. in case the last known label looks like vMAJ.MIN.PATCH e.g. v0.8.0 |
| # then we just carry on with a similar version to what git describe provides, which is |
| # vMAJ.MIN.PATCH.devNNNN+gGIT_REV |
| else: |
| dev_version = arr_info[0] |
| |
| pub_ver = "%s.dev%s" % (dev_version, arr_info[1]) |
| local_ver = "%s+%s" % (pub_ver, arr_info[2]) |
| return pub_ver, local_ver |
| |
| |
| # Implementations |
| def update(file_name, pattern, repl, dry_run=False): |
| update = [] |
| hit_counter = 0 |
| need_update = False |
| with open(file_name) as file: |
| for l in file: |
| result = re.findall(pattern, l) |
| if result: |
| assert len(result) == 1 |
| hit_counter += 1 |
| if result[0] != repl: |
| l = re.sub(pattern, repl, l) |
| need_update = True |
| print("%s: %s -> %s" % (file_name, result[0], repl)) |
| else: |
| print("%s: version is already %s" % (file_name, repl)) |
| |
| update.append(l) |
| if hit_counter != 1: |
| raise RuntimeError("Cannot find version in %s" % file_name) |
| |
| if need_update and not dry_run: |
| with open(file_name, "w") as output_file: |
| for l in update: |
| output_file.write(l) |
| |
| |
| def sync_version(pub_ver, local_ver, dry_run): |
| """Synchronize version.""" |
| # python uses the PEP-440: local version |
| update( |
| os.path.join(PROJ_ROOT, "python", "tvm", "_ffi", "libinfo.py"), |
| r"(?<=__version__ = \")[.0-9a-z\+]+", |
| local_ver, |
| dry_run, |
| ) |
| # Use public version for other parts for now |
| # Note that full git hash is already available in libtvm |
| # C++ header |
| update( |
| os.path.join(PROJ_ROOT, "include", "tvm", "runtime", "c_runtime_api.h"), |
| r'(?<=TVM_VERSION ")[.0-9a-z\+]+', |
| pub_ver, |
| dry_run, |
| ) |
| # conda |
| update( |
| os.path.join(PROJ_ROOT, "conda", "recipe", "meta.yaml"), |
| r"(?<=version = ')[.0-9a-z\+]+", |
| pub_ver, |
| dry_run, |
| ) |
| # web |
| # change to pre-release convention by npm |
| dev_pos = pub_ver.find(".dev") |
| npm_ver = pub_ver if dev_pos == -1 else "%s.0-%s" % (pub_ver[:dev_pos], pub_ver[dev_pos + 1 :]) |
| update( |
| os.path.join(PROJ_ROOT, "web", "package.json"), |
| r'(?<="version": ")[.0-9a-z\-\+]+', |
| npm_ver, |
| dry_run, |
| ) |
| |
| |
| def main(): |
| logging.basicConfig(level=logging.INFO) |
| parser = argparse.ArgumentParser(description="Detect and synchronize version.") |
| parser.add_argument( |
| "--print-version", |
| action="store_true", |
| help="Print version to the command line. No changes is applied to files.", |
| ) |
| parser.add_argument( |
| "--git-describe", |
| action="store_true", |
| help="Use git describe to generate development version.", |
| ) |
| parser.add_argument("--dry-run", action="store_true") |
| |
| opt = parser.parse_args() |
| pub_ver, local_ver = __version__, __version__ |
| if opt.git_describe: |
| pub_ver, local_ver = git_describe_version() |
| if opt.print_version: |
| print(local_ver) |
| else: |
| sync_version(pub_ver, local_ver, opt.dry_run) |
| |
| |
| if __name__ == "__main__": |
| main() |