blob: 5f4a3e470e68a9d0a03e36639cb1cc51f3254429 [file] [log] [blame]
# Copyright 2019,2020 NXP
#
# SPDX-License-Identifier: Apache-2.0
#
"""
Utility to generate version information files.
This file reads, version_info.txt from input directory and generates
files invarious formats.
Input:
Param 1: Directory where version_info.txt is kept
Param 2: Output file[s] with extension.
For generating with different date set environment
varaible VERSION_INFO_DATE.
e.g. SET VERSION_INFO_DATE=2019.02.01
version_info.txt needs these inputs:
# Product
prod_name = "HostLib"
prod_desc = "Host Libary"
# Programming language specific customizations
lang_c_prefix = prod_name.upper()
lang_namespace = "nxp::HostLib"
# Version
v_major = "01"
v_minor = "01"
v_dev = "01"
v_meta = "" # Meta string identifier
# Maturity of library. Alpha, Beta, Production
maturity = "A"
"""
import logging
import os
import sys
import time
from string import Template
log = logging.getLogger("version_update")
logging.basicConfig(level=logging.DEBUG)
# Version of this python script.
__VUP_VERSION__ = '2019.01.17_00'
# Version Log of this file:
#
#
# 2019.01.17_00 : PGh : Added _VER_MAJOR_MINOR and _VER_MAJOR_MINOR_DEV
# :
# 2019.01.12_00 : PGh : Re-Write based on old version_info.py
#
COPYRIGHTS = """Copyright 2019,2020 NXP
This software is owned or controlled by NXP and may only be used
strictly in accordance with the applicable license terms. By expressly
accepting such terms or by downloading, installing, activating and/or
otherwise using the software, you are agreeing that you have read, and
that you agree to comply with and are bound by, such license terms. If
you do not agree to be bound by the applicable license terms, then you
may not retain, install, activate or otherwise use the software.
"""
class KVINFO: # pylint: disable=too-few-public-methods
"""
Version info variable structure
"""
PROD_NAME = "prod_name"
PROD_DESC = "prod_desc"
#
# Programming language specific customizations
LANG_C_PREFIX = "lang_c_prefix"
LANG_NAMESPACE = "lang_namespace"
#
# Version
V_MAJOR = "v_major"
V_MINOR = "v_minor"
V_DEV = "v_dev"
V_META = "v_meta"
# Maturity of library. Alpha, Beta, Production
MATURITY = "maturity"
DEF_NAME = {
KVINFO.V_MAJOR: "VER_MAJOR",
KVINFO.V_MINOR: "VER_MINOR",
KVINFO.V_DEV: "VER_DEV",
}
class VersionInformation:
"""
Version information
"""
def __init__(self, vinfo, raw_contents):
self.vinfo = vinfo
self.raw_contents = raw_contents
self.values = None
def parse(self):
self.values = {}
self._parse_values()
self._parse_date_time_meta()
print(" v%s" % self.values['ver_date_string'])
def generate_files(self, file_names):
"""
Generate files
:param file_names: list of files
:return: None
"""
for file_name in file_names:
generator_fn = None
if file_name.endswith(".h"):
generator_fn = self.generate_h_output
elif file_name.endswith(".cs"):
generator_fn = self.generate_cs_output
elif file_name.endswith(".doxyfile"):
generator_fn = self.generate_doxy_output
elif file_name.endswith(".org"):
generator_fn = self.generate_org_output
elif file_name.endswith(".sh"):
generator_fn = self.generate_sh_output
elif file_name.endswith(".nsh"):
generator_fn = self.generate_nsh_output
elif file_name.endswith(".py"):
generator_fn = self.generate_py_output
elif file_name.endswith(".rst.txt"):
generator_fn = self.generate_rst_output
else:
raise Exception("Unsupported extension '%s'" % (file_name,))
if generator_fn:
with open(file_name, "w") as f:
generator_fn(f)
def _parse_values(self):
"""
Parse version info values
:return: None
"""
self.values["i_major"] = int(self.vinfo[KVINFO.V_MAJOR])
self.values["i_minor"] = int(self.vinfo[KVINFO.V_MINOR])
self.values["i_dev"] = int(self.vinfo[KVINFO.V_DEV])
self.values["v_major"] = self.vinfo[KVINFO.V_MAJOR]
self.values["v_minor"] = self.vinfo[KVINFO.V_MINOR]
self.values["v_dev"] = self.vinfo[KVINFO.V_DEV]
self.values["maturity"] = self.vinfo[KVINFO.MATURITY]
self.values["lang_c_prefix"] = self.vinfo[KVINFO.LANG_C_PREFIX]
self.values["prod_name"] = self.vinfo[KVINFO.PROD_NAME]
self.values["cs_namespace"] = self.vinfo[KVINFO.LANG_C_PREFIX]
self.values['i_major_minor'] = 0 \
+ (int(self.vinfo[KVINFO.V_MAJOR]) * 10000) \
+ (int(self.vinfo[KVINFO.V_MINOR]))
self.values['i_major_minor_dev'] = 0 \
+ (int(self.vinfo[KVINFO.V_MAJOR]) * 10000 * 10000) \
+ (int(self.vinfo[KVINFO.V_MINOR]) * 10000) \
+ (int(self.vinfo[KVINFO.V_DEV]))
def _parse_date_time_meta(self):
"""
Parse time stamp
:return: None
"""
# Timestamp
local_time = time.localtime()
datetime_str = os.getenv("VERSION_INFO_DATE")
if datetime_str:
log.warning("Over-riding datetime as '%s' because "
"environment varaible 'VERSION_INFO_DATE' is set", (datetime_str,))
elif self.vinfo[KVINFO.MATURITY] == 'B':
datetime_str = time.strftime("%Y%m%d", local_time)
elif self.vinfo[KVINFO.MATURITY] == 'A':
datetime_str = time.strftime("%Y%m%d", local_time) + "_" + \
time.strftime("%H%M", local_time)
elif self.vinfo[KVINFO.MATURITY] == 'P':
datetime_str = ''
else:
raise Exception("Invalid maturity='%s'. Expect either 'A', 'B' or 'P'"
% (self.vinfo[KVINFO.MATURITY],))
if len(datetime_str) > 0:
uc_datetime_str = '_' + datetime_str
else:
uc_datetime_str = ''
# ID String
if len(self.vinfo[KVINFO.V_META]) > 0:
uc_v_meta = "_" + self.vinfo[KVINFO.V_META]
else:
uc_v_meta = ""
self.values["ver_date_string"] = \
"%02d.%02d.%02d%s%s" % (
self.values["i_major"],
self.values["i_minor"],
self.values["i_dev"],
uc_datetime_str, uc_v_meta)
self.values["ver_date_string_u"] = \
"%02d_%02d_%02d%s%s" % (
self.values["i_major"],
self.values["i_minor"],
self.values["i_dev"], uc_datetime_str, uc_v_meta)
@classmethod
def put_comment_c(cls, f_object, raw_contents):
f_object.write("/*")
if isinstance(raw_contents, list):
f_object.write(" %s\n" % (raw_contents[0].rstrip(),))
for l in raw_contents[1:]:
f_object.write(" * %s\n" % (l.rstrip(),))
elif isinstance(raw_contents, str):
f_object.write(" %s\n" % (raw_contents.split("\n")[0].rstrip(),))
for l in raw_contents.split("\n")[1:]:
f_object.write(" * %s\n" % (l.rstrip(),))
f_object.write(" */\n\n")
# def dumpraw_contents_c(self, fObject):
# self.put_comment_c()
# fObject.write("\n/* %s" % ("______GENERATED_FROM____version_info.txt",))
# for l in self.raw_contents:
# fObject.write("\n * %s" % (l.rstrip(),))
# fObject.write("\n */\n")
def lang_c_get_header_guard(self):
header_guard_def = self.vinfo[KVINFO.LANG_C_PREFIX] + "_VERSION_INFO_H_INCLUDED"
return header_guard_def
def generate_h_output(self, output_f):
self.put_comment_c(output_f, COPYRIGHTS)
header_guard_def = self.lang_c_get_header_guard()
output_f.write("#ifndef " + header_guard_def + "\n")
output_f.write("#define " + header_guard_def + "\n\n")
output_f.write(TEMPLATE_C.substitute(self.values))
# self.putValues_c(o, c_defines)
self.put_comment_c(output_f, self.raw_contents)
output_f.write("#endif /* " + header_guard_def + " */\n")
def generate_doxy_output(self, output_f):
output_f.write(TEMPLATE_DOXYGEN.substitute(self.values))
def generate_sh_output(self, output_f):
output_f.write(TEMPLATE_SH.substitute(self.values))
def generate_org_output(self, output_f):
output_f.write(TEMPLATE_ORG.substitute(self.values))
def generate_nsh_output(self, output_f):
output_f.write(TEMPLATE_NSH.substitute(self.values))
def generate_cs_output(self, output_f):
self.put_comment_c(output_f, COPYRIGHTS)
output_f.write(TEMPLATE_CS.substitute(self.values))
# self.putValues_c(output_f, c_defines)
self.put_comment_c(output_f, self.raw_contents)
def generate_py_output(self, output_f):
output_f.write(TEMPLATE_PY.substitute(self.values))
def generate_rst_output(self, output_f):
output_f.write(TEMPLATE_RST.substitute(self.values))
# def generate_cs_file_string(cs_namespace, product_name, sw_vstr, v_major,
# v_minor, v_dev, maturity, rd_lib_stream):
# s = []
#
# return "\n".join(s)
TEMPLATE_C = Template("""
/* clang-format off */
#define ${lang_c_prefix}_PROD_NAME "${prod_name}"
#define ${lang_c_prefix}_VER_STRING_NUM "v${ver_date_string}"
#define ${lang_c_prefix}_PROD_NAME_VER_FULL "${prod_name}_v${ver_date_string}"
#define ${lang_c_prefix}_VER_MAJOR (${i_major}u)
#define ${lang_c_prefix}_VER_MINOR (${i_minor}u)
#define ${lang_c_prefix}_VER_DEV (${i_dev}u)
/* v${v_major}.${v_minor} = ${i_major_minor}u */
#define ${lang_c_prefix}_VER_MAJOR_MINOR ( 0 \\
| (${lang_c_prefix}_VER_MAJOR * 10000u) \\
| (${lang_c_prefix}_VER_MINOR))
/* v${v_major}.${v_minor}.${v_dev} = ${i_major_minor_dev}ULL */
#define ${lang_c_prefix}_VER_MAJOR_MINOR_DEV ( 0 \\
| (${lang_c_prefix}_VER_MAJOR * 10000*10000u) \\
| (${lang_c_prefix}_VER_MINOR * 10000u) \\
| (${lang_c_prefix}_VER_DEV))
/* clang-format on */
""")
TEMPLATE_DOXYGEN = Template("""
PROJECT_NUMBER = "v${v_major}.${v_minor}.${v_dev}"
""")
TEMPLATE_SH = Template("""
${prod_name}_Ver="v${v_major}.${v_minor}.${v_dev}"
${prod_name}_Ver_Full="v${ver_date_string}"
""")
TEMPLATE_ORG = Template("""
#+MACRO: ${prod_name}_Ver v${v_major}.${v_minor}.${v_dev}
""")
TEMPLATE_NSH = Template("""
!define ${lang_c_prefix}_PRODUCT_NAME "${prod_name}"
!define ${lang_c_prefix}_VER_STR "v${v_major}.${v_minor}.${v_dev}.00"
!define ${lang_c_prefix}_VER_STR_FULL "v${ver_date_string}"
!define ${lang_c_prefix}_MAJOR "${v_major}"
!define ${lang_c_prefix}_MINOR "${v_minor}"
!define ${lang_c_prefix}_DEV "${v_dev}"
""")
TEMPLATE_CS = Template("""
namespace ${cs_namespace} {
public class ${cs_namespace}_Ver {
/// <summary>
/// Name of the Product
/// </summary>
public const string ProductName = "${prod_name}";
/// <summary>
/// Version String/Description of the Binary
/// </summary>
public const string ProductNameWithVersion = "${prod_name}_v${ver_date_string}";
/// <summary>
/// Version of the binary (with patch as star)
/// </summary>
public const string AssemblyVersion = "${i_major}.${i_minor}.${i_dev}.*";
/// <summary>
/// Assembly file version
/// </summary>
public const string AssemblyFileVersion = "${i_major}.${i_minor}.${i_dev}.0";
}
}
""")
TEMPLATE_RST = Template("""
.. |${lang_c_prefix}_PRODUCT_NAME| replace:: :samp:`${prod_name}`
.. |${lang_c_prefix}_VER_STR| replace:: :samp:`v${v_major}.${v_minor}.${v_dev}`
.. |${lang_c_prefix}_VER_STR_FULL| replace:: :samp:`v${ver_date_string}`
.. |${lang_c_prefix}_MAJOR| replace:: :samp:`${v_major}`
.. |${lang_c_prefix}_MINOR| replace:: :samp:`${v_minor}`
.. |${lang_c_prefix}_DEV| replace:: :samp:`${v_dev}`
""")
TEMPLATE_PY = Template("""
${lang_c_prefix}_PRODUCT_NAME = "${prod_name}"
${lang_c_prefix}_VER_STR = "v${v_major}.${v_minor}.${v_dev}"
${lang_c_prefix}_VER_STR_FULL = "v${ver_date_string}"
${lang_c_prefix}_MAJOR = "${v_major}"
${lang_c_prefix}_MINOR = "${v_minor}"
${lang_c_prefix}_DEV = "${v_dev}"
""")
def usage(message):
print("ERROR!" + message)
print(
"""
Input:
1. Directory where version_info.txt is kept
2. Output files list with supported extensions
Following extensions are supported:
.h, .cs, .doxyfile, .nsh, .org, .py, .rst.txt, .sh,
To override date, set VERSION_INFO_DATE. e.g.
SET VERSION_INFO_DATE=DATE
""")
def get_v_info():
vinfo = {}
raw_contents = []
generator = os.path.relpath(sys.argv[0]) + " (v" + __VUP_VERSION__ + ")"
try:
version_info_file = os.path.relpath(sys.argv[1])
except IndexError:
version_info_file = "."
raw_contents.append("Version Information:")
raw_contents.append("Generated by:")
raw_contents.append(" " + generator)
raw_contents.append("")
raw_contents.append("Do not edit this file. Update:")
raw_contents.append(" " + version_info_file + "/version_info.txt instead.")
try:
exec(compile(open("version_info.txt", "rb").read(), "version_info.txt", 'exec'), {}, vinfo) # pylint: disable=exec-used
with open("version_info.txt", "r") as vf:
raw_contents.extend(vf.readlines())
except IOError:
usage("Missing version_info.txt in " + os.getcwd())
return None
return vinfo, raw_contents
def main_gen_required_file(vinfo_dir, input_files):
print("> vinfo Running on " + vinfo_dir)
the_pwd = os.getcwd()
os.chdir(vinfo_dir)
(vinfo, raw_contents) = get_v_info()
os.chdir(the_pwd)
if vinfo:
o = VersionInformation(vinfo, raw_contents)
o.parse()
o.generate_files(input_files)
def test():
ROOT = '/Users/ing05193/data/n/cm/scripts/vesion_update/'
DEST_F = "/Users/ing05193/data/n/cm/scripts/vesion_update/tst/out_prod_one/prod_one"
main_gen_required_file(
ROOT + "tst/prod_one",
[
DEST_F + ".h",
DEST_F + ".sh",
DEST_F + ".doxyfile",
DEST_F + ".org",
DEST_F + ".nsh",
DEST_F + ".cs",
])
def main():
# if len(sys.argv) == 1:
# test()
if len(sys.argv) >= 3:
main_gen_required_file(sys.argv[1], sys.argv[2:])
else:
usage("Insufficient arguments")
if __name__ == '__main__':
main()