9 Commits

Author SHA1 Message Date
inkeliz 2e72e8f0b2 gogio: [android] bump target sdk version
Match the "Target SDK" with the mininum required
by Google Play.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2026-02-28 01:10:27 +00:00
CoyAce f4d6788248 gogio: [ios] add CXX, CGO_CXXFLAGS environment variable for cgo cross-compilation and correct signKey
Signed-off-by: CoyAce <AkeyCoy@gmail.com>
2026-02-10 07:34:11 +01:00
CoyAce e5b1a4e6cd gogio: [android] add CXX environment variable for cgo cross-compilation
Adds the CXX environment variable when building Android shared libraries
to ensure proper C++ compiler selection for cgo-enabled packages.

Signed-off-by: CoyAce <AkeyCoy@gmail.com>
2026-01-26 16:24:59 +01:00
CoyAce f587d2f097 gogio: [Android] map the microphone permission to RECORD_AUDIO
Signed-off-by: CoyAce <AkeyCoy@gmail.com>
2026-01-07 12:34:13 +01:00
inkeliz 8de547d61d gogio: [Android] add support for querying apps
Previously, it was impossible to identify if a specific app
was installed on the user device. It was also impossible to
launch a external app from Gio using Intent.

Now, you can use `-queries` with a comma separed package names,
like `com.another.app`. That allows you to launch Intent to
`com.another.app`.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-12-16 21:53:56 +01:00
inkeliz 048614c60e gogio: [iOS] fix compatibility with Apple Connect and iPad requirement
This pach fixes a total of 6 issues caused by gogio,
when uploading .ipa to Apple Connect/Apple Store.

1. Asset validation failed (90474), caused by not set
"UIInterfaceOrientationPortraitUpside" in plist.

2. Asset validation failed (90482), the executable contains
bitcode. Now, gogio will use "bitcode_strip" to remove such
bitcode.

3. Asset validation failed (90060), the version can only have
three non-negative numbers. Using values from semVer is
invalid (such as 1.2.3.4), it must be either 1.2.3 or
1.2.34. Now, gogio uses the later one.

4. Asset validation failed (90476), supporting multitask on
iPad requires UILaunchScreen. That is tricky to solve, instead
gogio will NOT support multitask on iPad.

5. Asset validation failed (90208), version mismatch between
plist and binary. Now, gogio will use compile flags to set
the version AND will use the proper minSdk on plist.

6. Asset validation failed (90023), missing 152x152 icon for
iPad. Now, gogio will create such icon.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-12-16 18:40:23 +01:00
inkeliz ed8d0aa9a6 gogio: [macOS] support custom profile
This patch enables `-signkey` to load provisioning profiles.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-12-16 18:04:09 +01:00
inkeliz e1f06eb7b0 gogio: [wasm] fix compatibility with Go 1.23+
Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-12-15 22:06:47 +00:00
inkeliz ae8a780af9 gogio: add deeplink support
Add a new flag "-schemes" which links the URL schemes to the app.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-12-15 22:25:08 +01:00
9 changed files with 274 additions and 86 deletions
+38 -17
View File
@@ -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>`)
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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() {
+3
View File
@@ -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{
+9 -1
View File
@@ -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,