command-line: 增加 darwin package xxx.app 包制作

This commit is contained in:
zhangli10 2023-09-22 11:47:34 +08:00
parent 83ec151340
commit 43ff43c6ed
6 changed files with 272 additions and 6 deletions

View File

@ -49,7 +49,11 @@ func termRun() {
os.Exit(1)
}
if extraArgs, err := parser.ParseArgs(os.Args[1:]); err != nil {
println(err.Error())
if extraArgs[0] == "-v" || extraArgs[0] == "v" {
term.Section.Println(" ", term.CliVersion)
} else {
println(err.Error())
}
//exit <- 1
os.Exit(1)
} else {
@ -68,6 +72,9 @@ func termRun() {
cc.Index = 6
case "build":
cc.Index = 7
case "v":
term.Section.Println(" ", term.CliVersion)
return
}
cmd := commands[cc.Index]
if len(extraArgs) < 1 || extraArgs[len(extraArgs)-1] != "." {

View File

@ -6,8 +6,8 @@
<key>CFBundleName</key>
<string>{{.Name}}</string>
<key>CFBundleLocalizations</key>
<array>
<string>{{.PList.Locales}}</string>
<array>{{range $i,$value := .PList.Locals }}
<string>{{$value}}</string>{{end}}
</array>
<key>CFBundleIdentifier</key>
<string>com.{{.PList.CompanyName}}.{{.PList.ProductName}}</string>

View File

@ -24,6 +24,7 @@ type Config struct {
Env Env `command:"env" description:"display ENERGY_ HOME framework environment directory"`
Init Init `command:"init" description:"initialize the energy application project"`
Build Build `command:"build" description:"building an energy project"`
V string `command:"v" description:"energy cli version"`
}
type Command struct {

View File

@ -14,12 +14,25 @@
package packager
import (
"errors"
"fmt"
"github.com/energye/energy/v2/cmd/internal/assets"
"github.com/energye/energy/v2/cmd/internal/project"
"github.com/energye/energy/v2/cmd/internal/term"
"github.com/energye/energy/v2/cmd/internal/tools"
"github.com/energye/golcl/tools/command"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
)
const (
appContents = "Contents"
appContentsFrameworks = "Frameworks"
appContentsMacOS = "MacOS"
appContentsResources = "Resources"
)
const (
@ -27,11 +40,23 @@ const (
)
var (
pkgInfo = []byte{0x41, 0x50, 0x50, 0x4C, 0x3F, 0x3F, 0x3F, 0x3F, 0x0D, 0x0A}
pkgInfo = []byte{0x41, 0x50, 0x50, 0x4C, 0x3F, 0x3F, 0x3F, 0x3F, 0x0D, 0x0A}
sipsCmds = []string{
"-z 16 16 %s -o icons.iconset/%s_16x16.png",
"-z 32 32 %s -o icons.iconset/%s_16x16@2x.png",
"-z 32 32 %s -o icons.iconset/%s_32x32.png",
"-z 64 64 %s -o icons.iconset/%s_32x32@2x.png",
"-z 128 128 %s -o icons.iconset/%s_128x128.png",
"-z 256 256 %s -o icons.iconset/%s_128x128@2x.png",
"-z 256 256 %s -o icons.iconset/%s_256x256.png",
"-z 512 512 %s -o icons.iconset/%s_256x256@2x.png",
"-z 512 512 %s -o icons.iconset/%s_512x512.png",
"-z 1024 1024 %s -o icons.iconset/%s_512x512@2x.png",
}
)
func GeneraInstaller(proj *project.Project) error {
appRoot := fmt.Sprintf("linux/%s-%s", proj.Name, proj.Info.ProductVersion)
appRoot := fmt.Sprintf("darwin/%s.app", proj.Name)
buildOutDir := assets.BuildOutPath(proj)
buildOutDir = filepath.Join(buildOutDir, appRoot)
if !tools.IsExist(buildOutDir) {
@ -39,5 +64,237 @@ func GeneraInstaller(proj *project.Project) error {
return fmt.Errorf("unable to create directory: %w", err)
}
}
if err := createApp(proj, appRoot); err != nil {
return err
}
if err := generateICNS(proj, appRoot); err != nil {
return err
}
if err := createAppInfoPList(proj, appRoot); err != nil {
return err
}
if err := createAppPkgInfo(proj, appRoot); err != nil {
return err
}
if err := copyFrameworkFile(proj, appRoot); err != nil {
return err
}
return nil
}
func createApp(proj *project.Project, appRoot string) error {
term.Logger.Info("Generate app create app dir.")
buildOutDir := assets.BuildOutPath(proj)
appDir := filepath.Join(buildOutDir, appRoot)
// Contents
contents := filepath.Join(appDir, appContents)
if err := os.MkdirAll(contents, 0755); err != nil {
return fmt.Errorf("unable to create directory: %w", err)
}
// Contents/Frameworks
contentsFrameworks := filepath.Join(contents, appContentsFrameworks)
if err := os.MkdirAll(contentsFrameworks, 0755); err != nil {
return fmt.Errorf("unable to create directory: %w", err)
}
// Contents/MacOS
contentsMacOS := filepath.Join(contents, appContentsMacOS)
if err := os.MkdirAll(contentsMacOS, 0755); err != nil {
return fmt.Errorf("unable to create directory: %w", err)
}
// Contents/Resources
contentsResources := filepath.Join(contents, appContentsResources)
if err := os.MkdirAll(contentsResources, 0755); err != nil {
return fmt.Errorf("unable to create directory: %w", err)
}
return nil
}
func generateICNS(proj *project.Project, appRoot string) error {
term.Logger.Info("Generate app icns")
buildOutDir := assets.BuildOutPath(proj)
appDir := filepath.Join(buildOutDir, appRoot)
iconExt := strings.ToLower(filepath.Ext(proj.PList.Icon))
tmpWorkDir := filepath.Join(buildOutDir, "tmp")
os.Remove(tmpWorkDir)
if iconExt == ".png" {
term.Logger.Info("\tcreates icns")
src, err := os.Open(proj.PList.Icon)
if err != nil {
return err
}
var closeSrc = func() {
if src != nil {
src.Close()
src = nil
}
}
defer closeSrc()
_, icnsName := filepath.Split(proj.PList.Icon)
os.MkdirAll(tmpWorkDir, fs.ModePerm)
outIconsetPath := filepath.Join(tmpWorkDir, "icons.iconset")
os.MkdirAll(outIconsetPath, fs.ModePerm)
tmpIconFilePath := filepath.Join(tmpWorkDir, icnsName)
dst, err := os.OpenFile(tmpIconFilePath, os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
return err
}
io.Copy(dst, src)
dst.Close()
closeSrc()
name := icnsName[:len(icnsName)-4]
// 生成图标
cmd := command.NewCMD()
cmd.Dir = tmpWorkDir
cmd.IsPrint = false
cmd.MessageCallback = func(bytes []byte, e error) {
if err != nil {
err = e
}
}
for _, arg := range sipsCmds {
cmd.Command("sips", strings.Split(fmt.Sprintf(arg, icnsName, name), " ")...)
}
cmd.Command("iconutil", strings.Split(fmt.Sprintf("-c icns icons.iconset -o %s.icns", name), " ")...)
cmd.Close()
if err != nil {
return err
}
proj.PList.Icon = filepath.Join(tmpWorkDir, name+".icns")
}
if iconExt == ".png" || iconExt == ".icns" {
term.Logger.Info("\tcopy icns")
src, err := os.Open(proj.PList.Icon)
if err != nil {
return err
}
defer src.Close()
// Contents
contents := filepath.Join(appDir, appContents)
// Contents/Resources
_, icnsName := filepath.Split(proj.PList.Icon)
outIcnsFilePath := filepath.Join(contents, appContentsResources, icnsName)
os.Remove(outIcnsFilePath)
dst, err := os.OpenFile(outIcnsFilePath, os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
return err
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
return err
}
// 设置icon文件名称
proj.PList.Icon = icnsName
} else {
return errors.New("app icon, only supports png or icns")
}
return nil
}
func createAppInfoPList(proj *project.Project, appRoot string) error {
term.Logger.Info("Generate app Info.plist")
// Contents/Info.plist
if plistData, err := assets.ReadFile(proj, assetsFSPath, darwinInfoPList); err != nil {
return err
} else {
data := make(map[string]any)
data["Name"] = proj.Name
data["OutputFilename"] = proj.OutputFilename
data["PList"] = proj.PList
if content, err := tools.RenderTemplate(string(plistData), data); err != nil {
return err
} else {
infoPListFile := filepath.Join(appRoot, appContents, "Info.plist")
if err = assets.WriteFile(proj, infoPListFile, content); err != nil {
return err
}
}
}
return nil
}
func createAppPkgInfo(proj *project.Project, appRoot string) error {
term.Logger.Info("Generate app PkgInfo")
// Contents/PkgInfo
infoPListFile := filepath.Join(appRoot, appContents, "PkgInfo")
if err := assets.WriteFile(proj, infoPListFile, pkgInfo); err != nil {
return err
}
return nil
}
func copyFrameworkFile(proj *project.Project, appRoot string) error {
term.Logger.Info("Generate app copy framework:",
term.Logger.Args("company", proj.Info.CompanyName, "product", proj.Info.ProductName))
//buildOutDir := assets.BuildOutPath(proj)
//appDir := filepath.Join(buildOutDir, appRoot)
//contents := filepath.Join(appDir, appContents)
//var copyFiles = func(src, dst string) error {
// if srcFile, err := os.Open(src); err != nil {
// return err
// } else {
// st, err := srcFile.Stat()
// if err != nil {
// return err
// }
// if st.IsDir() {
// srcFile.Close() //close
// var pathLen = len(src)
// err := filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
// if path == src { // current root
// return nil
// }
// outPath := path[pathLen:]
// // exclude file or dir
// for _, p := range proj.Dpkg.Exclude {
// if strings.Contains(outPath, p) {
// return nil
// }
// }
// targetPath := filepath.Join(dst, outPath)
// info, _ := d.Info()
// if d.IsDir() {
// return os.MkdirAll(targetPath, info.Mode())
// } else {
// if tools.IsExistAndSize(targetPath, info.Size()) {
// term.Logger.Info("\tcopy skip: " + outPath)
// return nil
// }
// srcFile, err := os.Open(path)
// if err != nil {
// return err
// }
// defer srcFile.Close()
// dstFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY, info.Mode())
// if err != nil {
// return err
// }
// defer dstFile.Close()
// term.Logger.Info("\tcopy: " + outPath)
// _, err = io.Copy(dstFile, srcFile)
// return err
// }
// })
// if err != nil {
// return err
// }
// } else {
// defer srcFile.Close()
// dstFilePath := filepath.Join(dst, st.Name())
// dstFile, err := os.OpenFile(dstFilePath, os.O_CREATE|os.O_WRONLY, st.Mode())
// if err != nil {
// return err
// }
// defer dstFile.Close()
// _, err = io.Copy(dstFile, srcFile)
// if err != nil {
// return err
// }
// }
// }
// return nil
//}
return nil
}

View File

@ -162,6 +162,7 @@ type PList struct {
CFBundleShortVersionString string `json:"cfBundleShortVersionString"` //发布版本号版本
Copyright *string `json:"copyright"` //版权
Comments *string `json:"comments"` //exe详情描述
LSUIElement bool `json:"-"` //UI
}

View File

@ -16,7 +16,7 @@ import (
)
const (
energyCliVersion = "1.0.0"
CliVersion = "1.0.1"
)
var Logger *pterm.Logger