6 Release version entry format : <major>.<minor>.<patch>
8 Stable release version output format : <major>.<minor>.<patch>
9 Development release version output format: <major>.<minor>.<patch>~dev.<YYYYmmdd.HHMM>
11 The patch version of a development release should be the same as the
12 next stable release patch number. The string "~dev." and the
13 committer date will be added.
17 Entry version Entry collection Output
18 2.44.1 stable => 2.44.1
19 2.44.2 development => 2.44.2~dev.20200704.1652
20 2.44.2 stable => 2.44.2
21 2.44.3 development => 2.44.3~dev.20200824.1337
25 from datetime import datetime
27 from pathlib import Path
31 from typing import Tuple
33 CHANGELOG = Path(__file__).parent / "CHANGELOG.md"
34 assert CHANGELOG.exists(), "CHANGELOG.md file missing"
36 def get_version() -> Tuple[int, int, int, str]:
38 Derive a Graphviz version from the changelog information.
40 Returns a tuple of major version, minor version, patch version,
41 "stable"/"development".
44 # is this a development revision (as opposed to a stable release)?
45 is_development = False
47 with open(CHANGELOG, encoding="utf-8") as f:
50 # is this a version heading?
51 m = re.match(r"## \[(?P<heading>[^\]]*)\]", line)
54 heading = m.group("heading")
56 # is this the leading unreleased heading of a development version?
57 UNRELEASED_PREFIX = "Unreleased ("
58 if heading.startswith(UNRELEASED_PREFIX):
60 heading = heading[len(UNRELEASED_PREFIX):]
62 # extract the version components
63 m = re.match(r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)", heading)
65 raise RuntimeError("non-version ## heading encountered before seeing a "
68 major = int(m.group("major"))
69 minor = int(m.group("minor"))
70 patch = int(m.group("patch"))
74 # we read the whole changelog without finding a version
75 raise RuntimeError("no version found")
82 return major, minor, patch, coll
84 graphviz_date_format = "%Y%m%d.%H%M"
85 iso_date_format = "%Y-%m-%d %H:%M:%S"
87 parser = argparse.ArgumentParser(description="Generate Graphviz version.")
88 parser.add_argument("--committer-date-iso",
91 const=iso_date_format,
92 help="Print ISO formatted committer date in UTC instead of version"
94 parser.add_argument("--committer-date-graphviz",
97 const=graphviz_date_format,
98 help="Print graphviz special formatted committer date in UTC "
101 parser.add_argument("--major",
103 action="store_const",
105 help="Print major version")
106 parser.add_argument("--minor",
108 action="store_const",
110 help="Print minor version")
111 parser.add_argument("--patch",
113 action="store_const",
115 help="Print patch version")
116 parser.add_argument("--definition",
118 help="Print a C-style preprocessor #define")
119 parser.add_argument("--output",
120 type=argparse.FileType("wt", encoding="ascii"),
122 help="Path to write result to")
124 args = parser.parse_args()
126 date_format = args.date_format or graphviz_date_format
128 major_version, minor_version, patch_version, collection = get_version()
130 if collection == "development":
131 patch_version = f"{patch_version}~dev"
133 patch_version = str(patch_version)
135 if not patch_version.isnumeric() or args.date_format:
136 os.environ["TZ"] = "UTC"
138 committer_date = datetime.strptime(
139 subprocess.check_output(
146 "--date=format-local:%Y-%m-%d %H:%M:%S"
148 cwd=os.path.abspath(os.path.dirname(__file__)),
149 universal_newlines=True,
152 ).strftime(date_format)
153 except (subprocess.CalledProcessError, FileNotFoundError):
154 sys.stderr.write("Warning: build not started in a Git clone, or Git is not "
155 "installed: setting version date to 0.\n")
158 if not patch_version.isnumeric():
159 # Non-numerical patch version; add committer date
160 patch_version += f".{committer_date}"
164 args.output.write(f'#define BUILDDATE "{committer_date}"\n')
166 args.output.write(f"{committer_date}\n")
167 elif args.component == "major":
169 args.output.write(f'#define VERSION_MAJOR "{major_version}"\n')
171 args.output.write(f"{major_version}\n")
172 elif args.component == "minor":
174 args.output.write(f'#define VERSION_MINOR "{minor_version}"\n')
176 args.output.write(f"{minor_version}\n")
177 elif args.component == "patch":
179 args.output.write(f'#define VERSION_PATCH "{patch_version}"\n')
181 args.output.write(f"{patch_version}\n")
184 args.output.write(f'#define VERSION "{major_version}.{minor_version}.'
185 f'{patch_version}"\n')
187 args.output.write(f"{major_version}.{minor_version}.{patch_version}\n")