mirror of
https://git.sr.ht/~eliasnaur/gio-cmd
synced 2026-07-01 07:35:37 +00:00
ae8a780af9
Add a new flag "-schemes" which links the URL schemes to the app. Signed-off-by: inkeliz <inkeliz@inkeliz.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")
|
|
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 to be used to sign Android apk files.")
|
|
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
|
|
}
|