forked from joejulian/gio-cmd
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e72e8f0b2 | |||
| f4d6788248 | |||
| e5b1a4e6cd | |||
| f587d2f097 | |||
| 8de547d61d | |||
| 048614c60e | |||
| ed8d0aa9a6 | |||
| e1f06eb7b0 | |||
| ae8a780af9 |
+38
-17
@@ -40,14 +40,16 @@ type errWriter struct {
|
|||||||
var exeSuffix string
|
var exeSuffix string
|
||||||
|
|
||||||
type manifestData struct {
|
type manifestData struct {
|
||||||
AppID string
|
AppID string
|
||||||
Version Semver
|
Version Semver
|
||||||
MinSDK int
|
MinSDK int
|
||||||
TargetSDK int
|
TargetSDK int
|
||||||
Permissions []string
|
Permissions []string
|
||||||
Features []string
|
Features []string
|
||||||
IconSnip string
|
IconSnip string
|
||||||
AppName string
|
AppName string
|
||||||
|
Schemes []string
|
||||||
|
PackageQueries []string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -230,6 +232,7 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro
|
|||||||
"GOARM=7", // Avoid softfloat.
|
"GOARM=7", // Avoid softfloat.
|
||||||
"CGO_ENABLED=1",
|
"CGO_ENABLED=1",
|
||||||
"CC="+clang,
|
"CC="+clang,
|
||||||
|
"CXX="+clang+"++",
|
||||||
)
|
)
|
||||||
builds.Go(func() error {
|
builds.Go(func() error {
|
||||||
_, err := runCmd(cmd)
|
_, err := runCmd(cmd)
|
||||||
@@ -351,7 +354,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
|
|||||||
}
|
}
|
||||||
minSDK := max(bi.minsdk, 16)
|
minSDK := max(bi.minsdk, 16)
|
||||||
// https://developer.android.com/distribute/best-practices/develop/target-sdk
|
// https://developer.android.com/distribute/best-practices/develop/target-sdk
|
||||||
targetSDK := 33
|
targetSDK := 35
|
||||||
if bi.targetsdk > 0 {
|
if bi.targetsdk > 0 {
|
||||||
targetSDK = bi.targetsdk
|
targetSDK = bi.targetsdk
|
||||||
}
|
}
|
||||||
@@ -434,14 +437,16 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
|
|||||||
permissions, features := getPermissions(perms)
|
permissions, features := getPermissions(perms)
|
||||||
appName := UppercaseName(bi.name)
|
appName := UppercaseName(bi.name)
|
||||||
manifestSrc := manifestData{
|
manifestSrc := manifestData{
|
||||||
AppID: bi.appID,
|
AppID: bi.appID,
|
||||||
Version: bi.version,
|
Version: bi.version,
|
||||||
MinSDK: minSDK,
|
MinSDK: minSDK,
|
||||||
TargetSDK: targetSDK,
|
TargetSDK: targetSDK,
|
||||||
Permissions: permissions,
|
Permissions: permissions,
|
||||||
Features: features,
|
Features: features,
|
||||||
IconSnip: iconSnip,
|
IconSnip: iconSnip,
|
||||||
AppName: appName,
|
AppName: appName,
|
||||||
|
Schemes: bi.schemes,
|
||||||
|
PackageQueries: bi.packageQueries,
|
||||||
}
|
}
|
||||||
tmpl, err := template.New("test").Parse(
|
tmpl, err := template.New("test").Parse(
|
||||||
`<?xml version="1.0" encoding="utf-8"?>
|
`<?xml version="1.0" encoding="utf-8"?>
|
||||||
@@ -449,6 +454,13 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
|
|||||||
package="{{.AppID}}"
|
package="{{.AppID}}"
|
||||||
android:versionCode="{{.Version.VersionCode}}"
|
android:versionCode="{{.Version.VersionCode}}"
|
||||||
android:versionName="{{.Version}}">
|
android:versionName="{{.Version}}">
|
||||||
|
{{if .PackageQueries}}
|
||||||
|
<queries>
|
||||||
|
{{range .PackageQueries}}
|
||||||
|
<package android:name="{{.}}" />
|
||||||
|
{{end}}
|
||||||
|
</queries>
|
||||||
|
{{end}}
|
||||||
<uses-sdk android:minSdkVersion="{{.MinSDK}}" android:targetSdkVersion="{{.TargetSDK}}" />
|
<uses-sdk android:minSdkVersion="{{.MinSDK}}" android:targetSdkVersion="{{.TargetSDK}}" />
|
||||||
{{range .Permissions}} <uses-permission android:name="{{.}}"/>
|
{{range .Permissions}} <uses-permission android:name="{{.}}"/>
|
||||||
{{end}}{{range .Features}} <uses-feature android:{{.}} android:required="false"/>
|
{{end}}{{range .Features}} <uses-feature android:{{.}} android:required="false"/>
|
||||||
@@ -458,11 +470,20 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
|
|||||||
android:theme="@style/Theme.GioApp"
|
android:theme="@style/Theme.GioApp"
|
||||||
android:configChanges="screenSize|screenLayout|smallestScreenSize|orientation|keyboardHidden"
|
android:configChanges="screenSize|screenLayout|smallestScreenSize|orientation|keyboardHidden"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
{{range .Schemes}}
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"></action>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"></category>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"></category>
|
||||||
|
<data android:scheme="{{.}}"></data>
|
||||||
|
</intent-filter>
|
||||||
|
{{end}}
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>`)
|
</manifest>`)
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type buildInfo struct {
|
|||||||
notaryAppleID string
|
notaryAppleID string
|
||||||
notaryPassword string
|
notaryPassword string
|
||||||
notaryTeamID string
|
notaryTeamID string
|
||||||
|
schemes []string
|
||||||
|
packageQueries []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Semver struct {
|
type Semver struct {
|
||||||
@@ -78,6 +80,8 @@ func newBuildInfo(pkgPath string) (*buildInfo, error) {
|
|||||||
notaryAppleID: *notaryID,
|
notaryAppleID: *notaryID,
|
||||||
notaryPassword: *notaryPass,
|
notaryPassword: *notaryPass,
|
||||||
notaryTeamID: *notaryTeamID,
|
notaryTeamID: *notaryTeamID,
|
||||||
|
schemes: getCommaList(*schemes),
|
||||||
|
packageQueries: getCommaList(*pkgQueries),
|
||||||
}
|
}
|
||||||
return bi, nil
|
return bi, nil
|
||||||
}
|
}
|
||||||
@@ -92,6 +96,11 @@ func (s Semver) String() string {
|
|||||||
return fmt.Sprintf("%d.%d.%d.%d", s.Major, s.Minor, s.Patch, s.VersionCode)
|
return fmt.Sprintf("%d.%d.%d.%d", s.Major, s.Minor, s.Patch, s.VersionCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Semver) StringCompact() string {
|
||||||
|
// Used to meet CFBundleShortVersionString format.
|
||||||
|
return fmt.Sprintf("%d.%d.%d", s.Major, s.Minor, s.Patch)
|
||||||
|
}
|
||||||
|
|
||||||
func parseSemver(v string) (Semver, error) {
|
func parseSemver(v string) (Semver, error) {
|
||||||
var sv Semver
|
var sv Semver
|
||||||
_, err := fmt.Sscanf(v, "%d.%d.%d.%d", &sv.Major, &sv.Minor, &sv.Patch, &sv.VersionCode)
|
_, err := fmt.Sscanf(v, "%d.%d.%d.%d", &sv.Major, &sv.Minor, &sv.Patch, &sv.VersionCode)
|
||||||
@@ -147,6 +156,15 @@ func getLdFlags(appID string) string {
|
|||||||
return strings.Join(ldflags, " ")
|
return strings.Join(ldflags, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCommaList(s string) (list []string) {
|
||||||
|
for _, v := range strings.Split(s, ",") {
|
||||||
|
if v := strings.TrimSpace(v); v != "" {
|
||||||
|
list = append(list, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
type packageMetadata struct {
|
type packageMetadata struct {
|
||||||
PkgPath string
|
PkgPath string
|
||||||
Dir string
|
Dir string
|
||||||
|
|||||||
+11
-1
@@ -69,7 +69,8 @@ its deletion.
|
|||||||
The -x flag will print all the external commands executed by the gogio tool.
|
The -x flag will print all the external commands executed by the gogio tool.
|
||||||
|
|
||||||
The -signkey flag specifies the path of the keystore, used for signing Android apk/aab files
|
The -signkey flag specifies the path of the keystore, used for signing Android apk/aab files
|
||||||
or specifies the name of key on Keychain to sign MacOS app.
|
or specifies the name of key on Keychain to sign MacOS apps. On iOS and macOS it can be used
|
||||||
|
to specify the path of a provisioning profile (.mobileprovision/.provisionprofile).
|
||||||
|
|
||||||
The -signpass flag specifies the password of the keystore, ignored if -signkey is not provided.
|
The -signpass flag specifies the password of the keystore, ignored if -signkey is not provided.
|
||||||
If -signpass is not sepecified it will be read from the environment variable GOGIO_SIGNPASS.
|
If -signpass is not sepecified it will be read from the environment variable GOGIO_SIGNPASS.
|
||||||
@@ -82,4 +83,13 @@ for details. If not provided, the password will be prompted.
|
|||||||
|
|
||||||
The -notaryteamid flag specifies the team ID to use for notarization of MacOS app, ignored if
|
The -notaryteamid flag specifies the team ID to use for notarization of MacOS app, ignored if
|
||||||
-notaryid is not provided.
|
-notaryid is not provided.
|
||||||
|
|
||||||
|
The -schemes flag specifies a list of comma separated URI schemes that the program can
|
||||||
|
handle. For example, use -schemes yourAppName to receive a app.URLEvent for URIs
|
||||||
|
starting with yourAppName://. It is only supported on Android, iOS, macOS and Windows.
|
||||||
|
On Windows, it will restrict the program to a single instance.
|
||||||
|
|
||||||
|
The -queries flag specifies a list of comma separated package names used to query other apps,
|
||||||
|
that is useful to launch other apps and verify their presence. For example, use -queries
|
||||||
|
com.example.otherapp to query the app with that package name. It is only necessary on Android.
|
||||||
`
|
`
|
||||||
|
|||||||
+150
-48
@@ -4,6 +4,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -73,7 +75,29 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error {
|
|||||||
if err := exeIOS(tmpDir, target, appDir, bi); err != nil {
|
if err := exeIOS(tmpDir, target, appDir, bi); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := signIOS(bi, tmpDir, appDir); err != nil {
|
|
||||||
|
embedded := filepath.Join(appDir, "embedded.mobileprovision")
|
||||||
|
|
||||||
|
var provisions []string
|
||||||
|
if bi.key != "" {
|
||||||
|
if ext := filepath.Ext(bi.key); ext != ".mobileprovision" && ext != ".provisionprofile" {
|
||||||
|
return fmt.Errorf("sign: -signkey specifies an Apple provisioning profile, but %q does not end in .mobileprovision or .provisionprofile", bi.key)
|
||||||
|
}
|
||||||
|
provisions = []string{bi.key}
|
||||||
|
} else {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := filepath.Glob(filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles", "*.mobileprovision"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
provisions = p
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := signApple(bi.appID, tmpDir, embedded, appDir, provisions); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return zipDir(out, tmpDir, "Payload")
|
return zipDir(out, tmpDir, "Payload")
|
||||||
@@ -82,16 +106,8 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func signIOS(bi *buildInfo, tmpDir, app string) error {
|
// signApple is shared between iOS and macOS.
|
||||||
home, err := os.UserHomeDir()
|
func signApple(appID, tmpDir, embedded, app string, provisions []string) error {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
provPattern := filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles", "*.mobileprovision")
|
|
||||||
provisions, err := filepath.Glob(provPattern)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
provInfo := filepath.Join(tmpDir, "provision.plist")
|
provInfo := filepath.Join(tmpDir, "provision.plist")
|
||||||
var avail []string
|
var avail []string
|
||||||
for _, prov := range provisions {
|
for _, prov := range provisions {
|
||||||
@@ -115,17 +131,23 @@ func signIOS(bi *buildInfo, tmpDir, app string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
provAppID, err := runCmd(exec.Command("/usr/libexec/PlistBuddy", "-c", "Print:Entitlements:application-identifier", provInfo))
|
|
||||||
|
// iOS/macOS Catalyst
|
||||||
|
provAppIDSearchKey := "Print:Entitlements:application-identifier"
|
||||||
|
if filepath.Ext(prov) == ".provisionprofile" {
|
||||||
|
// macOS
|
||||||
|
provAppIDSearchKey = "Print:Entitlements:com.apple.application-identifier"
|
||||||
|
}
|
||||||
|
provAppID, err := runCmd(exec.Command("/usr/libexec/PlistBuddy", "-c", provAppIDSearchKey, provInfo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
expAppID := fmt.Sprintf("%s.%s", appIDPrefix, bi.appID)
|
expAppID := fmt.Sprintf("%s.%s", appIDPrefix, appID)
|
||||||
avail = append(avail, provAppID)
|
avail = append(avail, provAppID)
|
||||||
if expAppID != provAppID {
|
if expAppID != provAppID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Copy provisioning file.
|
// Copy provisioning file.
|
||||||
embedded := filepath.Join(app, "embedded.mobileprovision")
|
|
||||||
if err := copyFile(embedded, prov); err != nil {
|
if err := copyFile(embedded, prov); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -145,10 +167,18 @@ func signIOS(bi *buildInfo, tmpDir, app string) error {
|
|||||||
}
|
}
|
||||||
identity := sha1.Sum(certDER)
|
identity := sha1.Sum(certDER)
|
||||||
idHex := hex.EncodeToString(identity[:])
|
idHex := hex.EncodeToString(identity[:])
|
||||||
_, err = runCmd(exec.Command("codesign", "-s", idHex, "-v", "--entitlements", entFile, app))
|
_, err = runCmd(exec.Command(
|
||||||
|
"codesign",
|
||||||
|
"--sign", idHex,
|
||||||
|
"--deep",
|
||||||
|
"--force",
|
||||||
|
"--options", "runtime",
|
||||||
|
"--entitlements",
|
||||||
|
entFile,
|
||||||
|
app))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf("sign: no valid provisioning profile found for bundle id %q among %v", bi.appID, avail)
|
return fmt.Errorf("sign: no valid provisioning profile found for bundle id %q among %v", appID, avail)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
|
func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
|
||||||
@@ -172,6 +202,7 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
|
|||||||
}
|
}
|
||||||
cflags = append(cflags,
|
cflags = append(cflags,
|
||||||
"-fobjc-arc",
|
"-fobjc-arc",
|
||||||
|
fmt.Sprintf("-miphoneos-version-min=%d.0", bi.minsdk),
|
||||||
)
|
)
|
||||||
cflagsLine := strings.Join(cflags, " ")
|
cflagsLine := strings.Join(cflags, " ")
|
||||||
exeSlice := filepath.Join(tmpDir, "app-"+a)
|
exeSlice := filepath.Join(tmpDir, "app-"+a)
|
||||||
@@ -190,7 +221,9 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
|
|||||||
"GOARCH="+a,
|
"GOARCH="+a,
|
||||||
"CGO_ENABLED=1",
|
"CGO_ENABLED=1",
|
||||||
"CC="+clang,
|
"CC="+clang,
|
||||||
|
"CXX="+clang+"++",
|
||||||
"CGO_CFLAGS="+cflagsLine,
|
"CGO_CFLAGS="+cflagsLine,
|
||||||
|
"CGO_CXXFLAGS="+cflagsLine,
|
||||||
"CGO_LDFLAGS=-lresolv "+cflagsLine,
|
"CGO_LDFLAGS=-lresolv "+cflagsLine,
|
||||||
)
|
)
|
||||||
builds.Go(func() error {
|
builds.Go(func() error {
|
||||||
@@ -241,6 +274,9 @@ func iosIcons(bi *buildInfo, tmpDir, appDir, icon string) (string, error) {
|
|||||||
err := buildIcons(appIcon, icon, []iconVariant{
|
err := buildIcons(appIcon, icon, []iconVariant{
|
||||||
{path: "ios_2x.png", size: 120},
|
{path: "ios_2x.png", size: 120},
|
||||||
{path: "ios_3x.png", size: 180},
|
{path: "ios_3x.png", size: 180},
|
||||||
|
{path: "ipad_1x.png", size: 76},
|
||||||
|
{path: "ipad_2x.png", size: 152},
|
||||||
|
{path: "ipad_4x.png", size: 228},
|
||||||
// The App Store icon is not allowed to contain
|
// The App Store icon is not allowed to contain
|
||||||
// transparent pixels.
|
// transparent pixels.
|
||||||
{path: "ios_store.png", size: 1024, fill: true},
|
{path: "ios_store.png", size: 1024, fill: true},
|
||||||
@@ -249,26 +285,44 @@ func iosIcons(bi *buildInfo, tmpDir, appDir, icon string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
contentJson := `{
|
contentJson := `{
|
||||||
"images" : [
|
"images": [
|
||||||
{
|
{
|
||||||
"size" : "60x60",
|
"size": "60x60",
|
||||||
"idiom" : "iphone",
|
"idiom": "iphone",
|
||||||
"filename" : "ios_2x.png",
|
"filename": "ios_2x.png",
|
||||||
"scale" : "2x"
|
"scale": "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size" : "60x60",
|
"size": "60x60",
|
||||||
"idiom" : "iphone",
|
"idiom": "iphone",
|
||||||
"filename" : "ios_3x.png",
|
"filename": "ios_3x.png",
|
||||||
"scale" : "3x"
|
"scale": "3x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size" : "1024x1024",
|
"size": "76x76",
|
||||||
"idiom" : "ios-marketing",
|
"idiom": "ipad",
|
||||||
"filename" : "ios_store.png",
|
"filename": "ipad_1x.png",
|
||||||
"scale" : "1x"
|
"scale": "1x"
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
"size": "76x76",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "ipad_2x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "152x152",
|
||||||
|
"idiom": "ipad",
|
||||||
|
"filename": "ipad_4x.png",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "1024x1024",
|
||||||
|
"idiom": "ios-marketing",
|
||||||
|
"filename": "ios_store.png",
|
||||||
|
"scale": "1x"
|
||||||
|
}
|
||||||
|
]
|
||||||
}`
|
}`
|
||||||
contentFile := filepath.Join(appIcon, "Contents.json")
|
contentFile := filepath.Join(appIcon, "Contents.json")
|
||||||
if err := os.WriteFile(contentFile, []byte(contentJson), 0o600); err != nil {
|
if err := os.WriteFile(contentFile, []byte(contentJson), 0o600); err != nil {
|
||||||
@@ -302,36 +356,57 @@ func buildInfoPlist(bi *buildInfo) string {
|
|||||||
case "tvos":
|
case "tvos":
|
||||||
supportPlatform = "AppleTVOS"
|
supportPlatform = "AppleTVOS"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
manifestSrc := struct {
|
||||||
|
AppName string
|
||||||
|
AppID string
|
||||||
|
Version string
|
||||||
|
VersionCode uint32
|
||||||
|
Platform string
|
||||||
|
MinVersion int
|
||||||
|
SupportPlatform string
|
||||||
|
Schemes []string
|
||||||
|
}{
|
||||||
|
AppName: appName,
|
||||||
|
AppID: bi.appID,
|
||||||
|
Version: bi.version.StringCompact(),
|
||||||
|
VersionCode: bi.version.VersionCode,
|
||||||
|
Platform: platform,
|
||||||
|
MinVersion: bi.minsdk,
|
||||||
|
SupportPlatform: supportPlatform,
|
||||||
|
Schemes: bi.schemes,
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("manifest").Parse(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>%s</string>
|
<string>{{.AppName}}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>%s</string>
|
<string>{{.AppID}}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>%s</string>
|
<string>{{.AppName}}</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>%s</string>
|
<string>{{.Version}}</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>%d</string>
|
<string>{{.VersionCode}}</string>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array><string>arm64</string></array>
|
<array><string>arm64</string></array>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>%s</string>
|
<string>{{.Platform}}</string>
|
||||||
<key>DTPlatformVersion</key>
|
<key>DTPlatformVersion</key>
|
||||||
<string>12.4</string>
|
<string>12.4</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>%d</string>
|
<string>{{.MinVersion}}.0</string>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
<array>
|
<array>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
@@ -339,14 +414,17 @@ func buildInfoPlist(bi *buildInfo) string {
|
|||||||
</array>
|
</array>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>%s</string>
|
<string>{{.SupportPlatform}}</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UIRequiresFullScreen</key>
|
||||||
|
<true/>
|
||||||
<key>DTCompiler</key>
|
<key>DTCompiler</key>
|
||||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||||
<key>DTPlatformBuild</key>
|
<key>DTPlatformBuild</key>
|
||||||
@@ -354,13 +432,38 @@ func buildInfoPlist(bi *buildInfo) string {
|
|||||||
<key>DTSDKBuild</key>
|
<key>DTSDKBuild</key>
|
||||||
<string>16G73</string>
|
<string>16G73</string>
|
||||||
<key>DTSDKName</key>
|
<key>DTSDKName</key>
|
||||||
<string>%s12.4</string>
|
<string>{{.Platform}}12.4</string>
|
||||||
<key>DTXcode</key>
|
<key>DTXcode</key>
|
||||||
<string>1030</string>
|
<string>1030</string>
|
||||||
<key>DTXcodeBuild</key>
|
<key>DTXcodeBuild</key>
|
||||||
<string>10G8</string>
|
<string>10G8</string>
|
||||||
|
<key>UILaunchScreen</key>
|
||||||
|
<true/>
|
||||||
|
{{if .Schemes}}
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
{{range .Schemes}}
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>{{.}}</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
{{end}}
|
||||||
|
</array>
|
||||||
|
{{end}}
|
||||||
</dict>
|
</dict>
|
||||||
</plist>`, appName, bi.appID, appName, bi.version, bi.version.VersionCode, platform, minIOSVersion, supportPlatform, platform)
|
</plist>`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var manifestBuffer bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&manifestBuffer, manifestSrc); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifestBuffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func iosPlatformFor(target string) string {
|
func iosPlatformFor(target string) string {
|
||||||
@@ -501,7 +604,6 @@ func iosCompilerFor(target, arch string, minsdk int) (string, []string, error) {
|
|||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
cflags := []string{
|
cflags := []string{
|
||||||
"-fembed-bitcode",
|
|
||||||
"-arch", allArchs[arch].iosArch,
|
"-arch", allArchs[arch].iosArch,
|
||||||
"-isysroot", sdkPath,
|
"-isysroot", sdkPath,
|
||||||
"-m" + platformOS + "-version-min=" + strconv.Itoa(minsdk),
|
"-m" + platformOS + "-version-min=" + strconv.Itoa(minsdk),
|
||||||
|
|||||||
+1
-1
@@ -174,7 +174,7 @@ const (
|
|||||||
</html>`
|
</html>`
|
||||||
// jsSetGo sets the `window.go` variable.
|
// jsSetGo sets the `window.go` variable.
|
||||||
jsSetGo = `(() => {
|
jsSetGo = `(() => {
|
||||||
window.go = {argv: [], env: {}, importObject: {go: {}}};
|
window.go = {argv: [], env: {}, importObject: {go: {}, gojs: {}}};
|
||||||
const argv = new URLSearchParams(location.search).get("argv");
|
const argv = new URLSearchParams(location.search).get("argv");
|
||||||
if (argv) {
|
if (argv) {
|
||||||
window.go["argv"] = argv.split(" ");
|
window.go["argv"] = argv.split(" ");
|
||||||
|
|||||||
+40
-16
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -34,9 +35,7 @@ func buildMac(tmpDir string, bi *buildInfo) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := builder.setInfo(bi, name); err != nil {
|
builder.setInfo(bi, name)
|
||||||
return fmt.Errorf("can't build the resources: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arch := range bi.archs {
|
for _, arch := range bi.archs {
|
||||||
tmpDest := filepath.Join(builder.TempDir, filepath.Base(builder.DestDir))
|
tmpDest := filepath.Join(builder.TempDir, filepath.Base(builder.DestDir))
|
||||||
@@ -122,7 +121,20 @@ func (b *macBuilder) setIcon(path string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error {
|
func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) {
|
||||||
|
|
||||||
|
manifestSrc := struct {
|
||||||
|
Name string
|
||||||
|
Bundle string
|
||||||
|
Version Semver
|
||||||
|
Schemes []string
|
||||||
|
}{
|
||||||
|
Name: name,
|
||||||
|
Bundle: buildInfo.appID,
|
||||||
|
Version: buildInfo.version,
|
||||||
|
Schemes: buildInfo.schemes,
|
||||||
|
}
|
||||||
|
|
||||||
t, err := template.New("manifest").Parse(`<?xml version="1.0" encoding="UTF-8"?>
|
t, err := template.New("manifest").Parse(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
@@ -136,21 +148,29 @@ func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error {
|
|||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>BNDL</string>
|
||||||
|
{{if .Schemes}}
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
{{range .Schemes}}
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>{{.}}</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
{{end}}
|
||||||
|
</array>
|
||||||
|
{{end}}
|
||||||
</dict>
|
</dict>
|
||||||
</plist>`)
|
</plist>`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var manifest bufferCoff
|
var manifest bytes.Buffer
|
||||||
if err := t.Execute(&manifest, struct {
|
if err := t.Execute(&manifest, manifestSrc); err != nil {
|
||||||
Name, Bundle string
|
panic(err)
|
||||||
}{
|
|
||||||
Name: name,
|
|
||||||
Bundle: buildInfo.appID,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
b.Manifest = manifest.Bytes()
|
b.Manifest = manifest.Bytes()
|
||||||
|
|
||||||
@@ -164,8 +184,6 @@ func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error {
|
|||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>`)
|
</plist>`)
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *macBuilder) buildProgram(buildInfo *buildInfo, binDest string, name string, arch string) error {
|
func (b *macBuilder) buildProgram(buildInfo *buildInfo, binDest string, name string, arch string) error {
|
||||||
@@ -214,6 +232,12 @@ func (b *macBuilder) signProgram(buildInfo *buildInfo, binDest string, name stri
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the key is a provisioning profile use the same signing process as iOS
|
||||||
|
if filepath.Ext(buildInfo.key) == ".provisionprofile" {
|
||||||
|
embedded := filepath.Join(binDest, "Contents", "embedded.provisionprofile")
|
||||||
|
return signApple(buildInfo.appID, b.TempDir, embedded, binDest, []string{buildInfo.key})
|
||||||
|
}
|
||||||
|
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"codesign",
|
"codesign",
|
||||||
"--deep",
|
"--deep",
|
||||||
|
|||||||
+4
-2
@@ -29,18 +29,20 @@ var (
|
|||||||
destPath = flag.String("o", "", "output file or directory.\nFor -target ios or tvos, use the .app suffix to target simulators.")
|
destPath = flag.String("o", "", "output file or directory.\nFor -target ios or tvos, use the .app suffix to target simulators.")
|
||||||
appID = flag.String("appid", "", "app identifier (for -buildmode=exe)")
|
appID = flag.String("appid", "", "app identifier (for -buildmode=exe)")
|
||||||
name = flag.String("name", "", "app name (for -buildmode=exe)")
|
name = flag.String("name", "", "app name (for -buildmode=exe)")
|
||||||
version = flag.String("version", "1.0.0.1", "semver app version (for -buildmode=exe) on the form major.minor.patch.versioncode")
|
version = flag.String("version", "1.0.0.1", "semver app version (for -buildmode=exe) on the form major.minor.patch.versioncode. The versioncode is not used for iOS and macOS.")
|
||||||
printCommands = flag.Bool("x", false, "print the commands")
|
printCommands = flag.Bool("x", false, "print the commands")
|
||||||
keepWorkdir = flag.Bool("work", false, "print the name of the temporary work directory and do not delete it when exiting.")
|
keepWorkdir = flag.Bool("work", false, "print the name of the temporary work directory and do not delete it when exiting.")
|
||||||
linkMode = flag.String("linkmode", "", "set the -linkmode flag of the go tool")
|
linkMode = flag.String("linkmode", "", "set the -linkmode flag of the go tool")
|
||||||
extraLdflags = flag.String("ldflags", "", "extra flags to the Go linker")
|
extraLdflags = flag.String("ldflags", "", "extra flags to the Go linker")
|
||||||
extraTags = flag.String("tags", "", "extra tags to the Go tool")
|
extraTags = flag.String("tags", "", "extra tags to the Go tool")
|
||||||
iconPath = flag.String("icon", "", "specify an icon for iOS and Android")
|
iconPath = flag.String("icon", "", "specify an icon for iOS and Android")
|
||||||
signKey = flag.String("signkey", "", "specify the path of the keystore to be used to sign Android apk files.")
|
signKey = flag.String("signkey", "", "specify the path of the keystore (Android) or provisioning profile (macOS or iOS) for signing")
|
||||||
signPass = flag.String("signpass", "", "specify the password to decrypt the signkey.")
|
signPass = flag.String("signpass", "", "specify the password to decrypt the signkey.")
|
||||||
notaryID = flag.String("notaryid", "", "specify the apple id to use for notarization.")
|
notaryID = flag.String("notaryid", "", "specify the apple id to use for notarization.")
|
||||||
notaryPass = flag.String("notarypass", "", "specify app-specific password of the Apple ID to be used for notarization.")
|
notaryPass = flag.String("notarypass", "", "specify app-specific password of the Apple ID to be used for notarization.")
|
||||||
notaryTeamID = flag.String("notaryteamid", "", "specify the team id to use for notarization.")
|
notaryTeamID = flag.String("notaryteamid", "", "specify the team id to use for notarization.")
|
||||||
|
schemes = flag.String("schemes", "", "specify a list of comma separated URL schemes that the program accepts")
|
||||||
|
pkgQueries = flag.String("queries", "", "specify a list of comma separated package names used to query other apps on Android.")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ var AndroidPermissions = map[string][]string{
|
|||||||
"wakelock": {
|
"wakelock": {
|
||||||
"android.permission.WAKE_LOCK",
|
"android.permission.WAKE_LOCK",
|
||||||
},
|
},
|
||||||
|
"microphone": {
|
||||||
|
"android.permission.RECORD_AUDIO",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var AndroidFeatures = map[string][]string{
|
var AndroidFeatures = map[string][]string{
|
||||||
|
|||||||
@@ -202,10 +202,18 @@ func (b *windowsBuilder) buildProgram(buildInfo *buildInfo, name string, arch st
|
|||||||
dest = filepath.Join(filepath.Dir(b.DestDir), name+"_"+arch+".exe")
|
dest = filepath.Join(filepath.Dir(b.DestDir), name+"_"+arch+".exe")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ldflags := buildInfo.ldflags
|
||||||
|
if buildInfo.schemes != nil {
|
||||||
|
ldflags += ` -X "gioui.org/app.schemesURI=` + strings.Join(buildInfo.schemes, ",") + `" `
|
||||||
|
}
|
||||||
|
if buildInfo.appID != "" {
|
||||||
|
ldflags += ` -X "gioui.org/app.ID=` + buildInfo.appID + `" `
|
||||||
|
}
|
||||||
|
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"go",
|
"go",
|
||||||
"build",
|
"build",
|
||||||
"-ldflags=-H=windowsgui "+buildInfo.ldflags,
|
"-ldflags=-H=windowsgui "+ldflags,
|
||||||
"-tags="+buildInfo.tags,
|
"-tags="+buildInfo.tags,
|
||||||
"-o", dest,
|
"-o", dest,
|
||||||
buildInfo.pkgPath,
|
buildInfo.pkgPath,
|
||||||
|
|||||||
Reference in New Issue
Block a user