feat(打包): 添加Windows单文件打包脚本和工具安装脚本
添加install_build_tools.bat用于自动安装构建工具 添加package_windows_single_exe.ps1用于打包Flutter应用为单文件 改进inno_setup.sas增加卸载清理逻辑和进程终止功能
This commit is contained in:
parent
8ccfc6d272
commit
7956349c0a
64
install_build_tools.bat
Normal file
64
install_build_tools.bat
Normal file
@ -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
|
||||
168
package_windows_single_exe.ps1
Normal file
168
package_windows_single_exe.ps1
Normal file
@ -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."
|
||||
}
|
||||
@ -49,7 +49,7 @@ CloseApplications=force
|
||||
{% endfor %}
|
||||
|
||||
[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 %}
|
||||
[Files]
|
||||
Source: "{{SOURCE_DIR}}\\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
@ -62,14 +62,82 @@ Name: "{userstartup}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}";
|
||||
[Run]
|
||||
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]
|
||||
function InitializeSetup(): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
procedure AppendLog(S: string);
|
||||
begin
|
||||
Exec('taskkill', '/F /IM BearVPN.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
|
||||
Exec('net', 'stop "BearVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
|
||||
Exec('sc.exe', 'delete "BearVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
|
||||
SaveStringToFile(ExpandConstant('{tmp}\\{{DISPLAY_NAME}}_installer.log'), S + #13#10, True);
|
||||
end;
|
||||
|
||||
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 BearVPNCli.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
except
|
||||
AppendLog('terminate failed: BearVPNCli.exe');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure StopServices();
|
||||
var ResultCode: Integer;
|
||||
begin
|
||||
try
|
||||
Exec('net', 'stop "BearVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
Exec('sc.exe', 'delete "BearVPNTunnelService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
except
|
||||
AppendLog('service stop/delete failed: BearVPNTunnelService');
|
||||
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;
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeUninstall(): Boolean;
|
||||
begin
|
||||
TerminateProcesses();
|
||||
StopServices();
|
||||
CleanPaths();
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user