198 lines
5.7 KiB
Go
198 lines
5.7 KiB
Go
// Package windows 提供Windows平台安装包构建功能
|
||
package windows
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"git.kingecg.top/kingecg/installerbuilder/internal/builder"
|
||
"git.kingecg.top/kingecg/installerbuilder/internal/config"
|
||
"git.kingecg.top/kingecg/installerbuilder/internal/logger"
|
||
)
|
||
|
||
// MSIBuilder 是Windows MSI安装包构建器
|
||
type MSIBuilder struct {
|
||
config *config.Config
|
||
outputDir string
|
||
}
|
||
|
||
// 确保MSIBuilder实现了Builder接口
|
||
var _ builder.Builder = (*MSIBuilder)(nil)
|
||
|
||
// 注册构建器
|
||
func init() {
|
||
builder.RegisterBuilder("windows-msi", func(cfg *config.Config, outputDir string) builder.Builder {
|
||
return NewMSIBuilder(cfg, outputDir)
|
||
})
|
||
}
|
||
|
||
// NewMSIBuilder 创建一个新的MSI构建器
|
||
func NewMSIBuilder(cfg *config.Config, outputDir string) *MSIBuilder {
|
||
return &MSIBuilder{
|
||
config: cfg,
|
||
outputDir: outputDir,
|
||
}
|
||
}
|
||
|
||
// Name 返回构建器名称
|
||
func (b *MSIBuilder) Name() string {
|
||
return "Windows MSI Builder"
|
||
}
|
||
|
||
// SupportedPlatforms 返回支持的平台列表
|
||
func (b *MSIBuilder) SupportedPlatforms() []string {
|
||
return []string{"windows"}
|
||
}
|
||
|
||
// SupportedFormats 返回支持的格式列表
|
||
func (b *MSIBuilder) SupportedFormats() []string {
|
||
return []string{"msi"}
|
||
}
|
||
|
||
// Build 构建MSI安装包
|
||
func (b *MSIBuilder) Build() error {
|
||
logger.Info("开始构建Windows MSI安装包")
|
||
|
||
// 创建临时工作目录
|
||
workDir, err := os.MkdirTemp("", "installer-builder-msi-*")
|
||
if err != nil {
|
||
return fmt.Errorf("创建临时工作目录失败: %w", err)
|
||
}
|
||
defer os.RemoveAll(workDir)
|
||
|
||
logger.Debugf("使用临时工作目录: %s", workDir)
|
||
|
||
// 创建WiX源文件
|
||
wixFile := filepath.Join(workDir, "installer.wxs")
|
||
if err := b.createWixSource(wixFile); err != nil {
|
||
return fmt.Errorf("创建WiX源文件失败: %w", err)
|
||
}
|
||
|
||
// 复制文件到临时目录
|
||
if err := b.copyFiles(workDir); err != nil {
|
||
return fmt.Errorf("复制文件失败: %w", err)
|
||
}
|
||
|
||
// 编译WiX源文件
|
||
objFile := filepath.Join(workDir, "installer.wixobj")
|
||
if err := b.compileWixSource(wixFile, objFile); err != nil {
|
||
return fmt.Errorf("编译WiX源文件失败: %w", err)
|
||
}
|
||
|
||
// 链接生成MSI
|
||
msiFile := filepath.Join(b.outputDir, fmt.Sprintf("%s-%s.msi", b.config.Name, b.config.Version))
|
||
if err := b.linkMsi(objFile, msiFile); err != nil {
|
||
return fmt.Errorf("链接MSI文件失败: %w", err)
|
||
}
|
||
|
||
logger.Infof("成功构建MSI安装包: %s", msiFile)
|
||
return nil
|
||
}
|
||
|
||
// createWixSource 创建WiX源文件
|
||
func (b *MSIBuilder) createWixSource(outputFile string) error {
|
||
// 这里是一个简化的WiX源文件生成示例
|
||
// 实际应用中,应该根据配置生成更复杂的WiX源文件
|
||
content := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
|
||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||
<Product Id="*"
|
||
Name="%s"
|
||
Language="1033"
|
||
Version="%s"
|
||
Manufacturer="%s"
|
||
UpgradeCode="%s">
|
||
<Package InstallerVersion="200"
|
||
Compressed="yes"
|
||
InstallScope="perMachine"
|
||
Description="%s" />
|
||
|
||
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
||
<MediaTemplate EmbedCab="yes" />
|
||
|
||
<Feature Id="ProductFeature" Title="Main Product" Level="1">
|
||
<ComponentGroupRef Id="ProductComponents" />
|
||
</Feature>
|
||
|
||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||
<Directory Id="ProgramFilesFolder">
|
||
<Directory Id="INSTALLFOLDER" Name="%s">
|
||
<!-- 组件将在这里定义 -->
|
||
</Directory>
|
||
</Directory>
|
||
</Directory>
|
||
|
||
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
|
||
<!-- 组件将在这里定义 -->
|
||
</ComponentGroup>
|
||
</Product>
|
||
</Wix>`,
|
||
b.config.Name,
|
||
b.config.Version,
|
||
b.config.Platforms.Windows.Msi.Manufacturer,
|
||
b.config.Platforms.Windows.Msi.UpgradeCode,
|
||
b.config.Description,
|
||
b.config.Platforms.Windows.Msi.InstallDir)
|
||
|
||
return os.WriteFile(outputFile, []byte(content), 0644)
|
||
}
|
||
|
||
// copyFiles 复制文件到临时目录
|
||
func (b *MSIBuilder) copyFiles(workDir string) error {
|
||
// 这里应该实现文件复制逻辑
|
||
// 实际应用中,应该根据配置复制文件到临时目录
|
||
logger.Debug("复制文件到临时目录")
|
||
return nil
|
||
}
|
||
|
||
// compileWixSource 编译WiX源文件
|
||
func (b *MSIBuilder) compileWixSource(sourceFile, outputFile string) error {
|
||
// 检查是否安装了WiX工具集
|
||
candle, err := exec.LookPath("candle")
|
||
if err != nil {
|
||
return fmt.Errorf("未找到WiX Toolset的candle工具,请确保已安装WiX Toolset并添加到PATH中")
|
||
}
|
||
|
||
// 构建命令
|
||
cmd := exec.Command(candle, "-nologo", "-arch", "x64", "-out", outputFile, sourceFile)
|
||
cmd.Stdout = os.Stdout
|
||
cmd.Stderr = os.Stderr
|
||
|
||
// 执行命令
|
||
logger.Debugf("执行命令: %s", strings.Join(cmd.Args, " "))
|
||
if err := cmd.Run(); err != nil {
|
||
return fmt.Errorf("执行candle命令失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// linkMsi 链接生成MSI
|
||
func (b *MSIBuilder) linkMsi(objFile, msiFile string) error {
|
||
// 检查是否安装了WiX工具集
|
||
light, err := exec.LookPath("light")
|
||
if err != nil {
|
||
return fmt.Errorf("未找到WiX Toolset的light工具,请确保已安装WiX Toolset并添加到PATH中")
|
||
}
|
||
|
||
// 确保输出目录存在
|
||
if err := os.MkdirAll(filepath.Dir(msiFile), 0755); err != nil {
|
||
return fmt.Errorf("创建输出目录失败: %w", err)
|
||
}
|
||
|
||
// 构建命令
|
||
cmd := exec.Command(light, "-nologo", "-ext", "WixUIExtension", "-out", msiFile, objFile)
|
||
cmd.Stdout = os.Stdout
|
||
cmd.Stderr = os.Stderr
|
||
|
||
// 执行命令
|
||
logger.Debugf("执行命令: %s", strings.Join(cmd.Args, " "))
|
||
if err := cmd.Run(); err != nil {
|
||
return fmt.Errorf("执行light命令失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|