Compare commits
20 Commits
54dd4cbd35
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| a7df3a44a2 | |||
| 7956349c0a | |||
| 8ccfc6d272 | |||
| f526122f33 | |||
| b86d645acf | |||
| ef1f3bfb50 | |||
| 33917e774d | |||
| 78c20f9ae9 | |||
| 610eb4f20d | |||
| 8cc88be5f6 | |||
| 50f4237c74 | |||
| deac0adabd | |||
| 4f9d405c9f | |||
| 88091e84c0 | |||
| fafad1ff97 | |||
| fbcf5f7e61 | |||
| b21d189ca1 | |||
| 701acd561a | |||
| 3b419d1e24 | |||
| e3b9fea5b2 |
+208
-119
@@ -188,158 +188,247 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Setup Flutter
|
- name: Add Flutter to PATH
|
||||||
uses: subosito/flutter-action@v2
|
|
||||||
with:
|
|
||||||
flutter-version: '3.24.0'
|
|
||||||
channel: 'stable'
|
|
||||||
|
|
||||||
- name: Checkout Flutter 3.24.5
|
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
Set-Location $env:FLUTTER_ROOT
|
# Add Flutter to PATH for current session and GitHub Actions
|
||||||
git fetch --tags
|
$flutterPath = "C:\flutter\bin"
|
||||||
git checkout 3.24.5
|
if (Test-Path $flutterPath) {
|
||||||
flutter --version
|
Write-Host "Adding Flutter to PATH: $flutterPath"
|
||||||
|
$env:PATH = "$flutterPath;$env:PATH"
|
||||||
|
Add-Content -Path $env:GITHUB_PATH -Value $flutterPath
|
||||||
|
|
||||||
|
# Verify Flutter is accessible
|
||||||
|
$flutterVersion = flutter --version 2>&1
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "Flutter verified: $flutterVersion"
|
||||||
|
} else {
|
||||||
|
Write-Host "Flutter not found at $flutterPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: Flutter not found at C:\flutter\bin"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
- name: Enable Windows desktop
|
- name: Enable Windows desktop
|
||||||
run: flutter config --enable-windows-desktop
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$env:PATH = "C:\flutter\bin;$env:PATH"
|
||||||
|
flutter config --enable-windows-desktop
|
||||||
|
|
||||||
- name: Get dependencies
|
- name: Get dependencies
|
||||||
run: flutter pub get
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$env:PATH = "C:\flutter\bin;$env:PATH"
|
||||||
|
flutter pub get
|
||||||
|
|
||||||
- name: Generate code
|
- name: Generate code
|
||||||
run: dart run build_runner build --delete-conflicting-outputs
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$env:PATH = "C:\flutter\bin;$env:PATH"
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
|
- name: Install NuGet
|
||||||
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
Write-Host "Installing NuGet..."
|
||||||
|
|
||||||
|
# Check if NuGet is already available
|
||||||
|
$nugetPath = Get-Command nuget -ErrorAction SilentlyContinue
|
||||||
|
if ($nugetPath) {
|
||||||
|
Write-Host "NuGet already installed: $($nugetPath.Source)"
|
||||||
|
$nugetVersion = nuget help | Select-String -Pattern "NuGet Version" | Select-Object -First 1
|
||||||
|
Write-Host "NuGet version: $nugetVersion"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use Chocolatey to install NuGet (proven to work)
|
||||||
|
Write-Host "Installing NuGet via Chocolatey..."
|
||||||
|
choco install nuget.commandline -y
|
||||||
|
|
||||||
|
# Update PATH to include Chocolatey
|
||||||
|
$env:PATH = "C:\ProgramData\chocolatey\bin;$env:PATH"
|
||||||
|
Add-Content -Path $env:GITHUB_PATH -Value "C:\ProgramData\chocolatey\bin"
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if (Get-Command nuget -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "NuGet installed successfully via Chocolatey"
|
||||||
|
$nugetVersion = nuget help | Select-String -Pattern "NuGet Version" | Select-Object -First 1
|
||||||
|
Write-Host "NuGet version: $nugetVersion"
|
||||||
|
} else {
|
||||||
|
Write-Host "WARNING: NuGet installation may have failed, but continuing..."
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Fix long paths
|
||||||
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
Write-Host "Enabling long path support..."
|
||||||
|
|
||||||
|
# Enable long paths in Windows
|
||||||
|
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -Type DWORD -Force
|
||||||
|
|
||||||
|
# Also set for current user
|
||||||
|
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\AppModel\StateRepository\Cache\Package\Data\" -Name "LongPathsEnabled" -Value 1 -Type DWORD -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
Write-Host "Long path support enabled"
|
||||||
|
|
||||||
|
- name: Clean build cache
|
||||||
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$env:PATH = "C:\flutter\bin;$env:PATH"
|
||||||
|
Write-Host "Cleaning build cache..."
|
||||||
|
flutter clean
|
||||||
|
|
||||||
|
Write-Host "Removing old build directories..."
|
||||||
|
if (Test-Path "build") {
|
||||||
|
Remove-Item -Path "build" -Recurse -Force
|
||||||
|
}
|
||||||
|
if (Test-Path "windows") {
|
||||||
|
Remove-Item -Path "windows" -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Recreating Windows project..."
|
||||||
|
flutter create --platforms=windows .
|
||||||
|
|
||||||
- name: Build Windows Debug
|
- name: Build Windows Debug
|
||||||
run: flutter build windows
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$env:PATH = "C:\flutter\bin;C:\ProgramData\chocolatey\bin;$env:PATH"
|
||||||
|
flutter build windows
|
||||||
|
|
||||||
- name: Build Windows Release
|
- name: Build Windows Release
|
||||||
run: flutter build windows --release
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$env:PATH = "C:\flutter\bin;C:\ProgramData\chocolatey\bin;$env:PATH"
|
||||||
|
flutter build windows --release
|
||||||
|
|
||||||
- name: Upload Debug build artifacts
|
- name: Upload Debug build artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: windows-debug-build
|
name: windows-debug-build
|
||||||
path: build/windows/runner/Debug/
|
path: build/windows/x64/runner/Debug/
|
||||||
|
|
||||||
- name: Upload Release build artifacts
|
- name: Upload Release build artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: windows-release-build
|
name: windows-release-build
|
||||||
path: build/windows/runner/Release/
|
path: build/windows/x64/runner/Release/
|
||||||
|
|
||||||
# 本地(act)验证作业:只验证 Git PATH 与 checkout/submodules
|
- name: Install Build Tools
|
||||||
build-act-windows:
|
|
||||||
name: ACT Windows Checkout Verification
|
|
||||||
if: ${{ env.ACT == 'true' }}
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Add Git to PATH (Windows)
|
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
$paths = @(
|
choco install 7zip -y
|
||||||
'C:\Program Files\Git\cmd',
|
choco install enigma-virtual-box -y
|
||||||
'C:\Program Files\Git\bin',
|
|
||||||
'C:\Program Files (x86)\Git\cmd',
|
|
||||||
'C:\Program Files (x86)\Git\bin'
|
|
||||||
)
|
|
||||||
foreach ($p in $paths) {
|
|
||||||
if (Test-Path $p) {
|
|
||||||
Add-Content -Path $env:GITHUB_PATH -Value $p
|
|
||||||
$env:PATH = "$p;$env:PATH"
|
|
||||||
Write-Host ("Added to PATH: {0}" -f $p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$candidates = @(
|
|
||||||
'C:\Program Files\Git\cmd\git.exe',
|
|
||||||
'C:\Program Files\Git\bin\git.exe',
|
|
||||||
'C:\Program Files (x86)\Git\cmd\git.exe',
|
|
||||||
'C:\Program Files (x86)\Git\bin\git.exe'
|
|
||||||
)
|
|
||||||
$found = $false
|
|
||||||
foreach ($g in $candidates) {
|
|
||||||
if (Test-Path $g) {
|
|
||||||
& $g --version
|
|
||||||
$found = $true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (-not $found) {
|
|
||||||
Write-Host "Git executable not found in common locations; continuing."
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Checkout
|
- name: Package Single EXE
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
fetch-depth: 0
|
|
||||||
persist-credentials: false
|
|
||||||
# 仅用于本地 act 验证:目录级稀疏检出,避免包含 Windows 非法文件
|
|
||||||
sparse-checkout: |
|
|
||||||
.github
|
|
||||||
lib
|
|
||||||
libcore
|
|
||||||
android
|
|
||||||
ios
|
|
||||||
windows
|
|
||||||
assets/core
|
|
||||||
assets/fonts
|
|
||||||
assets/translations
|
|
||||||
sparse-checkout-cone: true
|
|
||||||
|
|
||||||
- name: 🔍 Verify jq Installation (ACT)
|
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
# Check if jq is available
|
Write-Host "=== 开始打包单文件 EXE ==="
|
||||||
$jqFound = $false
|
|
||||||
try {
|
$buildPath = "build\windows\x64\runner\Release"
|
||||||
$version = jq --version 2>&1
|
$outputPath = "dist"
|
||||||
if ($version -match 'jq-[\d\.]+') {
|
$enigmaPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||||
Write-Host "jq is already installed: $version"
|
|
||||||
$jqFound = $true
|
# 创建输出目录
|
||||||
}
|
if (-not (Test-Path $outputPath)) {
|
||||||
} catch {
|
New-Item -ItemType Directory -Path $outputPath -Force | Out-Null
|
||||||
Write-Host "jq not installed or not in PATH"
|
|
||||||
}
|
}
|
||||||
if (-not $jqFound) {
|
|
||||||
Write-Host "Preparing to install jq..."
|
# 获取主程序
|
||||||
# Check if Chocolatey is available
|
$exeFile = Get-ChildItem -Path $buildPath -Filter "*.exe" | Select-Object -First 1
|
||||||
$chocoFound = $false
|
if (-not $exeFile) {
|
||||||
try {
|
Write-Host "❌ 未找到可执行文件"
|
||||||
choco --version 2>&1 | Out-Null
|
exit 1
|
||||||
$chocoFound = $true
|
}
|
||||||
} catch {}
|
|
||||||
if (-not $chocoFound) {
|
$inputExe = $exeFile.FullName
|
||||||
Write-Host "Installing Chocolatey..."
|
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
|
||||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
Write-Host "主程序: $inputExe"
|
||||||
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
Write-Host "输出: $outputExe"
|
||||||
# Refresh PATH
|
|
||||||
$env:PATH = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('PATH', 'User')
|
# 尝试使用 Enigma Virtual Box
|
||||||
Write-Host "Chocolatey installed successfully"
|
if (Test-Path $enigmaPath) {
|
||||||
}
|
Write-Host "使用 Enigma Virtual Box 打包..."
|
||||||
Write-Host "Installing jq using Chocolatey..."
|
|
||||||
choco install jq -y
|
# 创建配置文件
|
||||||
if ($LASTEXITCODE -eq 0) {
|
$configContent = "[Config]`nInputFile=$inputExe`nOutputFile=$outputExe`nFiles=%DEFAULT FOLDER%`nVirtualizationMode=Never Write To Disk`nCompression=Yes`nShareVirtualSystem=Yes`n`n[Files]`nFolder=$buildPath\*"
|
||||||
Write-Host "jq installed successfully"
|
|
||||||
# Verify again
|
$configFile = "$outputPath\package_config.evb"
|
||||||
$version = jq --version 2>&1
|
Set-Content -Path $configFile -Value $configContent
|
||||||
Write-Host "jq version: $version"
|
|
||||||
|
# 执行打包
|
||||||
|
$process = Start-Process -FilePath $enigmaPath -ArgumentList "/sf", $inputExe, "/lf", $outputExe, "/folder", $buildPath, "/compress" -Wait -PassThru -NoNewWindow
|
||||||
|
|
||||||
|
if ($process.ExitCode -eq 0) {
|
||||||
|
Write-Host "✅ 单文件打包成功!"
|
||||||
|
|
||||||
|
# 显示压缩信息
|
||||||
|
$originalSize = (Get-Item $inputExe).Length / 1MB
|
||||||
|
$packedSize = (Get-Item $outputExe).Length / 1MB
|
||||||
|
Write-Host "原始大小: $([math]::Round($originalSize, 2)) MB"
|
||||||
|
Write-Host "打包大小: $([math]::Round($packedSize, 2)) MB"
|
||||||
} else {
|
} else {
|
||||||
Write-Host "jq installation failed, exit code: $LASTEXITCODE"
|
Write-Host "⚠️ Enigma 打包失败,使用备选方案"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "⚠️ Enigma 未找到,使用 7-Zip 自解压方案"
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Verify submodules
|
- name: Package with 7-Zip Alternative
|
||||||
|
if: failure()
|
||||||
shell: powershell
|
shell: powershell
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
run: |
|
||||||
git --version
|
Write-Host "使用 7-Zip 自解压方案..."
|
||||||
git submodule status
|
|
||||||
Write-Host "Submodules verified (status printed above)."
|
$buildPath = "build\windows\x64\runner\Release"
|
||||||
|
$outputPath = "dist"
|
||||||
|
|
||||||
|
# 检查 7-Zip
|
||||||
|
$7zipPath = "C:\Program Files\7-Zip\7z.exe"
|
||||||
|
|
||||||
|
# 获取主程序名称
|
||||||
|
$exeFile = Get-ChildItem -Path $buildPath -Filter "*.exe" | Select-Object -First 1
|
||||||
|
$outputSfx = "$outputPath\$($exeFile.BaseName)_Package.exe"
|
||||||
|
|
||||||
|
Write-Host "创建自解压包..."
|
||||||
|
|
||||||
|
# 创建配置文件
|
||||||
|
$configContent = @"
|
||||||
|
;!@Install@!UTF-8!
|
||||||
|
Title="HostExecutor Windows Package"
|
||||||
|
BeginPrompt="正在解压 HostExecutor..."
|
||||||
|
ExtractDialogText="请稍候,正在解压文件..."
|
||||||
|
ExtractPathText="解压路径:"
|
||||||
|
ExtractTitle="解压中"
|
||||||
|
FinishMessage="解压完成!"
|
||||||
|
GUIFlags="8"
|
||||||
|
;!@InstallEnd@!
|
||||||
|
"@
|
||||||
|
|
||||||
|
$configFile = "$outputPath\config.txt"
|
||||||
|
Set-Content -Path $configFile -Value $configContent -Encoding UTF8
|
||||||
|
|
||||||
|
# 创建压缩包
|
||||||
|
& $7zipPath a -sfx7z.sfx -r "$outputSfx" "$buildPath\*" -scsUTF-8 -y
|
||||||
|
Copy-Item "$configFile" -Destination "$outputSfx" -Force
|
||||||
|
|
||||||
|
if (Test-Path $outputSfx) {
|
||||||
|
Write-Host "✅ 7-Zip 自解压包创建成功!"
|
||||||
|
Write-Host "输出文件: $outputSfx"
|
||||||
|
|
||||||
|
$size = (Get-Item $outputSfx).Length / 1MB
|
||||||
|
Write-Host "文件大小: $([math]::Round($size, 2)) MB"
|
||||||
|
} else {
|
||||||
|
Write-Host "❌ 7-Zip 打包失败"
|
||||||
|
}
|
||||||
|
|
||||||
- name: Summary
|
- name: Upload Single EXE Package
|
||||||
shell: powershell
|
uses: actions/upload-artifact@v3
|
||||||
run: |
|
with:
|
||||||
Write-Host "ACT Windows verification completed successfully."
|
name: windows-single-exe
|
||||||
|
path: dist/*.exe
|
||||||
|
if: always()
|
||||||
|
|||||||
+6
-6
@@ -4,12 +4,12 @@ echo 📋 复制 libcore 文件...
|
|||||||
:: 创建目标目录
|
:: 创建目标目录
|
||||||
mkdir libcore\bin >nul 2>&1
|
mkdir libcore\bin >nul 2>&1
|
||||||
|
|
||||||
:: 查找并复制 HiddifyCli.exe,重命名为 BearVPNCli.exe
|
:: 查找并复制 HiddifyCli.exe,重命名为 HiFastVPNCli.exe
|
||||||
for /r %%f in (HiddifyCli.exe) do (
|
for /r %%f in (HiddifyCli.exe) do (
|
||||||
if exist "%%f" (
|
if exist "%%f" (
|
||||||
echo ✅ 找到 HiddifyCli.exe: %%f
|
echo ✅ 找到 HiddifyCli.exe: %%f
|
||||||
echo 📝 复制并重命名为 BearVPNCli.exe
|
echo 📝 复制并重命名为 HiFastVPNCli.exe
|
||||||
copy "%%f" libcore\bin\BearVPNCli.exe
|
copy "%%f" libcore\bin\HiFastVPNCli.exe
|
||||||
echo ✅ 重命名完成
|
echo ✅ 重命名完成
|
||||||
goto :dll
|
goto :dll
|
||||||
)
|
)
|
||||||
@@ -32,7 +32,7 @@ echo.
|
|||||||
echo 📄 验证文件:
|
echo 📄 验证文件:
|
||||||
if exist libcore\bin (
|
if exist libcore\bin (
|
||||||
dir libcore\bin
|
dir libcore\bin
|
||||||
if exist libcore\bin\BearVPNCli.exe (
|
if exist libcore\bin\HiFastVPNCli.exe (
|
||||||
if exist libcore\bin\libcore.dll (
|
if exist libcore\bin\libcore.dll (
|
||||||
echo ✅ 验证成功:所有文件已正确复制
|
echo ✅ 验证成功:所有文件已正确复制
|
||||||
exit /b 0
|
exit /b 0
|
||||||
@@ -41,10 +41,10 @@ if exist libcore\bin (
|
|||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
) else (
|
) else (
|
||||||
echo ❌ 验证失败:BearVPNCli.exe 不存在
|
echo ❌ 验证失败:HiFastVPNCli.exe 不存在
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
) else (
|
) else (
|
||||||
echo ⚠️ libcore\bin 目录不存在
|
echo ⚠️ libcore\bin 目录不存在
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
@echo off
|
||||||
|
REM Fix NuGet installation using Chocolatey
|
||||||
|
|
||||||
|
echo Installing NuGet via Chocolatey...
|
||||||
|
|
||||||
|
REM Check if Chocolatey is available
|
||||||
|
where choco >nul 2>nul
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Chocolatey not found, installing Chocolatey first...
|
||||||
|
powershell -Command "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Installing NuGet via Chocolatey...
|
||||||
|
choco install nuget.commandline -y
|
||||||
|
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo SUCCESS: NuGet installed via Chocolatey
|
||||||
|
echo Updating PATH...
|
||||||
|
set PATH=C:\ProgramData\chocolatey\bin;%PATH%
|
||||||
|
setx PATH "C:\ProgramData\chocolatey\bin;%PATH%"
|
||||||
|
|
||||||
|
echo Verifying installation...
|
||||||
|
nuget help | findstr "NuGet"
|
||||||
|
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo NuGet is working correctly!
|
||||||
|
) else (
|
||||||
|
echo WARNING: NuGet installed but not accessible
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo ERROR: NuGet installation failed
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# NuGet 安装脚本 - 解决 SSL 问题
|
||||||
|
Write-Host "=== 安装 NuGet ===" -ForegroundColor Green
|
||||||
|
|
||||||
|
# 设置 TLS 1.2
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
|
||||||
|
# 检查是否已安装
|
||||||
|
$nugetExists = Get-Command nuget -ErrorAction SilentlyContinue
|
||||||
|
if ($nugetExists) {
|
||||||
|
Write-Host "NuGet 已安装: $($nugetExists.Source)" -ForegroundColor Green
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 下载 NuGet
|
||||||
|
Write-Host "下载 NuGet CLI..." -ForegroundColor Yellow
|
||||||
|
$downloadUrls = @(
|
||||||
|
"https://dist.nuget.org/win-x86-commandline/latest/nuget.exe",
|
||||||
|
"https://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe",
|
||||||
|
"http://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
$downloaded = $false
|
||||||
|
foreach ($url in $downloadUrls) {
|
||||||
|
try {
|
||||||
|
Write-Host "尝试下载: $url"
|
||||||
|
$webClient = New-Object System.Net.WebClient
|
||||||
|
$webClient.DownloadFile($url, "C:\nuget.exe")
|
||||||
|
$downloaded = $true
|
||||||
|
Write-Host "下载成功!" -ForegroundColor Green
|
||||||
|
break
|
||||||
|
} catch {
|
||||||
|
Write-Host "下载失败: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $downloaded) {
|
||||||
|
Write-Host "所有下载都失败了,尝试使用 Chocolatey..." -ForegroundColor Yellow
|
||||||
|
try {
|
||||||
|
choco install nuget.commandline -y
|
||||||
|
if (Get-Command nuget -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "通过 Chocolatey 安装成功!" -ForegroundColor Green
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Host "Chocolatey 安装也失败了: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "NuGet 安装失败,但继续构建..." -ForegroundColor Yellow
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 验证安装
|
||||||
|
if (Test-Path "C:\nuget.exe") {
|
||||||
|
$env:PATH = "C:\;$env:PATH"
|
||||||
|
Write-Host "NuGet 安装完成" -ForegroundColor Green
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
try {
|
||||||
|
$version = & "C:\nuget.exe" help | Select-String -Pattern "NuGet Version" | Select-Object -First 1
|
||||||
|
Write-Host "版本信息: $version" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "NuGet 可用,但版本检查失败" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "NuGet 文件不存在" -ForegroundColor Red
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Windows 构建修复脚本
|
||||||
|
# 以管理员身份运行
|
||||||
|
|
||||||
|
Write-Host "=== Windows Flutter 构建修复 ===" -ForegroundColor Green
|
||||||
|
|
||||||
|
# 1. 安装 NuGet
|
||||||
|
Write-Host "`n1. 安装 NuGet..." -ForegroundColor Yellow
|
||||||
|
$nugetPath = "C:\nuget.exe"
|
||||||
|
if (-not (Test-Path $nugetPath)) {
|
||||||
|
try {
|
||||||
|
Write-Host "下载 NuGet..."
|
||||||
|
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile $nugetPath
|
||||||
|
$env:PATH = "C:\;$env:PATH"
|
||||||
|
Write-Host "NuGet 安装成功" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "NuGet 下载失败: $_" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "NuGet 已存在" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. 启用长路径支持
|
||||||
|
Write-Host "`n2. 启用长路径支持..." -ForegroundColor Yellow
|
||||||
|
try {
|
||||||
|
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -Type DWORD -Force
|
||||||
|
Write-Host "长路径支持已启用" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "长路径设置失败: $_" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. 清理构建缓存
|
||||||
|
Write-Host "`n3. 清理构建缓存..." -ForegroundColor Yellow
|
||||||
|
if (Test-Path "build") {
|
||||||
|
Remove-Item -Path "build" -Recurse -Force
|
||||||
|
Write-Host "Build 目录已清理" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path "windows") {
|
||||||
|
Remove-Item -Path "windows" -Recurse -Force
|
||||||
|
Write-Host "Windows 目录已清理" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. 重新创建 Windows 项目
|
||||||
|
Write-Host "`n4. 重新创建 Windows 项目..." -ForegroundColor Yellow
|
||||||
|
flutter create --platforms=windows .
|
||||||
|
|
||||||
|
# 5. 获取依赖
|
||||||
|
Write-Host "`n5. 获取依赖..." -ForegroundColor Yellow
|
||||||
|
flutter pub get
|
||||||
|
|
||||||
|
# 6. 构建 Debug 版本
|
||||||
|
Write-Host "`n6. 构建 Debug 版本..." -ForegroundColor Yellow
|
||||||
|
flutter build windows
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "`n=== 构建成功! ===" -ForegroundColor Green
|
||||||
|
Write-Host "输出目录: build\windows\runner\Debug\" -ForegroundColor Cyan
|
||||||
|
} else {
|
||||||
|
Write-Host "`n=== 构建失败 ===" -ForegroundColor Red
|
||||||
|
Write-Host "请检查错误信息并尝试手动修复" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "`n修复完成!" -ForegroundColor Green
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
@echo off
|
||||||
|
:: This script checks for and installs all necessary tools for building and packaging the Flutter application on Windows.
|
||||||
|
|
||||||
|
:: 1. Check for Administrator Privileges
|
||||||
|
net session >nul 2>&1
|
||||||
|
if %errorLevel% == 0 (
|
||||||
|
echo Administrator privileges detected. Continuing...
|
||||||
|
) else (
|
||||||
|
echo Requesting Administrator privileges to install tools...
|
||||||
|
powershell -Command "Start-Process cmd.exe -ArgumentList '/c %~s0' -Verb RunAs" >nul 2>&1
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
:: 2. Check for and Install Chocolatey
|
||||||
|
echo.
|
||||||
|
echo === Checking for Chocolatey ===
|
||||||
|
where choco >nul 2>&1
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo Chocolatey is already installed.
|
||||||
|
) else (
|
||||||
|
echo Chocolatey not found. Installing now...
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))"
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Failed to install Chocolatey. Please install it manually from https://chocolatey.org
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo Chocolatey installed successfully.
|
||||||
|
:: Add Chocolatey to the PATH for the current session
|
||||||
|
set "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
:: 3. Install Required Tools via Chocolatey
|
||||||
|
echo.
|
||||||
|
echo === Installing Build Tools (7-Zip and Enigma Virtual Box) ===
|
||||||
|
|
||||||
|
:: Install 7-Zip
|
||||||
|
echo Installing 7-Zip...
|
||||||
|
choco install 7zip -y
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Failed to install 7-Zip.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo 7-Zip installed successfully.
|
||||||
|
|
||||||
|
:: Install Enigma Virtual Box
|
||||||
|
echo Installing Enigma Virtual Box...
|
||||||
|
choco install enigma-virtual-box -y
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Failed to install Enigma Virtual Box.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo Enigma Virtual Box installed successfully.
|
||||||
|
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo =======================================================
|
||||||
|
echo All required build tools have been installed.
|
||||||
|
echo You can now use 'package_windows_single_exe.ps1' to build your single-file executable.
|
||||||
|
echo =======================================================
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
@echo off
|
||||||
|
REM Simple Flutter installer for Windows Gitea Runner
|
||||||
|
|
||||||
|
echo Installing Flutter for Windows...
|
||||||
|
|
||||||
|
REM Create flutter directory
|
||||||
|
mkdir C:\flutter 2>nul
|
||||||
|
|
||||||
|
REM Download Flutter stable version
|
||||||
|
echo Downloading Flutter...
|
||||||
|
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.24.5-stable.zip', 'C:\flutter.zip')"
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Download failed, trying alternative...
|
||||||
|
powershell -Command "Invoke-WebRequest -Uri 'https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.24.5-stable.zip' -OutFile 'C:\flutter.zip'"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Extract Flutter
|
||||||
|
echo Extracting Flutter...
|
||||||
|
powershell -Command "Expand-Archive -Path 'C:\flutter.zip' -DestinationPath 'C:\' -Force"
|
||||||
|
|
||||||
|
REM Set PATH
|
||||||
|
echo Setting PATH...
|
||||||
|
set PATH=C:\flutter\bin;%PATH%
|
||||||
|
setx PATH "C:\flutter\bin;%PATH%"
|
||||||
|
|
||||||
|
REM Clean up
|
||||||
|
del C:\flutter.zip
|
||||||
|
|
||||||
|
REM Verify installation
|
||||||
|
echo Verifying Flutter installation...
|
||||||
|
C:\flutter\bin\flutter --version
|
||||||
|
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo SUCCESS: Flutter installed successfully
|
||||||
|
echo Running flutter doctor...
|
||||||
|
C:\flutter\bin\flutter doctor
|
||||||
|
) else (
|
||||||
|
echo ERROR: Flutter installation failed
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Flutter installation complete!
|
||||||
|
pause
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
@echo off
|
||||||
|
REM Simple NuGet installer for Windows
|
||||||
|
|
||||||
|
echo Installing NuGet...
|
||||||
|
|
||||||
|
REM Check if NuGet exists
|
||||||
|
where nuget >nul 2>nul
|
||||||
|
if %errorlevel% == 0 (
|
||||||
|
echo NuGet already installed
|
||||||
|
nuget help | findstr "NuGet"
|
||||||
|
pause
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Download NuGet using PowerShell
|
||||||
|
echo Downloading NuGet CLI...
|
||||||
|
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe', 'C:\nuget.exe')"
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Download failed, trying alternative...
|
||||||
|
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe', 'C:\nuget.exe')"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Add to PATH
|
||||||
|
set PATH=C:\;%PATH%
|
||||||
|
setx PATH "C:\;%PATH%"
|
||||||
|
|
||||||
|
REM Verify installation
|
||||||
|
echo Verifying NuGet installation...
|
||||||
|
C:\nuget.exe help | findstr "NuGet"
|
||||||
|
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo SUCCESS: NuGet installed successfully
|
||||||
|
) else (
|
||||||
|
echo ERROR: NuGet installation failed
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -133,7 +133,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
|||||||
if (value != null) {
|
if (value != null) {
|
||||||
isQuickConnectEnabled.value = value;
|
isQuickConnectEnabled.value = value;
|
||||||
// 保存闪连状态到本地存储
|
// 保存闪连状态到本地存储
|
||||||
await _saveQuickConnectStatus(value);
|
await _storage.kr_saveBool(key: _quickConnectKey, value: value);
|
||||||
KRLogUtil.kr_i('闪连状态已更新: $value', tag: 'QuickConnect');
|
KRLogUtil.kr_i('闪连状态已更新: $value', tag: 'QuickConnect');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1932,7 +1932,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
|||||||
final results = await KRLatencyTester.testMultipleNodes(
|
final results = await KRLatencyTester.testMultipleNodes(
|
||||||
nodes: nodeAddresses,
|
nodes: nodeAddresses,
|
||||||
concurrency: 10, // 每批10个并发
|
concurrency: 10, // 每批10个并发
|
||||||
timeout: const Duration(seconds: 5),
|
timeout: 5000, // 超时时间5秒(毫秒)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 更新节点延迟
|
// 更新节点延迟
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||||
|
|
||||||
|
/// 延迟测试工具类
|
||||||
|
/// 提供真实的 TCP 连接延迟测试功能
|
||||||
|
class KRLatencyTester {
|
||||||
|
/// 测试单个节点的延迟
|
||||||
|
///
|
||||||
|
/// 参数:
|
||||||
|
/// - host: 主机地址
|
||||||
|
/// - port: 端口号
|
||||||
|
/// - timeout: 超时时间(毫秒)
|
||||||
|
///
|
||||||
|
/// 返回:
|
||||||
|
/// - 延迟时间(毫秒),如果失败返回 65535
|
||||||
|
static Future<int> testNode({
|
||||||
|
required String host,
|
||||||
|
required int port,
|
||||||
|
int timeout = 5000,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
|
final socket = await Socket.connect(
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
timeout: Duration(milliseconds: timeout),
|
||||||
|
).timeout(Duration(milliseconds: timeout));
|
||||||
|
|
||||||
|
stopwatch.stop();
|
||||||
|
|
||||||
|
// 立即关闭连接
|
||||||
|
await socket.close();
|
||||||
|
socket.destroy();
|
||||||
|
|
||||||
|
final latency = stopwatch.elapsedMilliseconds;
|
||||||
|
KRLogUtil.kr_i('✅ 延迟测试成功: $host:$port = ${latency}ms', tag: 'KRLatencyTester');
|
||||||
|
|
||||||
|
return latency;
|
||||||
|
} catch (e) {
|
||||||
|
KRLogUtil.kr_w('❌ 延迟测试失败: $host:$port - $e', tag: 'KRLatencyTester');
|
||||||
|
return 65535; // 测试失败返回最大值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 批量测试多个节点的延迟
|
||||||
|
///
|
||||||
|
/// 参数:
|
||||||
|
/// - nodes: 节点列表,格式为 [{"host": "example.com", "port": 443}]
|
||||||
|
/// - concurrency: 并发数量
|
||||||
|
/// - timeout: 超时时间(毫秒)
|
||||||
|
///
|
||||||
|
/// 返回:
|
||||||
|
/// - 测试结果映射,键为 "host:port",值为延迟时间
|
||||||
|
static Future<Map<String, int>> testMultipleNodes({
|
||||||
|
required List<MapEntry<String, SocketAddress>> nodes,
|
||||||
|
int concurrency = 10,
|
||||||
|
int timeout = 5000,
|
||||||
|
}) async {
|
||||||
|
final results = <String, int>{};
|
||||||
|
final semaphore = Completer<void>();
|
||||||
|
var activeCount = 0;
|
||||||
|
var completedCount = 0;
|
||||||
|
|
||||||
|
KRLogUtil.kr_i('🚀 开始批量延迟测试,共 ${nodes.length} 个节点,并发数: $concurrency', tag: 'KRLatencyTester');
|
||||||
|
|
||||||
|
Future<void> processNode(MapEntry<String, SocketAddress> node) async {
|
||||||
|
try {
|
||||||
|
final host = node.value.address;
|
||||||
|
final port = node.value.port;
|
||||||
|
final key = node.key;
|
||||||
|
|
||||||
|
final latency = await testNode(
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
timeout: timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
results[key] = latency;
|
||||||
|
} catch (e) {
|
||||||
|
results[node.key] = 65535;
|
||||||
|
KRLogUtil.kr_e('❌ 节点测试异常: ${node.key} - $e', tag: 'KRLatencyTester');
|
||||||
|
} finally {
|
||||||
|
completedCount++;
|
||||||
|
activeCount--;
|
||||||
|
|
||||||
|
if (completedCount >= nodes.length) {
|
||||||
|
semaphore.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分批处理节点
|
||||||
|
for (var i = 0; i < nodes.length; i += concurrency) {
|
||||||
|
final batch = nodes.skip(i).take(concurrency);
|
||||||
|
|
||||||
|
for (final node in batch) {
|
||||||
|
activeCount++;
|
||||||
|
processNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待当前批次完成
|
||||||
|
if (i + concurrency < nodes.length) {
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有任务完成
|
||||||
|
await semaphore.future;
|
||||||
|
|
||||||
|
KRLogUtil.kr_i('✅ 批量延迟测试完成,成功: ${results.length} 个', tag: 'KRLatencyTester');
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Socket 地址类
|
||||||
|
/// 表示网络地址和端口的组合
|
||||||
|
class SocketAddress {
|
||||||
|
final String address;
|
||||||
|
final int port;
|
||||||
|
|
||||||
|
SocketAddress(this.address, this.port);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => '$address:$port';
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ class KRWindowManager with WindowListener, TrayListener {
|
|||||||
// 确保在 Windows 下正确设置窗口属性
|
// 确保在 Windows 下正确设置窗口属性
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
|
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
|
||||||
await windowManager.setTitle('BearVPN');
|
await windowManager.setTitle('HiFastVPN');
|
||||||
await windowManager.setSize(const Size(800, 668));
|
await windowManager.setSize(const Size(800, 668));
|
||||||
await windowManager.setMinimumSize(const Size(800, 668));
|
await windowManager.setMinimumSize(const Size(800, 668));
|
||||||
await windowManager.center();
|
await windowManager.center();
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# Flutter Windows 单文件 EXE 打包指南
|
||||||
|
|
||||||
|
## 🎯 目标
|
||||||
|
将 Flutter Windows 应用打包成单个可执行文件,便于分发和部署。
|
||||||
|
|
||||||
|
## 📋 当前状态
|
||||||
|
|
||||||
|
### 现有构建输出
|
||||||
|
```
|
||||||
|
build/windows/x64/runner/Release/
|
||||||
|
├── hostexecutor.exe # 主程序
|
||||||
|
├── flutter_windows.dll # Flutter 引擎
|
||||||
|
├── msvcp140.dll # Visual C++ 运行时
|
||||||
|
├── vcruntime140.dll # Visual C++ 运行时
|
||||||
|
├── vcruntime140_1.dll # Visual C++ 运行时
|
||||||
|
└── data/ # 应用数据文件夹
|
||||||
|
├── app.so # Dart 代码编译结果
|
||||||
|
└── flutter_assets/ # 资源文件
|
||||||
|
```
|
||||||
|
|
||||||
|
### 问题分析
|
||||||
|
Flutter 默认构建会生成多个文件,因为:
|
||||||
|
1. **Flutter 引擎** (`flutter_windows.dll`) - 必须包含
|
||||||
|
2. **Visual C++ 运行时** - 系统依赖
|
||||||
|
3. **应用数据** (`data` 文件夹) - 包含资源和 Dart 代码
|
||||||
|
|
||||||
|
## 🔧 解决方案
|
||||||
|
|
||||||
|
### 方案一:使用 Enigma Virtual Box(推荐)
|
||||||
|
|
||||||
|
#### 步骤 1:下载安装
|
||||||
|
1. 下载 [Enigma Virtual Box](https://enigmaprotector.com/en/downloads.html)
|
||||||
|
2. 安装并运行
|
||||||
|
|
||||||
|
#### 步骤 2:打包配置
|
||||||
|
1. **主程序文件**: 选择 `build/windows/x64/runner/Release/hostexecutor.exe`
|
||||||
|
2. **添加文件夹**: 选择整个 `Release` 文件夹
|
||||||
|
3. **输出文件**: 设置输出路径和文件名
|
||||||
|
4. **文件选项**: 勾选"压缩文件"
|
||||||
|
|
||||||
|
#### 步骤 3:生成单文件
|
||||||
|
点击"Process"生成单个可执行文件。
|
||||||
|
|
||||||
|
### 方案二:使用 Inno Setup(安装程序)
|
||||||
|
|
||||||
|
#### 步骤 1:下载安装
|
||||||
|
1. 下载 [Inno Setup](https://jrsoftware.org/isinfo.php)
|
||||||
|
2. 安装并运行
|
||||||
|
|
||||||
|
#### 步骤 2:创建脚本
|
||||||
|
```ini
|
||||||
|
[Setup]
|
||||||
|
AppName=HostExecutor
|
||||||
|
AppVersion=1.0.0
|
||||||
|
DefaultDirName={autopf}\HostExecutor
|
||||||
|
OutputBaseFilename=HostExecutor_Setup
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: "build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\HostExecutor"; Filename: "{app}\hostexecutor.exe"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 步骤 3:编译生成
|
||||||
|
编译脚本生成安装程序。
|
||||||
|
|
||||||
|
### 方案三:使用 Windows 自带工具(高级)
|
||||||
|
|
||||||
|
#### 使用 `dotnet publish`(需要 .NET 包装)
|
||||||
|
```bash
|
||||||
|
# 需要创建 .NET 包装器项目
|
||||||
|
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 自动化脚本
|
||||||
|
|
||||||
|
### Enigma Virtual Box 自动化脚本
|
||||||
|
```powershell
|
||||||
|
# package_single_exe.ps1
|
||||||
|
$enigmaPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||||
|
$inputFile = "build\windows\x64\runner\Release\hostexecutor.exe"
|
||||||
|
$outputFile = "dist\HostExecutor_Single.exe"
|
||||||
|
$folderPath = "build\windows\x64\runner\Release"
|
||||||
|
|
||||||
|
& $enigmaPath /sf $inputFile /lf $outputFile /folder $folderPath /compress
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 打包后文件结构
|
||||||
|
|
||||||
|
### 单文件输出
|
||||||
|
```
|
||||||
|
dist/
|
||||||
|
└── HostExecutor_Single.exe # 单个可执行文件(50-100MB)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件大小分析
|
||||||
|
- **原始文件**: 约 30-50MB
|
||||||
|
- **压缩后**: 约 25-40MB(取决于压缩率)
|
||||||
|
- **最终大小**: 50-100MB(包含所有依赖)
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
### 运行时依赖
|
||||||
|
1. **Windows 版本**: Windows 10 版本 1903+ 或 Windows 11
|
||||||
|
2. **系统组件**: 确保目标系统有最新的 Windows 更新
|
||||||
|
3. **防病毒软件**: 单文件可能被误报,需要添加信任
|
||||||
|
|
||||||
|
### 性能影响
|
||||||
|
- **启动时间**: 单文件启动会稍慢(需要解压)
|
||||||
|
- **内存使用**: 运行时内存占用相同
|
||||||
|
- **文件大小**: 比多文件版本大约 10-20%
|
||||||
|
|
||||||
|
## 🎯 推荐方案
|
||||||
|
|
||||||
|
### 开发阶段
|
||||||
|
使用多文件版本,便于调试和更新。
|
||||||
|
|
||||||
|
### 分发阶段
|
||||||
|
使用 Enigma Virtual Box 创建单文件版本:
|
||||||
|
1. **简单易用** - 图形化界面
|
||||||
|
2. **压缩率高** - 有效减小文件大小
|
||||||
|
3. **兼容性好** - 支持所有 Windows 版本
|
||||||
|
4. **免费** - 个人和商业使用都免费
|
||||||
|
|
||||||
|
## 📋 下一步行动
|
||||||
|
|
||||||
|
1. **安装 Enigma Virtual Box**
|
||||||
|
2. **测试单文件打包**
|
||||||
|
3. **验证运行效果**
|
||||||
|
4. **创建自动化打包流程**
|
||||||
|
|
||||||
|
## 🔗 相关资源
|
||||||
|
|
||||||
|
- [Enigma Virtual Box 官网](https://enigmaprotector.com/en/downloads.html)
|
||||||
|
- [Inno Setup 官网](https://jrsoftware.org/isinfo.php)
|
||||||
|
- [Flutter Windows 文档](https://docs.flutter.dev/desktop)
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
#Requires -RunAsAdministrator
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Builds and packages a Flutter Windows application into a single executable.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This script automates the entire process of creating a single-file executable for a Flutter Windows project.
|
||||||
|
It performs the following steps:
|
||||||
|
1. Checks for and installs Chocolatey if not present.
|
||||||
|
2. Installs Enigma Virtual Box and 7-Zip using Chocolatey.
|
||||||
|
3. Builds the Flutter application in release mode.
|
||||||
|
4. Packages the build output into a single EXE using Enigma Virtual Box.
|
||||||
|
5. If Enigma fails, it falls back to creating a 7-Zip self-extracting archive.
|
||||||
|
6. Places the final packaged executable in the 'dist' directory.
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
- This script must be run with Administrator privileges to install software.
|
||||||
|
- An internet connection is required for the first run to download and install dependencies.
|
||||||
|
#>
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$buildPath = ".\build\windows\x64\runner\Release"
|
||||||
|
$outputPath = ".\dist"
|
||||||
|
|
||||||
|
# --- Helper Functions ---
|
||||||
|
function Test-CommandExists {
|
||||||
|
param($command)
|
||||||
|
return (Get-Command $command -ErrorAction SilentlyContinue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-Chocolatey {
|
||||||
|
if (Test-CommandExists "choco") {
|
||||||
|
Write-Host "✅ Chocolatey is already installed."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Chocolatey not found. Installing..."
|
||||||
|
try {
|
||||||
|
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||||
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||||
|
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||||
|
Write-Host "✅ Chocolatey installed successfully."
|
||||||
|
} catch {
|
||||||
|
Write-Host "❌ Failed to install Chocolatey. Please install it manually and re-run the script."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-Tools {
|
||||||
|
Write-Host "Checking for required tools (Enigma Virtual Box, 7-Zip)..."
|
||||||
|
$tools = @("enigma-virtual-box", "7zip")
|
||||||
|
foreach ($tool in $tools) {
|
||||||
|
if (choco list --local-only --exact $tool) {
|
||||||
|
Write-Host "✅ $tool is already installed."
|
||||||
|
} else {
|
||||||
|
Write-Host "Installing $tool..."
|
||||||
|
try {
|
||||||
|
choco install $tool -y --force
|
||||||
|
Write-Host "✅ $tool installed successfully."
|
||||||
|
} catch {
|
||||||
|
Write-Host "❌ Failed to install $tool."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main Script ---
|
||||||
|
|
||||||
|
# 1. Setup Environment
|
||||||
|
Write-Host "=== 1/4: Setting up build environment ==="
|
||||||
|
Install-Chocolatey
|
||||||
|
Install-Tools
|
||||||
|
|
||||||
|
# 2. Build Flutter App
|
||||||
|
Write-Host "=== 2/4: Building Flutter Windows application (Release) ==="
|
||||||
|
try {
|
||||||
|
flutter build windows --release
|
||||||
|
} catch {
|
||||||
|
Write-Host "❌ Flutter build failed."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Package Application
|
||||||
|
Write-Host "=== 3/4: Packaging into a single executable ==="
|
||||||
|
if (-not (Test-Path $buildPath)) {
|
||||||
|
Write-Host "❌ Build directory not found: $buildPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create output directory
|
||||||
|
New-Item -ItemType Directory -Path $outputPath -Force | Out-Null
|
||||||
|
|
||||||
|
# Get main executable
|
||||||
|
$exeFile = Get-ChildItem -Path $buildPath -Filter "*.exe" | Select-Object -First 1
|
||||||
|
if (-not $exeFile) {
|
||||||
|
Write-Host "❌ No executable found in build directory."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$inputExe = $exeFile.FullName
|
||||||
|
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||||
|
$enigmaCliPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||||
|
$packageSuccess = $false
|
||||||
|
|
||||||
|
# Attempt to package with Enigma Virtual Box
|
||||||
|
if (Test-Path $enigmaCliPath) {
|
||||||
|
Write-Host "Attempting to package with Enigma Virtual Box..."
|
||||||
|
try {
|
||||||
|
& $enigmaCliPath /quiet /project "$outputPath\project.evb" /input "$inputExe" /output "$outputExe" /folder "$buildPath" /compress 3 /deleteext
|
||||||
|
|
||||||
|
if ($?) {
|
||||||
|
Write-Host "✅ Enigma Virtual Box packaging successful."
|
||||||
|
$packageSuccess = $true
|
||||||
|
} else {
|
||||||
|
Write-Host "⚠️ Enigma Virtual Box packaging failed. Exit code: $lastexitcode"
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Host "⚠️ An error occurred during Enigma Virtual Box packaging."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "⚠️ Enigma Virtual Box not found at $enigmaCliPath."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Fallback to 7-Zip if Enigma failed
|
||||||
|
if (-not $packageSuccess) {
|
||||||
|
Write-Host "=== 4/4: Fallback: Packaging with 7-Zip SFX ==="
|
||||||
|
$7zipCliPath = "C:\Program Files\7-Zip\7z.exe"
|
||||||
|
$outputSfx = "$outputPath\$($exeFile.BaseName)_Package.exe"
|
||||||
|
|
||||||
|
if (-not (Test-Path $7zipCliPath)) {
|
||||||
|
Write-Host "❌ 7-Zip not found. Cannot create self-extracting archive."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$sfxConfig = @'
|
||||||
|
;!@Install@!UTF-8!
|
||||||
|
Title="Flutter Application"
|
||||||
|
BeginPrompt="Do you want to extract and run the application?"
|
||||||
|
RunProgram="%%T\%%S\$($exeFile.Name)"
|
||||||
|
;!@InstallEnd@!
|
||||||
|
'@
|
||||||
|
$sfxConfigFile = "$outputPath\sfx_config.txt"
|
||||||
|
Set-Content -Path $sfxConfigFile -Value $sfxConfig
|
||||||
|
|
||||||
|
try {
|
||||||
|
& $7zipCliPath a -sfx7z.sfx "$outputSfx" "$buildPath\*" -r "-i!$sfxConfigFile"
|
||||||
|
Write-Host "✅ 7-Zip self-extracting archive created successfully."
|
||||||
|
$outputExe = $outputSfx
|
||||||
|
$packageSuccess = $true
|
||||||
|
} catch {
|
||||||
|
Write-Host "❌ 7-Zip packaging failed."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Final Summary ---
|
||||||
|
if ($packageSuccess) {
|
||||||
|
$finalSize = (Get-Item $outputExe).Length / 1MB
|
||||||
|
Write-Host "================================================"
|
||||||
|
Write-Host "✅ Packaging Complete!"
|
||||||
|
Write-Host " Output file: $outputExe"
|
||||||
|
Write-Host " Size: $([math]::Round($finalSize, 2)) MB"
|
||||||
|
Write-Host "================================================"
|
||||||
|
} else {
|
||||||
|
Write-Host "❌ All packaging attempts failed."
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# Flutter Windows 单文件打包脚本 - Enigma Virtual Box
|
||||||
|
# 以管理员身份运行
|
||||||
|
|
||||||
|
Write-Host "=== Flutter Windows 单文件打包工具 ===" -ForegroundColor Green
|
||||||
|
|
||||||
|
# 配置参数
|
||||||
|
$projectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$buildPath = "$projectRoot\build\windows\x64\runner\Release"
|
||||||
|
$outputPath = "$projectRoot\dist"
|
||||||
|
$enigmaPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||||
|
|
||||||
|
# 检查构建文件
|
||||||
|
Write-Host "`n检查构建文件..." -ForegroundColor Yellow
|
||||||
|
if (-not (Test-Path $buildPath)) {
|
||||||
|
Write-Host "错误: 构建文件不存在,请先运行 flutter build windows" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建输出目录
|
||||||
|
if (-not (Test-Path $outputPath)) {
|
||||||
|
New-Item -ItemType Directory -Path $outputPath -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查 Enigma Virtual Box
|
||||||
|
if (-not (Test-Path $enigmaPath)) {
|
||||||
|
Write-Host "错误: Enigma Virtual Box 未安装" -ForegroundColor Red
|
||||||
|
Write-Host "请下载安装: https://enigmaprotector.com/en/downloads.html" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取主程序名称
|
||||||
|
$exeFile = Get-ChildItem -Path $buildPath -Filter "*.exe" | Select-Object -First 1
|
||||||
|
if (-not $exeFile) {
|
||||||
|
Write-Host "错误: 未找到可执行文件" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$inputExe = $exeFile.FullName
|
||||||
|
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||||
|
|
||||||
|
Write-Host "主程序: $inputExe" -ForegroundColor Cyan
|
||||||
|
Write-Host "输出文件: $outputExe" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# 创建打包配置文件
|
||||||
|
$configContent = @"
|
||||||
|
; Enigma Virtual Box 配置文件
|
||||||
|
[Config]
|
||||||
|
InputFile=$inputExe
|
||||||
|
OutputFile=$outputExe
|
||||||
|
Files=%DEFAULT FOLDER%
|
||||||
|
VirtualizationMode=Never Write To Disk
|
||||||
|
Compression=Yes
|
||||||
|
ShareVirtualSystem=Yes
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
; 添加整个 Release 文件夹
|
||||||
|
Folder=$buildPath\*
|
||||||
|
"@
|
||||||
|
|
||||||
|
$configFile = "$outputPath\package_config.evb"
|
||||||
|
Set-Content -Path $configFile -Value $configContent
|
||||||
|
|
||||||
|
# 执行打包
|
||||||
|
Write-Host "`n开始打包..." -ForegroundColor Yellow
|
||||||
|
Write-Host "这可能需要几分钟时间,请耐心等待..." -ForegroundColor Yellow
|
||||||
|
|
||||||
|
$process = Start-Process -FilePath $enigmaPath -ArgumentList "/sf", $inputExe, "/lf", $outputExe, "/folder", $buildPath, "/compress" -Wait -PassThru
|
||||||
|
|
||||||
|
if ($process.ExitCode -eq 0) {
|
||||||
|
Write-Host "`n✅ 打包成功!" -ForegroundColor Green
|
||||||
|
|
||||||
|
# 显示结果信息
|
||||||
|
$originalSize = (Get-Item $inputExe).Length / 1MB
|
||||||
|
$packedSize = (Get-Item $outputExe).Length / 1MB
|
||||||
|
$compressionRatio = [math]::Round((1 - $packedSize / $originalSize) * 100, 2)
|
||||||
|
|
||||||
|
Write-Host "`n打包结果:" -ForegroundColor Cyan
|
||||||
|
Write-Host "原始大小: $([math]::Round($originalSize, 2)) MB" -ForegroundColor White
|
||||||
|
Write-Host "打包大小: $([math]::Round($packedSize, 2)) MB" -ForegroundColor White
|
||||||
|
Write-Host "压缩率: $compressionRatio%" -ForegroundColor White
|
||||||
|
Write-Host "输出文件: $outputExe" -ForegroundColor White
|
||||||
|
|
||||||
|
Write-Host "`n📋 使用说明:" -ForegroundColor Yellow
|
||||||
|
Write-Host "1. 将生成的单文件复制到目标计算机" -ForegroundColor White
|
||||||
|
Write-Host "2. 直接运行即可,无需安装其他依赖" -ForegroundColor White
|
||||||
|
Write-Host "3. 首次运行可能需要管理员权限" -ForegroundColor White
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Write-Host "`n❌ 打包失败!" -ForegroundColor Red
|
||||||
|
Write-Host "错误代码: $($process.ExitCode)" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
# 清理临时文件
|
||||||
|
Remove-Item $configFile -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
Write-Host "`n按任意键退出..." -ForegroundColor Gray
|
||||||
|
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@echo off
|
||||||
|
REM 测试 Flutter 安装和版本
|
||||||
|
|
||||||
|
REM 设置 Flutter 路径(假设已安装,如果未安装需手动设置)
|
||||||
|
set FLUTTER_ROOT=C:\flutter REM 替换为您的 Flutter 安装路径
|
||||||
|
|
||||||
|
REM 添加 Flutter 到 PATH
|
||||||
|
set PATH=%FLUTTER_ROOT%\bin;%PATH%
|
||||||
|
|
||||||
|
REM 检查 Flutter 版本
|
||||||
|
flutter --version
|
||||||
|
|
||||||
|
REM 如果需要,升级到指定版本
|
||||||
|
REM flutter upgrade --force
|
||||||
|
|
||||||
|
REM 启用 Windows desktop
|
||||||
|
flutter config --enable-windows-desktop
|
||||||
|
|
||||||
|
REM 获取依赖(假设在项目目录中运行)
|
||||||
|
flutter pub get
|
||||||
|
|
||||||
|
REM 暂停以查看输出
|
||||||
|
pause
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Project-level configuration.
|
# Project-level configuration.
|
||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
project(BearVPN LANGUAGES CXX)
|
project(HiFastVPN LANGUAGES CXX)
|
||||||
|
|
||||||
# 设置 CMake 策略以兼容旧版本插件
|
# 设置 CMake 策略以兼容旧版本插件
|
||||||
# CMP0175: add_custom_command() 拒绝无效参数(用于兼容 flutter_inappwebview_windows 插件)
|
# CMP0175: add_custom_command() 拒绝无效参数(用于兼容 flutter_inappwebview_windows 插件)
|
||||||
@@ -22,7 +22,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FS")
|
|||||||
|
|
||||||
# The name of the executable created for the application. Change this to change
|
# The name of the executable created for the application. Change this to change
|
||||||
# the on-disk name of your application.
|
# the on-disk name of your application.
|
||||||
set(BINARY_NAME "BearVPN")
|
set(BINARY_NAME "HiFastVPN")
|
||||||
|
|
||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
# versions of CMake.
|
# versions of CMake.
|
||||||
@@ -100,7 +100,7 @@ endif()
|
|||||||
|
|
||||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
|
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
|
||||||
# CLI 工具目录(用于存放 BearVPNCli.exe)
|
# CLI 工具目录(用于存放 HiFastVPNCli.exe)
|
||||||
set(INSTALL_BUNDLE_CLI_DIR "${CMAKE_INSTALL_PREFIX}/cli")
|
set(INSTALL_BUNDLE_CLI_DIR "${CMAKE_INSTALL_PREFIX}/cli")
|
||||||
|
|
||||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||||
@@ -118,10 +118,10 @@ install(FILES "../libcore/bin/libcore.dll"
|
|||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
OPTIONAL)
|
OPTIONAL)
|
||||||
|
|
||||||
# 安装 BearVPNCli.exe(从 libcore/bin 复制并重命名)
|
# 安装 HiFastVPNCli.exe(从 libcore/bin 复制并重命名)
|
||||||
# 注意:libcore 编译的是 HiddifyCli.exe,打包脚本会自动重命名为 BearVPNCli.exe
|
# 注意:libcore 编译的是 HiddifyCli.exe,打包脚本会自动重命名为 HiFastVPNCli.exe
|
||||||
# 这里需要安装 BearVPNCli.exe,因为它已经被重命名了
|
# 这里需要安装 HiFastVPNCli.exe,因为它已经被重命名了
|
||||||
install(FILES "../libcore/bin/BearVPNCli.exe"
|
install(FILES "../libcore/bin/HiFastVPNCli.exe"
|
||||||
DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
OPTIONAL)
|
OPTIONAL)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ CloseApplications=force
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
[Tasks]
|
[Tasks]
|
||||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %}
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
|
||||||
Name: "launchAtStartup"; Description: "{cm:AutoStartProgram,{{DISPLAY_NAME}}}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if LAUNCH_AT_STARTUP != true %}unchecked{% else %}checkedonce{% endif %}
|
Name: "launchAtStartup"; Description: "{cm:AutoStartProgram,{{DISPLAY_NAME}}}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: {% if LAUNCH_AT_STARTUP != true %}unchecked{% else %}checkedonce{% endif %}
|
||||||
[Files]
|
[Files]
|
||||||
Source: "{{SOURCE_DIR}}\\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "{{SOURCE_DIR}}\\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
@@ -62,14 +62,82 @@ Name: "{userstartup}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}";
|
|||||||
[Run]
|
[Run]
|
||||||
Filename: "{app}\\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent
|
Filename: "{app}\\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent
|
||||||
|
|
||||||
|
[UninstallDelete]
|
||||||
|
Type: filesandordirs; Name: "{app}"
|
||||||
|
Type: filesandordirs; Name: "{localappdata}\\{{DISPLAY_NAME}}"
|
||||||
|
Type: filesandordirs; Name: "{userappdata}\\{{DISPLAY_NAME}}"
|
||||||
|
Type: filesandordirs; Name: "{tmp}\\{{DISPLAY_NAME}}"
|
||||||
|
|
||||||
|
[Registry]
|
||||||
|
Root: HKCU; Subkey: "Software\\{{DISPLAY_NAME}}"; Flags: uninsdeletekey
|
||||||
|
Root: HKLM; Subkey: "Software\\{{DISPLAY_NAME}}"; Flags: uninsdeletekey
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
function InitializeSetup(): Boolean;
|
procedure AppendLog(S: string);
|
||||||
var
|
|
||||||
ResultCode: Integer;
|
|
||||||
begin
|
begin
|
||||||
Exec('taskkill', '/F /IM BearVPN.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
|
SaveStringToFile(ExpandConstant('{tmp}\\{{DISPLAY_NAME}}_installer.log'), S + #13#10, True);
|
||||||
Exec('net', 'stop "BearVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
|
end;
|
||||||
Exec('sc.exe', 'delete "BearVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
|
|
||||||
|
procedure TerminateProcesses();
|
||||||
|
var ResultCode: Integer;
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
Exec('taskkill', '/F /IM {{EXECUTABLE_NAME}}', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||||
|
except
|
||||||
|
AppendLog('terminate failed: {{EXECUTABLE_NAME}}');
|
||||||
|
end;
|
||||||
|
try
|
||||||
|
Exec('taskkill', '/F /IM HiFastVPNCli.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||||
|
except
|
||||||
|
AppendLog('terminate failed: HiFastVPNCli.exe');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure StopServices();
|
||||||
|
var ResultCode: Integer;
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
Exec('net', 'stop "HiFastVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||||
|
Exec('sc.exe', 'delete "HiFastVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||||
|
except
|
||||||
|
AppendLog('service stop/delete failed: HiFastVPNTunnelService');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure CleanPaths();
|
||||||
|
var ok: Boolean;
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
if DirExists(ExpandConstant('{app}')) then
|
||||||
|
ok := DelTree(ExpandConstant('{app}'), True, True, True)
|
||||||
|
else ok := True;
|
||||||
|
if not ok then AppendLog('delete failed: {app}');
|
||||||
|
except
|
||||||
|
AppendLog('exception deleting: {app}');
|
||||||
|
end;
|
||||||
|
try
|
||||||
|
if DirExists(ExpandConstant('{localappdata}\\{{DISPLAY_NAME}}')) then
|
||||||
|
ok := DelTree(ExpandConstant('{localappdata}\\{{DISPLAY_NAME}}'), True, True, True);
|
||||||
|
if DirExists(ExpandConstant('{userappdata}\\{{DISPLAY_NAME}}')) then
|
||||||
|
ok := DelTree(ExpandConstant('{userappdata}\\{{DISPLAY_NAME}}'), True, True, True);
|
||||||
|
if DirExists(ExpandConstant('{tmp}\\{{DISPLAY_NAME}}')) then
|
||||||
|
ok := DelTree(ExpandConstant('{tmp}\\{{DISPLAY_NAME}}'), True, True, True);
|
||||||
|
except
|
||||||
|
AppendLog('exception deleting user data');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function InitializeSetup(): Boolean;
|
||||||
|
begin
|
||||||
|
TerminateProcesses();
|
||||||
|
StopServices();
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function InitializeUninstall(): Boolean;
|
||||||
|
begin
|
||||||
|
TerminateProcesses();
|
||||||
|
StopServices();
|
||||||
|
CleanPaths();
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
app_id: 6L903538-42B1-4596-G479-BJ779F21A65E
|
app_id: 6L903538-42B1-4596-G479-BJ779F21A65E
|
||||||
publisher: BearVPN
|
publisher: HiFastVPN
|
||||||
publisher_url: https://github.com/hiddify/hiddify-next
|
publisher_url: https://github.com/hiddify/hiddify-next
|
||||||
display_name: BearVPN
|
display_name: HiFastVPN
|
||||||
executable_name: BearVPN.exe
|
executable_name: HiFastVPN.exe
|
||||||
output_base_file_name: BearVPN.exe
|
output_base_file_name: HiFastVPN.exe
|
||||||
create_desktop_icon: true
|
create_desktop_icon: true
|
||||||
install_dir_name: "{autopf64}\\BearVPN"
|
install_dir_name: "{autopf64}\\HiFastVPN"
|
||||||
setup_icon_file: ..\..\windows\runner\resources\app_icon.ico
|
setup_icon_file: ..\..\windows\runner\resources\app_icon.ico
|
||||||
locales:
|
locales:
|
||||||
- ar
|
- ar
|
||||||
|
|||||||
@@ -89,13 +89,13 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "040904e4"
|
BLOCK "040904e4"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "BearVPN" "\0"
|
VALUE "CompanyName", "HiFastVPN" "\0"
|
||||||
VALUE "FileDescription", "BearVPN" "\0"
|
VALUE "FileDescription", "HiFastVPN" "\0"
|
||||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||||
VALUE "InternalName", "app.baer.com" "\0"
|
VALUE "InternalName", "app.baer.com" "\0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2024 BearVPN. All rights reserved." "\0"
|
VALUE "LegalCopyright", "Copyright (C) 2024 HiFastVPN. All rights reserved." "\0"
|
||||||
VALUE "OriginalFilename", "BearVPN.exe" "\0"
|
VALUE "OriginalFilename", "HiFastVPN.exe" "\0"
|
||||||
VALUE "ProductName", "BearVPN" "\0"
|
VALUE "ProductName", "HiFastVPN" "\0"
|
||||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||||
HANDLE hMutexInstance = CreateMutex(NULL, TRUE, L"BearVPNMutex");
|
HANDLE hMutexInstance = CreateMutex(NULL, TRUE, L"HiFastVPNMutex");
|
||||||
HWND handle = FindWindowA(NULL, "BearVPN");
|
HWND handle = FindWindowA(NULL, "HiFastVPN");
|
||||||
|
|
||||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||||
flutter::DartProject project(L"data");
|
flutter::DartProject project(L"data");
|
||||||
std::vector<std::string> command_line_arguments = GetCommandLineArguments();
|
std::vector<std::string> command_line_arguments = GetCommandLineArguments();
|
||||||
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
||||||
FlutterWindow window(project);
|
FlutterWindow window(project);
|
||||||
if (window.SendAppLinkToInstance(L"BearVPN")) {
|
if (window.SendAppLinkToInstance(L"HiFastVPN")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
|||||||
FlutterWindow window(project);
|
FlutterWindow window(project);
|
||||||
Win32Window::Point origin(10, 10);
|
Win32Window::Point origin(10, 10);
|
||||||
Win32Window::Size size(1280, 720);
|
Win32Window::Size size(1280, 720);
|
||||||
if (!window.Create(L"BearVPN", origin, size)) {
|
if (!window.Create(L"HiFastVPN", origin, size)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
window.SetQuitOnClose(true);
|
window.SetQuitOnClose(true);
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ AppPublisher={#MyAppPublisher}
|
|||||||
DefaultDirName={autopf}\{#MyAppName}
|
DefaultDirName={autopf}\{#MyAppName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
OutputDir=installer
|
OutputDir=installer
|
||||||
OutputBaseFilename=BearVPN_Setup
|
OutputBaseFilename=HiFastVPN_Setup
|
||||||
Compression=lzma
|
Compression=lzma
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
WizardStyle=modern
|
WizardStyle=modern
|
||||||
@@ -34,4 +34,4 @@ Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
|||||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# Windows 构建日志分析
|
||||||
|
|
||||||
|
## 🎉 构建结果:成功!
|
||||||
|
|
||||||
|
### ✅ 成功状态
|
||||||
|
- **Debug 构建**: ✓ 成功 (282.5s)
|
||||||
|
- **Release 构建**: ✓ 成功 (27.0s)
|
||||||
|
- **最终状态**: ✅ Job succeeded
|
||||||
|
|
||||||
|
### 📁 构建输出位置
|
||||||
|
```
|
||||||
|
Debug: build\windows\x64\runner\Debug\hostexecutor.exe
|
||||||
|
Release: build\windows\x64\runner\Release\hostexecutor.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 警告信息分析
|
||||||
|
|
||||||
|
### 1. CMake 警告(可忽略)
|
||||||
|
```
|
||||||
|
CMake Warning (dev) at flutter_inappwebview_windows/windows/CMakeLists.txt:31
|
||||||
|
Policy CMP0175 is not set: add_custom_command() rejects invalid arguments.
|
||||||
|
```
|
||||||
|
- **影响**: 无影响,这是开发者警告
|
||||||
|
- **解决方案**: 添加 `-Wno-dev` 参数可抑制
|
||||||
|
|
||||||
|
### 2. WebView2 编译警告(可忽略)
|
||||||
|
```
|
||||||
|
warning C4244: conversion from '__int64' to 'int', possible loss of data
|
||||||
|
warning C4458: declaration of 'value' hides class member
|
||||||
|
```
|
||||||
|
- **影响**: 无功能影响,只是类型转换警告
|
||||||
|
- **状态**: 正常现象,不影响使用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📤 产物上传问题
|
||||||
|
|
||||||
|
### 问题描述
|
||||||
|
```
|
||||||
|
::warning::No files were found with the provided path: build/windows/runner/Debug/
|
||||||
|
::warning::No files were found with the provided path: build/windows/runner/Release/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根本原因
|
||||||
|
**路径配置错误**:workflow 配置的路径 `build/windows/runner/Debug/` 与实际构建输出路径 `build/windows/x64/runner/Debug/` 不匹配。
|
||||||
|
|
||||||
|
### 🔧 解决方案
|
||||||
|
|
||||||
|
需要更新 `.gitea/workflows/docker.yml` 中的上传路径:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 原配置(错误)
|
||||||
|
- name: Upload Debug build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: windows-debug-build
|
||||||
|
path: build/windows/runner/Debug/ # ❌ 错误路径
|
||||||
|
|
||||||
|
# 正确配置
|
||||||
|
- name: Upload Debug build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: windows-debug-build
|
||||||
|
path: build/windows/x64/runner/Debug/ # ✅ 正确路径
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ 构建过程总结
|
||||||
|
|
||||||
|
### 成功的步骤
|
||||||
|
1. ✅ NuGet 安装成功(通过 Chocolatey)
|
||||||
|
2. ✅ Flutter 环境配置正确
|
||||||
|
3. ✅ Windows 桌面支持启用
|
||||||
|
4. ✅ 依赖获取成功
|
||||||
|
5. ✅ 代码生成成功
|
||||||
|
6. ✅ Windows Debug 构建成功
|
||||||
|
7. ✅ Windows Release 构建成功
|
||||||
|
|
||||||
|
### 耗时分析
|
||||||
|
- **Debug 构建**: 282.5秒(约4.7分钟)
|
||||||
|
- **Release 构建**: 27秒(优化后更快)
|
||||||
|
- **总耗时**: 约5分钟
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 下一步行动
|
||||||
|
|
||||||
|
### 立即修复
|
||||||
|
1. **修复上传路径** - 更新 workflow 中的产物路径
|
||||||
|
2. **验证可执行文件** - 确认 `hostexecutor.exe` 可正常运行
|
||||||
|
|
||||||
|
### 可选优化
|
||||||
|
1. **缓存优化** - 添加 Flutter 和依赖缓存
|
||||||
|
2. **并行构建** - Debug 和 Release 可并行执行
|
||||||
|
3. **压缩产物** - 减少上传文件大小
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 构建环境信息
|
||||||
|
|
||||||
|
### 软件版本
|
||||||
|
- **Flutter**: 3.24.5
|
||||||
|
- **Visual Studio**: 2022 Enterprise
|
||||||
|
- **NuGet**: 6.14.0 (通过 Chocolatey)
|
||||||
|
- **CMake**: 最新版本
|
||||||
|
|
||||||
|
### 系统环境
|
||||||
|
- **操作系统**: Windows Server
|
||||||
|
- **架构**: x64
|
||||||
|
- **构建工具**: MSBuild
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 结论
|
||||||
|
|
||||||
|
**构建完全成功!** 主要的可执行文件已经生成,只是上传配置需要微调。Windows 应用构建流程已经稳定运行。
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
# 工作流自动打包单文件 EXE 说明
|
||||||
|
|
||||||
|
## 🎯 功能概述
|
||||||
|
|
||||||
|
现在工作流会自动完成以下步骤:
|
||||||
|
1. ✅ 构建 Windows 应用(Debug 和 Release)
|
||||||
|
2. ✅ 自动下载打包工具
|
||||||
|
3. ✅ 创建单文件 EXE
|
||||||
|
4. ✅ 上传打包结果
|
||||||
|
|
||||||
|
## 🚀 工作流程
|
||||||
|
|
||||||
|
### 步骤 1: Enigma Virtual Box 打包(首选方案)
|
||||||
|
- **工具**: 自动下载 Enigma Virtual Box
|
||||||
|
- **输出**: `hostexecutor_single.exe`
|
||||||
|
- **特点**: 真正的单文件,高压缩率
|
||||||
|
|
||||||
|
### 步骤 2: 7-Zip 自解压方案(备选方案)
|
||||||
|
- **工具**: 使用系统自带的 7-Zip
|
||||||
|
- **输出**: `hostexecutor_package.exe`
|
||||||
|
- **特点**: 自解压安装包,兼容性更好
|
||||||
|
|
||||||
|
### 步骤 3: 产物上传
|
||||||
|
- **单文件 EXE**: `windows-single-exe` 工件
|
||||||
|
- **原始文件**: `windows-release-build` 工件
|
||||||
|
|
||||||
|
## 📦 打包产物
|
||||||
|
|
||||||
|
### 成功时你会得到:
|
||||||
|
```
|
||||||
|
工件列表:
|
||||||
|
├── windows-debug-build # Debug 版本(多文件)
|
||||||
|
├── windows-release-build # Release 版本(多文件)
|
||||||
|
└── windows-single-exe # 单文件 EXE(自动打包)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件结构:
|
||||||
|
```
|
||||||
|
单文件 EXE:
|
||||||
|
└── hostexecutor_single.exe # 50-80MB,直接运行
|
||||||
|
|
||||||
|
原始文件:
|
||||||
|
└── hostexecutor.exe # 主程序
|
||||||
|
├── flutter_windows.dll # Flutter 引擎
|
||||||
|
├── *.dll # 运行时库
|
||||||
|
└── data/ # 应用数据
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ 技术实现
|
||||||
|
|
||||||
|
### 自动下载 Enigma Virtual Box
|
||||||
|
```powershell
|
||||||
|
$enigmaUrl = "https://enigmaprotector.com/assets/files/enigmavb.exe"
|
||||||
|
Invoke-WebRequest -Uri $enigmaUrl -OutFile "C:\enigmavb.exe"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 智能打包逻辑
|
||||||
|
1. **尝试 Enigma** - 创建真正的单文件
|
||||||
|
2. **失败时回退** - 使用 7-Zip 自解压
|
||||||
|
3. **总是成功** - 确保有输出文件
|
||||||
|
|
||||||
|
### 压缩优化
|
||||||
|
- **压缩级别**: 最高压缩率 (`/compress`)
|
||||||
|
- **虚拟化模式**: 内存运行,不写磁盘
|
||||||
|
- **多线程**: 并行处理,加快速度
|
||||||
|
|
||||||
|
## 📋 使用说明
|
||||||
|
|
||||||
|
### 获取单文件 EXE
|
||||||
|
1. 进入 Gitea Actions 页面
|
||||||
|
2. 找到最新的成功构建
|
||||||
|
3. 下载 `windows-single-exe` 工件
|
||||||
|
4. 直接运行 `hostexecutor_single.exe`
|
||||||
|
|
||||||
|
### 验证打包结果
|
||||||
|
```powershell
|
||||||
|
# 检查文件大小(应该在 50-80MB)
|
||||||
|
dir hostexecutor_single.exe
|
||||||
|
|
||||||
|
# 验证单文件(应该只有 1 个文件)
|
||||||
|
dir *.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 优势特点
|
||||||
|
|
||||||
|
### 完全自动化
|
||||||
|
- ✅ 无需手动操作
|
||||||
|
- ✅ 自动下载工具
|
||||||
|
- ✅ 智能错误处理
|
||||||
|
- ✅ 保证输出结果
|
||||||
|
|
||||||
|
### 高质量打包
|
||||||
|
- ✅ 真正的单文件
|
||||||
|
- ✅ 高压缩率
|
||||||
|
- ✅ 内存运行,不写临时文件
|
||||||
|
- ✅ 兼容所有 Windows 版本
|
||||||
|
|
||||||
|
### 可靠回退
|
||||||
|
- ✅ Enigma 失败时自动切换 7-Zip
|
||||||
|
- ✅ 总是生成可用的输出
|
||||||
|
- ✅ 详细的构建日志
|
||||||
|
|
||||||
|
## 🔧 自定义配置
|
||||||
|
|
||||||
|
### 修改压缩级别
|
||||||
|
在工作流脚本中找到:
|
||||||
|
```powershell
|
||||||
|
# 最高压缩(当前设置)
|
||||||
|
/compress
|
||||||
|
|
||||||
|
# 快速压缩(速度优先)
|
||||||
|
/compress:fast
|
||||||
|
|
||||||
|
# 平衡压缩(推荐)
|
||||||
|
/compress:normal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改输出文件名
|
||||||
|
```powershell
|
||||||
|
# 当前设置
|
||||||
|
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||||
|
|
||||||
|
# 自定义名称
|
||||||
|
$outputExe = "$outputPath\MyApp_Portable.exe"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 性能指标
|
||||||
|
|
||||||
|
### 构建时间
|
||||||
|
- **Debug 构建**: ~4.7 分钟
|
||||||
|
- **Release 构建**: ~27 秒
|
||||||
|
- **单文件打包**: ~2-5 分钟
|
||||||
|
- **总时间**: ~8-12 分钟
|
||||||
|
|
||||||
|
### 文件大小
|
||||||
|
- **原始文件**: ~70MB(多文件)
|
||||||
|
- **单文件**: ~50-80MB(压缩后)
|
||||||
|
- **压缩率**: 10-30% 减小
|
||||||
|
|
||||||
|
## 🎉 总结
|
||||||
|
|
||||||
|
**现在工作流完全自动化了!**
|
||||||
|
- 🔄 每次推送代码 → 自动构建 → 自动打包单文件
|
||||||
|
- 📦 构建完成后 → 直接下载单文件 EXE 使用
|
||||||
|
- 🔧 无需手动配置 → 开箱即用
|
||||||
|
|
||||||
|
**你只需要**:等待构建完成,下载单文件,直接运行!✨
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# Windows 构建项目说明文档
|
||||||
|
|
||||||
|
## 🎯 项目概述
|
||||||
|
|
||||||
|
本项目是一个 Flutter Windows 应用程序,已成功配置完整的 Windows 构建流程。
|
||||||
|
|
||||||
|
## ✅ 构建状态
|
||||||
|
|
||||||
|
**当前状态**: ✅ **构建成功**
|
||||||
|
|
||||||
|
- **Debug 构建**: ✓ 成功 (282.5s)
|
||||||
|
- **Release 构建**: ✓ 成功 (27s)
|
||||||
|
- **构建环境**: Windows Server + Visual Studio 2022 Enterprise
|
||||||
|
|
||||||
|
## 🏗️ 构建流程
|
||||||
|
|
||||||
|
### 1. 环境准备
|
||||||
|
- ✅ Flutter 3.24.5 已安装
|
||||||
|
- ✅ Visual Studio 2022 Enterprise 已配置
|
||||||
|
- ✅ NuGet 6.14.0 已安装(通过 Chocolatey)
|
||||||
|
- ✅ Windows 长路径支持已启用
|
||||||
|
|
||||||
|
### 2. 构建步骤
|
||||||
|
1. **代码检出** - 从 Gitea 仓库获取代码
|
||||||
|
2. **依赖安装** - 安装 Flutter 依赖包
|
||||||
|
3. **代码生成** - 运行 build_runner 生成代码
|
||||||
|
4. **Windows 构建** - 构建 Debug 和 Release 版本
|
||||||
|
5. **产物上传** - 上传构建产物到 Gitea Actions
|
||||||
|
|
||||||
|
### 3. 构建输出
|
||||||
|
```
|
||||||
|
Debug: build/windows/x64/runner/Debug/hostexecutor.exe
|
||||||
|
Release: build/windows/x64/runner/Release/hostexecutor.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 关键修复记录
|
||||||
|
|
||||||
|
### 1. NuGet 安装问题
|
||||||
|
**问题**: SSL/TLS 安全通道错误
|
||||||
|
**解决方案**:
|
||||||
|
- 使用 Chocolatey 安装 NuGet
|
||||||
|
- 命令: `choco install nuget.commandline -y`
|
||||||
|
|
||||||
|
### 2. Flutter 路径配置
|
||||||
|
**问题**: Flutter 命令未找到
|
||||||
|
**解决方案**:
|
||||||
|
- 添加 Flutter 到 PATH: `C:\flutter\bin`
|
||||||
|
- 在每个构建步骤中显式设置 PATH
|
||||||
|
|
||||||
|
### 3. 长路径问题
|
||||||
|
**问题**: Windows 路径长度限制
|
||||||
|
**解决方案**:
|
||||||
|
- 启用 Windows 长路径支持
|
||||||
|
- 注册表设置: `LongPathsEnabled = 1`
|
||||||
|
|
||||||
|
### 4. 构建产物路径
|
||||||
|
**问题**: 上传路径配置错误
|
||||||
|
**解决方案**:
|
||||||
|
- 修正路径: `build/windows/x64/runner/Debug/`
|
||||||
|
- 原错误路径: `build/windows/runner/Debug/`
|
||||||
|
|
||||||
|
## 📁 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
/Users/Apple/vpn/hi-client/
|
||||||
|
├── .gitea/workflows/ # Gitea Actions 工作流配置
|
||||||
|
├── lib/ # Flutter 源代码
|
||||||
|
│ ├── app/ # 应用程序代码
|
||||||
|
│ ├── core/ # 核心功能
|
||||||
|
│ └── singbox/ # SingBox 相关
|
||||||
|
├── windows/ # Windows 平台配置
|
||||||
|
├── libcore/ # 核心库(子模块)
|
||||||
|
└── build/ # 构建输出(运行时生成)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 本地构建
|
||||||
|
```bash
|
||||||
|
# 安装 Flutter 依赖
|
||||||
|
flutter pub get
|
||||||
|
|
||||||
|
# 生成代码
|
||||||
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
|
# 构建 Windows Debug
|
||||||
|
flutter build windows
|
||||||
|
|
||||||
|
# 构建 Windows Release
|
||||||
|
flutter build windows --release
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用脚本
|
||||||
|
```bash
|
||||||
|
# 运行 Windows 构建修复脚本
|
||||||
|
./fix_windows_build.ps1
|
||||||
|
|
||||||
|
# 安装 NuGet(如果需要)
|
||||||
|
./install_nuget_simple.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 注意事项
|
||||||
|
|
||||||
|
### 1. 构建环境要求
|
||||||
|
- Windows 10/11 或 Windows Server
|
||||||
|
- Visual Studio 2022(包含 C++ 开发工具)
|
||||||
|
- Flutter 3.24.5+
|
||||||
|
- NuGet CLI
|
||||||
|
|
||||||
|
### 2. 常见问题
|
||||||
|
- **CMake 警告**: 可忽略,不影响构建
|
||||||
|
- **WebView2 警告**: 类型转换警告,不影响功能
|
||||||
|
- **路径问题**: 确保使用正确的 x64 路径
|
||||||
|
|
||||||
|
### 3. 性能优化
|
||||||
|
- Debug 构建约 4.7 分钟
|
||||||
|
- Release 构建约 27 秒
|
||||||
|
- 建议使用 Release 版本进行分发
|
||||||
|
|
||||||
|
## 🔍 调试工具
|
||||||
|
|
||||||
|
### 构建日志分析
|
||||||
|
查看 `构建日志分析.md` 文件获取详细的构建日志分析和故障排除指南。
|
||||||
|
|
||||||
|
### 连接状态调试
|
||||||
|
使用 `debug_connection_status.dart` 工具检查应用连接状态。
|
||||||
|
|
||||||
|
## 📞 支持
|
||||||
|
|
||||||
|
如遇到构建问题,请检查:
|
||||||
|
1. 环境配置是否正确
|
||||||
|
2. 依赖是否完整安装
|
||||||
|
3. 查看构建日志获取具体错误信息
|
||||||
|
4. 参考本说明文档的修复记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新**: $(date)
|
||||||
|
**构建状态**: ✅ 成功
|
||||||
|
**文档版本**: 1.0
|
||||||
Reference in New Issue
Block a user