1、支持pyinstaller打包方式
2、将python打包其他参数都放入params中
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_cli.h"
|
||||
#include "gitver_nuitka.h"
|
||||
#include "gitver_pyinstaller.h"
|
||||
#include "gitver_process.h"
|
||||
#include "gitver_rewrite.h"
|
||||
#include "gitver_tag.h"
|
||||
@@ -350,8 +351,9 @@ void PrintFullUsageExamples()
|
||||
_T(" gitver (显示帮助后进入当前分支创建 tag 流程)\n")
|
||||
_T(" gitver rewrite [PEType可选] [-f可选]\n")
|
||||
_T(" gitver setver=<pid> [repodir=<path>可选] [-test可选]\n")
|
||||
_T(" gitver nuitkabuild=<pid> <mainPy> [repodir=<path>可选] [-test可选] [params=\"<nuitka参数>\"可选]\n")
|
||||
_T(" gitver nuitkapydbuild=<pid> <modulePy> [repodir=<path>可选] [-test可选] [params=\"<nuitka参数>\"可选]\n")
|
||||
_T(" gitver nuitkabuild=<pid> [repodir=<path>可选] [-test可选] [params=\"<mainPy> <nuitka参数>\"可选]\n")
|
||||
_T(" gitver nuitkapydbuild=<pid> [repodir=<path>可选] [-test可选] [params=\"<modulePy> <nuitka参数>\"可选]\n")
|
||||
_T(" gitver pyinstaller=<pid> [repodir=<path>可选] [-test可选] [params=\"<mainPy|specFile> <pyinstaller参数>\"可选]\n")
|
||||
_T(" gitver -setup=0|1 [pid] [repodir=<path>可选] [-test可选]\n")
|
||||
_T(" -setup=0: 使用 Inno Setup 脚本 (setup.iss)\n")
|
||||
_T(" -setup=1: 使用 NSIS 脚本 (setup.nsh)\n")
|
||||
@@ -363,15 +365,21 @@ void PrintFullUsageExamples()
|
||||
_T(" gitver setver=5 repodir=E:\\Code\\OTH\\gitver\n")
|
||||
_T(" gitver setver=5\n")
|
||||
_T(" gitver setver=5 -test\n")
|
||||
_T(" gitver nuitkabuild=5 main.py\n")
|
||||
_T(" gitver nuitkabuild=5 main.py -test\n")
|
||||
_T(" gitver nuitkabuild=5 src\\app.py repodir=E:\\Code\\MyPyProj params=\"--standalone --output-dir=dist\"\n")
|
||||
_T(" gitver nuitkapydbuild=5 module.py\n")
|
||||
_T(" gitver nuitkapydbuild=5 module.py -test\n")
|
||||
_T(" gitver nuitkapydbuild=5 src\\core.py repodir=E:\\Code\\MyPyProj params=\"--output-dir=dist\"\n")
|
||||
_T(" gitver nuitkabuild=5 params=\"main.py\"\n")
|
||||
_T(" gitver nuitkabuild=5 -test params=\"main.py\"\n")
|
||||
_T(" gitver nuitkabuild=5 repodir=E:\\Code\\MyPyProj params=\"src\\\\app.py --standalone --output-dir=dist\"\n")
|
||||
_T(" gitver nuitkapydbuild=5 params=\"module.py\"\n")
|
||||
_T(" gitver nuitkapydbuild=5 -test params=\"module.py\"\n")
|
||||
_T(" gitver nuitkapydbuild=5 repodir=E:\\Code\\MyPyProj params=\"src\\\\core.py --output-dir=dist\"\n")
|
||||
_T(" gitver pyinstaller=5 params=\"main.py\"\n")
|
||||
_T(" gitver pyinstaller=5 -test params=\"main.py\"\n")
|
||||
_T(" gitver pyinstaller=5 repodir=E:\\Code\\MyPyProj params=\"src\\\\app.py --onefile --name=MyApp\"\n")
|
||||
_T(" gitver pyinstaller=5 params=\"myapp.spec\"\n")
|
||||
_T(" gitver pyinstaller=5 -test params=\"myapp.spec\"\n")
|
||||
_T(" gitver pyinstaller=5 repodir=E:\\Code\\MyPyProj params=\"myapp.spec\"\n")
|
||||
_T(" gitver -setup=0 5\n")
|
||||
_T(" gitver -setup=1 5 repodir=E:\\Code\\MyProj\n")
|
||||
_T("params: 传递给 Nuitka 的额外参数,用双引号括起来,如 params=\"--standalone --output-dir=dist\"。\n")
|
||||
_T("params: 传递给 Nuitka/PyInstaller 的参数(含入口文件),用双引号括起来,如 params=\"main.py --standalone --output-dir=dist\"。\n")
|
||||
_T("-test: 将产品版本号的 major 和 minor 都置为 0(用于测试版本构建)。\n")
|
||||
_T("无参数时会读取当前分支最近三次 tag,并提示选择 major 加 1 或 minor 加 1,然后创建新 tag。\n")
|
||||
_T("未找到当前分支 tag 时自动使用默认版本 1.0。\n");
|
||||
@@ -386,8 +394,9 @@ void PrintShortCommandUsage()
|
||||
{
|
||||
_tprintf(_T("请使用:gitver rewrite [PE类型可选]\n"));
|
||||
_tprintf(_T("请使用:gitver setver=<pid> [repodir=<path>可选]\n"));
|
||||
_tprintf(_T("或:gitver nuitkabuild=<pid> <mainPy> [repodir=<path>可选] [params=\"<nuitka参数>\"可选]\n"));
|
||||
_tprintf(_T("或:gitver nuitkapydbuild=<pid> <modulePy> [repodir=<path>可选] [params=\"<nuitka参数>\"可选]\n"));
|
||||
_tprintf(_T("或:gitver nuitkabuild=<pid> [repodir=<path>可选] [params=\"<mainPy> <nuitka参数>\"可选]\n"));
|
||||
_tprintf(_T("或:gitver nuitkapydbuild=<pid> [repodir=<path>可选] [params=\"<modulePy> <nuitka参数>\"可选]\n"));
|
||||
_tprintf(_T("或:gitver pyinstaller=<pid> [repodir=<path>可选] [params=\"<mainPy|specFile> <pyinstaller参数>\"可选]\n"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -482,6 +491,20 @@ LPCTSTR GetExitCodeHint(int nRetCode)
|
||||
return _T("setup: modify setup script failed");
|
||||
case 39:
|
||||
return _T("setup: compiler not found");
|
||||
case 40:
|
||||
return _T("pyinstaller: missing args");
|
||||
case 41:
|
||||
return _T("pyinstaller: invalid pid");
|
||||
case 42:
|
||||
return _T("pyinstaller: get branch or bid failed");
|
||||
case 43:
|
||||
return _T("pyinstaller: get tag failed");
|
||||
case 44:
|
||||
return _T("pyinstaller: get today commit count failed");
|
||||
case 45:
|
||||
return _T("pyinstaller: write version_info.txt failed");
|
||||
case 46:
|
||||
return _T("pyinstaller: inject version into spec file failed");
|
||||
default:
|
||||
return _T("");
|
||||
}
|
||||
@@ -891,6 +914,15 @@ int main(int argc, TCHAR* argv[], TCHAR* envp[])
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
if (argc >= 2 && _tcsnicmp(argv[1], _T("pyinstaller="), 12) == 0)
|
||||
{
|
||||
DWORD dwStartTick = LogCommandStart(_T("pyinstaller"));
|
||||
int nCmdRet = HandlePyinstallerBuildCommand(argc, argv);
|
||||
LogCommandEnd(_T("pyinstaller"), dwStartTick, nCmdRet);
|
||||
PrintCommandFailedWithCode(_T("pyinstaller"), nCmdRet);
|
||||
return nCmdRet;
|
||||
}
|
||||
|
||||
if (argc >= 2 && _tcsnicmp(argv[1], _T("-setup="), 7) == 0)
|
||||
{
|
||||
DWORD dwStartTick = LogCommandStart(_T("-setup"));
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
<ClInclude Include="GitVer_cli.h" />
|
||||
<ClInclude Include="GitVer_common.h" />
|
||||
<ClInclude Include="GitVer_nuitka.h" />
|
||||
<ClInclude Include="GitVer_pyinstaller.h" />
|
||||
<ClInclude Include="GitVer_process.h" />
|
||||
<ClInclude Include="GitVer_rewrite.h" />
|
||||
<ClInclude Include="GitVer_setup.h" />
|
||||
@@ -164,6 +165,7 @@
|
||||
<ClCompile Include="GitVer_cli.cpp" />
|
||||
<ClCompile Include="GitVer_common.cpp" />
|
||||
<ClCompile Include="GitVer_nuitka.cpp" />
|
||||
<ClCompile Include="GitVer_pyinstaller.cpp" />
|
||||
<ClCompile Include="GitVer_process.cpp" />
|
||||
<ClCompile Include="GitVer_rewrite.cpp" />
|
||||
<ClCompile Include="GitVer_setup.cpp" />
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
<ClInclude Include="GitVer_nuitka.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_pyinstaller.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GitVer_process.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
@@ -71,6 +74,9 @@
|
||||
<ClCompile Include="GitVer_nuitka.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_pyinstaller.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GitVer_process.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -19,7 +17,7 @@ static int ParseNuitkaOptions(int argc, TCHAR* argv[], LPCTSTR& lpRepoPath, CStr
|
||||
strExtraArgs.Empty();
|
||||
bTestMode = FALSE;
|
||||
|
||||
for (int i = 3; i < argc; ++i)
|
||||
for (int i = 2; i < argc; ++i)
|
||||
{
|
||||
CString strArg = argv[i];
|
||||
|
||||
@@ -95,8 +93,6 @@ static int HandleNuitkaBuildCommandCore(
|
||||
BOOL bBuildModule,
|
||||
LPCTSTR lpStartMessage,
|
||||
LPCTSTR lpUsageName,
|
||||
LPCTSTR lpEntryArgName,
|
||||
int nArgLackErrorCode,
|
||||
int nPidErrorCode,
|
||||
int nBidErrorCode,
|
||||
int nTagErrorCode,
|
||||
@@ -111,14 +107,6 @@ static int HandleNuitkaBuildCommandCore(
|
||||
return nPidErrorCode;
|
||||
}
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
_tprintf(_T("错误: 参数不足,缺少 %s。\n"), lpEntryArgName);
|
||||
_tprintf(_T("用法:gitver %s=<pid> <%s> [repodir=<path>可选] [-test可选] [params=\"<nuitka参数>\"\u53ef选]\n"), lpUsageName, lpEntryArgName);
|
||||
return nArgLackErrorCode;
|
||||
}
|
||||
|
||||
CString strMainOrModulePy = argv[2];
|
||||
LPCTSTR lpRepoPath = NULL;
|
||||
CString strExtraArgs;
|
||||
BOOL bTestMode = FALSE;
|
||||
@@ -159,20 +147,18 @@ static int HandleNuitkaBuildCommandCore(
|
||||
if (bBuildModule)
|
||||
{
|
||||
strNuitkaCmd.Format(
|
||||
_T("cmd /c python -m nuitka --module --windows-product-version=%s --windows-file-version=%s %s %s"),
|
||||
_T("cmd /c python -m nuitka --module --windows-product-version=%s --windows-file-version=%s %s"),
|
||||
strProductVersion.GetString(),
|
||||
strFileVersion.GetString(),
|
||||
strExtraArgs.GetString(),
|
||||
QuoteCmdArg(strMainOrModulePy).GetString());
|
||||
strExtraArgs.GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
strNuitkaCmd.Format(
|
||||
_T("cmd /c python -m nuitka --windows-product-version=%s --windows-file-version=%s %s %s"),
|
||||
_T("cmd /c python -m nuitka --windows-product-version=%s --windows-file-version=%s %s"),
|
||||
strProductVersion.GetString(),
|
||||
strFileVersion.GetString(),
|
||||
strExtraArgs.GetString(),
|
||||
QuoteCmdArg(strMainOrModulePy).GetString());
|
||||
strExtraArgs.GetString());
|
||||
}
|
||||
|
||||
RunNuitkaBuild(lpStartMessage, strNuitkaCmd, strProductVersion, strFileVersion, lpRepoPath);
|
||||
@@ -188,8 +174,6 @@ int HandleNuitkaBuildCommand(int argc, TCHAR* argv[])
|
||||
FALSE,
|
||||
_T("Nuitka 打包开始.."),
|
||||
_T("nuitkabuild"),
|
||||
_T("mainPy"),
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
@@ -205,8 +189,6 @@ int HandleNuitkaPydBuildCommand(int argc, TCHAR* argv[])
|
||||
TRUE,
|
||||
_T("Nuitka pyd 打包开始.."),
|
||||
_T("nuitkapydbuild"),
|
||||
_T("modulePy"),
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
|
||||
560
GitVer/GitVer_pyinstaller.cpp
Normal file
560
GitVer/GitVer_pyinstaller.cpp
Normal file
@@ -0,0 +1,560 @@
|
||||
#include "pch.h"
|
||||
#include "gitver.h"
|
||||
#include "gitver_cli.h"
|
||||
#include "gitver_common.h"
|
||||
#include "gitver_process.h"
|
||||
#include "gitver_version.h"
|
||||
#include "gitver_pyinstaller.h"
|
||||
|
||||
extern TCHAR g_szCurModuleDir[MAX_PATH];
|
||||
|
||||
const UINT DEFAULT_MAJOR_WHEN_NO_TAG_FOR_PYINSTALLER = 1;
|
||||
const UINT DEFAULT_MINOR_WHEN_NO_TAG_FOR_PYINSTALLER = 0;
|
||||
|
||||
static int ParsePyinstallerOptions(int argc, TCHAR* argv[], LPCTSTR& lpRepoPath, CString& strExtraArgs, BOOL& bTestMode)
|
||||
{
|
||||
lpRepoPath = NULL;
|
||||
strExtraArgs.Empty();
|
||||
bTestMode = FALSE;
|
||||
|
||||
for (int i = 2; i < argc; ++i)
|
||||
{
|
||||
CString strArg = argv[i];
|
||||
|
||||
if (strArg.CompareNoCase(_T("-test")) == 0)
|
||||
{
|
||||
bTestMode = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strArg.GetLength() > 8 && strArg.Left(8).CompareNoCase(_T("repodir=")) == 0)
|
||||
{
|
||||
if (lpRepoPath != NULL)
|
||||
{
|
||||
_tprintf(_T("错误: repodir 参数重复: %s\n"), argv[i]);
|
||||
return 23;
|
||||
}
|
||||
LPCTSTR lpPath = argv[i] + 8;
|
||||
if (!PathIsDirectory(lpPath))
|
||||
{
|
||||
PrintInvalidRepoPathError(lpPath);
|
||||
return 23;
|
||||
}
|
||||
lpRepoPath = lpPath;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strArg.GetLength() > 7 && strArg.Left(7).CompareNoCase(_T("params=")) == 0)
|
||||
{
|
||||
if (!strExtraArgs.IsEmpty())
|
||||
{
|
||||
_tprintf(_T("错误: params 参数重复。\n"));
|
||||
return 3;
|
||||
}
|
||||
CString strVal = strArg.Mid(7);
|
||||
// 去掉外层双引号(如 params="--onefile")
|
||||
if (strVal.GetLength() >= 2 && strVal[0] == _T('"') && strVal[strVal.GetLength() - 1] == _T('"'))
|
||||
strVal = strVal.Mid(1, strVal.GetLength() - 2);
|
||||
strExtraArgs = strVal;
|
||||
continue;
|
||||
}
|
||||
|
||||
_tprintf(_T("错误: 未知的参数 %s\n"), argv[i]);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (lpRepoPath == NULL)
|
||||
{
|
||||
lpRepoPath = g_szCurModuleDir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 解析版本字符串 "a.b.c.d" → 四个分量
|
||||
static BOOL ParseVersionTuple(LPCTSTR lpVer, UINT& v1, UINT& v2, UINT& v3, UINT& v4)
|
||||
{
|
||||
CString str(lpVer);
|
||||
int p1 = str.Find(_T('.'));
|
||||
if (p1 < 0) return FALSE;
|
||||
int p2 = str.Find(_T('.'), p1 + 1);
|
||||
if (p2 < 0) return FALSE;
|
||||
int p3 = str.Find(_T('.'), p2 + 1);
|
||||
if (p3 < 0) return FALSE;
|
||||
v1 = (UINT)_ttoi(str.Left(p1));
|
||||
v2 = (UINT)_ttoi(str.Mid(p1 + 1, p2 - p1 - 1));
|
||||
v3 = (UINT)_ttoi(str.Mid(p2 + 1, p3 - p2 - 1));
|
||||
v4 = (UINT)_ttoi(str.Mid(p3 + 1));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 提取 params 中第一个空格分隔的 token(支持双引号括起的路径)
|
||||
static CString ExtractFirstToken(const CString& strParams)
|
||||
{
|
||||
CString str = strParams;
|
||||
str.TrimLeft();
|
||||
if (str.IsEmpty()) return _T("");
|
||||
if (str[0] == _T('"'))
|
||||
{
|
||||
int nEnd = str.Find(_T('"'), 1);
|
||||
if (nEnd < 0) return str.Mid(1);
|
||||
return str.Mid(1, nEnd - 1);
|
||||
}
|
||||
int nSpace = str.Find(_T(' '));
|
||||
if (nSpace < 0) return str;
|
||||
return str.Left(nSpace);
|
||||
}
|
||||
|
||||
// 生成 version_info.txt 到 lpDir 目录(PyInstaller VSVersionInfo 格式)
|
||||
static BOOL WriteVersionInfoFile(LPCTSTR lpDir, LPCTSTR lpProductVersion, LPCTSTR lpFileVersion, CString& strOutPath)
|
||||
{
|
||||
UINT pv1 = 0, pv2 = 0, pv3 = 0, pv4 = 0;
|
||||
UINT fv1 = 0, fv2 = 0, fv3 = 0, fv4 = 0;
|
||||
if (!ParseVersionTuple(lpProductVersion, pv1, pv2, pv3, pv4)) return FALSE;
|
||||
if (!ParseVersionTuple(lpFileVersion, fv1, fv2, fv3, fv4)) return FALSE;
|
||||
|
||||
CStringA strProdVerA(lpProductVersion);
|
||||
CStringA strFileVerA(lpFileVersion);
|
||||
const char* szProdVer = strProdVerA;
|
||||
const char* szFileVer = strFileVerA;
|
||||
|
||||
strOutPath.Format(_T("%s\\version_info.txt"), lpDir);
|
||||
|
||||
FILE* fp = NULL;
|
||||
_tfopen_s(&fp, strOutPath.GetString(), _T("wb"));
|
||||
if (!fp) return FALSE;
|
||||
|
||||
fprintf(fp,
|
||||
"# UTF-8\n"
|
||||
"VSVersionInfo(\n"
|
||||
" ffi=FixedFileInfo(\n"
|
||||
" filevers=(%u, %u, %u, %u),\n"
|
||||
" prodvers=(%u, %u, %u, %u),\n"
|
||||
" mask=0x3f,\n"
|
||||
" flags=0x0,\n"
|
||||
" OS=0x40004,\n"
|
||||
" fileType=0x1,\n"
|
||||
" subtype=0x0,\n"
|
||||
" date=(0, 0)\n"
|
||||
" ),\n"
|
||||
" kids=[\n"
|
||||
" StringFileInfo([\n"
|
||||
" StringTable(\n"
|
||||
" u'040904B0',\n"
|
||||
" [StringStruct(u'CompanyName', u''),\n"
|
||||
" StringStruct(u'FileDescription', u''),\n"
|
||||
" StringStruct(u'FileVersion', u'%s'),\n"
|
||||
" StringStruct(u'InternalName', u''),\n"
|
||||
" StringStruct(u'LegalCopyright', u''),\n"
|
||||
" StringStruct(u'OriginalFilename', u''),\n"
|
||||
" StringStruct(u'ProductName', u''),\n"
|
||||
" StringStruct(u'ProductVersion', u'%s'),\n"
|
||||
" StringStruct(u'Comments', u''),\n"
|
||||
" StringStruct(u'LegalTrademarks', u''),\n"
|
||||
" ])\n"
|
||||
" ]),\n"
|
||||
" VarFileInfo([VarStruct(u'Translation', [0x0409, 1200])])\n"
|
||||
" ]\n"
|
||||
")\n",
|
||||
fv1, fv2, fv3, fv4,
|
||||
pv1, pv2, pv3, pv4,
|
||||
szFileVer,
|
||||
szProdVer);
|
||||
|
||||
fclose(fp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 向 .spec 文件注入或更新 version= 参数
|
||||
// 若已有 version= 则更新其值,否则在 name= 参数后新增一行
|
||||
static BOOL InjectVersionIntoSpec(LPCTSTR lpSpecPath)
|
||||
{
|
||||
if (!PathFileExists(lpSpecPath))
|
||||
{
|
||||
_tprintf(_T("错误: spec 文件不存在 %s\n"), lpSpecPath);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CFile myFile;
|
||||
CFileException fileEx;
|
||||
if (!myFile.Open(lpSpecPath, CFile::modeReadWrite, &fileEx))
|
||||
{
|
||||
_tprintf(_T("错误: 无法打开 spec 文件 %s\n"), lpSpecPath);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwLen = (DWORD)myFile.GetLength();
|
||||
std::vector<char> buf(dwLen + 1, 0);
|
||||
myFile.Read(buf.data(), dwLen);
|
||||
myFile.Close();
|
||||
|
||||
std::string strContent(buf.data(), dwLen);
|
||||
const std::string strNewValue = "version='version_info.txt'";
|
||||
|
||||
bool bFound = false;
|
||||
std::string::size_type nPos = 0;
|
||||
while ((nPos = strContent.find("version=", nPos)) != std::string::npos)
|
||||
{
|
||||
// 确保 version= 是独立参数(前驱字符为空白或换行,而非字母/数字)
|
||||
bool bIsParam = (nPos == 0
|
||||
|| strContent[nPos - 1] == '\n'
|
||||
|| strContent[nPos - 1] == '\r'
|
||||
|| strContent[nPos - 1] == ' '
|
||||
|| strContent[nPos - 1] == '\t');
|
||||
if (bIsParam)
|
||||
{
|
||||
// 获取行首(保留缩进)
|
||||
std::string::size_type nLineStart = strContent.rfind('\n', nPos);
|
||||
nLineStart = (nLineStart == std::string::npos) ? 0 : nLineStart + 1;
|
||||
std::string strIndent = strContent.substr(nLineStart, nPos - nLineStart);
|
||||
|
||||
// 获取行末
|
||||
std::string::size_type nLineEnd = strContent.find('\n', nPos);
|
||||
if (nLineEnd == std::string::npos) nLineEnd = strContent.size();
|
||||
|
||||
// 替换整行(保留缩进,统一加尾随逗号)
|
||||
std::string strNewLine = strIndent + strNewValue + ",";
|
||||
strContent.replace(nLineStart, nLineEnd - nLineStart, strNewLine);
|
||||
_tprintf(_T("成功: 已更新 spec 文件 version 字段 -> version_info.txt\n"));
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
nPos += 8; // len("version=")
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
{
|
||||
// 未找到 version=,在 name= 参数后插入新行
|
||||
std::string::size_type nNamePos = strContent.find("name=");
|
||||
if (nNamePos != std::string::npos)
|
||||
{
|
||||
// 获取 name= 行的缩进
|
||||
std::string::size_type nLineStart = strContent.rfind('\n', nNamePos);
|
||||
nLineStart = (nLineStart == std::string::npos) ? 0 : nLineStart + 1;
|
||||
std::string strIndent = strContent.substr(nLineStart, nNamePos - nLineStart);
|
||||
|
||||
// 找到 name= 行末
|
||||
std::string::size_type nLineEnd = strContent.find('\n', nNamePos);
|
||||
if (nLineEnd == std::string::npos) nLineEnd = strContent.size() - 1;
|
||||
|
||||
// 在 name= 行后插入 version=
|
||||
std::string strInsert = "\n" + strIndent + strNewValue + ",";
|
||||
strContent.insert(nLineEnd, strInsert);
|
||||
_tprintf(_T("成功: 已向 spec 文件新增 version 字段 -> version_info.txt\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
_tprintf(_T("警告: spec 文件中未找到 version= 或 name= 参数,跳过版本信息注入。\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// 写回文件
|
||||
if (!myFile.Open(lpSpecPath, CFile::modeCreate | CFile::modeWrite, &fileEx))
|
||||
{
|
||||
_tprintf(_T("错误: 无法写入 spec 文件 %s\n"), lpSpecPath);
|
||||
return FALSE;
|
||||
}
|
||||
myFile.Write(strContent.c_str(), (UINT)strContent.size());
|
||||
myFile.Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 从 .spec 文件读取 version= 参数的值(单/双引号括起的路径)
|
||||
// 返回 FALSE 表示未找到、值为 None 或值为空
|
||||
static BOOL ReadVersionPathFromSpec(LPCTSTR lpSpecPath, CString& strVersionPath)
|
||||
{
|
||||
strVersionPath.Empty();
|
||||
if (!PathFileExists(lpSpecPath)) return FALSE;
|
||||
|
||||
CFile myFile;
|
||||
CFileException fileEx;
|
||||
if (!myFile.Open(lpSpecPath, CFile::modeRead, &fileEx)) return FALSE;
|
||||
|
||||
DWORD dwLen = (DWORD)myFile.GetLength();
|
||||
std::vector<char> buf(dwLen + 1, 0);
|
||||
myFile.Read(buf.data(), dwLen);
|
||||
myFile.Close();
|
||||
|
||||
std::string strContent(buf.data(), dwLen);
|
||||
|
||||
std::string::size_type nPos = 0;
|
||||
while ((nPos = strContent.find("version=", nPos)) != std::string::npos)
|
||||
{
|
||||
bool bIsParam = (nPos == 0
|
||||
|| strContent[nPos - 1] == '\n'
|
||||
|| strContent[nPos - 1] == '\r'
|
||||
|| strContent[nPos - 1] == ' '
|
||||
|| strContent[nPos - 1] == '\t');
|
||||
if (bIsParam)
|
||||
{
|
||||
std::string::size_type nValStart = nPos + 8; // skip "version="
|
||||
while (nValStart < strContent.size() && strContent[nValStart] == ' ')
|
||||
nValStart++;
|
||||
if (nValStart >= strContent.size()) break;
|
||||
|
||||
char cQuote = strContent[nValStart];
|
||||
if (cQuote == '\'' || cQuote == '"')
|
||||
{
|
||||
std::string::size_type nValEnd = strContent.find(cQuote, nValStart + 1);
|
||||
if (nValEnd != std::string::npos && nValEnd > nValStart + 1)
|
||||
{
|
||||
strVersionPath = strContent.substr(nValStart + 1, nValEnd - nValStart - 1).c_str();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
break; // version=None 或空值,视为无
|
||||
}
|
||||
nPos += 8;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 更新已有 version_info 文件的版本字段;若文件不存在则创建
|
||||
static BOOL UpdateVersionInfoFile(LPCTSTR lpPath, LPCTSTR lpProductVersion, LPCTSTR lpFileVersion)
|
||||
{
|
||||
UINT pv1 = 0, pv2 = 0, pv3 = 0, pv4 = 0;
|
||||
UINT fv1 = 0, fv2 = 0, fv3 = 0, fv4 = 0;
|
||||
if (!ParseVersionTuple(lpProductVersion, pv1, pv2, pv3, pv4)) return FALSE;
|
||||
if (!ParseVersionTuple(lpFileVersion, fv1, fv2, fv3, fv4)) return FALSE;
|
||||
|
||||
CStringA strProdVerA(lpProductVersion);
|
||||
CStringA strFileVerA(lpFileVersion);
|
||||
const char* szProdVer = strProdVerA;
|
||||
const char* szFileVer = strFileVerA;
|
||||
|
||||
if (!PathFileExists(lpPath))
|
||||
{
|
||||
// 文件不存在,直接创建
|
||||
FILE* fp = NULL;
|
||||
_tfopen_s(&fp, lpPath, _T("wb"));
|
||||
if (!fp) return FALSE;
|
||||
fprintf(fp,
|
||||
"# UTF-8\n"
|
||||
"VSVersionInfo(\n"
|
||||
" ffi=FixedFileInfo(\n"
|
||||
" filevers=(%u, %u, %u, %u),\n"
|
||||
" prodvers=(%u, %u, %u, %u),\n"
|
||||
" mask=0x3f,\n"
|
||||
" flags=0x0,\n"
|
||||
" OS=0x40004,\n"
|
||||
" fileType=0x1,\n"
|
||||
" subtype=0x0,\n"
|
||||
" date=(0, 0)\n"
|
||||
" ),\n"
|
||||
" kids=[\n"
|
||||
" StringFileInfo([\n"
|
||||
" StringTable(\n"
|
||||
" u'040904B0',\n"
|
||||
" [StringStruct(u'CompanyName', u''),\n"
|
||||
" StringStruct(u'FileDescription', u''),\n"
|
||||
" StringStruct(u'FileVersion', u'%s'),\n"
|
||||
" StringStruct(u'InternalName', u''),\n"
|
||||
" StringStruct(u'LegalCopyright', u''),\n"
|
||||
" StringStruct(u'OriginalFilename', u''),\n"
|
||||
" StringStruct(u'ProductName', u''),\n"
|
||||
" StringStruct(u'ProductVersion', u'%s'),\n"
|
||||
" StringStruct(u'Comments', u''),\n"
|
||||
" StringStruct(u'LegalTrademarks', u''),\n"
|
||||
" ])\n"
|
||||
" ]),\n"
|
||||
" VarFileInfo([VarStruct(u'Translation', [0x0409, 1200])])\n"
|
||||
" ]\n"
|
||||
")\n",
|
||||
fv1, fv2, fv3, fv4,
|
||||
pv1, pv2, pv3, pv4,
|
||||
szFileVer,
|
||||
szProdVer);
|
||||
fclose(fp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 读取现有文件并就地更新版本字段
|
||||
CFile myFile;
|
||||
CFileException fileEx;
|
||||
if (!myFile.Open(lpPath, CFile::modeReadWrite, &fileEx)) return FALSE;
|
||||
DWORD dwLen = (DWORD)myFile.GetLength();
|
||||
std::vector<char> buf(dwLen + 1, 0);
|
||||
myFile.Read(buf.data(), dwLen);
|
||||
myFile.Close();
|
||||
|
||||
std::string s(buf.data(), dwLen);
|
||||
char szNew[128];
|
||||
|
||||
// 更新 filevers 元组
|
||||
{
|
||||
const std::string key = "filevers=(";
|
||||
auto p = s.find(key);
|
||||
if (p != std::string::npos) {
|
||||
auto pValStart = p + key.size();
|
||||
auto pValEnd = s.find(')', pValStart);
|
||||
if (pValEnd != std::string::npos) {
|
||||
snprintf(szNew, sizeof(szNew), "%u, %u, %u, %u", fv1, fv2, fv3, fv4);
|
||||
s.replace(pValStart, pValEnd - pValStart, szNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新 prodvers 元组
|
||||
{
|
||||
const std::string key = "prodvers=(";
|
||||
auto p = s.find(key);
|
||||
if (p != std::string::npos) {
|
||||
auto pValStart = p + key.size();
|
||||
auto pValEnd = s.find(')', pValStart);
|
||||
if (pValEnd != std::string::npos) {
|
||||
snprintf(szNew, sizeof(szNew), "%u, %u, %u, %u", pv1, pv2, pv3, pv4);
|
||||
s.replace(pValStart, pValEnd - pValStart, szNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新 FileVersion 字符串值
|
||||
{
|
||||
const std::string key = "u'FileVersion', u'";
|
||||
auto p = s.find(key);
|
||||
if (p != std::string::npos) {
|
||||
auto pValStart = p + key.size();
|
||||
auto pValEnd = s.find('\'', pValStart);
|
||||
if (pValEnd != std::string::npos)
|
||||
s.replace(pValStart, pValEnd - pValStart, szFileVer);
|
||||
}
|
||||
}
|
||||
// 更新 ProductVersion 字符串值
|
||||
{
|
||||
const std::string key = "u'ProductVersion', u'";
|
||||
auto p = s.find(key);
|
||||
if (p != std::string::npos) {
|
||||
auto pValStart = p + key.size();
|
||||
auto pValEnd = s.find('\'', pValStart);
|
||||
if (pValEnd != std::string::npos)
|
||||
s.replace(pValStart, pValEnd - pValStart, szProdVer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!myFile.Open(lpPath, CFile::modeCreate | CFile::modeWrite, &fileEx)) return FALSE;
|
||||
myFile.Write(s.c_str(), (UINT)s.size());
|
||||
myFile.Close();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int HandlePyinstallerBuildCommand(int argc, TCHAR* argv[])
|
||||
{
|
||||
// 解析 pid(内嵌在 argv[1] 中,如 "pyinstaller=5")
|
||||
// "pyinstaller=" 为 13 个字符
|
||||
CString strPid = CString(argv[1]).Mid(13);
|
||||
UINT nPid = 0;
|
||||
if (strPid.IsEmpty() || !TryParseUInt16(strPid, nPid))
|
||||
{
|
||||
_tprintf(_T("错误: pyinstaller= 后的 pid 无效:%s,应为 0-65535 范围的整数。\n"), strPid.GetString());
|
||||
return 41;
|
||||
}
|
||||
|
||||
LPCTSTR lpRepoPath = NULL;
|
||||
CString strExtraArgs;
|
||||
BOOL bTestMode = FALSE;
|
||||
int nRepoArgRet = ParsePyinstallerOptions(argc, argv, lpRepoPath, strExtraArgs, bTestMode);
|
||||
if (nRepoArgRet != 0)
|
||||
{
|
||||
return nRepoArgRet;
|
||||
}
|
||||
|
||||
CString strProductVersion;
|
||||
CString strFileVersion;
|
||||
VersionBuildErrorCodes errorCodes = { 42, 43, 44 };
|
||||
int nVersionRet = BuildVersionsFromRepo(
|
||||
lpRepoPath,
|
||||
nPid,
|
||||
errorCodes,
|
||||
strProductVersion,
|
||||
strFileVersion,
|
||||
DEFAULT_MAJOR_WHEN_NO_TAG_FOR_PYINSTALLER,
|
||||
DEFAULT_MINOR_WHEN_NO_TAG_FOR_PYINSTALLER);
|
||||
if (nVersionRet != 0)
|
||||
{
|
||||
return nVersionRet;
|
||||
}
|
||||
|
||||
if (bTestMode)
|
||||
{
|
||||
int nDot1 = strProductVersion.Find(_T('.'));
|
||||
int nDot2 = (nDot1 >= 0) ? strProductVersion.Find(_T('.'), nDot1 + 1) : -1;
|
||||
if (nDot2 > nDot1)
|
||||
{
|
||||
strProductVersion = strProductVersion.Left(nDot2 + 1) + _T("0.0");
|
||||
}
|
||||
_tprintf(_T("[test] 测试版本号: ProductVersion=%s\n"), strProductVersion.GetString());
|
||||
}
|
||||
|
||||
// 检测是否为 spec 模式,若是则生成 version_info.txt 并注入/更新 spec
|
||||
CString strFirstToken = ExtractFirstToken(strExtraArgs);
|
||||
BOOL bIsSpec = (strFirstToken.GetLength() >= 5
|
||||
&& strFirstToken.Right(5).CompareNoCase(_T(".spec")) == 0);
|
||||
if (bIsSpec)
|
||||
{
|
||||
// 解析 spec 文件完整路径
|
||||
CString strSpecPath;
|
||||
if (PathIsRelative(strFirstToken))
|
||||
strSpecPath.Format(_T("%s\\%s"), lpRepoPath, strFirstToken.GetString());
|
||||
else
|
||||
strSpecPath = strFirstToken;
|
||||
|
||||
// 获取 spec 所在目录
|
||||
TCHAR szSpecDir[MAX_PATH] = {};
|
||||
_tcscpy_s(szSpecDir, strSpecPath.GetString());
|
||||
PathRemoveFileSpec(szSpecDir);
|
||||
|
||||
// 检查 spec 是否已指定版本信息文件
|
||||
CString strExistingVersionPath;
|
||||
if (ReadVersionPathFromSpec(strSpecPath, strExistingVersionPath))
|
||||
{
|
||||
// spec 已有 version=,直接更新所指向的文件(不存在则创建)
|
||||
CString strVersionInfoFullPath;
|
||||
if (PathIsRelative(strExistingVersionPath))
|
||||
strVersionInfoFullPath.Format(_T("%s\\%s"), szSpecDir, strExistingVersionPath.GetString());
|
||||
else
|
||||
strVersionInfoFullPath = strExistingVersionPath;
|
||||
|
||||
if (!UpdateVersionInfoFile(strVersionInfoFullPath, strProductVersion, strFileVersion))
|
||||
{
|
||||
_tprintf(_T("错误: 无法更新版本信息文件: %s。\n"), strVersionInfoFullPath.GetString());
|
||||
return 45;
|
||||
}
|
||||
_tprintf(_T("版本信息文件已更新: %s\n"), strVersionInfoFullPath.GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// spec 未指定 version=,生成 version_info.txt 并注入 spec
|
||||
CString strVersionInfoPath;
|
||||
if (!WriteVersionInfoFile(szSpecDir, strProductVersion, strFileVersion, strVersionInfoPath))
|
||||
{
|
||||
_tprintf(_T("错误: 无法生成 version_info.txt(目录: %s)。\n"), szSpecDir);
|
||||
return 45;
|
||||
}
|
||||
_tprintf(_T("版本信息文件已生成: %s\n"), strVersionInfoPath.GetString());
|
||||
|
||||
if (!InjectVersionIntoSpec(strSpecPath))
|
||||
{
|
||||
_tprintf(_T("错误: 无法修改 spec 文件: %s。\n"), strSpecPath.GetString());
|
||||
return 46;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_tprintf(_T("PyInstaller 打包开始..\n"));
|
||||
_tprintf(_T("ProductVersion=%s\n"), strProductVersion.GetString());
|
||||
_tprintf(_T("FileVersion=%s\n"), strFileVersion.GetString());
|
||||
|
||||
CString strCmd;
|
||||
strCmd.Format(
|
||||
_T("cmd /c python -m PyInstaller %s"),
|
||||
strExtraArgs.GetString());
|
||||
|
||||
CString strBuildResult = StartProcess(NULL, strCmd.GetBuffer(), lpRepoPath);
|
||||
strCmd.ReleaseBuffer();
|
||||
|
||||
if (strBuildResult.IsEmpty())
|
||||
{
|
||||
_tprintf(_T("错误: PyInstaller 打包失败。\n"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
3
GitVer/GitVer_pyinstaller.h
Normal file
3
GitVer/GitVer_pyinstaller.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int HandlePyinstallerBuildCommand(int argc, TCHAR* argv[]);
|
||||
235
README.md
235
README.md
@@ -10,8 +10,9 @@
|
||||
gitver
|
||||
gitver rewrite [PEType] [-f]
|
||||
gitver setver=<pid> [repodir=<path>] [-test] [-setup=0|1]
|
||||
gitver nuitkabuild=<pid> <mainPy> [repodir=<path>] [-test] [params="<nuitka参数>"]
|
||||
gitver nuitkapydbuild=<pid> <modulePy> [repodir=<path>] [-test] [params="<nuitka参数>"]
|
||||
gitver nuitkabuild=<pid> [repodir=<path>] [-test] [params="<mainPy> <nuitka参数>"]
|
||||
gitver nuitkapydbuild=<pid> [repodir=<path>] [-test] [params="<modulePy> <nuitka参数>"]
|
||||
gitver pyinstaller=<pid> [repodir=<path>] [-test] [params="<mainPy|specFile> <pyinstaller参数>"]
|
||||
gitver -setup=0|1 <pid> [repodir=<path>] [-test]
|
||||
```
|
||||
|
||||
@@ -22,7 +23,7 @@ gitver -setup=0|1 <pid> [repodir=<path>] [-test]
|
||||
| `pid` | 产品 ID,整数,范围 0-65535,内嵌在命令名中(如 `setver=5`) |
|
||||
| `repodir=<path>` | Git 仓库目录,缺省使用当前运行目录 |
|
||||
| `-test` | 将产品版本号的 major/minor 置为 0(测试版本构建) |
|
||||
| `params="..."` | 传递给 Nuitka 的额外参数,用双引号括起来 |
|
||||
| `params="..."` | 传递给 Nuitka 或 PyInstaller 的参数(**含入口文件名**),用双引号括起来 |
|
||||
|
||||
**BID 规则:**
|
||||
`main`/`master` 分支固定 `bid=0`;其他分支格式须为 `<描述>.<数字>`,自动读取末尾数字作为 `bid`。
|
||||
@@ -145,21 +146,21 @@ FileVersion=5.26.0519.3
|
||||
生成版本号并调用 Nuitka 打包 Python 程序(EXE 模式)。
|
||||
|
||||
```
|
||||
gitver nuitkabuild=<pid> <mainPy> [repodir=<path>] [-test] [params="<nuitka参数>"]
|
||||
gitver nuitkabuild=<pid> [repodir=<path>] [-test] [params="<mainPy> <nuitka参数>"]
|
||||
```
|
||||
|
||||
自动调用:
|
||||
|
||||
```
|
||||
python -m nuitka --windows-product-version=<版本> --windows-file-version=<版本> <extra> <mainPy>
|
||||
python -m nuitka --windows-product-version=<版本> --windows-file-version=<版本> <params>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
|
||||
```
|
||||
gitver nuitkabuild=5 main.py
|
||||
gitver nuitkabuild=5 main.py -test
|
||||
gitver nuitkabuild=5 src\app.py repodir=E:\Code\MyPyProj params="--standalone --output-dir=dist"
|
||||
gitver nuitkabuild=5 params="main.py"
|
||||
gitver nuitkabuild=5 -test params="main.py"
|
||||
gitver nuitkabuild=5 repodir=E:\Code\MyPyProj params="src\\app.py --standalone --output-dir=dist"
|
||||
```
|
||||
|
||||
**输出示例:**
|
||||
@@ -179,27 +180,73 @@ FileVersion=5.26.0519.3
|
||||
生成版本号并调用 Nuitka 打包 Python 模块(pyd/DLL 模式)。
|
||||
|
||||
```
|
||||
gitver nuitkapydbuild=<pid> <modulePy> [repodir=<path>] [-test] [params="<nuitka参数>"]
|
||||
gitver nuitkapydbuild=<pid> [repodir=<path>] [-test] [params="<modulePy> <nuitka参数>"]
|
||||
```
|
||||
|
||||
自动调用:
|
||||
|
||||
```
|
||||
python -m nuitka --module --windows-product-version=<版本> --windows-file-version=<版本> <extra> <modulePy>
|
||||
python -m nuitka --module --windows-product-version=<版本> --windows-file-version=<版本> <params>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
|
||||
```
|
||||
gitver nuitkapydbuild=5 module.py
|
||||
gitver nuitkapydbuild=5 module.py -test
|
||||
gitver nuitkapydbuild=5 src\core.py repodir=E:\Code\MyPyProj params="--output-dir=dist"
|
||||
gitver nuitkapydbuild=5 params="module.py"
|
||||
gitver nuitkapydbuild=5 -test params="module.py"
|
||||
gitver nuitkapydbuild=5 repodir=E:\Code\MyPyProj params="src\\core.py --output-dir=dist"
|
||||
```
|
||||
|
||||
> 需要当前环境已安装 Nuitka(可通过 `python -m nuitka --version` 验证)。
|
||||
|
||||
---
|
||||
|
||||
### `gitver pyinstaller=<pid>`
|
||||
|
||||
生成版本号并调用 PyInstaller 打包 Python 程序,支持直接传入 `.py` 入口脚本或 `.spec` 构建配置文件。
|
||||
|
||||
```
|
||||
gitver pyinstaller=<pid> [repodir=<path>] [-test] [params="<mainPy|specFile> <pyinstaller参数>"]
|
||||
```
|
||||
|
||||
自动调用:
|
||||
|
||||
```
|
||||
python -m PyInstaller <params>
|
||||
```
|
||||
|
||||
版本号照常从 Git tag 计算并输出,但 PyInstaller 本身不接受 `--windows-product-version` 等命令行参数。如需将版本信息嵌入 EXE,建议在 `.spec` 文件中通过 `version=` 字段或外部 version resource 文件实现。
|
||||
|
||||
**两种打包模式:**
|
||||
|
||||
| 模式 | 参数示例 | 说明 |
|
||||
|---|---|---|
|
||||
| 脚本模式 | `params="main.py"` | 由 PyInstaller 自动生成 spec 并打包 |
|
||||
| spec 模式 | `params="myapp.spec"` | 使用已有 spec 文件,打包配置完全由 spec 控制,`params=` 仅适用于路径类运行时选项(`--distpath` 等) |
|
||||
|
||||
**示例:**
|
||||
|
||||
```
|
||||
gitver pyinstaller=5 params="main.py"
|
||||
gitver pyinstaller=5 -test params="main.py"
|
||||
gitver pyinstaller=5 repodir=E:\Code\MyPyProj params="src\\app.py --onefile --name=MyApp"
|
||||
gitver pyinstaller=5 params="myapp.spec"
|
||||
gitver pyinstaller=5 -test params="myapp.spec"
|
||||
gitver pyinstaller=5 repodir=E:\Code\MyPyProj params="myapp.spec"
|
||||
```
|
||||
|
||||
**输出示例:**
|
||||
|
||||
```
|
||||
PyInstaller 打包开始.. (main.py)
|
||||
ProductVersion=5.0.1920.11
|
||||
FileVersion=5.26.0519.3
|
||||
```
|
||||
|
||||
> 需要当前环境已安装 PyInstaller(可通过 `python -m PyInstaller --version` 验证)。
|
||||
|
||||
---
|
||||
|
||||
### `gitver -setup=0|1`(独立打包命令)
|
||||
|
||||
独立调用安装脚本打包,不依赖 `setver=` 命令。
|
||||
@@ -217,6 +264,112 @@ gitver -setup=1 5 repodir=E:\Code\MyProj
|
||||
|
||||
---
|
||||
|
||||
## Nuitka 与 PyInstaller 常用参数速查
|
||||
|
||||
### Nuitka 常用参数
|
||||
|
||||
通过 `params="..."` 传递给 `gitver nuitkabuild=` / `nuitkapydbuild=`。
|
||||
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `--standalone` | 独立模式:将所有依赖打包进输出目录,可单独分发 |
|
||||
| `--onefile` | 单文件模式:将独立包压缩为单个 EXE(需配合 `--standalone`) |
|
||||
| `--output-dir=<dir>` | 指定输出目录,如 `--output-dir=dist` |
|
||||
| `--windows-icon-from-ico=<ico>` | 设置 EXE 图标 |
|
||||
| `--windows-company-name=<name>` | 设置公司名(写入文件属性) |
|
||||
| `--windows-product-name=<name>` | 设置产品名 |
|
||||
| `--enable-plugin=<plugin>` | 启用插件,如 `pyside6`、`tk-inter`、`numpy` |
|
||||
| `--follow-imports` | 跟踪所有隐式导入(打包更完整,体积也更大) |
|
||||
| `--nofollow-import-to=<mod>` | 排除指定模块不展开,如 `--nofollow-import-to=tests` |
|
||||
| `--include-package=<pkg>` | 强制包含指定包 |
|
||||
| `--include-data-files=<src>=<dst>` | 打包额外数据文件 |
|
||||
| `--remove-output` | 打包前先清理上次输出 |
|
||||
| `--lto=yes` | 启用链接时优化(Release 构建推荐) |
|
||||
| `--jobs=<n>` | 并行编译线程数,如 `--jobs=4` |
|
||||
|
||||
**典型用法示例:**
|
||||
|
||||
```bash
|
||||
# 最小单文件 EXE
|
||||
gitver nuitkabuild=5 params="main.py --standalone --onefile --output-dir=dist"
|
||||
|
||||
# 带图标、公司名的独立包
|
||||
gitver nuitkabuild=5 params="main.py --standalone --windows-icon-from-ico=app.ico --windows-company-name=MyCompany --output-dir=dist"
|
||||
|
||||
# PySide6 GUI 应用
|
||||
gitver nuitkabuild=5 params="main.py --standalone --enable-plugin=pyside6 --output-dir=dist"
|
||||
|
||||
# 打包 pyd 模块
|
||||
gitver nuitkapydbuild=5 params="core.py --output-dir=dist"
|
||||
|
||||
# 异地仓库
|
||||
gitver nuitkabuild=5 repodir=E:\Code\MyProj params="main.py --standalone --onefile --output-dir=dist"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PyInstaller 常用参数
|
||||
|
||||
通过 `params="..."` 传递给 `gitver pyinstaller=`。
|
||||
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `--onefile` / `-F` | 单文件模式:打包成单个 EXE |
|
||||
| `--onedir` / `-D` | 单目录模式(默认) |
|
||||
| `--name=<name>` / `-n` | 指定输出文件名(不含后缀) |
|
||||
| `--icon=<ico>` / `-i` | 设置 EXE 图标 |
|
||||
| `--distpath=<dir>` | 指定输出目录,默认 `dist` |
|
||||
| `--workpath=<dir>` | 中间文件目录,默认 `build` |
|
||||
| `--specpath=<dir>` | spec 文件生成位置,默认当前目录 |
|
||||
| `--noconsole` / `-w` | 不显示控制台窗口(GUI 应用) |
|
||||
| `--hidden-import=<mod>` | 手动指定隐式导入的模块 |
|
||||
| `--add-data=<src>;<dst>` | 打包额外数据文件,Windows 分隔符为 `;`,Linux/macOS 为 `:` |
|
||||
| `--version-file=<file>` | 从文件读入 Windows 版本信息嵌入 EXE |
|
||||
| `--uac-admin` | 请求管理员权限运行 |
|
||||
| `--clean` | 构建前清理缓存 |
|
||||
| `--log-level=WARN` | 减少构建输出噪音,可选 `DEBUG`、`INFO`、`WARN`、`ERROR` |
|
||||
|
||||
**典型用法示例:**
|
||||
|
||||
```bash
|
||||
# 最小单文件 EXE
|
||||
gitver pyinstaller=5 params="main.py --onefile --name=MyApp"
|
||||
|
||||
# GUI 应用(关指控制台)
|
||||
gitver pyinstaller=5 params="main.py --onefile --noconsole --icon=app.ico --name=MyApp"
|
||||
|
||||
# 包含资源文件
|
||||
gitver pyinstaller=5 params="main.py --onefile --add-data=assets;assets --name=MyApp"
|
||||
|
||||
# 指定输出目录
|
||||
gitver pyinstaller=5 params="main.py --onefile --distpath=dist --workpath=build --name=MyApp"
|
||||
|
||||
# 使用 spec 文件(配置均在 spec 中,不需要其他 params)
|
||||
gitver pyinstaller=5 params="myapp.spec"
|
||||
|
||||
# 使用 spec 并覆盖输出路径
|
||||
gitver pyinstaller=5 params="myapp.spec --distpath=release"
|
||||
|
||||
# 异地仓库
|
||||
gitver pyinstaller=5 repodir=E:\Code\MyProj params="main.py --onefile --name=MyApp"
|
||||
```
|
||||
|
||||
**spec 文件嵌入版本信息(推荐方式):**
|
||||
|
||||
PyInstaller 不支持命令行版本参数。需将版本号嵌入 EXE 时,建议先用 `gitver setver=<pid>` 生成版本号,再在 spec 文件中设置 `version_file` 字段:
|
||||
|
||||
```python
|
||||
# myapp.spec 片段
|
||||
exe = EXE(
|
||||
pyz,
|
||||
...,
|
||||
version='version_info.txt', # Windows 版本信息文件
|
||||
name='MyApp',
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码
|
||||
|
||||
| 码 | 含义 |
|
||||
@@ -249,6 +402,11 @@ gitver -setup=1 5 repodir=E:\Code\MyProj
|
||||
| 37 | 未找到安装脚本(setup.iss / setup.nsi) |
|
||||
| 38 | 修改安装脚本失败 |
|
||||
| 39 | 未找到安装编译器(ISCC.exe / makensis.exe) |
|
||||
| 40 | `pyinstaller=` 参数不足(缺少 mainPy 或 specFile) |
|
||||
| 41 | `pyinstaller=` 的 pid 非法 |
|
||||
| 42 | `pyinstaller=` 无法根据分支名计算 bid |
|
||||
| 43 | `pyinstaller=` 读取 tag 失败 |
|
||||
| 44 | `pyinstaller=` 获取当天分支提交次数失败 |
|
||||
|
||||
---
|
||||
|
||||
@@ -304,13 +462,27 @@ gitver setver=5 -test
|
||||
### 验证 `nuitkabuild=`
|
||||
|
||||
```bash
|
||||
gitver nuitkabuild=5 main.py
|
||||
gitver nuitkabuild=5 params="main.py"
|
||||
# 预期: 调用 python -m nuitka,注入版本号参数
|
||||
|
||||
gitver nuitkabuild=5 src\app.py params="--standalone --output-dir=dist"
|
||||
gitver nuitkabuild=5 params="src\\app.py --standalone --output-dir=dist"
|
||||
# 预期: 额外参数传递给 Nuitka
|
||||
```
|
||||
|
||||
### 验证 `pyinstaller=`
|
||||
|
||||
```bash
|
||||
gitver pyinstaller=5 params="main.py"
|
||||
# 预期: 调用 python -m PyInstaller main.py
|
||||
# 输出 ProductVersion / FileVersion
|
||||
|
||||
gitver pyinstaller=5 params="myapp.spec"
|
||||
# 预期: 调用 python -m PyInstaller myapp.spec(spec 模式)
|
||||
|
||||
gitver pyinstaller=5 params="main.py --onefile --name=MyApp"
|
||||
# 预期: 额外参数传递给 PyInstaller
|
||||
```
|
||||
|
||||
### 验证 `-setup=`
|
||||
|
||||
```bash
|
||||
@@ -347,21 +519,34 @@ gitver setver=5 -setup=0
|
||||
|
||||
| 命令 | 预期返回码 |
|
||||
|---|---|
|
||||
| `gitver nuitkabuild=5 main.py` | 0 / 19 / 21 |
|
||||
| `gitver nuitkabuild=5 main.py -test` | 0 |
|
||||
| `gitver nuitkabuild=5 main.py params="--standalone"` | 0 |
|
||||
| `gitver nuitkabuild=5 main.py repodir=E:\NotExists` | 23 |
|
||||
| `gitver nuitkabuild=5` | 17 |
|
||||
| `gitver nuitkabuild=5 params="main.py"` | 0 / 19 / 21 |
|
||||
| `gitver nuitkabuild=5 -test params="main.py"` | 0 |
|
||||
| `gitver nuitkabuild=5 params="main.py --standalone"` | 0 |
|
||||
| `gitver nuitkabuild=5 repodir=E:\NotExists params="main.py"` | 23 |
|
||||
| `gitver nuitkabuild=5` | 0 |
|
||||
|
||||
### nuitkapydbuild= 组
|
||||
|
||||
| 命令 | 预期返回码 |
|
||||
|---|---|
|
||||
| `gitver nuitkapydbuild=5 module.py` | 0 / 26 / 28 |
|
||||
| `gitver nuitkapydbuild=5 module.py -test` | 0 |
|
||||
| `gitver nuitkapydbuild=5 module.py params="--output-dir=dist"` | 0 |
|
||||
| `gitver nuitkapydbuild=5 module.py repodir=E:\NotExists` | 23 |
|
||||
| `gitver nuitkapydbuild=5` | 24 |
|
||||
| `gitver nuitkapydbuild=5 params="module.py"` | 0 / 26 / 28 |
|
||||
| `gitver nuitkapydbuild=5 -test params="module.py"` | 0 |
|
||||
| `gitver nuitkapydbuild=5 params="module.py --output-dir=dist"` | 0 |
|
||||
| `gitver nuitkapydbuild=5 repodir=E:\NotExists params="module.py"` | 23 |
|
||||
| `gitver nuitkapydbuild=5` | 0 |
|
||||
|
||||
### pyinstaller= 组
|
||||
|
||||
| 命令 | 预期返回码 |
|
||||
|---|---|
|
||||
| `gitver pyinstaller=5 params="main.py"` | 0 / 42 / 44 |
|
||||
| `gitver pyinstaller=5 -test params="main.py"` | 0 |
|
||||
| `gitver pyinstaller=5 params="main.py --onefile"` | 0 |
|
||||
| `gitver pyinstaller=5 params="myapp.spec"` | 0 |
|
||||
| `gitver pyinstaller=5 -test params="myapp.spec"` | 0 |
|
||||
| `gitver pyinstaller=5 repodir=E:\NotExists params="main.py"` | 23 |
|
||||
| `gitver pyinstaller=5` | 0 |
|
||||
| `gitver pyinstaller=abc params="main.py"` | 41 |
|
||||
|
||||
> **注意**:如果打包时提示“未找到安装编译器(ISCC.exe / makensis.exe)”,请手动下载安装对应工具,并将其可执行文件路径(如 `ISCC.exe` 或 `makensis.exe` 所在目录)添加到系统环境变量 `PATH`。常见下载地址:
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user