1、加入实现代码
2、配置项目
This commit is contained in:
20
GitVer.sln
20
GitVer.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.37203.1 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GitVer", "GitVer\GitVer.vcxproj", "{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GitVer", "GitVer\GitVer.vcxproj", "{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -13,19 +13,19 @@ Global
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Debug|x64.Build.0 = Debug|x64
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Debug|x86.Build.0 = Debug|Win32
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Release|x64.ActiveCfg = Release|x64
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Release|x64.Build.0 = Release|x64
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Release|x86.ActiveCfg = Release|Win32
|
||||
{8C09E04E-D13C-4D8F-86E1-3B2A712FA540}.Release|x86.Build.0 = Release|Win32
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Debug|x64.Build.0 = Debug|x64
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Debug|x86.Build.0 = Debug|Win32
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Release|x64.ActiveCfg = Release|x64
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Release|x64.Build.0 = Release|x64
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Release|x86.ActiveCfg = Release|Win32
|
||||
{480198B5-83D1-4EBB-85BB-FBA5F8A0061E}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4B466BE8-D922-416A-BE35-C3BB9F65B8E7}
|
||||
SolutionGuid = {CD2858C1-027F-4A5B-8470-01D454B48B60}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,20 +1,872 @@
|
||||
// GitVer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "pch.h"
|
||||
#include "framework.h"
|
||||
#include "GitVer.h"
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_cli.h"
|
||||
#include "gitver_nuitka.h"
|
||||
#include "gitver_process.h"
|
||||
#include "gitver_rewrite.h"
|
||||
#include "gitver_tag.h"
|
||||
#include "gitver_types.h"
|
||||
#include "gitver_version.h"
|
||||
|
||||
int main()
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#endif
|
||||
|
||||
|
||||
// 唯一的应用程序对象
|
||||
|
||||
CWinApp theApp;
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern TCHAR g_szCurModuleFileName[MAX_PATH] = { 0 };
|
||||
extern TCHAR g_szCurModuleDir[MAX_PATH] = { 0 };
|
||||
extern TCHAR g_szFna[_MAX_FNAME] = { 0 };
|
||||
extern TCHAR g_szExt[_MAX_EXT] = { 0 };
|
||||
extern TCHAR g_szFolderName[MAX_PATH] = { 0 };
|
||||
|
||||
const UINT DEFAULT_MAJOR_WHEN_NO_TAG = 1;
|
||||
const UINT DEFAULT_MINOR_WHEN_NO_TAG = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
#define RC_DIR _T("F:\\Code\\HS\\SHWS_dev\\SHWS\\")
|
||||
#define RC_NAME _T("SHWS")
|
||||
#endif
|
||||
|
||||
int HandleSetVerCommand(int argc, TCHAR* argv[]);
|
||||
|
||||
/// <summary>
|
||||
/// 将宽字符字符串转换为ANSI字符串。
|
||||
/// </summary>
|
||||
/// <param name="lpText">要转换的宽字符字符串。</param>
|
||||
/// <returns>转换后的ANSI字符串。</returns>
|
||||
std::string ToAnsiString(LPCTSTR lpText)
|
||||
{
|
||||
std::cout << "Hello World!\n";
|
||||
if (lpText == NULL)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
|
||||
// 调试程序: F5 或调试 >“开始调试”菜单
|
||||
#ifdef UNICODE
|
||||
int nSize = WideCharToMultiByte(CP_ACP, 0, lpText, -1, NULL, 0, NULL, NULL);
|
||||
if (nSize <= 1)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// 入门使用技巧:
|
||||
// 1. 使用解决方案资源管理器窗口添加/管理文件
|
||||
// 2. 使用团队资源管理器窗口连接到源代码管理
|
||||
// 3. 使用输出窗口查看生成输出和其他消息
|
||||
// 4. 使用错误列表窗口查看错误
|
||||
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
|
||||
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
|
||||
std::string strValue;
|
||||
strValue.resize(nSize - 1);
|
||||
WideCharToMultiByte(CP_ACP, 0, lpText, -1, &strValue[0], nSize, NULL, NULL);
|
||||
return strValue;
|
||||
#else
|
||||
return std::string(lpText);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定目录下是否存在与模式匹配的文件。
|
||||
/// </summary>
|
||||
/// <param name="lpBaseDir">要搜索的基目录。</param>
|
||||
/// <param name="lpPattern">要匹配的文件模式。</param>
|
||||
/// <returns>如果存在匹配的文件,则返回TRUE;否则返回FALSE。</returns>
|
||||
BOOL HasMatchingFile(LPCTSTR lpBaseDir, LPCTSTR lpPattern)
|
||||
{
|
||||
CString strSearchPath;
|
||||
strSearchPath.Format(_T("%s%s"), lpBaseDir, lpPattern);
|
||||
|
||||
WIN32_FIND_DATA findData = { 0 };
|
||||
HANDLE hFind = ::FindFirstFile(strSearchPath, &findData);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
::FindClose(hFind);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归查找与模式匹配的第一个文件。
|
||||
/// </summary>
|
||||
/// <param name="lpBaseDir">要搜索的基目录。</param>
|
||||
/// <param name="lpPattern">要匹配的文件模式。</param>
|
||||
/// <param name="strFoundPath">找到的文件的完整路径。</param>
|
||||
/// <returns>如果找到匹配的文件,则返回TRUE;否则返回FALSE。</returns>
|
||||
BOOL FindFirstFileByPatternRecursive(LPCTSTR lpBaseDir, LPCTSTR lpPattern, CString& strFoundPath)
|
||||
{
|
||||
strFoundPath.Empty();
|
||||
if (lpBaseDir == NULL || lpPattern == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strSearchPath;
|
||||
strSearchPath.Format(_T("%s*"), lpBaseDir);
|
||||
|
||||
WIN32_FIND_DATA findData = { 0 };
|
||||
HANDLE hFind = ::FindFirstFile(strSearchPath, &findData);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (_tcscmp(findData.cFileName, _T(".")) == 0 || _tcscmp(findData.cFileName, _T("..")) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CString strFullPath;
|
||||
strFullPath.Format(_T("%s%s"), lpBaseDir, findData.cFileName);
|
||||
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if (_tcsicmp(findData.cFileName, _T(".git")) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CString strSubDir = strFullPath + _T("\\");
|
||||
if (FindFirstFileByPatternRecursive(strSubDir, lpPattern, strFoundPath))
|
||||
{
|
||||
::FindClose(hFind);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PathMatchSpec(findData.cFileName, lpPattern))
|
||||
{
|
||||
strFoundPath = strFullPath;
|
||||
::FindClose(hFind);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
} while (::FindNextFile(hFind, &findData));
|
||||
|
||||
::FindClose(hFind);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建相对于仓库的路径。
|
||||
/// </summary>
|
||||
/// <param name="lpBaseDir">基目录。</param>
|
||||
/// <param name="lpFullPath">完整路径。</param>
|
||||
/// <param name="strRelativePath">相对路径。</param>
|
||||
/// <returns>如果成功构建相对路径,则返回TRUE;否则返回FALSE。</returns>
|
||||
BOOL BuildRepoRelativePath(LPCTSTR lpBaseDir, LPCTSTR lpFullPath, CString& strRelativePath)
|
||||
{
|
||||
strRelativePath.Empty();
|
||||
if (lpBaseDir == NULL || lpFullPath == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strBase = lpBaseDir;
|
||||
CString strFull = lpFullPath;
|
||||
if (strBase.IsEmpty() || strFull.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strBase.Right(1) != _T("\\"))
|
||||
{
|
||||
strBase += _T("\\");
|
||||
}
|
||||
|
||||
if (strFull.GetLength() <= strBase.GetLength())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strFull.Left(strBase.GetLength()).CompareNoCase(strBase) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
strRelativePath = strFull.Mid(strBase.GetLength());
|
||||
return !strRelativePath.IsEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件名(不包含扩展名)。
|
||||
/// </summary>
|
||||
/// <param name="lpFilePath">文件路径。</param>
|
||||
/// <returns>不包含扩展名的文件名。</returns>
|
||||
CString GetFileNameWithoutExtension(LPCTSTR lpFilePath)
|
||||
{
|
||||
if (lpFilePath == NULL)
|
||||
{
|
||||
return _T("");
|
||||
}
|
||||
|
||||
CString strFileName = PathFindFileName(lpFilePath);
|
||||
int nDot = strFileName.ReverseFind(_T('.'));
|
||||
if (nDot > 0)
|
||||
{
|
||||
strFileName = strFileName.Left(nDot);
|
||||
}
|
||||
|
||||
return strFileName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找首选的C++资源文件(.rc)。
|
||||
/// </summary>
|
||||
/// <param name="lpBaseDir">要搜索的基目录。</param>
|
||||
/// <param name="strResFile">找到的资源文件的完整路径。</param>
|
||||
/// <returns>如果找到首选的资源文件,则返回TRUE;否则返回FALSE。</returns>
|
||||
BOOL FindPreferredCppRcFileAt(LPCTSTR lpBaseDir, CString& strResFile)
|
||||
{
|
||||
strResFile.Empty();
|
||||
if (lpBaseDir == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strBaseDir = lpBaseDir;
|
||||
if (!strBaseDir.IsEmpty() && strBaseDir.Right(1) != _T("\\"))
|
||||
{
|
||||
strBaseDir += _T("\\");
|
||||
}
|
||||
|
||||
CString strBaseName = strBaseDir;
|
||||
if (!strBaseName.IsEmpty() && strBaseName.Right(1) == _T("\\"))
|
||||
{
|
||||
strBaseName.Delete(strBaseName.GetLength() - 1);
|
||||
}
|
||||
int nPos = strBaseName.ReverseFind(_T('\\'));
|
||||
if (nPos >= 0)
|
||||
{
|
||||
strBaseName = strBaseName.Mid(nPos + 1);
|
||||
}
|
||||
|
||||
CString strPreferredPath;
|
||||
strPreferredPath.Format(_T("%s%s.rc"), strBaseDir.GetString(), strBaseName.GetString());
|
||||
if (PathFileExists(strPreferredPath))
|
||||
{
|
||||
strResFile = strPreferredPath;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FindFirstFileByPatternRecursive(strBaseDir, _T("*.rc"), strResFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找首选的AssemblyInfo.cs文件。
|
||||
/// </summary>
|
||||
/// <param name="lpBaseDir">要搜索的基目录。</param>
|
||||
/// <param name="strResFile">找到的AssemblyInfo.cs文件的完整路径。</param>
|
||||
/// <returns>如果找到首选的AssemblyInfo.cs文件,则返回TRUE;否则返回FALSE。</returns>
|
||||
BOOL FindPreferredAssemblyInfoFileAt(LPCTSTR lpBaseDir, CString& strResFile)
|
||||
{
|
||||
strResFile.Empty();
|
||||
if (lpBaseDir == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strBaseDir = lpBaseDir;
|
||||
if (!strBaseDir.IsEmpty() && strBaseDir.Right(1) != _T("\\"))
|
||||
{
|
||||
strBaseDir += _T("\\");
|
||||
}
|
||||
|
||||
CString strPreferredPath = strBaseDir + _T("Properties\\AssemblyInfo.cs");
|
||||
if (PathFileExists(strPreferredPath))
|
||||
{
|
||||
strResFile = strPreferredPath;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FindFirstFileByPatternRecursive(strBaseDir, _T("AssemblyInfo.cs"), strResFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测项目的源码类型。
|
||||
/// </summary>
|
||||
/// <param name="lpBaseDir">要检测的基目录。</param>
|
||||
/// <returns>返回项目的源码类型枚举值。</returns>
|
||||
int DetectProjectCodeType(LPCTSTR lpBaseDir)
|
||||
{
|
||||
_tprintf(_T("检测项目源码类型:%s\n"), lpBaseDir);
|
||||
if (PathFileExists(CString(lpBaseDir) + _T("\\Properties\\AssemblyInfo.cs"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.csproj"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.cs")))
|
||||
{
|
||||
return PROJECT_CSHARP;
|
||||
}
|
||||
|
||||
if (PathFileExists(CString(lpBaseDir) + _T("pyproject.toml"))
|
||||
|| PathFileExists(CString(lpBaseDir) + _T("setup.py"))
|
||||
|| PathFileExists(CString(lpBaseDir) + _T("requirements.txt"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.py")))
|
||||
{
|
||||
return PROJECT_PYTHON;
|
||||
}
|
||||
|
||||
if (HasMatchingFile(lpBaseDir, _T("*.vcxproj"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.vcproj"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.sln"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.cpp"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.h"))
|
||||
|| HasMatchingFile(lpBaseDir, _T("*.rc")))
|
||||
{
|
||||
return PROJECT_CPP;
|
||||
}
|
||||
|
||||
return PROJECT_UNKNOWN;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取项目源码类型的名称。
|
||||
/// </summary>
|
||||
/// <param name="nCodeType">项目源码类型枚举值。</param>
|
||||
/// <returns>项目源码类型的名称。</returns>
|
||||
LPCTSTR GetProjectCodeTypeName(int nCodeType)
|
||||
{
|
||||
switch (nCodeType)
|
||||
{
|
||||
case PROJECT_CPP:
|
||||
return _T("C++");
|
||||
case PROJECT_CSHARP:
|
||||
return _T("C#");
|
||||
case PROJECT_PYTHON:
|
||||
return _T("Python");
|
||||
default:
|
||||
return _T("Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打印完整的使用示例。
|
||||
/// </summary>
|
||||
void PrintFullUsageExamples()
|
||||
{
|
||||
LPCTSTR lpUsageText =
|
||||
_T("用法:\n")
|
||||
_T(" gitver (显示帮助后进入当前分支创建 tag 流程)\n")
|
||||
_T(" gitver rewrite [PEType可选]\n")
|
||||
_T(" gitver setver [pid] [repoPath可选]\n")
|
||||
_T(" gitver nuitkabuild [pid] [mainPy] [repoPath可选] [nuitka额外参数可选]\n")
|
||||
_T(" gitver nuitkapydbuild [pid] [modulePy] [repoPath可选] [nuitka额外参数可选]\n")
|
||||
_T("\n示例:\n")
|
||||
_T(" gitver\n")
|
||||
_T(" gitver rewrite\n")
|
||||
_T(" gitver rewrite 2\n")
|
||||
_T(" gitver rewrite -f\n")
|
||||
_T(" gitver setver 5 E:\\Code\\OTH\\gitver\n")
|
||||
_T(" gitver setver 5\n")
|
||||
_T(" gitver setver 5 -f\n")
|
||||
_T(" gitver nuitkabuild 5 main.py\n")
|
||||
_T(" gitver nuitkabuild 5 main.py -f\n")
|
||||
_T(" gitver nuitkabuild 5 src\\app.py E:\\Code\\MyPyProj --standalone --output-dir=dist\n")
|
||||
_T(" gitver nuitkabuild 5 main.py --standalone -f\n")
|
||||
_T(" gitver nuitkapydbuild 5 module.py\n")
|
||||
_T(" gitver nuitkapydbuild 5 module.py -f\n")
|
||||
_T(" gitver nuitkapydbuild 5 src\\core.py E:\\Code\\MyPyProj --output-dir=dist\n")
|
||||
_T("\n说明: nuitkabuild/nuitkapydbuild 中,-f 在额外参数开始前表示 gitver 选项;进入额外参数后会透传给 Nuitka。\n")
|
||||
_T("无参数时会读取当前分支最近三次 tag,并提示选择 major 加 1 或 minor 加 1,然后创建新 tag。\n");
|
||||
|
||||
_tprintf(_T("%s"), lpUsageText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打印命令简要用法(用于未知命令等场景)。
|
||||
/// </summary>
|
||||
void PrintShortCommandUsage()
|
||||
{
|
||||
_tprintf(_T("请使用:gitver rewrite [PE类型可选]\n"));
|
||||
_tprintf(_T("请使用:gitver setver [pid] [repoPath可选]\n"));
|
||||
_tprintf(_T("或:gitver nuitkabuild [pid] [mainPy] [repoPath可选] [nuitka额外参数可选]\n"));
|
||||
_tprintf(_T("或:gitver nuitkapydbuild [pid] [modulePy] [repoPath可选] [nuitka额外参数可选]\n"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打印命令参数不足时的标准用法与示例。
|
||||
/// </summary>
|
||||
/// <param name="lpCommandName">命令名称。</param>
|
||||
/// <param name="lpRequiredArgs">必选参数占位文本。</param>
|
||||
/// <param name="lpExampleRequiredArgs">示例中的必选参数。</param>
|
||||
void PrintCommandUsageAndExamples(LPCTSTR lpCommandName, LPCTSTR lpRequiredArgs, LPCTSTR lpExampleRequiredArgs)
|
||||
{
|
||||
_tprintf(_T("用法:gitver %s %s [repoPath可选]\n"), lpCommandName, lpRequiredArgs);
|
||||
if (lpExampleRequiredArgs != NULL && _tcslen(lpExampleRequiredArgs) > 0)
|
||||
{
|
||||
_tprintf(_T("示例:gitver %s 5 %s E:\\Code\\OTH\\gitver\n"), lpCommandName, lpExampleRequiredArgs);
|
||||
_tprintf(_T("示例:gitver %s 5 %s\n"), lpCommandName, lpExampleRequiredArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tprintf(_T("示例:gitver %s 5 E:\\Code\\OTH\\gitver\n"), lpCommandName);
|
||||
_tprintf(_T("示例:gitver %s 5\n"), lpCommandName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取退出码的简要说明。
|
||||
/// </summary>
|
||||
LPCTSTR GetExitCodeHint(int nRetCode)
|
||||
{
|
||||
switch (nRetCode)
|
||||
{
|
||||
case 0:
|
||||
return _T("success");
|
||||
case 1:
|
||||
return _T("mfc init failed");
|
||||
case 2:
|
||||
return _T("get commit id failed");
|
||||
case 3:
|
||||
return _T("invalid args or unknown command");
|
||||
case 4:
|
||||
return _T("setver: invalid pid");
|
||||
case 5:
|
||||
return _T("setver: get branch or bid failed");
|
||||
case 6:
|
||||
return _T("setver: get tag failed");
|
||||
case 9:
|
||||
return _T("setver: get today commit count failed");
|
||||
case 15:
|
||||
return _T("unsupported or unknown project type");
|
||||
case 16:
|
||||
return _T("rewrite: python project unsupported");
|
||||
case 17:
|
||||
return _T("nuitkabuild: missing args");
|
||||
case 18:
|
||||
return _T("nuitkabuild: invalid pid");
|
||||
case 19:
|
||||
return _T("nuitkabuild: get branch or bid failed");
|
||||
case 20:
|
||||
return _T("nuitkabuild: get tag failed");
|
||||
case 21:
|
||||
return _T("nuitkabuild: get today commit count failed");
|
||||
case 22:
|
||||
return _T("rewrite: invalid pe type");
|
||||
case 23:
|
||||
return _T("invalid repo path");
|
||||
case 24:
|
||||
return _T("nuitkapydbuild: missing args");
|
||||
case 25:
|
||||
return _T("nuitkapydbuild: invalid pid");
|
||||
case 26:
|
||||
return _T("nuitkapydbuild: get branch or bid failed");
|
||||
case 27:
|
||||
return _T("nuitkapydbuild: get tag failed");
|
||||
case 28:
|
||||
return _T("nuitkapydbuild: get today commit count failed");
|
||||
case 30:
|
||||
return _T("interactive: get recent tags failed");
|
||||
case 31:
|
||||
return _T("interactive: read input failed");
|
||||
case 32:
|
||||
return _T("interactive: invalid choice or version overflow");
|
||||
case 33:
|
||||
return _T("interactive: create tag failed");
|
||||
case 34:
|
||||
return _T("interactive: verify tag failed");
|
||||
case 35:
|
||||
return _T("rewrite version file failed");
|
||||
default:
|
||||
return _T("");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在命令执行返回非零时输出统一错误日志。
|
||||
/// </summary>
|
||||
void PrintCommandFailedWithCode(LPCTSTR lpCommandName, int nRetCode)
|
||||
{
|
||||
if (nRetCode != 0)
|
||||
{
|
||||
LPCTSTR lpHint = GetExitCodeHint(nRetCode);
|
||||
if (lpHint != NULL && _tcslen(lpHint) > 0)
|
||||
{
|
||||
_tprintf(_T("ERROR: command %s failed, code %d (%s)\n"), lpCommandName, nRetCode, lpHint);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tprintf(_T("ERROR: command %s failed, code %d\n"), lpCommandName, nRetCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出命令执行结束日志(含耗时和退出码)。
|
||||
/// </summary>
|
||||
void LogCommandEnd(LPCTSTR lpCommandName, DWORD dwStartTick, int nRetCode)
|
||||
{
|
||||
DWORD dwElapsed = GetTickCount() - dwStartTick;
|
||||
_tprintf(_T("命令结束: %s,耗时 %u ms,退出码 %d\n"), lpCommandName, dwElapsed, nRetCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前模块的目录信息。
|
||||
/// </summary>
|
||||
/// <returns>返回值。</returns>
|
||||
void GetDirInfo()
|
||||
{
|
||||
TCHAR szDrive[_MAX_DRIVE] = { 0 };
|
||||
TCHAR szDir[_MAX_DIR] = { 0 };
|
||||
TCHAR szFna[_MAX_FNAME] = { 0 };
|
||||
TCHAR szExt[_MAX_EXT] = { 0 };
|
||||
DWORD dwRet = ::GetModuleFileName(NULL, g_szCurModuleFileName, sizeof(g_szCurModuleFileName) / sizeof(TCHAR));
|
||||
|
||||
_tsplitpath_s(g_szCurModuleFileName, szDrive, szDir, g_szFna, g_szExt);
|
||||
_tcscat_s(g_szCurModuleDir, MAX_PATH, szDrive);
|
||||
_tcscat_s(g_szCurModuleDir, MAX_PATH, szDir);
|
||||
|
||||
CString strVal = g_szCurModuleDir;
|
||||
if (!strVal.IsEmpty() && strVal.GetAt(strVal.GetLength() - 1) == _T('\\'))
|
||||
{
|
||||
strVal.Delete(strVal.GetLength() - 1);
|
||||
_stprintf_s(g_szCurModuleDir, _T("%s"), strVal.GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
strVal += _T("\\");
|
||||
_stprintf_s(g_szCurModuleDir, _T("%s"), strVal.GetString());
|
||||
}
|
||||
|
||||
int nPos = strVal.ReverseFind(_T('\\'));
|
||||
if (nPos != -1)
|
||||
{
|
||||
strVal = strVal.Right(strVal.GetLength() - nPos - 1);
|
||||
_stprintf_s(g_szFolderName, _T("%s"), strVal.GetString());
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
_stprintf_s(g_szFolderName, RC_NAME);
|
||||
_stprintf_s(g_szCurModuleDir, RC_DIR);
|
||||
#endif
|
||||
SetCurrentDirectory(g_szCurModuleDir);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 处理设置版本命令。
|
||||
/// </summary>
|
||||
/// <param name="argc">参数个数。</param>
|
||||
/// <param name="argv">参数数组。</param>
|
||||
/// <returns>返回值。</returns>
|
||||
int HandleSetVerCommand(int argc, TCHAR* argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
_tprintf(_T("错误: 参数不足。\n"));
|
||||
PrintCommandUsageAndExamples(_T("setver"), _T("[pid]"), _T(""));
|
||||
_tprintf(_T("参数说明:\n"));
|
||||
_tprintf(_T(" pid: 产品ID,必填,范围0-65535。\n"));
|
||||
_tprintf(_T(" repoPath: 仓库路径,可选,默认使用当前目录。\n"));
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
UINT nPid = 0;
|
||||
int nArgRet = ParseUInt16ArgOrError(argv, 2, _T("pid"), nPid, 4);
|
||||
if (nArgRet != 0)
|
||||
{
|
||||
return nArgRet;
|
||||
}
|
||||
|
||||
LPCTSTR lpRepoPath = NULL;
|
||||
BOOL bUseDefaultTagWhenMissing = FALSE;
|
||||
int nRepoArgRet = ParseSetVerOptions(argc, argv, lpRepoPath, bUseDefaultTagWhenMissing);
|
||||
if (nRepoArgRet != 0)
|
||||
{
|
||||
return nRepoArgRet;
|
||||
}
|
||||
|
||||
CString strProductVersion;
|
||||
CString strFileVersion;
|
||||
VersionBuildErrorCodes errorCodes = { 5, 6, 9 };
|
||||
int nVersionRet = BuildVersionsFromRepo(
|
||||
lpRepoPath,
|
||||
nPid,
|
||||
errorCodes,
|
||||
strProductVersion,
|
||||
strFileVersion,
|
||||
bUseDefaultTagWhenMissing,
|
||||
DEFAULT_MAJOR_WHEN_NO_TAG,
|
||||
DEFAULT_MINOR_WHEN_NO_TAG);
|
||||
if (nVersionRet != 0)
|
||||
{
|
||||
return nVersionRet;
|
||||
}
|
||||
|
||||
_tprintf(_T("ProductVersion=%s\n"), strProductVersion.GetString());
|
||||
_tprintf(_T("FileVersion=%s\n"), strFileVersion.GetString());
|
||||
|
||||
return RewriteSetVerByProjectType(lpRepoPath, strProductVersion, strFileVersion);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// 准备C++重写内容。
|
||||
/// </summary>
|
||||
/// <param name="strCommitId">提交ID。</param>
|
||||
/// <param name="nPEType">PE类型。</param>
|
||||
/// <param name="strResFile">资源文件路径。</param>
|
||||
/// <param name="vtOldContent">旧内容向量。</param>
|
||||
/// <param name="vtNewContent">新内容向量。</param>
|
||||
/// </summary>
|
||||
void PrepareCppRewriteContent(const CString& strCommitId, int nPEType, CString& strResFile, std::vector<std::string>& vtOldContent, std::vector<std::string>& vtNewContent)
|
||||
{
|
||||
TCHAR szValue[MAX_PATH] = { 0 };
|
||||
|
||||
if (!FindPreferredCppRcFileAt(g_szCurModuleDir, strResFile))
|
||||
{
|
||||
_tprintf(_T("错误: 未找到C++ .rc资源文件。\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
CString strRcRelativePath;
|
||||
if (BuildRepoRelativePath(g_szCurModuleDir, strResFile, strRcRelativePath))
|
||||
{
|
||||
CString strCheckoutCmd;
|
||||
strCheckoutCmd.Format(_T("cmd /c git checkout -- %s"), QuoteCmdArg(strRcRelativePath).GetString());
|
||||
StartProcess(NULL, strCheckoutCmd.GetBuffer(), g_szCurModuleDir);
|
||||
strCheckoutCmd.ReleaseBuffer();
|
||||
}
|
||||
|
||||
#if 0
|
||||
vtOldContent.push_back(_T("FILEVERSION 1,0,0,1"));
|
||||
vtOldContent.push_back(_T("VALUE \"FileVersion\", \"1.0.0.1\""));
|
||||
|
||||
_stprintf_s(szValue, _T("FILEVERSION 1.0.%s"), strCommitId.GetString());
|
||||
vtNewContent.push_back(szValue);
|
||||
|
||||
_stprintf_s(szValue, _T("VALUE \"FileVersion\", \"1.0.%s\""), strCommitId.GetString());
|
||||
vtNewContent.push_back(szValue);
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
CString strModuleName = GetFileNameWithoutExtension(strResFile);
|
||||
if (strModuleName.IsEmpty())
|
||||
{
|
||||
strModuleName = g_szFolderName;
|
||||
}
|
||||
|
||||
_stprintf_s(szValue, _T("VALUE \"OriginalFilename\", \"%s.%s\""), strModuleName.GetString(), nPEType == 1 ? _T("exe") : _T("dll"));
|
||||
vtOldContent.push_back(ToAnsiString(szValue));
|
||||
_tprintf(_T("Rewrite old token: %s\n"), szValue);
|
||||
|
||||
_stprintf_s(szValue, _T("VALUE \"OriginalFilename\", \"%s\""), strCommitId.GetString());
|
||||
vtNewContent.push_back(ToAnsiString(szValue));
|
||||
_tprintf(_T("Rewrite new token: %s\n"), szValue);
|
||||
#if 0
|
||||
_stprintf_s(szValue, "VALUE \"FileDescription\", \"TODO: <文件说明>\"");
|
||||
vtOldContent.push_back(szValue);
|
||||
|
||||
_stprintf_s(szValue, "VALUE \"FileDescription\", \"%s\"", strCommitId.GetString());
|
||||
vtNewContent.push_back(szValue);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 准备C#重写内容。
|
||||
/// </summary>
|
||||
/// <param name="strCommitId">提交ID。</param>
|
||||
/// <param name="strResFile">资源文件路径。</param>
|
||||
/// <param name="vtOldContent">旧内容向量。</param>
|
||||
/// <param name="vtNewContent">新内容向量。</param>
|
||||
void PrepareCSharpRewriteContent(const CString& strCommitId, CString& strResFile, std::vector<std::string>& vtOldContent, std::vector<std::string>& vtNewContent)
|
||||
{
|
||||
TCHAR szValue[MAX_PATH] = { 0 };
|
||||
if (!FindPreferredAssemblyInfoFileAt(g_szCurModuleDir, strResFile))
|
||||
{
|
||||
_tprintf(_T("错误: 未找到首选的 AssemblyInfo 文件。\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
CString strAssemblyRelativePath;
|
||||
if (BuildRepoRelativePath(g_szCurModuleDir, strResFile, strAssemblyRelativePath))
|
||||
{
|
||||
CString strCheckoutCmd;
|
||||
strCheckoutCmd.Format(_T("cmd /c git checkout -- %s"), QuoteCmdArg(strAssemblyRelativePath).GetString());
|
||||
StartProcess(NULL, strCheckoutCmd.GetBuffer(), g_szCurModuleDir);
|
||||
strCheckoutCmd.ReleaseBuffer();
|
||||
}
|
||||
|
||||
#if 0
|
||||
vtOldContent.push_back(_T("[assembly: AssemblyFileVersion(\"1.0.0.1\")]"));
|
||||
|
||||
_stprintf_s(szValue, _T("[assembly: AssemblyFileVersion(\"1.0.0.%s\")]"), strCommitId.GetString());
|
||||
vtNewContent.push_back(szValue);
|
||||
#endif
|
||||
|
||||
vtOldContent.push_back("[assembly: AssemblyCopyright(\"\")]");
|
||||
|
||||
_stprintf_s(szValue, _T("[assembly: AssemblyCopyright(\"%s\")]"), strCommitId.GetString());
|
||||
vtNewContent.push_back(ToAnsiString(szValue));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 处理重写命令。
|
||||
/// </summary>
|
||||
/// <param name="argc">参数个数。</param>
|
||||
/// <param name="argv">参数数组。</param>
|
||||
/// <returns>返回值。</returns>
|
||||
int HandleRewriteCommand(int argc, TCHAR* argv[])
|
||||
{
|
||||
int nCodeType = DetectProjectCodeType(g_szCurModuleDir);
|
||||
|
||||
int nPEType = 1;
|
||||
BOOL bForceRewrite = FALSE;
|
||||
int nRewriteOptRet = ParseRewriteOptions(argc, argv, nPEType, bForceRewrite);
|
||||
if (nRewriteOptRet != 0)
|
||||
{
|
||||
return nRewriteOptRet;
|
||||
}
|
||||
|
||||
if (nCodeType == PROJECT_UNKNOWN)
|
||||
{
|
||||
_tprintf(_T("错误: 未知项目类型。\n"));
|
||||
return 15;
|
||||
}
|
||||
|
||||
_tprintf(_T("项目类型: %s\n"), GetProjectCodeTypeName(nCodeType));
|
||||
|
||||
#ifdef _DEBUG
|
||||
CString strValue = StartProcess(NULL, _T("cmd /c git rev-parse --short HEAD"), RC_DIR);
|
||||
#else
|
||||
CString strValue = StartProcess(NULL, _T("cmd /c git rev-parse --short HEAD"), NULL);
|
||||
#endif
|
||||
strValue.Replace(_T("\n"), _T(""));
|
||||
strValue.Replace(_T("\r"), _T(""));
|
||||
strValue.Trim();
|
||||
if (strValue.IsEmpty())
|
||||
{
|
||||
_tprintf(_T("错误: 获取提交ID失败。\n"));
|
||||
return 2;
|
||||
}
|
||||
|
||||
CString strResFile;
|
||||
std::vector<std::string> vtOldContent;
|
||||
std::vector<std::string> vtNewContent;
|
||||
|
||||
if (nCodeType == PROJECT_CPP)
|
||||
{
|
||||
PrepareCppRewriteContent(strValue, nPEType, strResFile, vtOldContent, vtNewContent);
|
||||
}
|
||||
else if (nCodeType == PROJECT_CSHARP)
|
||||
{
|
||||
PrepareCSharpRewriteContent(strValue, strResFile, vtOldContent, vtNewContent);
|
||||
}
|
||||
else if (nCodeType == PROJECT_PYTHON)
|
||||
{
|
||||
_tprintf(_T("错误: 不支持的项目类型: Python。\n"));
|
||||
return 16;
|
||||
}
|
||||
|
||||
if (strResFile.IsEmpty())
|
||||
{
|
||||
if (bForceRewrite)
|
||||
{
|
||||
_tprintf(_T("警告: 资源文件路径为空,已按 -f 忽略本次重写失败。\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
_tprintf(_T("错误: 资源文件路径为空。\n"));
|
||||
return 35;
|
||||
}
|
||||
|
||||
if (!ReplaceFileContent(strResFile, vtOldContent, vtNewContent))
|
||||
{
|
||||
if (bForceRewrite)
|
||||
{
|
||||
_tprintf(_T("警告: 替换文件内容失败,已按 -f 忽略本次重写失败。\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 35;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, TCHAR* argv[], TCHAR* envp[])
|
||||
{
|
||||
int nRetCode = 0;
|
||||
|
||||
// 添加图标;
|
||||
HWND hwnd = GetForegroundWindow();
|
||||
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON_APP)));
|
||||
HMODULE hModule = ::GetModuleHandle(NULL);
|
||||
|
||||
GetDirInfo();
|
||||
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
|
||||
{
|
||||
_tprintf(_T("错误:MFC 初始化失败。\n"));
|
||||
nRetCode = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
CString strArgs = BuildArgsForLog(argc, argv);
|
||||
_tprintf(_T("命令参数: %s\n"), strArgs.GetString());
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
_tprintf(_T("进入交互式标签创建模式。\n"));
|
||||
PrintFullUsageExamples();
|
||||
DWORD dwStartTick = LogCommandStart(_T("interactive"));
|
||||
int nCmdRet = HandleCreateTagInteractive();
|
||||
LogCommandEnd(_T("interactive"), dwStartTick, nCmdRet);
|
||||
PrintCommandFailedWithCode(_T("interactive"), nCmdRet);
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
if (argc >= 2 && _tcsicmp(argv[1], _T("nuitkabuild")) == 0)
|
||||
{
|
||||
DWORD dwStartTick = LogCommandStart(_T("nuitkabuild"));
|
||||
int nCmdRet = HandleNuitkaBuildCommand(argc, argv);
|
||||
LogCommandEnd(_T("nuitkabuild"), dwStartTick, nCmdRet);
|
||||
PrintCommandFailedWithCode(_T("nuitkabuild"), nCmdRet);
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
if (argc >= 2 && _tcsicmp(argv[1], _T("nuitkapydbuild")) == 0)
|
||||
{
|
||||
DWORD dwStartTick = LogCommandStart(_T("nuitkapydbuild"));
|
||||
int nCmdRet = HandleNuitkaPydBuildCommand(argc, argv);
|
||||
LogCommandEnd(_T("nuitkapydbuild"), dwStartTick, nCmdRet);
|
||||
PrintCommandFailedWithCode(_T("nuitkapydbuild"), nCmdRet);
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
if (argc >= 2 && _tcsicmp(argv[1], _T("setver")) == 0)
|
||||
{
|
||||
DWORD dwStartTick = LogCommandStart(_T("setver"));
|
||||
int nCmdRet = HandleSetVerCommand(argc, argv);
|
||||
LogCommandEnd(_T("setver"), dwStartTick, nCmdRet);
|
||||
PrintCommandFailedWithCode(_T("setver"), nCmdRet);
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
if (argc >= 2 && _tcsicmp(argv[1], _T("rewrite")) == 0)
|
||||
{
|
||||
DWORD dwStartTick = LogCommandStart(_T("rewrite"));
|
||||
int nCmdRet = HandleRewriteCommand(argc - 1, argv + 1);
|
||||
LogCommandEnd(_T("rewrite"), dwStartTick, nCmdRet);
|
||||
PrintCommandFailedWithCode(_T("rewrite"), nCmdRet);
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
_tprintf(_T("错误:未知命令:%s\n"), argv[1]);
|
||||
PrintShortCommandUsage();
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
return nRetCode;
|
||||
}
|
||||
|
||||
3
GitVer/GitVer.h
Normal file
3
GitVer/GitVer.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "resource.h"
|
||||
BIN
GitVer/GitVer.rc
Normal file
BIN
GitVer/GitVer.rc
Normal file
Binary file not shown.
@@ -21,7 +21,7 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{8c09e04e-d13c-4d8f-86e1-3b2a712fa540}</ProjectGuid>
|
||||
<ProjectGuid>{480198b5-83d1-4ebb-85bb-fba5f8a0061e}</ProjectGuid>
|
||||
<RootNamespace>GitVer</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
@@ -31,6 +31,7 @@
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>Dynamic</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
@@ -38,19 +39,22 @@
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>Dynamic</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<UseOfMfc>Dynamic</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<UseOfMfc>Static</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -70,12 +74,23 @@
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>..\..\..\..\bin\$(ProjectName)\</OutDir>
|
||||
<IntDir>$(OutDir)$(Configuration)\</IntDir>
|
||||
<TargetName>$(ProjectName)d</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>..\..\..\..\bin\$(ProjectName)\</OutDir>
|
||||
<IntDir>$(OutDir)$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -90,6 +105,8 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -102,6 +119,8 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -116,14 +135,50 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="GitVer.h" />
|
||||
<ClInclude Include="GitVer_cli.h" />
|
||||
<ClInclude Include="GitVer_common.h" />
|
||||
<ClInclude Include="GitVer_nuitka.h" />
|
||||
<ClInclude Include="GitVer_process.h" />
|
||||
<ClInclude Include="GitVer_rewrite.h" />
|
||||
<ClInclude Include="GitVer_tag.h" />
|
||||
<ClInclude Include="GitVer_types.h" />
|
||||
<ClInclude Include="GitVer_version.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="Resource.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GitVer.cpp" />
|
||||
<ClCompile Include="GitVer_cli.cpp" />
|
||||
<ClCompile Include="GitVer_common.cpp" />
|
||||
<ClCompile Include="GitVer_nuitka.cpp" />
|
||||
<ClCompile Include="GitVer_process.cpp" />
|
||||
<ClCompile Include="GitVer_rewrite.cpp" />
|
||||
<ClCompile Include="GitVer_tag.cpp" />
|
||||
<ClCompile Include="GitVer_version.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GitVer.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="app.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -14,9 +14,84 @@
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Resource.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_cli.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_common.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_nuitka.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_process.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_rewrite.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_tag.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_types.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_version.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GitVer.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_cli.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_common.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_nuitka.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_process.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_rewrite.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_tag.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_version.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GitVer.rc">
|
||||
<Filter>资源文件</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="app.ico">
|
||||
<Filter>资源文件</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
89
GitVer/GitVer_cli.cpp
Normal file
89
GitVer/GitVer_cli.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_cli.h"
|
||||
|
||||
extern TCHAR g_szCurModuleDir[MAX_PATH];
|
||||
|
||||
void PrintInvalidRepoPathError(LPCTSTR lpRepoPath)
|
||||
{
|
||||
_tprintf(_T("错误: 无效的仓库路径 %s\n"), lpRepoPath == NULL ? _T("<null>") : lpRepoPath);
|
||||
}
|
||||
|
||||
int ParseSetVerOptions(int argc, TCHAR* argv[], LPCTSTR& lpRepoPath, BOOL& bUseDefaultTagWhenMissing)
|
||||
{
|
||||
lpRepoPath = NULL;
|
||||
bUseDefaultTagWhenMissing = FALSE;
|
||||
|
||||
for (int i = 3; i < argc; ++i)
|
||||
{
|
||||
CString strArg = argv[i];
|
||||
if (strArg.CompareNoCase(_T("-f")) == 0)
|
||||
{
|
||||
bUseDefaultTagWhenMissing = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lpRepoPath == NULL)
|
||||
{
|
||||
if (!PathIsDirectory(argv[i]))
|
||||
{
|
||||
PrintInvalidRepoPathError(argv[i]);
|
||||
return 23;
|
||||
}
|
||||
lpRepoPath = argv[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
_tprintf(_T("错误: 多余的参数 %s\n"), argv[i]);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (lpRepoPath == NULL)
|
||||
{
|
||||
lpRepoPath = g_szCurModuleDir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ParseRewriteOptions(int argc, TCHAR* argv[], int& nPEType, BOOL& bForceRewrite)
|
||||
{
|
||||
nPEType = 1;
|
||||
bForceRewrite = FALSE;
|
||||
BOOL bHasPETypeArg = FALSE;
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
CString strArg = argv[i];
|
||||
if (strArg.CompareNoCase(_T("-f")) == 0)
|
||||
{
|
||||
bForceRewrite = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
UINT nArgValue = 0;
|
||||
if (!TryParseUInt16(strArg, nArgValue))
|
||||
{
|
||||
_tprintf(_T("错误: 无效的 PE 类型 %s\n"), strArg.GetString());
|
||||
return 22;
|
||||
}
|
||||
|
||||
if (bHasPETypeArg)
|
||||
{
|
||||
_tprintf(_T("错误: PE 类型参数重复 %s\n"), strArg.GetString());
|
||||
return 22;
|
||||
}
|
||||
|
||||
nPEType = (int)nArgValue;
|
||||
bHasPETypeArg = TRUE;
|
||||
}
|
||||
|
||||
if (nPEType != 1 && nPEType != 2)
|
||||
{
|
||||
_tprintf(_T("错误: 无效的 PE 类型 %d\n"), nPEType);
|
||||
return 22;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
5
GitVer/GitVer_cli.h
Normal file
5
GitVer/GitVer_cli.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void PrintInvalidRepoPathError(LPCTSTR lpRepoPath);
|
||||
int ParseSetVerOptions(int argc, TCHAR* argv[], LPCTSTR& lpRepoPath, BOOL& bUseDefaultTagWhenMissing);
|
||||
int ParseRewriteOptions(int argc, TCHAR* argv[], int& nPEType, BOOL& bForceRewrite);
|
||||
95
GitVer/GitVer_common.cpp
Normal file
95
GitVer/GitVer_common.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_common.h"
|
||||
|
||||
CString BuildArgsForLog(int argc, TCHAR* argv[])
|
||||
{
|
||||
CString strArgs;
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
strArgs += _T(" ");
|
||||
}
|
||||
strArgs += QuoteCmdArg(argv[i]);
|
||||
}
|
||||
return strArgs;
|
||||
}
|
||||
|
||||
DWORD LogCommandStart(LPCTSTR lpCommandName)
|
||||
{
|
||||
DWORD dwStartTick = GetTickCount();
|
||||
_tprintf(_T("Command start: %s\n"), lpCommandName);
|
||||
return dwStartTick;
|
||||
}
|
||||
|
||||
CString QuoteCmdArg(const CString& strArg)
|
||||
{
|
||||
if (strArg.Find(_T(' ')) == -1 && strArg.Find(_T('\t')) == -1 && strArg.Find(_T('"')) == -1)
|
||||
{
|
||||
return strArg;
|
||||
}
|
||||
|
||||
CString strEscaped = strArg;
|
||||
strEscaped.Replace(_T("\""), _T("\\\""));
|
||||
CString strQuoted;
|
||||
strQuoted.Format(_T("\"%s\""), strEscaped.GetString());
|
||||
return strQuoted;
|
||||
}
|
||||
|
||||
BOOL TryParseUInt16(const CString& strValue, UINT& nValue)
|
||||
{
|
||||
CString strTrimmed = strValue;
|
||||
strTrimmed.Trim();
|
||||
if (strTrimmed.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < strTrimmed.GetLength(); ++i)
|
||||
{
|
||||
if (!_istdigit(strTrimmed[i]))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ULONGLONG ullValue = _tcstoui64(strTrimmed, NULL, 10);
|
||||
if (ullValue > 65535)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nValue = (UINT)ullValue;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int ParseUInt16ArgOrError(TCHAR* argv[], int nIndex, LPCTSTR lpArgName, UINT& nValue, int nErrorCode)
|
||||
{
|
||||
if (!TryParseUInt16(argv[nIndex], nValue))
|
||||
{
|
||||
_tprintf(_T("Error: invalid %s %s\n"), lpArgName, argv[nIndex]);
|
||||
return nErrorCode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL IsLikelyPathArg(LPCTSTR lpArg)
|
||||
{
|
||||
if (lpArg == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strArg = lpArg;
|
||||
if (strArg.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return strArg.Find(_T('\\')) >= 0
|
||||
|| strArg.Find(_T('/')) >= 0
|
||||
|| strArg.Find(_T(':')) >= 0
|
||||
|| strArg.Left(1) == _T(".");
|
||||
}
|
||||
8
GitVer/GitVer_common.h
Normal file
8
GitVer/GitVer_common.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
CString BuildArgsForLog(int argc, TCHAR* argv[]);
|
||||
DWORD LogCommandStart(LPCTSTR lpCommandName);
|
||||
CString QuoteCmdArg(const CString& strArg);
|
||||
BOOL TryParseUInt16(const CString& strValue, UINT& nValue);
|
||||
int ParseUInt16ArgOrError(TCHAR* argv[], int nIndex, LPCTSTR lpArgName, UINT& nValue, int nErrorCode);
|
||||
BOOL IsLikelyPathArg(LPCTSTR lpArg);
|
||||
190
GitVer/GitVer_nuitka.cpp
Normal file
190
GitVer/GitVer_nuitka.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_cli.h"
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_process.h"
|
||||
#include "gitver_version.h"
|
||||
#include "gitver_nuitka.h"
|
||||
|
||||
extern TCHAR g_szCurModuleDir[MAX_PATH];
|
||||
|
||||
void PrintCommandUsageAndExamples(LPCTSTR lpCommand, LPCTSTR lpRequiredArgs, LPCTSTR lpEntryExample);
|
||||
|
||||
const UINT DEFAULT_MAJOR_WHEN_NO_TAG_FOR_NUITKA = 1;
|
||||
const UINT DEFAULT_MINOR_WHEN_NO_TAG_FOR_NUITKA = 0;
|
||||
|
||||
static int ParseNuitkaOptions(int argc, TCHAR* argv[], LPCTSTR& lpRepoPath, BOOL& bUseDefaultTagWhenMissing, CString& strExtraArgs)
|
||||
{
|
||||
lpRepoPath = NULL;
|
||||
bUseDefaultTagWhenMissing = FALSE;
|
||||
strExtraArgs.Empty();
|
||||
BOOL bExtraArgsStarted = FALSE;
|
||||
|
||||
for (int i = 4; i < argc; ++i)
|
||||
{
|
||||
CString strArg = argv[i];
|
||||
BOOL bOptionStyleArg = !strArg.IsEmpty() && strArg.Left(1) == _T("-");
|
||||
|
||||
// 仅在额外参数尚未开始时,将 -f 解释为 gitver 选项;
|
||||
// 一旦进入额外参数阶段,-f 应透传给 Nuitka。
|
||||
if (!bExtraArgsStarted && strArg.CompareNoCase(_T("-f")) == 0)
|
||||
{
|
||||
bUseDefaultTagWhenMissing = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bExtraArgsStarted && lpRepoPath == NULL && !bOptionStyleArg && PathIsDirectory(argv[i]))
|
||||
{
|
||||
lpRepoPath = argv[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bExtraArgsStarted && lpRepoPath == NULL && !bOptionStyleArg && IsLikelyPathArg(argv[i]))
|
||||
{
|
||||
PrintInvalidRepoPathError(argv[i]);
|
||||
return 23;
|
||||
}
|
||||
|
||||
bExtraArgsStarted = TRUE;
|
||||
|
||||
if (!strExtraArgs.IsEmpty())
|
||||
{
|
||||
strExtraArgs += _T(" ");
|
||||
}
|
||||
strExtraArgs += QuoteCmdArg(argv[i]);
|
||||
}
|
||||
|
||||
if (lpRepoPath == NULL)
|
||||
{
|
||||
lpRepoPath = g_szCurModuleDir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void RunNuitkaBuild(LPCTSTR lpStartMessage, CString& strNuitkaCmd, const CString& strProductVersion, const CString& strFileVersion, LPCTSTR lpRepoPath)
|
||||
{
|
||||
_tprintf(_T("%s\n"), lpStartMessage);
|
||||
_tprintf(_T("ProductVersion=%s\n"), strProductVersion.GetString());
|
||||
_tprintf(_T("FileVersion=%s\n"), strFileVersion.GetString());
|
||||
|
||||
CString strBuildResult = StartProcess(NULL, strNuitkaCmd.GetBuffer(), lpRepoPath);
|
||||
strNuitkaCmd.ReleaseBuffer();
|
||||
|
||||
if (strBuildResult.IsEmpty())
|
||||
{
|
||||
_tprintf(_T("错误: Nuitka 构建失败。\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static int HandleNuitkaBuildCommandCore(
|
||||
int argc,
|
||||
TCHAR* argv[],
|
||||
BOOL bBuildModule,
|
||||
LPCTSTR lpStartMessage,
|
||||
LPCTSTR lpUsageName,
|
||||
LPCTSTR lpEntryArgName,
|
||||
int nArgLackErrorCode,
|
||||
int nPidErrorCode,
|
||||
int nBidErrorCode,
|
||||
int nTagErrorCode,
|
||||
int nCommitErrorCode)
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
_tprintf(_T("错误: 参数不足。\n"));
|
||||
CString strRequiredArgs;
|
||||
strRequiredArgs.Format(_T("[pid] [%s]"), lpEntryArgName);
|
||||
PrintCommandUsageAndExamples(lpUsageName, strRequiredArgs.GetString(), lpEntryArgName);
|
||||
return nArgLackErrorCode;
|
||||
}
|
||||
|
||||
UINT nPid = 0;
|
||||
int nArgRet = ParseUInt16ArgOrError(argv, 2, _T("pid"), nPid, nPidErrorCode);
|
||||
if (nArgRet != 0)
|
||||
{
|
||||
return nArgRet;
|
||||
}
|
||||
|
||||
CString strMainOrModulePy = argv[3];
|
||||
LPCTSTR lpRepoPath = NULL;
|
||||
BOOL bUseDefaultTagWhenMissing = FALSE;
|
||||
CString strExtraArgs;
|
||||
int nRepoArgRet = ParseNuitkaOptions(argc, argv, lpRepoPath, bUseDefaultTagWhenMissing, strExtraArgs);
|
||||
if (nRepoArgRet != 0)
|
||||
{
|
||||
return nRepoArgRet;
|
||||
}
|
||||
|
||||
CString strProductVersion;
|
||||
CString strFileVersion;
|
||||
VersionBuildErrorCodes errorCodes = { nBidErrorCode, nTagErrorCode, nCommitErrorCode };
|
||||
int nVersionRet = BuildVersionsFromRepo(
|
||||
lpRepoPath,
|
||||
nPid,
|
||||
errorCodes,
|
||||
strProductVersion,
|
||||
strFileVersion,
|
||||
bUseDefaultTagWhenMissing,
|
||||
DEFAULT_MAJOR_WHEN_NO_TAG_FOR_NUITKA,
|
||||
DEFAULT_MINOR_WHEN_NO_TAG_FOR_NUITKA);
|
||||
if (nVersionRet != 0)
|
||||
{
|
||||
return nVersionRet;
|
||||
}
|
||||
|
||||
CString strNuitkaCmd;
|
||||
if (bBuildModule)
|
||||
{
|
||||
strNuitkaCmd.Format(
|
||||
_T("cmd /c python -m nuitka --module --windows-product-version=%s --windows-file-version=%s %s %s"),
|
||||
strProductVersion.GetString(),
|
||||
strFileVersion.GetString(),
|
||||
strExtraArgs.GetString(),
|
||||
QuoteCmdArg(strMainOrModulePy).GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
strNuitkaCmd.Format(
|
||||
_T("cmd /c python -m nuitka --windows-product-version=%s --windows-file-version=%s %s %s"),
|
||||
strProductVersion.GetString(),
|
||||
strFileVersion.GetString(),
|
||||
strExtraArgs.GetString(),
|
||||
QuoteCmdArg(strMainOrModulePy).GetString());
|
||||
}
|
||||
|
||||
RunNuitkaBuild(lpStartMessage, strNuitkaCmd, strProductVersion, strFileVersion, lpRepoPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HandleNuitkaBuildCommand(int argc, TCHAR* argv[])
|
||||
{
|
||||
return HandleNuitkaBuildCommandCore(
|
||||
argc,
|
||||
argv,
|
||||
FALSE,
|
||||
_T("Nuitka 打包开始.."),
|
||||
_T("nuitkabuild"),
|
||||
_T("mainPy"),
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21);
|
||||
}
|
||||
|
||||
int HandleNuitkaPydBuildCommand(int argc, TCHAR* argv[])
|
||||
{
|
||||
return HandleNuitkaBuildCommandCore(
|
||||
argc,
|
||||
argv,
|
||||
TRUE,
|
||||
_T("Nuitka pyd 打包开始.."),
|
||||
_T("nuitkapydbuild"),
|
||||
_T("modulePy"),
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28);
|
||||
}
|
||||
4
GitVer/GitVer_nuitka.h
Normal file
4
GitVer/GitVer_nuitka.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
int HandleNuitkaBuildCommand(int argc, TCHAR* argv[]);
|
||||
int HandleNuitkaPydBuildCommand(int argc, TCHAR* argv[]);
|
||||
170
GitVer/GitVer_process.cpp
Normal file
170
GitVer/GitVer_process.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_process.h"
|
||||
|
||||
BOOL ReplaceFileContent(LPCTSTR lpFile, std::vector<std::string>& vtOldContent, std::vector<std::string>& vtNewContent)
|
||||
{
|
||||
if (vtOldContent.size() != vtNewContent.size())
|
||||
{
|
||||
_tprintf(_T("错误: 旧内容和新内容的数量不匹配。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!PathFileExists(lpFile))
|
||||
{
|
||||
_tprintf(_T("错误: 文件不存在 %s\n"), lpFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CFile myFile;
|
||||
CFileException fileExp;
|
||||
if (!myFile.Open(lpFile, CFile::modeReadWrite, &fileExp))
|
||||
{
|
||||
_tprintf(_T("错误: 打开文件失败 %s\n"), lpFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_tprintf(_T("成功: 打开文件 %s\n"), lpFile);
|
||||
DWORD dwFileLength = myFile.GetLength();
|
||||
BYTE* pData = new BYTE[dwFileLength];
|
||||
memset(pData, 0, dwFileLength);
|
||||
myFile.Read(pData, dwFileLength);
|
||||
|
||||
std::string strContent;
|
||||
strContent.append((char*)pData, dwFileLength);
|
||||
delete[]pData;
|
||||
pData = NULL;
|
||||
myFile.Close();
|
||||
|
||||
for (std::vector<std::string>::iterator it_old = vtOldContent.begin(); it_old != vtOldContent.end(); ++it_old)
|
||||
{
|
||||
if (strContent.find(it_old->c_str()) == std::string::npos)
|
||||
{
|
||||
_tprintf(_T("错误: 未找到要替换的内容 %s\n"), it_old->c_str());
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it_old;
|
||||
std::vector<std::string>::iterator it_new;
|
||||
for (it_old = vtOldContent.begin(), it_new = vtNewContent.begin(); it_old != vtOldContent.end() && it_new != vtNewContent.end(); it_old++, it_new++)
|
||||
{
|
||||
std::string::size_type nPos = strContent.find(it_old->c_str());
|
||||
if (nPos != std::string::npos)
|
||||
{
|
||||
strContent.replace(nPos, it_old->size(), it_new->c_str());
|
||||
_tprintf(_T("成功: 替换 %s -> %s\n"), it_old->c_str(), it_new->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!myFile.Open(lpFile, CFile::modeCreate | CFile::modeWrite, &fileExp))
|
||||
{
|
||||
_tprintf(_T("错误: 打开文件失败 %s\n"), lpFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
myFile.Write(strContent.c_str(), strContent.size());
|
||||
myFile.Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
CString StartProcess(LPCTSTR program, LPCTSTR args, LPCTSTR lpCurrentDirectory)
|
||||
{
|
||||
CString strValue = _T("");
|
||||
const int MY_PIPE_BUFFER_SIZE = 8912;
|
||||
CString strMutableArgs = args == NULL ? _T("") : args;
|
||||
LPTSTR lpArgs = strMutableArgs.IsEmpty() ? NULL : strMutableArgs.GetBuffer();
|
||||
HANDLE hPipeRead = NULL;
|
||||
HANDLE hPipeWrite = NULL;
|
||||
SECURITY_ATTRIBUTES saOutPipe;
|
||||
::ZeroMemory(&saOutPipe, sizeof(saOutPipe));
|
||||
saOutPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saOutPipe.lpSecurityDescriptor = NULL;
|
||||
saOutPipe.bInheritHandle = TRUE;
|
||||
if (CreatePipe(&hPipeRead, &hPipeWrite, &saOutPipe, MY_PIPE_BUFFER_SIZE))
|
||||
{
|
||||
PROCESS_INFORMATION processInfo;
|
||||
::ZeroMemory(&processInfo, sizeof(processInfo));
|
||||
STARTUPINFO startupInfo;
|
||||
::ZeroMemory(&startupInfo, sizeof(startupInfo));
|
||||
startupInfo.cb = sizeof(STARTUPINFO);
|
||||
startupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||
startupInfo.hStdOutput = hPipeWrite;
|
||||
startupInfo.hStdError = hPipeWrite;
|
||||
startupInfo.wShowWindow = SW_HIDE;
|
||||
|
||||
BOOL bRet = ::CreateProcess(program,
|
||||
lpArgs,
|
||||
NULL,
|
||||
NULL,
|
||||
TRUE,
|
||||
0,
|
||||
NULL,
|
||||
lpCurrentDirectory,
|
||||
&startupInfo,
|
||||
&processInfo);
|
||||
if (bRet == 0)
|
||||
{
|
||||
int nRet = GetLastError();
|
||||
_tprintf(_T("错误: 创建进程失败,错误码 %d\n"), nRet);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hPipeWrite)
|
||||
{
|
||||
CloseHandle(hPipeWrite);
|
||||
hPipeWrite = NULL;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char szPipeOut[MY_PIPE_BUFFER_SIZE];
|
||||
::ZeroMemory(szPipeOut, sizeof(szPipeOut));
|
||||
|
||||
DWORD dwStdLen = 0;
|
||||
if (!ReadFile(hPipeRead, szPipeOut, MY_PIPE_BUFFER_SIZE - 1, &dwStdLen, NULL) || dwStdLen == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
szPipeOut[dwStdLen] = '\0';
|
||||
#ifdef UNICODE
|
||||
strValue.AppendFormat(_T("%hs"), szPipeOut);
|
||||
#else
|
||||
strValue += szPipeOut;
|
||||
#endif
|
||||
}
|
||||
|
||||
WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||
}
|
||||
if (processInfo.hProcess)
|
||||
{
|
||||
CloseHandle(processInfo.hProcess);
|
||||
}
|
||||
if (processInfo.hThread)
|
||||
{
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
}
|
||||
if (hPipeRead)
|
||||
{
|
||||
CloseHandle(hPipeRead);
|
||||
}
|
||||
if (hPipeWrite)
|
||||
{
|
||||
CloseHandle(hPipeWrite);
|
||||
}
|
||||
if (!strMutableArgs.IsEmpty())
|
||||
{
|
||||
strMutableArgs.ReleaseBuffer();
|
||||
}
|
||||
|
||||
_tprintf(_T("StartProcess program=%s args=%s cwd=%s output=%s\n"),
|
||||
program == NULL ? _T("<null>") : program,
|
||||
args == NULL ? _T("<null>") : args,
|
||||
lpCurrentDirectory == NULL ? _T("<null>") : lpCurrentDirectory,
|
||||
strValue.GetString());
|
||||
|
||||
return strValue;
|
||||
}
|
||||
4
GitVer/GitVer_process.h
Normal file
4
GitVer/GitVer_process.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
BOOL ReplaceFileContent(LPCTSTR lpFile, std::vector<std::string>& vtOldContent, std::vector<std::string>& vtNewContent);
|
||||
CString StartProcess(LPCTSTR program, LPCTSTR args, LPCTSTR lpCurrentDirectory);
|
||||
442
GitVer/GitVer_rewrite.cpp
Normal file
442
GitVer/GitVer_rewrite.cpp
Normal file
@@ -0,0 +1,442 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_rewrite.h"
|
||||
#include "gitver_types.h"
|
||||
|
||||
extern TCHAR g_szCurModuleDir[MAX_PATH];
|
||||
|
||||
std::string ToAnsiString(LPCTSTR lpText);
|
||||
BOOL FindPreferredCppRcFileAt(LPCTSTR lpBaseDir, CString& strResFile);
|
||||
BOOL FindPreferredAssemblyInfoFileAt(LPCTSTR lpBaseDir, CString& strResFile);
|
||||
int DetectProjectCodeType(LPCTSTR lpBaseDir);
|
||||
LPCTSTR GetProjectCodeTypeName(int nCodeType);
|
||||
|
||||
/// <summary>
|
||||
/// 替换指定范围内包含指定标记的整行内容。
|
||||
/// </summary>
|
||||
BOOL ReplaceWholeLineByTokenInRange(std::string& strContent, const std::string& strToken, const std::string& strNewLine, size_t nRangeStart, size_t nRangeEnd)
|
||||
{
|
||||
if (nRangeStart == std::string::npos || nRangeEnd == std::string::npos || nRangeStart >= nRangeEnd)
|
||||
{
|
||||
_tprintf(_T("错误: 无效的范围参数。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::string::size_type nPos = strContent.find(strToken, nRangeStart);
|
||||
if (nPos == std::string::npos || nPos >= nRangeEnd)
|
||||
{
|
||||
_tprintf(_T("错误: 未找到指定的标记 %s。\n"), strToken.c_str());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::string::size_type nLineStart = strContent.rfind('\n', nPos);
|
||||
if (nLineStart == std::string::npos)
|
||||
{
|
||||
nLineStart = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++nLineStart;
|
||||
}
|
||||
|
||||
std::string::size_type nLineEnd = strContent.find('\n', nPos);
|
||||
if (nLineEnd == std::string::npos)
|
||||
{
|
||||
nLineEnd = strContent.size();
|
||||
}
|
||||
|
||||
if (nLineEnd > nLineStart && strContent[nLineEnd - 1] == '\r')
|
||||
{
|
||||
--nLineEnd;
|
||||
}
|
||||
|
||||
strContent.replace(nLineStart, nLineEnd - nLineStart, strNewLine);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ReplaceWholeLineByToken(std::string& strContent, const std::string& strToken, const std::string& strNewLine)
|
||||
{
|
||||
return ReplaceWholeLineByTokenInRange(strContent, strToken, strNewLine, 0, strContent.size());
|
||||
}
|
||||
|
||||
BOOL EnsureSingleLineByToken(std::string& strContent, const std::string& strToken, const std::string& strNewLine)
|
||||
{
|
||||
std::string::size_type nPos = strContent.find(strToken);
|
||||
if (nPos == std::string::npos)
|
||||
{
|
||||
_tprintf(_T("错误: 未找到指定的标记 %s。\n"), strToken.c_str());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::string::size_type nLineStart = strContent.rfind('\n', nPos);
|
||||
if (nLineStart == std::string::npos)
|
||||
{
|
||||
nLineStart = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++nLineStart;
|
||||
}
|
||||
|
||||
std::string::size_type nLineEnd = strContent.find('\n', nPos);
|
||||
if (nLineEnd == std::string::npos)
|
||||
{
|
||||
nLineEnd = strContent.size();
|
||||
}
|
||||
|
||||
if (nLineEnd > nLineStart && strContent[nLineEnd - 1] == '\r')
|
||||
{
|
||||
--nLineEnd;
|
||||
}
|
||||
|
||||
strContent.replace(nLineStart, nLineEnd - nLineStart, strNewLine);
|
||||
|
||||
std::string::size_type nSearchPos = nLineStart + strNewLine.size();
|
||||
for (;;)
|
||||
{
|
||||
std::string::size_type nDupPos = strContent.find(strToken, nSearchPos);
|
||||
if (nDupPos == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::string::size_type nDupLineStart = strContent.rfind('\n', nDupPos);
|
||||
if (nDupLineStart == std::string::npos)
|
||||
{
|
||||
nDupLineStart = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++nDupLineStart;
|
||||
}
|
||||
|
||||
std::string::size_type nDupLineEnd = strContent.find('\n', nDupPos);
|
||||
if (nDupLineEnd == std::string::npos)
|
||||
{
|
||||
nDupLineEnd = strContent.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
++nDupLineEnd;
|
||||
}
|
||||
|
||||
strContent.erase(nDupLineStart, nDupLineEnd - nDupLineStart);
|
||||
nSearchPos = nDupLineStart;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL EnsureSingleLineByTokenOrAppend(std::string& strContent, const std::string& strToken, const std::string& strNewLine)
|
||||
{
|
||||
if (strContent.find(strToken) != std::string::npos)
|
||||
{
|
||||
return EnsureSingleLineByToken(strContent, strToken, strNewLine);
|
||||
}
|
||||
|
||||
if (!strContent.empty())
|
||||
{
|
||||
if (strContent.rfind("\r\n") != strContent.size() - 2 && strContent[strContent.size() - 1] != '\n')
|
||||
{
|
||||
strContent += "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
strContent += strNewLine;
|
||||
strContent += "\r\n";
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL FindVersionInfoBlockRange(const std::string& strContent, size_t& nBlockStart, size_t& nBlockEnd)
|
||||
{
|
||||
nBlockStart = std::string::npos;
|
||||
nBlockEnd = std::string::npos;
|
||||
|
||||
std::string::size_type nVersionPos = strContent.find("VS_VERSION_INFO VERSIONINFO");
|
||||
if (nVersionPos == std::string::npos)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::string::size_type nStart = strContent.rfind('\n', nVersionPos);
|
||||
nBlockStart = (nStart == std::string::npos) ? 0 : (nStart + 1);
|
||||
|
||||
BOOL bSeenBegin = FALSE;
|
||||
int nDepth = 0;
|
||||
|
||||
std::string::size_type nLineStart = nBlockStart;
|
||||
while (nLineStart < strContent.size())
|
||||
{
|
||||
std::string::size_type nNextLine = strContent.find('\n', nLineStart);
|
||||
std::string::size_type nLineEnd = (nNextLine == std::string::npos) ? strContent.size() : nNextLine;
|
||||
std::string strLine = strContent.substr(nLineStart, nLineEnd - nLineStart);
|
||||
if (!strLine.empty() && strLine[strLine.size() - 1] == '\r')
|
||||
{
|
||||
strLine.erase(strLine.size() - 1);
|
||||
}
|
||||
|
||||
if (!bSeenBegin)
|
||||
{
|
||||
if (strLine.find("BEGIN") != std::string::npos)
|
||||
{
|
||||
bSeenBegin = TRUE;
|
||||
nDepth = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strLine.find("BEGIN") != std::string::npos)
|
||||
{
|
||||
++nDepth;
|
||||
}
|
||||
if (strLine.find("END") != std::string::npos)
|
||||
{
|
||||
--nDepth;
|
||||
if (nDepth == 0)
|
||||
{
|
||||
nBlockEnd = (nNextLine == std::string::npos) ? strContent.size() : (nNextLine + 1);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nNextLine == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
nLineStart = nNextLine + 1;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL DetectSupportedTextBom(const std::string& strData, size_t& nBomLength)
|
||||
{
|
||||
nBomLength = 0;
|
||||
if (strData.size() >= 3
|
||||
&& (unsigned char)strData[0] == 0xEF
|
||||
&& (unsigned char)strData[1] == 0xBB
|
||||
&& (unsigned char)strData[2] == 0xBF)
|
||||
{
|
||||
nBomLength = 3;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (strData.size() >= 2)
|
||||
{
|
||||
unsigned char b0 = (unsigned char)strData[0];
|
||||
unsigned char b1 = (unsigned char)strData[1];
|
||||
if ((b0 == 0xFF && b1 == 0xFE) || (b0 == 0xFE && b1 == 0xFF))
|
||||
{
|
||||
_tprintf(_T("错误: 不支持 UTF-16 编码的文本文件。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ReadTextFileAsAnsi(LPCTSTR lpFile, std::string& strContent, std::string& strBomPrefix)
|
||||
{
|
||||
strContent.clear();
|
||||
strBomPrefix.clear();
|
||||
if (!PathFileExists(lpFile))
|
||||
{
|
||||
_tprintf(_T("错误: 文件不存在 %s\n"), lpFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CFile myFile;
|
||||
CFileException fileExp;
|
||||
if (!myFile.Open(lpFile, CFile::modeRead, &fileExp))
|
||||
{
|
||||
_tprintf(_T("错误: 打开文件失败 %s\n"), lpFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwFileLength = myFile.GetLength();
|
||||
BYTE* pData = new BYTE[dwFileLength];
|
||||
memset(pData, 0, dwFileLength);
|
||||
myFile.Read(pData, dwFileLength);
|
||||
myFile.Close();
|
||||
|
||||
std::string strRaw((char*)pData, dwFileLength);
|
||||
delete[] pData;
|
||||
pData = NULL;
|
||||
|
||||
size_t nBomLength = 0;
|
||||
if (!DetectSupportedTextBom(strRaw, nBomLength))
|
||||
{
|
||||
_tprintf(_T("错误: 检测不支持的文本 BOM。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (nBomLength > 0)
|
||||
{
|
||||
strBomPrefix = strRaw.substr(0, nBomLength);
|
||||
strContent = strRaw.substr(nBomLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
strContent = strRaw;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WriteTextFileAsAnsi(LPCTSTR lpFile, const std::string& strContent, const std::string& strBomPrefix)
|
||||
{
|
||||
CFile myFile;
|
||||
CFileException fileExp;
|
||||
if (!myFile.Open(lpFile, CFile::modeCreate | CFile::modeWrite, &fileExp))
|
||||
{
|
||||
_tprintf(_T("错误: 打开文件失败 %s\n"), lpFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!strBomPrefix.empty())
|
||||
{
|
||||
myFile.Write(strBomPrefix.c_str(), strBomPrefix.size());
|
||||
}
|
||||
myFile.Write(strContent.c_str(), strContent.size());
|
||||
myFile.Close();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL RewriteSetVerToCppRc(const CString& strRcFile, const CString& strProductVersion, const CString& strFileVersion)
|
||||
{
|
||||
std::string strContent;
|
||||
std::string strBomPrefix;
|
||||
if (!ReadTextFileAsAnsi(strRcFile, strContent, strBomPrefix))
|
||||
{
|
||||
_tprintf(_T("错误: 读取 .rc 文件失败 %s\n"), strRcFile.GetString());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strFileVersionComma = strFileVersion;
|
||||
CString strProductVersionComma = strProductVersion;
|
||||
strFileVersionComma.Replace(_T('.'), _T(','));
|
||||
strProductVersionComma.Replace(_T('.'), _T(','));
|
||||
|
||||
std::string strLineFileVersion = ToAnsiString(CString(_T(" FILEVERSION ")) + strFileVersionComma);
|
||||
std::string strLineProductVersion = ToAnsiString(CString(_T(" PRODUCTVERSION ")) + strProductVersionComma);
|
||||
|
||||
CString strFileValueLine;
|
||||
strFileValueLine.Format(_T(" VALUE \"FileVersion\", \"%s\""), strFileVersion.GetString());
|
||||
CString strProductValueLine;
|
||||
strProductValueLine.Format(_T(" VALUE \"ProductVersion\", \"%s\""), strProductVersion.GetString());
|
||||
|
||||
size_t nVersionBlockStart = std::string::npos;
|
||||
size_t nVersionBlockEnd = std::string::npos;
|
||||
if (!FindVersionInfoBlockRange(strContent, nVersionBlockStart, nVersionBlockEnd))
|
||||
{
|
||||
_tprintf(_T("错误: 找不到版本信息块。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL bOk = TRUE;
|
||||
bOk = ReplaceWholeLineByTokenInRange(strContent, "FILEVERSION", strLineFileVersion, nVersionBlockStart, nVersionBlockEnd) && bOk;
|
||||
bOk = ReplaceWholeLineByTokenInRange(strContent, "PRODUCTVERSION", strLineProductVersion, nVersionBlockStart, nVersionBlockEnd) && bOk;
|
||||
bOk = ReplaceWholeLineByTokenInRange(strContent, "VALUE \"FileVersion\",", ToAnsiString(strFileValueLine), nVersionBlockStart, nVersionBlockEnd) && bOk;
|
||||
bOk = ReplaceWholeLineByTokenInRange(strContent, "VALUE \"ProductVersion\",", ToAnsiString(strProductValueLine), nVersionBlockStart, nVersionBlockEnd) && bOk;
|
||||
|
||||
if (!bOk)
|
||||
{
|
||||
_tprintf(_T("错误: 重写 .rc 文件版本信息失败。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!WriteTextFileAsAnsi(strRcFile, strContent, strBomPrefix))
|
||||
{
|
||||
_tprintf(_T("错误: 写入 .rc 文件失败 %s\n"), strRcFile.GetString());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_tprintf(_T("成功: 重写 .rc 文件版本信息 %s\n"), strRcFile.GetString());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL RewriteSetVerToAssemblyInfo(const CString& strAssemblyInfoFile, const CString& strProductVersion, const CString& strFileVersion)
|
||||
{
|
||||
std::string strContent;
|
||||
std::string strBomPrefix;
|
||||
if (!ReadTextFileAsAnsi(strAssemblyInfoFile, strContent, strBomPrefix))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strAssemblyVersionLine;
|
||||
strAssemblyVersionLine.Format(_T("[assembly: AssemblyVersion(\"%s\")]"), strProductVersion.GetString());
|
||||
CString strAssemblyFileVersionLine;
|
||||
strAssemblyFileVersionLine.Format(_T("[assembly: AssemblyFileVersion(\"%s\")]"), strFileVersion.GetString());
|
||||
CString strAssemblyInformationalVersionLine;
|
||||
strAssemblyInformationalVersionLine.Format(_T("[assembly: AssemblyInformationalVersion(\"%s\")]"), strProductVersion.GetString());
|
||||
|
||||
BOOL bOk = TRUE;
|
||||
bOk = EnsureSingleLineByToken(strContent, "[assembly: AssemblyVersion(\"", ToAnsiString(strAssemblyVersionLine)) && bOk;
|
||||
bOk = EnsureSingleLineByToken(strContent, "[assembly: AssemblyFileVersion(\"", ToAnsiString(strAssemblyFileVersionLine)) && bOk;
|
||||
bOk = EnsureSingleLineByTokenOrAppend(strContent, "[assembly: AssemblyInformationalVersion(\"", ToAnsiString(strAssemblyInformationalVersionLine)) && bOk;
|
||||
|
||||
if (!bOk)
|
||||
{
|
||||
_tprintf(_T("错误: 重写 AssemblyInfo 文件版本信息失败。\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!WriteTextFileAsAnsi(strAssemblyInfoFile, strContent, strBomPrefix))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_tprintf(_T("成功: 重写 AssemblyInfo 文件版本信息 %s\n"), strAssemblyInfoFile.GetString());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int RewriteSetVerByProjectType(LPCTSTR lpRepoPath, const CString& strProductVersion, const CString& strFileVersion)
|
||||
{
|
||||
LPCTSTR lpBaseDir = lpRepoPath == NULL ? g_szCurModuleDir : lpRepoPath;
|
||||
int nCodeType = DetectProjectCodeType(lpBaseDir);
|
||||
_tprintf(_T("重写版本信息,项目路径: %s,项目类型: %s\n"),
|
||||
lpBaseDir == NULL ? _T("<null>") : lpBaseDir,
|
||||
GetProjectCodeTypeName(nCodeType));
|
||||
if (nCodeType == PROJECT_CPP)
|
||||
{
|
||||
CString strRcFile;
|
||||
if (!FindPreferredCppRcFileAt(lpBaseDir, strRcFile))
|
||||
{
|
||||
_tprintf(_T("错误: 找不到首选的 .rc 文件。\n"));
|
||||
return 35;
|
||||
}
|
||||
if (!RewriteSetVerToCppRc(strRcFile, strProductVersion, strFileVersion))
|
||||
{
|
||||
_tprintf(_T("错误: 重写 .rc 文件版本信息失败。\n"));
|
||||
return 35;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nCodeType == PROJECT_CSHARP)
|
||||
{
|
||||
CString strAssemblyInfoFile;
|
||||
if (!FindPreferredAssemblyInfoFileAt(lpBaseDir, strAssemblyInfoFile))
|
||||
{
|
||||
_tprintf(_T("错误: 找不到首选的 AssemblyInfo 文件。\n"));
|
||||
return 35;
|
||||
}
|
||||
if (!RewriteSetVerToAssemblyInfo(strAssemblyInfoFile, strProductVersion, strFileVersion))
|
||||
{
|
||||
_tprintf(_T("错误: 重写 AssemblyInfo 文件版本信息失败。\n"));
|
||||
return 35;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nCodeType == PROJECT_PYTHON)
|
||||
{
|
||||
_tprintf(_T("错误: 不支持的项目类型: Python。\n"));
|
||||
return 15;
|
||||
}
|
||||
|
||||
_tprintf(_T("错误: 不支持的项目类型。\n"));
|
||||
return 15;
|
||||
}
|
||||
3
GitVer/GitVer_rewrite.h
Normal file
3
GitVer/GitVer_rewrite.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int RewriteSetVerByProjectType(LPCTSTR lpRepoPath, const CString& strProductVersion, const CString& strFileVersion);
|
||||
235
GitVer/GitVer_tag.cpp
Normal file
235
GitVer/GitVer_tag.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_process.h"
|
||||
#include "gitver_version.h"
|
||||
#include "gitver_tag.h"
|
||||
|
||||
extern TCHAR g_szCurModuleDir[MAX_PATH];
|
||||
|
||||
const UINT DEFAULT_MAJOR_WHEN_NO_TAG_FOR_TAG = 1;
|
||||
const UINT DEFAULT_MINOR_WHEN_NO_TAG_FOR_TAG = 0;
|
||||
|
||||
BOOL GetRecentMajorMinorTags(LPCTSTR lpRepoPath, CString& strBranch, std::vector<BranchTagInfo>& vtTags, int nMaxCount)
|
||||
{
|
||||
vtTags.clear();
|
||||
if (nMaxCount <= 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
strBranch.Empty();
|
||||
if (!TryGetCurrentBranch(lpRepoPath, strBranch))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_tprintf(_T("获取最近标签列表:\n"));
|
||||
CString strTagList = StartProcess(NULL, _T("cmd /c git tag --list --sort=v:refname"), lpRepoPath);
|
||||
if (strTagList.IsEmpty())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
std::vector<BranchTagInfo> vtAllTags;
|
||||
int nCurPos = 0;
|
||||
CString strLine = strTagList.Tokenize(_T("\r\n"), nCurPos);
|
||||
while (!strLine.IsEmpty())
|
||||
{
|
||||
strLine.Trim();
|
||||
if (strLine.Left(6).CompareNoCase(_T("fatal:")) == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BranchTagInfo tagInfo;
|
||||
tagInfo.nMajor = 0;
|
||||
tagInfo.nMinor = 0;
|
||||
if (TryParseTagMajorMinor(strBranch, strLine, tagInfo.nMajor, tagInfo.nMinor))
|
||||
{
|
||||
tagInfo.strTag = strLine;
|
||||
vtAllTags.push_back(tagInfo);
|
||||
}
|
||||
|
||||
strLine = strTagList.Tokenize(_T("\r\n"), nCurPos);
|
||||
}
|
||||
|
||||
if (vtAllTags.empty())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int nStart = (int)vtAllTags.size() - nMaxCount;
|
||||
if (nStart < 0)
|
||||
{
|
||||
nStart = 0;
|
||||
}
|
||||
|
||||
for (int i = nStart; i < (int)vtAllTags.size(); ++i)
|
||||
{
|
||||
vtTags.push_back(vtAllTags[i]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int HandleCreateTagInteractive()
|
||||
{
|
||||
CString strBranch;
|
||||
LPCTSTR lpRepoPath = g_szCurModuleDir;
|
||||
_tprintf(_T("标签创建流程启动,仓库路径: %s\n"), lpRepoPath == NULL ? _T("<null>") : lpRepoPath);
|
||||
std::vector<BranchTagInfo> vtTags;
|
||||
if (!GetRecentMajorMinorTags(lpRepoPath, strBranch, vtTags, 3))
|
||||
{
|
||||
_tprintf(_T("错误: 获取最近标签失败\n"));
|
||||
return 30;
|
||||
}
|
||||
|
||||
UINT nNewMajor = 0;
|
||||
UINT nNewMinor = 0;
|
||||
if (vtTags.empty())
|
||||
{
|
||||
_tprintf(_T("当前分支 %s\n"), strBranch.GetString());
|
||||
_tprintf(_T("未找到符合 <当前分支>.major.minor 格式的历史标签。\n"));
|
||||
_tprintf(_T("请设置首个标签的默认 major/minor(直接回车将使用默认值)。\n"));
|
||||
|
||||
UINT nInputMajor = DEFAULT_MAJOR_WHEN_NO_TAG_FOR_TAG;
|
||||
UINT nInputMinor = DEFAULT_MINOR_WHEN_NO_TAG_FOR_TAG;
|
||||
|
||||
TCHAR szMajorInput[32] = { 0 };
|
||||
_tprintf(_T("请输入 major(默认 %u):\n"), DEFAULT_MAJOR_WHEN_NO_TAG_FOR_TAG);
|
||||
if (_fgetts(szMajorInput, sizeof(szMajorInput) / sizeof(TCHAR), stdin) == NULL)
|
||||
{
|
||||
_tprintf(_T("错误: 读取输入失败\n"));
|
||||
return 31;
|
||||
}
|
||||
|
||||
CString strMajorInput = szMajorInput;
|
||||
strMajorInput.Trim();
|
||||
if (!strMajorInput.IsEmpty() && !TryParseUInt16(strMajorInput, nInputMajor))
|
||||
{
|
||||
_tprintf(_T("错误: 无效的 major %s\n"), strMajorInput.GetString());
|
||||
return 32;
|
||||
}
|
||||
|
||||
TCHAR szMinorInput[32] = { 0 };
|
||||
_tprintf(_T("请输入 minor(默认 %u):\n"), DEFAULT_MINOR_WHEN_NO_TAG_FOR_TAG);
|
||||
if (_fgetts(szMinorInput, sizeof(szMinorInput) / sizeof(TCHAR), stdin) == NULL)
|
||||
{
|
||||
_tprintf(_T("错误: 读取输入失败\n"));
|
||||
return 31;
|
||||
}
|
||||
|
||||
CString strMinorInput = szMinorInput;
|
||||
strMinorInput.Trim();
|
||||
if (!strMinorInput.IsEmpty() && !TryParseUInt16(strMinorInput, nInputMinor))
|
||||
{
|
||||
_tprintf(_T("错误: 无效的 minor %s\n"), strMinorInput.GetString());
|
||||
return 32;
|
||||
}
|
||||
|
||||
nNewMajor = nInputMajor;
|
||||
nNewMinor = nInputMinor;
|
||||
_tprintf(_T("将使用起始版本: %s.%u.%u\n"), strBranch.GetString(), nNewMajor, nNewMinor);
|
||||
}
|
||||
else
|
||||
{
|
||||
const BranchTagInfo& lastTag = vtTags[vtTags.size() - 1];
|
||||
_tprintf(_T("当前分支 %s\n"), strBranch.GetString());
|
||||
_tprintf(_T("最近标签数量 %u\n"), (UINT)vtTags.size());
|
||||
for (size_t i = 0; i < vtTags.size(); ++i)
|
||||
{
|
||||
_tprintf(_T(" %u. %s\n"), (UINT)(i + 1), vtTags[i].strTag.GetString());
|
||||
}
|
||||
|
||||
_tprintf(_T("最近标签 %s\n"), lastTag.strTag.GetString());
|
||||
_tprintf(_T("请选择操作:\n"));
|
||||
_tprintf(_T(" 1. 增加主版本号\n"));
|
||||
_tprintf(_T(" 2. 增加次版本号\n"));
|
||||
_tprintf(_T("请输入 1 或 2:\n"));
|
||||
|
||||
TCHAR szInput[32] = { 0 };
|
||||
if (_fgetts(szInput, sizeof(szInput) / sizeof(TCHAR), stdin) == NULL)
|
||||
{
|
||||
_tprintf(_T("错误: 读取输入失败\n"));
|
||||
return 31;
|
||||
}
|
||||
|
||||
CString strChoice = szInput;
|
||||
strChoice.Trim();
|
||||
|
||||
nNewMajor = lastTag.nMajor;
|
||||
nNewMinor = lastTag.nMinor;
|
||||
if (strChoice == _T("1"))
|
||||
{
|
||||
if (nNewMajor >= 65534)
|
||||
{
|
||||
_tprintf(_T("错误: 主版本号超过最大值 %u\n"), nNewMajor);
|
||||
return 32;
|
||||
}
|
||||
++nNewMajor;
|
||||
nNewMinor = 0;
|
||||
}
|
||||
else if (strChoice == _T("2"))
|
||||
{
|
||||
if (nNewMinor >= 65534)
|
||||
{
|
||||
_tprintf(_T("错误: 次版本号超过最大值 %u\n"), nNewMinor);
|
||||
return 32;
|
||||
}
|
||||
++nNewMinor;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tprintf(_T("错误: 无效的选择 %s\n"), strChoice.GetString());
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
CString strNewTag;
|
||||
strNewTag.Format(_T("%s.%u.%u"), strBranch.GetString(), nNewMajor, nNewMinor);
|
||||
_tprintf(_T("待创建标签: %s\n"), strNewTag.GetString());
|
||||
_tprintf(_T("确认创建请输入 y 或 yes,其它任意输入将取消:\n"));
|
||||
|
||||
TCHAR szConfirm[32] = { 0 };
|
||||
if (_fgetts(szConfirm, sizeof(szConfirm) / sizeof(TCHAR), stdin) == NULL)
|
||||
{
|
||||
_tprintf(_T("错误: 读取输入失败\n"));
|
||||
return 31;
|
||||
}
|
||||
|
||||
CString strConfirm = szConfirm;
|
||||
strConfirm.Trim();
|
||||
if (strConfirm.CompareNoCase(_T("y")) != 0 && strConfirm.CompareNoCase(_T("yes")) != 0)
|
||||
{
|
||||
_tprintf(_T("错误: 未确认创建标签 %s\n"), strNewTag.GetString());
|
||||
return 0;
|
||||
}
|
||||
|
||||
CString strCreateCmd;
|
||||
strCreateCmd.Format(_T("cmd /c git tag %s"), QuoteCmdArg(strNewTag).GetString());
|
||||
CString strCreateResult = StartProcess(NULL, strCreateCmd.GetBuffer(), lpRepoPath);
|
||||
strCreateCmd.ReleaseBuffer();
|
||||
strCreateResult.Trim();
|
||||
if (strCreateResult.Left(6).CompareNoCase(_T("fatal:")) == 0 || strCreateResult.Left(6).CompareNoCase(_T("error:")) == 0)
|
||||
{
|
||||
_tprintf(_T("错误: 创建标签失败 %s\n"), strCreateResult.GetString());
|
||||
return 33;
|
||||
}
|
||||
|
||||
CString strVerifyCmd;
|
||||
strVerifyCmd.Format(_T("cmd /c git tag --list %s"), QuoteCmdArg(strNewTag).GetString());
|
||||
CString strVerifyResult = StartProcess(NULL, strVerifyCmd.GetBuffer(), lpRepoPath);
|
||||
strVerifyCmd.ReleaseBuffer();
|
||||
strVerifyResult.Replace(_T("\r"), _T(""));
|
||||
strVerifyResult.Replace(_T("\n"), _T(""));
|
||||
strVerifyResult.Trim();
|
||||
if (strVerifyResult.CompareNoCase(strNewTag) != 0)
|
||||
{
|
||||
_tprintf(_T("错误: 验证标签 %s 失败\n"), strNewTag.GetString());
|
||||
return 34;
|
||||
}
|
||||
|
||||
_tprintf(_T("成功: 创建标签 %s\n"), strNewTag.GetString());
|
||||
return 0;
|
||||
}
|
||||
11
GitVer/GitVer_tag.h
Normal file
11
GitVer/GitVer_tag.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
struct BranchTagInfo
|
||||
{
|
||||
CString strTag;
|
||||
UINT nMajor;
|
||||
UINT nMinor;
|
||||
};
|
||||
|
||||
BOOL GetRecentMajorMinorTags(LPCTSTR lpRepoPath, CString& strBranch, std::vector<BranchTagInfo>& vtTags, int nMaxCount);
|
||||
int HandleCreateTagInteractive();
|
||||
9
GitVer/GitVer_types.h
Normal file
9
GitVer/GitVer_types.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum ProjectCodeType
|
||||
{
|
||||
PROJECT_UNKNOWN = 0,
|
||||
PROJECT_CPP = 1,
|
||||
PROJECT_CSHARP = 2,
|
||||
PROJECT_PYTHON = 3,
|
||||
};
|
||||
244
GitVer/GitVer_version.cpp
Normal file
244
GitVer/GitVer_version.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_process.h"
|
||||
#include "gitver_version.h"
|
||||
|
||||
BOOL TryGetCurrentBranch(LPCTSTR lpRepoPath, CString& strBranch)
|
||||
{
|
||||
_tprintf(_T("获取当前分支名称:\n"));
|
||||
strBranch = StartProcess(NULL, _T("cmd /c git rev-parse --abbrev-ref HEAD"), lpRepoPath);
|
||||
strBranch.Replace(_T("\r"), _T(""));
|
||||
strBranch.Replace(_T("\n"), _T(""));
|
||||
strBranch.Trim();
|
||||
|
||||
if (strBranch.IsEmpty() || strBranch.Left(6).CompareNoCase(_T("fatal:")) == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL TryGetBidFromBranch(const CString& strBranch, UINT& nBid)
|
||||
{
|
||||
nBid = 0;
|
||||
if (strBranch.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strBranch.CompareNoCase(_T("main")) == 0 || strBranch.CompareNoCase(_T("master")) == 0)
|
||||
{
|
||||
nBid = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int nDotPos = strBranch.ReverseFind(_T('.'));
|
||||
if (nDotPos <= 0 || nDotPos >= strBranch.GetLength() - 1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strNum = strBranch.Mid(nDotPos + 1);
|
||||
return TryParseUInt16(strNum, nBid);
|
||||
}
|
||||
|
||||
BOOL TryParseTagMajorMinor(const CString& strBranch, const CString& strTag, UINT& nMajor, UINT& nMinor)
|
||||
{
|
||||
if (strBranch.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strPrefix = strBranch + _T(".");
|
||||
if (strTag.GetLength() <= strPrefix.GetLength())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strTag.Left(strPrefix.GetLength()).CompareNoCase(strPrefix) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strVersion = strTag.Mid(strPrefix.GetLength());
|
||||
int nDotPos = strVersion.Find(_T('.'));
|
||||
if (nDotPos <= 0 || nDotPos != strVersion.ReverseFind(_T('.')))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strMajor = strVersion.Left(nDotPos);
|
||||
CString strMinor = strVersion.Mid(nDotPos + 1);
|
||||
if (!TryParseUInt16(strMajor, nMajor) || !TryParseUInt16(strMinor, nMinor))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL GetLastMajorMinorTag(LPCTSTR lpRepoPath, const CString& strBranch, CString& strTag, UINT& nMajor, UINT& nMinor)
|
||||
{
|
||||
strTag.Empty();
|
||||
nMajor = 0;
|
||||
nMinor = 0;
|
||||
if (strBranch.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CString strTagList = StartProcess(NULL, _T("cmd /c git tag --list --sort=v:refname"), lpRepoPath);
|
||||
if (strTagList.IsEmpty())
|
||||
{
|
||||
_tprintf(_T("错误: 获取标签列表失败\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int nCurPos = 0;
|
||||
CString strLine = strTagList.Tokenize(_T("\r\n"), nCurPos);
|
||||
while (!strLine.IsEmpty())
|
||||
{
|
||||
strLine.Trim();
|
||||
if (strLine.Left(6).CompareNoCase(_T("fatal:")) == 0)
|
||||
{
|
||||
_tprintf(_T("错误: 获取标签列表失败 %s\n"), strLine.GetString());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT nTmpMajor = 0;
|
||||
UINT nTmpMinor = 0;
|
||||
if (TryParseTagMajorMinor(strBranch, strLine, nTmpMajor, nTmpMinor))
|
||||
{
|
||||
strTag = strLine;
|
||||
nMajor = nTmpMajor;
|
||||
nMinor = nTmpMinor;
|
||||
}
|
||||
|
||||
strLine = strTagList.Tokenize(_T("\r\n"), nCurPos);
|
||||
}
|
||||
|
||||
return !strTag.IsEmpty();
|
||||
}
|
||||
|
||||
void GetTodayFileVersionDate(UINT& nYY, UINT& nMMDD)
|
||||
{
|
||||
SYSTEMTIME stLocal = { 0 };
|
||||
::GetLocalTime(&stLocal);
|
||||
|
||||
nYY = stLocal.wYear % 100;
|
||||
nMMDD = (stLocal.wMonth + 10) * 100 + stLocal.wDay;
|
||||
}
|
||||
|
||||
BOOL GetTodayBranchCommitCount(LPCTSTR lpRepoPath, const CString& strBranch, UINT& nId)
|
||||
{
|
||||
nId = 0;
|
||||
if (strBranch.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SYSTEMTIME stLocal = { 0 };
|
||||
::GetLocalTime(&stLocal);
|
||||
|
||||
CString strDate;
|
||||
strDate.Format(_T("%04u-%02u-%02u"), stLocal.wYear, stLocal.wMonth, stLocal.wDay);
|
||||
|
||||
CString strCmd;
|
||||
strCmd.Format(
|
||||
_T("cmd /c git rev-list --count --since=\"%s 00:00:00\" --until=\"%s 23:59:59\" \"%s\""),
|
||||
strDate.GetString(),
|
||||
strDate.GetString(),
|
||||
strBranch.GetString());
|
||||
|
||||
CString strCount = StartProcess(NULL, strCmd.GetBuffer(), lpRepoPath);
|
||||
strCmd.ReleaseBuffer();
|
||||
|
||||
if (strCount.IsEmpty())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
strCount.Replace(_T("\r"), _T(""));
|
||||
strCount.Replace(_T("\n"), _T(""));
|
||||
strCount.Trim();
|
||||
|
||||
if (strCount.IsEmpty() || strCount.Left(6).CompareNoCase(_T("fatal:")) == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TryParseUInt16(strCount, nId);
|
||||
}
|
||||
|
||||
int BuildVersionsFromRepo(
|
||||
LPCTSTR lpRepoPath,
|
||||
UINT nPid,
|
||||
const VersionBuildErrorCodes& errorCodes,
|
||||
CString& strProductVersion,
|
||||
CString& strFileVersion,
|
||||
BOOL bUseDefaultTagWhenMissing,
|
||||
UINT nDefaultMajor,
|
||||
UINT nDefaultMinor)
|
||||
{
|
||||
_tprintf(_T("开始构建版本: 仓库路径=%s pid=%u 默认Tag(%s)=%u.%u\n"),
|
||||
lpRepoPath == NULL ? _T("<null>") : lpRepoPath,
|
||||
nPid,
|
||||
bUseDefaultTagWhenMissing ? _T("启用") : _T("关闭"),
|
||||
nDefaultMajor,
|
||||
nDefaultMinor);
|
||||
|
||||
UINT nBid = 0;
|
||||
CString strBranch;
|
||||
if (!TryGetCurrentBranch(lpRepoPath, strBranch))
|
||||
{
|
||||
_tprintf(_T("错误: 获取当前分支失败。\n"));
|
||||
return errorCodes.nBidErrorCode;
|
||||
}
|
||||
_tprintf(_T("当前分支: %s\n"), strBranch.GetString());
|
||||
|
||||
if (!TryGetBidFromBranch(strBranch, nBid))
|
||||
{
|
||||
_tprintf(_T("错误: 从分支获取 bid 失败。\n"));
|
||||
return errorCodes.nBidErrorCode;
|
||||
}
|
||||
_tprintf(_T("分支 bid: %u\n"), nBid);
|
||||
|
||||
CString strLastTag;
|
||||
UINT nMajor = 0;
|
||||
UINT nMinor = 0;
|
||||
if (!GetLastMajorMinorTag(lpRepoPath, strBranch, strLastTag, nMajor, nMinor))
|
||||
{
|
||||
if (!bUseDefaultTagWhenMissing)
|
||||
{
|
||||
_tprintf(_T("错误: 获取标签失败。\n"));
|
||||
return errorCodes.nTagErrorCode;
|
||||
}
|
||||
|
||||
nMajor = nDefaultMajor;
|
||||
nMinor = nDefaultMinor;
|
||||
_tprintf(_T("使用默认标签 %u %u\n"), nMajor, nMinor);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tprintf(_T("命中最新标签: %s (major=%u minor=%u)\n"), strLastTag.GetString(), nMajor, nMinor);
|
||||
}
|
||||
|
||||
UINT nYY = 0;
|
||||
UINT nMMDD = 0;
|
||||
GetTodayFileVersionDate(nYY, nMMDD);
|
||||
|
||||
UINT nId = 0;
|
||||
if (!GetTodayBranchCommitCount(lpRepoPath, strBranch, nId))
|
||||
{
|
||||
_tprintf(_T("错误: 获取今日分支提交计数失败。\n"));
|
||||
return errorCodes.nCommitErrorCode;
|
||||
}
|
||||
_tprintf(_T("今日提交计数: %u\n"), nId);
|
||||
|
||||
strProductVersion.Format(_T("%u.%u.%u.%u"), nPid, nBid, nMajor, nMinor);
|
||||
strFileVersion.Format(_T("%u.%u.%u.%u"), nPid, nYY, nMMDD, nId);
|
||||
_tprintf(_T("版本构建完成: ProductVersion=%s FileVersion=%s\n"), strProductVersion.GetString(), strFileVersion.GetString());
|
||||
return 0;
|
||||
}
|
||||
24
GitVer/GitVer_version.h
Normal file
24
GitVer/GitVer_version.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
struct VersionBuildErrorCodes
|
||||
{
|
||||
int nBidErrorCode;
|
||||
int nTagErrorCode;
|
||||
int nCommitErrorCode;
|
||||
};
|
||||
|
||||
BOOL TryGetCurrentBranch(LPCTSTR lpRepoPath, CString& strBranch);
|
||||
BOOL TryGetBidFromBranch(const CString& strBranch, UINT& nBid);
|
||||
BOOL TryParseTagMajorMinor(const CString& strBranch, const CString& strTag, UINT& nMajor, UINT& nMinor);
|
||||
BOOL GetLastMajorMinorTag(LPCTSTR lpRepoPath, const CString& strBranch, CString& strTag, UINT& nMajor, UINT& nMinor);
|
||||
void GetTodayFileVersionDate(UINT& nYY, UINT& nMMDD);
|
||||
BOOL GetTodayBranchCommitCount(LPCTSTR lpRepoPath, const CString& strBranch, UINT& nId);
|
||||
int BuildVersionsFromRepo(
|
||||
LPCTSTR lpRepoPath,
|
||||
UINT nPid,
|
||||
const VersionBuildErrorCodes& errorCodes,
|
||||
CString& strProductVersion,
|
||||
CString& strFileVersion,
|
||||
BOOL bUseDefaultTagWhenMissing,
|
||||
UINT nDefaultMajor,
|
||||
UINT nDefaultMinor);
|
||||
BIN
GitVer/app.ico
Normal file
BIN
GitVer/app.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
26
GitVer/framework.h
Normal file
26
GitVer/framework.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 部分 CString 构造函数将是显式的
|
||||
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // 移除对话框中的 MFC 控件支持
|
||||
|
||||
#ifndef VC_EXTRALEAN
|
||||
#define VC_EXTRALEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
#endif
|
||||
|
||||
#include <afx.h>
|
||||
#include <afxwin.h> // MFC 核心组件和标准组件
|
||||
#include <afxext.h> // MFC 扩展
|
||||
#ifndef _AFX_NO_OLE_SUPPORT
|
||||
#include <afxdtctl.h> // MFC 对 Internet Explorer 4 公共控件的支持
|
||||
#endif
|
||||
#ifndef _AFX_NO_AFXCMN_SUPPORT
|
||||
#include <afxcmn.h> // MFC 对 Windows 公共控件的支持
|
||||
#endif // _AFX_NO_AFXCMN_SUPPORT
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <Shlwapi.h>
|
||||
5
GitVer/pch.cpp
Normal file
5
GitVer/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: 与预编译标头对应的源文件
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
|
||||
13
GitVer/pch.h
Normal file
13
GitVer/pch.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// pch.h: 这是预编译标头文件。
|
||||
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
|
||||
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
|
||||
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
|
||||
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// 添加要在此处预编译的标头
|
||||
#include "framework.h"
|
||||
|
||||
#endif //PCH_H
|
||||
18
GitVer/resource.h
Normal file
18
GitVer/resource.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ 生成的包含文件。
|
||||
// 供 GitVer.rc 使用
|
||||
//
|
||||
#define IDI_ICON_APP 100
|
||||
#define IDI_ICON1 101
|
||||
#define IDS_APP_TITLE 103
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
6
GitVer/targetver.h
Normal file
6
GitVer/targetver.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// // 包含 SDKDDKVer.h 可定义可用的最高版本的 Windows 平台。
|
||||
// 如果希望为之前的 Windows 平台构建应用程序,在包含 SDKDDKVer.h 之前请先包含 WinSDKVer.h 并
|
||||
// 将 _WIN32_WINNT 宏设置为想要支持的平台。
|
||||
#include <SDKDDKVer.h>
|
||||
Reference in New Issue
Block a user