1"""
2Generate header file with macros defining MicroPython version info.
3
4This script works with Python 2.6, 2.7, 3.3 and 3.4.
5"""
6
7from __future__ import print_function
8
9import sys
10import os
11import datetime
12import subprocess
13
14
15def get_version_info_from_git():
16    # Python 2.6 doesn't have check_output, so check for that
17    try:
18        subprocess.check_output
19        subprocess.check_call
20    except AttributeError:
21        return None
22
23    # Note: git describe doesn't work if no tag is available
24    try:
25        git_tag = subprocess.check_output(
26            ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
27            stderr=subprocess.STDOUT,
28            universal_newlines=True,
29        ).strip()
30    except subprocess.CalledProcessError as er:
31        if er.returncode == 128:
32            # git exit code of 128 means no repository found
33            return None
34        git_tag = ""
35    except OSError:
36        return None
37    try:
38        git_hash = subprocess.check_output(
39            ["git", "rev-parse", "--short", "HEAD"],
40            stderr=subprocess.STDOUT,
41            universal_newlines=True,
42        ).strip()
43    except subprocess.CalledProcessError:
44        git_hash = "unknown"
45    except OSError:
46        return None
47
48    try:
49        # Check if there are any modified files.
50        subprocess.check_call(
51            ["git", "diff", "--no-ext-diff", "--quiet", "--exit-code"], stderr=subprocess.STDOUT
52        )
53        # Check if there are any staged files.
54        subprocess.check_call(
55            ["git", "diff-index", "--cached", "--quiet", "HEAD", "--"], stderr=subprocess.STDOUT
56        )
57    except subprocess.CalledProcessError:
58        git_hash += "-dirty"
59    except OSError:
60        return None
61
62    return git_tag, git_hash
63
64
65def get_version_info_from_docs_conf():
66    with open(os.path.join(os.path.dirname(sys.argv[0]), "..", "docs", "conf.py")) as f:
67        for line in f:
68            if line.startswith("version = release = '"):
69                ver = line.strip().split(" = ")[2].strip("'")
70                git_tag = "v" + ver
71                return git_tag, "<no hash>"
72    return None
73
74
75def make_version_header(filename):
76    # Get version info using git, with fallback to docs/conf.py
77    info = get_version_info_from_git()
78    if info is None:
79        info = get_version_info_from_docs_conf()
80
81    git_tag, git_hash = info
82
83    build_date = datetime.date.today()
84    if "SOURCE_DATE_EPOCH" in os.environ:
85        build_date = datetime.datetime.utcfromtimestamp(
86            int(os.environ["SOURCE_DATE_EPOCH"])
87        ).date()
88
89    # Generate the file with the git and version info
90    file_data = """\
91// This file was generated by py/makeversionhdr.py
92#define MICROPY_GIT_TAG "%s"
93#define MICROPY_GIT_HASH "%s"
94#define MICROPY_BUILD_DATE "%s"
95""" % (
96        git_tag,
97        git_hash,
98        build_date.strftime("%Y-%m-%d"),
99    )
100
101    # Check if the file contents changed from last time
102    write_file = True
103    if os.path.isfile(filename):
104        with open(filename, "r") as f:
105            existing_data = f.read()
106        if existing_data == file_data:
107            write_file = False
108
109    # Only write the file if we need to
110    if write_file:
111        print("GEN %s" % filename)
112        with open(filename, "w") as f:
113            f.write(file_data)
114
115
116if __name__ == "__main__":
117    make_version_header(sys.argv[1])
118