mirror of
https://git.sr.ht/~eliasnaur/gio-cmd
synced 2026-07-01 07:35:37 +00:00
048614c60e
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>
232 lines
6.0 KiB
Go
232 lines
6.0 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"image/png"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/image/draw"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
var (
|
|
target = flag.String("target", "", "specify target (ios, tvos, android, js).\n")
|
|
archNames = flag.String("arch", "", "specify architecture(s) to include (arm, arm64, amd64).")
|
|
minsdk = flag.Int("minsdk", 0, "specify the minimum supported operating system level")
|
|
targetsdk = flag.Int("targetsdk", 0, "specify the target supported operating system level for Android")
|
|
buildMode = flag.String("buildmode", "exe", "specify buildmode (archive, exe)")
|
|
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)")
|
|
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. The versioncode is not used for iOS and macOS.")
|
|
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.")
|
|
linkMode = flag.String("linkmode", "", "set the -linkmode flag of the go tool")
|
|
extraLdflags = flag.String("ldflags", "", "extra flags to the Go linker")
|
|
extraTags = flag.String("tags", "", "extra tags to the Go tool")
|
|
iconPath = flag.String("icon", "", "specify an icon for iOS and Android")
|
|
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.")
|
|
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.")
|
|
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")
|
|
)
|
|
|
|
func main() {
|
|
flag.Usage = func() {
|
|
fmt.Fprint(os.Stderr, mainUsage)
|
|
}
|
|
flag.Parse()
|
|
if err := flagValidate(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "gogio: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
buildInfo, err := newBuildInfo(flag.Arg(0))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "gogio: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if err := build(buildInfo); err != nil {
|
|
fmt.Fprintf(os.Stderr, "gogio: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
func flagValidate() error {
|
|
pkgPathArg := flag.Arg(0)
|
|
if pkgPathArg == "" {
|
|
return errors.New("specify a package")
|
|
}
|
|
if *target == "" {
|
|
return errors.New("please specify -target")
|
|
}
|
|
switch *target {
|
|
case "ios", "tvos", "android", "js", "windows", "macos":
|
|
default:
|
|
return fmt.Errorf("invalid -target %s", *target)
|
|
}
|
|
switch *buildMode {
|
|
case "archive", "exe":
|
|
default:
|
|
return fmt.Errorf("invalid -buildmode %s", *buildMode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func build(bi *buildInfo) error {
|
|
tmpDir, err := os.MkdirTemp("", "gogio-")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if *keepWorkdir {
|
|
fmt.Fprintf(os.Stderr, "WORKDIR=%s\n", tmpDir)
|
|
} else {
|
|
defer os.RemoveAll(tmpDir)
|
|
}
|
|
switch *target {
|
|
case "js":
|
|
return buildJS(bi)
|
|
case "ios", "tvos":
|
|
return buildIOS(tmpDir, *target, bi)
|
|
case "android":
|
|
return buildAndroid(tmpDir, bi)
|
|
case "windows":
|
|
return buildWindows(tmpDir, bi)
|
|
case "macos":
|
|
return buildMac(tmpDir, bi)
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
}
|
|
|
|
func runCmdRaw(cmd *exec.Cmd) ([]byte, error) {
|
|
if *printCommands {
|
|
fmt.Printf("%s\n", strings.Join(cmd.Args, " "))
|
|
}
|
|
out, err := cmd.Output()
|
|
if err == nil {
|
|
return out, nil
|
|
}
|
|
if err, ok := err.(*exec.ExitError); ok {
|
|
return nil, fmt.Errorf("%s failed: %s%s", strings.Join(cmd.Args, " "), out, err.Stderr)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func runCmd(cmd *exec.Cmd) (string, error) {
|
|
out, err := runCmdRaw(cmd)
|
|
return string(bytes.TrimSpace(out)), err
|
|
}
|
|
|
|
func copyFile(dst, src string) (err error) {
|
|
r, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer r.Close()
|
|
w, err := os.Create(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if cerr := w.Close(); err == nil {
|
|
err = cerr
|
|
}
|
|
}()
|
|
_, err = io.Copy(w, r)
|
|
return err
|
|
}
|
|
|
|
type arch struct {
|
|
iosArch string
|
|
jniArch string
|
|
clangArch string
|
|
}
|
|
|
|
var allArchs = map[string]arch{
|
|
"arm": {
|
|
iosArch: "armv7",
|
|
jniArch: "armeabi-v7a",
|
|
clangArch: "armv7a-linux-androideabi",
|
|
},
|
|
"arm64": {
|
|
iosArch: "arm64",
|
|
jniArch: "arm64-v8a",
|
|
clangArch: "aarch64-linux-android",
|
|
},
|
|
"386": {
|
|
iosArch: "i386",
|
|
jniArch: "x86",
|
|
clangArch: "i686-linux-android",
|
|
},
|
|
"amd64": {
|
|
iosArch: "x86_64",
|
|
jniArch: "x86_64",
|
|
clangArch: "x86_64-linux-android",
|
|
},
|
|
}
|
|
|
|
type iconVariant struct {
|
|
path string
|
|
size int
|
|
fill bool
|
|
}
|
|
|
|
func buildIcons(baseDir, icon string, variants []iconVariant) error {
|
|
f, err := os.Open(icon)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
img, _, err := image.Decode(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var resizes errgroup.Group
|
|
for _, v := range variants {
|
|
v := v
|
|
resizes.Go(func() (err error) {
|
|
path := filepath.Join(baseDir, v.path)
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
|
return err
|
|
}
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if cerr := f.Close(); err == nil {
|
|
err = cerr
|
|
}
|
|
}()
|
|
return png.Encode(f, resizeIcon(v, img))
|
|
})
|
|
}
|
|
return resizes.Wait()
|
|
}
|
|
|
|
func resizeIcon(v iconVariant, img image.Image) *image.NRGBA {
|
|
scaled := image.NewNRGBA(image.Rectangle{Max: image.Point{X: v.size, Y: v.size}})
|
|
op := draw.Src
|
|
if v.fill {
|
|
op = draw.Over
|
|
draw.Draw(scaled, scaled.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src)
|
|
}
|
|
draw.CatmullRom.Scale(scaled, scaled.Bounds(), img, img.Bounds(), op, nil)
|
|
|
|
return scaled
|
|
}
|