import re import subprocess import os import sys import datetime import io import locale system_encoding = locale.getpreferredencoding(False) sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding=system_encoding) # 语义化版本 https://semver.org/lang/zh-CN/spec/v2.0.0.html default_major_version = 0 # 主版本号 defalut_minor_version = 1 # 次版本号 defalut_revision_version = 0 # 修订版本号 defalut_pre_release_version = "" # 预发行版本号 def get_version_from_file(path): """从项目根目录的version.txt文件中读取版本信息""" version_file = os.path.join(path, "version.txt") # 默认版本号 versions = { "major_version": default_major_version, "minor_version": defalut_minor_version, "revision_version": defalut_revision_version, "pre_release_version": defalut_pre_release_version } # 检查文件是否存在 if not os.path.exists(version_file): # 创建默认version.txt文件 try: with open(version_file, 'w', encoding='utf-8') as f: f.write(f"major_version={versions['major_version']}\n") f.write(f"minor_version={versions['minor_version']}\n") f.write(f"revision_version={versions['revision_version']}\n") f.write( f"pre_release_version={versions['pre_release_version']}\n") print(f"已在项目根目录创建默认的version.txt文件") except Exception as e: print(f"创建version.txt文件失败: {str(e)}") return versions # 读取version.txt文件 try: with open(version_file, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if '=' in line: key, value = line.split('=', 1) key = key.strip() value = value.strip() if key in versions: # 处理整数类型的版本号 if key != "pre_release_version": try: versions[key] = int(value) except ValueError: raise ValueError( f"版本号解析错误: {key}必须是整数,当前值为'{value}'") else: versions[key] = value except Exception as e: raise Exception(f"解析version.txt文件失败: {str(e)}") return versions def generate_version_file(name, path, file_version, file_build_time): if not os.path.exists(f"{path}/Core/App/AutoGen/"): os.makedirs(f"{path}/Core/App/AutoGen/", exist_ok=True) with open(f"{path}/Core/App/AutoGen/autogen_version.c", "w", encoding="utf-8") as f: f.write("// 这个文件是自动生成的\n") f.write("// 对它作任何修改都将在编译前由程序自动覆盖\n") f.write("// 不需要修改这个文件的内容\n") f.write("\n") f.write(f'const char *g_name = "{name}";\n') f.write(f'const char *g_version = "{file_version}";\n') f.write(f'const char *g_buildTime = "{file_build_time}";\n') def generate_version(path): # 从version.txt获取版本信息 try: versions = get_version_from_file(path) major_version = versions["major_version"] minor_version = versions["minor_version"] revision_version = versions["revision_version"] pre_release_version = versions["pre_release_version"] except Exception as e: print(f"错误: {str(e)}") sys.exit(-1) generated_version = f"{major_version}.{minor_version}.{revision_version}" git_commit_id = "" # 检查是否在Git仓库中 git_process = subprocess.run( ["git", "rev-parse"], capture_output=True, text=True, cwd=path) if git_process.returncode == 0: # 检查是否有未提交的更改 commit_process = subprocess.run( ["git", "status", "-s"], capture_output=True, text=True, cwd=path) if commit_process.stdout != "": # 有未提交的更改,获取当前commit id并加* process = subprocess.run( ["git", "rev-parse", "--short", "HEAD"], capture_output=True, text=True, cwd=path) git_commit_id = process.stdout.strip() + "*" else: # 没有未提交的更改,直接获取当前commit id process = subprocess.run(["git", "rev-parse", "--short=8", "HEAD"], capture_output=True, text=True, cwd=path) git_commit_id = process.stdout.strip() # 根据 pre_release_version 和 git_commit_id 更新版本号 if pre_release_version and git_commit_id: generated_version += f"-{pre_release_version}+{git_commit_id}" elif pre_release_version: generated_version += f"-{pre_release_version}" elif git_commit_id: generated_version += f"+{git_commit_id}" # https://regex101.com/r/Ly7O1x/3/ semver_regex = ( r"^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[" r"a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[" r"0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*|\*|.*\*))?$") if not re.match(semver_regex, generated_version): print(f"生成的版本号 {generated_version} 不符合语义化版本规范") return generated_version if __name__ == "__main__": try: name = sys.argv[1] if len(sys.argv) > 1 else 'McuProgram' project_directory = sys.argv[2] if len(sys.argv) > 2 else '.' if not os.path.isdir(project_directory): raise Exception("Invalid project directory") version = generate_version(project_directory) build_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") generate_version_file( name, project_directory, version, build_time) print(f"Program name: {name}") print(f"Version: {version}") print(f"Build time: {build_time}") sys.exit(0) except Exception as e: print(e) sys.exit(-1)