Skip to content

Commit b17687d

Browse files
committed
Add support for darwin-pkg
1 parent aac59c6 commit b17687d

File tree

7 files changed

+247
-54
lines changed

7 files changed

+247
-54
lines changed

cmd/build.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/go-flutter-desktop/hover/internal/versioncheck"
2020
"github.com/go-flutter-desktop/hover/internal/build"
2121
"github.com/go-flutter-desktop/hover/internal/pubspec"
22+
"github.com/go-flutter-desktop/hover/internal/androidmanifest"
2223
"github.com/go-flutter-desktop/hover/cmd/packaging"
2324
)
2425

@@ -53,6 +54,7 @@ func init() {
5354
buildCmd.AddCommand(buildLinuxDebCmd)
5455
buildCmd.AddCommand(buildDarwinCmd)
5556
buildCmd.AddCommand(buildDarwinBundleCmd)
57+
buildCmd.AddCommand(buildDarwinPkgCmd)
5658
buildCmd.AddCommand(buildWindowsCmd)
5759
rootCmd.AddCommand(buildCmd)
5860
}
@@ -130,6 +132,22 @@ var buildDarwinBundleCmd = &cobra.Command{
130132
},
131133
}
132134

135+
var buildDarwinPkgCmd = &cobra.Command{
136+
Use: "darwin-pkg",
137+
Short: "Build a desktop release for darwin and package it for OSX pkg installer",
138+
Run: func(cmd *cobra.Command, args []string) {
139+
assertHoverInitialized()
140+
packaging.AssertPackagingFormatInitialized("darwin-pkg")
141+
142+
if !packaging.DockerInstalled() {
143+
os.Exit(1)
144+
}
145+
146+
buildNormal("darwin", nil)
147+
packaging.BuildDarwinPkg()
148+
},
149+
}
150+
133151
var buildWindowsCmd = &cobra.Command{
134152
Use: "windows",
135153
Short: "Build a desktop release for windows",
@@ -495,7 +513,7 @@ func buildCommand(targetOS string, vmArguments []string, outputBinaryPath string
495513
pubspec.GetPubSpec().Version,
496514
currentTag,
497515
pubspec.GetPubSpec().Name,
498-
androidOrganizationName()))
516+
androidmanifest.AndroidOrganizationName()))
499517

500518
outputCommand := []string{
501519
"go",

cmd/common.go

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ package cmd
22

33
import (
44
"bufio"
5-
"encoding/xml"
6-
"io/ioutil"
75
"os"
86
"os/exec"
97
"path/filepath"
108
"strings"
119

12-
"github.com/go-flutter-desktop/hover/internal/pubspec"
1310
"github.com/go-flutter-desktop/hover/internal/build"
1411
"github.com/go-flutter-desktop/hover/internal/log"
12+
"github.com/go-flutter-desktop/hover/internal/pubspec"
1513
)
1614

1715
func initBinaries() {
@@ -103,47 +101,3 @@ func askForConfirmation() bool {
103101
}
104102
return false
105103
}
106-
107-
// AndroidManifest is a file that describes the essential information about
108-
// an android app.
109-
type AndroidManifest struct {
110-
Package string `xml:"package,attr"`
111-
}
112-
113-
// androidOrganizationName fetch the android package name (default:
114-
// 'com.example').
115-
// Can by set upon flutter create (--org flag)
116-
//
117-
// If errors occurs when reading the android package name, the string value
118-
// will correspond to 'hover.failed.to.retrieve.package.name'
119-
func androidOrganizationName() string {
120-
// Default value
121-
androidManifestFile := "android/app/src/main/AndroidManifest.xml"
122-
123-
// Open AndroidManifest file
124-
xmlFile, err := os.Open(androidManifestFile)
125-
if err != nil {
126-
log.Errorf("Failed to retrieve the organization name: %v", err)
127-
return "hover.failed.to.retrieve.package.name"
128-
}
129-
defer xmlFile.Close()
130-
131-
byteXMLValue, err := ioutil.ReadAll(xmlFile)
132-
if err != nil {
133-
log.Errorf("Failed to retrieve the organization name: %v", err)
134-
return "hover.failed.to.retrieve.package.name"
135-
}
136-
137-
var androidManifest AndroidManifest
138-
err = xml.Unmarshal(byteXMLValue, &androidManifest)
139-
if err != nil {
140-
log.Errorf("Failed to retrieve the organization name: %v", err)
141-
return "hover.failed.to.retrieve.package.name"
142-
}
143-
javaPackage := strings.Split(androidManifest.Package, ".")
144-
orgName := strings.Join(javaPackage[:len(javaPackage)-1], ".")
145-
if orgName == "" {
146-
return "hover.failed.to.retrieve.package.name"
147-
}
148-
return orgName
149-
}

cmd/packaging.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ func init() {
1010
initPackagingCmd.AddCommand(initLinuxSnapCmd)
1111
initPackagingCmd.AddCommand(initLinuxDebCmd)
1212
initPackagingCmd.AddCommand(initDarwinBundleCmd)
13+
initPackagingCmd.AddCommand(initDarwinPkgCmd)
1314
rootCmd.AddCommand(initPackagingCmd)
1415
}
1516

@@ -50,3 +51,14 @@ var initDarwinBundleCmd = &cobra.Command{
5051
packaging.InitDarwinBundle()
5152
},
5253
}
54+
55+
var initDarwinPkgCmd = &cobra.Command{
56+
Use: "darwin-pkg",
57+
Short: "Create configuration files for OSX pkg installer packaging",
58+
Run: func(cmd *cobra.Command, args []string) {
59+
assertHoverInitialized()
60+
packaging.DockerInstalled()
61+
62+
packaging.InitDarwinPkg()
63+
},
64+
}

cmd/packaging/darwin-bundle.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package packaging
22

33
import (
4-
"github.com/otiai10/copy"
54
"os"
65
"path/filepath"
76

7+
"github.com/otiai10/copy"
8+
89
"github.com/go-flutter-desktop/hover/internal/build"
910
"github.com/go-flutter-desktop/hover/internal/log"
1011
"github.com/go-flutter-desktop/hover/internal/pubspec"
12+
"github.com/go-flutter-desktop/hover/internal/androidmanifest"
1113
)
1214

1315
func InitDarwinBundle() {
@@ -71,21 +73,21 @@ func InitDarwinBundle() {
7173
` <key>CFBundleIconFile</key>`,
7274
` <string>icon.icns</string>`,
7375
` <key>CFBundleIdentifier</key>`,
74-
` <string></string>`,
76+
` <string>` + androidmanifest.AndroidOrganizationName() + `</string>`,
7577
` <key>CFBundleInfoDictionaryVersion</key>`,
7678
` <string>6.0</string>`,
7779
` <key>CFBundleLongVersionString</key>`,
78-
` <string></string>`,
80+
` <string>` + pubspec.GetPubSpec().Version + `</string>`,
7981
` <key>CFBundleName</key>`,
80-
` <string></string>`,
82+
` <string>` + projectName + `</string>`,
8183
` <key>CFBundlePackageType</key>`,
8284
` <string>APPL</string>`,
8385
` <key>CFBundleShortVersionString</key>`,
84-
` <string></string>`,
86+
` <string>` + pubspec.GetPubSpec().Version + `</string>`,
8587
` <key>CFBundleSignature</key>`,
8688
` <string>????</string>`,
8789
` <key>CFBundleVersion</key>`,
88-
` <string></string>`,
90+
` <string>` + pubspec.GetPubSpec().Version + `</string>`,
8991
` <key>CSResourcesFileMapped</key>`,
9092
` <true/>`,
9193
` <key>NSHumanReadableCopyright</key>`,

cmd/packaging/darwin-pkg.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package packaging
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/otiai10/copy"
8+
9+
"github.com/go-flutter-desktop/hover/internal/build"
10+
"github.com/go-flutter-desktop/hover/internal/log"
11+
"github.com/go-flutter-desktop/hover/internal/pubspec"
12+
"github.com/go-flutter-desktop/hover/internal/androidmanifest"
13+
)
14+
15+
func InitDarwinPkg() {
16+
projectName := pubspec.GetPubSpec().Name
17+
packagingFormat := "darwin-pkg"
18+
if _, err := os.Stat(packagingFormatPath("darwin-bundle")); os.IsNotExist(err) {
19+
log.Errorf("You have to init `darwin-bundle` first. Run `hover init-packaging darwin-bundle`")
20+
os.Exit(1)
21+
}
22+
createPackagingFormatDirectory(packagingFormat)
23+
pkgDirectoryPath := packagingFormatPath(packagingFormat)
24+
25+
basePkgDirectoryPath, err := filepath.Abs(filepath.Join(pkgDirectoryPath, "flat", "base.pkg"))
26+
if err != nil {
27+
log.Errorf("Failed to resolve absolute path for base.pkg directory: %v", err)
28+
os.Exit(1)
29+
}
30+
err = os.MkdirAll(basePkgDirectoryPath, 0775)
31+
if err != nil {
32+
log.Errorf("Failed to create base.pkg directory %s: %v", basePkgDirectoryPath, err)
33+
os.Exit(1)
34+
}
35+
36+
packageInfoFilePath, err := filepath.Abs(filepath.Join(basePkgDirectoryPath, "PackageInfo"))
37+
if err != nil {
38+
log.Errorf("Failed to resolve absolute path for PackageInfo file %s: %v", packageInfoFilePath, err)
39+
os.Exit(1)
40+
}
41+
packageInfoFile, err := os.Create(packageInfoFilePath)
42+
if err != nil {
43+
log.Errorf("Failed to create PackageInfo file %s: %v", packageInfoFilePath, err)
44+
os.Exit(1)
45+
}
46+
packageInfoFileContent := []string{
47+
`<pkg-info format-version="2" identifier="` + androidmanifest.AndroidOrganizationName() + `.base.pkg" version="` + pubspec.GetPubSpec().Version + `" install-location="/" auth="root">`,
48+
` <bundle-version>`,
49+
` <bundle id="` + androidmanifest.AndroidOrganizationName() + `" CFBundleIdentifier="` + androidmanifest.AndroidOrganizationName() + `" path="./Applications/` + projectName + `.app" CFBundleVersion="` + pubspec.GetPubSpec().Version + `"/>`,
50+
` </bundle-version>`,
51+
`</pkg-info>`,
52+
}
53+
54+
for _, line := range packageInfoFileContent {
55+
if _, err := packageInfoFile.WriteString(line + "\n"); err != nil {
56+
log.Errorf("Could not write PackageInfo file: %v", err)
57+
os.Exit(1)
58+
}
59+
}
60+
err = packageInfoFile.Close()
61+
if err != nil {
62+
log.Errorf("Could not close PackageInfo file: %v", err)
63+
os.Exit(1)
64+
}
65+
66+
distributionFilePath, err := filepath.Abs(filepath.Join(pkgDirectoryPath, "flat", "Distribution"))
67+
if err != nil {
68+
log.Errorf("Failed to resolve absolute path for Distribution file %s: %v", distributionFilePath, err)
69+
os.Exit(1)
70+
}
71+
distributionFile, err := os.Create(distributionFilePath)
72+
if err != nil {
73+
log.Errorf("Failed to create Distribution file %s: %v", packageInfoFilePath, err)
74+
os.Exit(1)
75+
}
76+
distributionFileContent := []string{
77+
`<?xml version="1.0" encoding="utf-8"?>`,
78+
`<installer-gui-script minSpecVersion="1">`,
79+
` <title>` + projectName + `</title>`,
80+
` <background alignment="topleft" file="root/Applications/` + projectName + `.app/Contents/MacOS/assets/icon.png"/>`,
81+
` <choices-outline>`,
82+
` <line choice="choiceBase"/>`,
83+
` </choices-outline>`,
84+
` <choice id="choiceBase" title="base">`,
85+
` <pkg-ref id="` + androidmanifest.AndroidOrganizationName() + `.base.pkg"/>`,
86+
` </choice>`,
87+
` <pkg-ref id="` + androidmanifest.AndroidOrganizationName() + `.base.pkg" version="` + pubspec.GetPubSpec().Version + `" auth="Root">#base.pkg</pkg-ref>`,
88+
`</installer-gui-script>`,
89+
}
90+
91+
for _, line := range distributionFileContent {
92+
if _, err := distributionFile.WriteString(line + "\n"); err != nil {
93+
log.Errorf("Could not write Distribution file: %v", err)
94+
os.Exit(1)
95+
}
96+
}
97+
err = distributionFile.Close()
98+
if err != nil {
99+
log.Errorf("Could not close Distribution file: %v", err)
100+
os.Exit(1)
101+
}
102+
103+
createDockerfile(packagingFormat)
104+
105+
printInitFinished(packagingFormat)
106+
}
107+
108+
func BuildDarwinPkg() {
109+
log.Infof("Building darwin-bundle first")
110+
BuildDarwinBundle()
111+
projectName := pubspec.GetPubSpec().Name
112+
packagingFormat := "darwin-pkg"
113+
tmpPath := getTemporaryBuildDirectory(projectName, packagingFormat)
114+
log.Infof("Packaging pkg in %s", tmpPath)
115+
116+
err := copy.Copy(build.OutputDirectoryPath("darwin-bundle"), filepath.Join(tmpPath, "flat", "root", "Applications"))
117+
if err != nil {
118+
log.Errorf("Could not copy build folder: %v", err)
119+
os.Exit(1)
120+
}
121+
err = copy.Copy(packagingFormatPath(packagingFormat), filepath.Join(tmpPath))
122+
if err != nil {
123+
log.Errorf("Could not copy packaging configuration folder: %v", err)
124+
os.Exit(1)
125+
}
126+
127+
outputFileName := projectName + " " + pubspec.GetPubSpec().Version + " Installer.pkg"
128+
outputFilePath := filepath.Join(build.OutputDirectoryPath("darwin-pkg"), outputFileName)
129+
runDockerPackaging(tmpPath, packagingFormat, []string{
130+
"(cd flat/root && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > flat/base.pkg/Payload",
131+
"&&", "mkbom -u 0 -g 80 flat/root flat/base.pkg/Bom",
132+
"&&", "(cd flat && xar --compression none -cf '../" + projectName + " " + pubspec.GetPubSpec().Version + " Installer.pkg' * )",
133+
})
134+
err = os.Rename(filepath.Join(tmpPath, outputFileName), outputFilePath)
135+
if err != nil {
136+
log.Errorf("Could not move pkg directory: %v", err)
137+
os.Exit(1)
138+
}
139+
err = os.RemoveAll(tmpPath)
140+
if err != nil {
141+
log.Errorf("Could not remove temporary build directory: %v", err)
142+
os.Exit(1)
143+
}
144+
printPackagingFinished(packagingFormat)
145+
}

cmd/packaging/packaging.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ func createDockerfile(packagingFormat string) {
114114
"FROM ubuntu:bionic",
115115
"RUN apt-get update && apt-get install icnsutils -y",
116116
}
117+
} else if packagingFormat == "darwin-pkg" {
118+
dockerFileContent = []string{
119+
"FROM ubuntu:bionic",
120+
"RUN apt-get update && apt-get install cpio git make g++ wget libxml2-dev libssl1.0-dev zlib1g-dev -y",
121+
"WORKDIR /tmp",
122+
"RUN git clone https://github.com/hogliux/bomutils && cd bomutils && make > /dev/null && make install > /dev/null",
123+
"RUN wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/xar/xar-1.5.2.tar.gz && tar -zxvf xar-1.5.2.tar.gz > /dev/null && cd xar-1.5.2 && ./configure > /dev/null && make > /dev/null && make install > /dev/null",
124+
}
117125
} else {
118126
log.Errorf("Tried to create Dockerfile for unknown packaging format %s", packagingFormat)
119127
os.Exit(1)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package androidmanifest
2+
3+
import (
4+
"encoding/xml"
5+
"io/ioutil"
6+
"os"
7+
"strings"
8+
9+
"github.com/go-flutter-desktop/hover/internal/log"
10+
)
11+
12+
// AndroidManifest is a file that describes the essential information about
13+
// an android app.
14+
type AndroidManifest struct {
15+
Package string `xml:"package,attr"`
16+
}
17+
18+
// AndroidOrganizationName fetch the android package name (default:
19+
// 'com.example').
20+
// Can by set upon flutter create (--org flag)
21+
//
22+
// If errors occurs when reading the android package name, the string value
23+
// will correspond to 'hover.failed.to.retrieve.package.name'
24+
func AndroidOrganizationName() string {
25+
// Default value
26+
androidManifestFile := "android/app/src/main/AndroidManifest.xml"
27+
28+
// Open AndroidManifest file
29+
xmlFile, err := os.Open(androidManifestFile)
30+
if err != nil {
31+
log.Errorf("Failed to retrieve the organization name: %v", err)
32+
return "hover.failed.to.retrieve.package.name"
33+
}
34+
defer xmlFile.Close()
35+
36+
byteXMLValue, err := ioutil.ReadAll(xmlFile)
37+
if err != nil {
38+
log.Errorf("Failed to retrieve the organization name: %v", err)
39+
return "hover.failed.to.retrieve.package.name"
40+
}
41+
42+
var androidManifest AndroidManifest
43+
err = xml.Unmarshal(byteXMLValue, &androidManifest)
44+
if err != nil {
45+
log.Errorf("Failed to retrieve the organization name: %v", err)
46+
return "hover.failed.to.retrieve.package.name"
47+
}
48+
javaPackage := strings.Split(androidManifest.Package, ".")
49+
orgName := strings.Join(javaPackage[:len(javaPackage)-1], ".")
50+
if orgName == "" {
51+
return "hover.failed.to.retrieve.package.name"
52+
}
53+
return orgName
54+
}

0 commit comments

Comments
 (0)