build_info.go raw
1 package main
2
3 import (
4 "flag"
5 "fmt"
6 "os"
7 "os/exec"
8 "path"
9 "path/filepath"
10 "runtime"
11 "strings"
12 )
13
14 type buildInfo struct {
15 appID string
16 archs []string
17 ldflags string
18 minsdk int
19 name string
20 pkgDir string
21 pkgPath string
22 iconPath string
23 tags string
24 target string
25 version int
26 key string
27 password string
28 }
29
30 func newBuildInfo(pkgPath string) (*buildInfo, error) {
31 pkgMetadata, err := getPkgMetadata(pkgPath)
32 if err != nil {
33 return nil, err
34 }
35 appID := getAppID(pkgMetadata)
36 appIcon := filepath.Join(pkgMetadata.Dir, "appicon.png")
37 if *iconPath != "" {
38 appIcon = *iconPath
39 }
40 bi := &buildInfo{
41 appID: appID,
42 archs: getArchs(),
43 ldflags: getLdFlags(appID),
44 minsdk: *minsdk,
45 name: getPkgName(pkgMetadata),
46 pkgDir: pkgMetadata.Dir,
47 pkgPath: pkgPath,
48 iconPath: appIcon,
49 tags: *extraTags,
50 target: *target,
51 version: *version,
52 key: *signKey,
53 password: *signPass,
54 }
55 return bi, nil
56 }
57
58 func getArchs() []string {
59 if *archNames != "" {
60 return strings.Split(*archNames, ",")
61 }
62 switch *target {
63 case "js":
64 return []string{"wasm"}
65 case "ios", "tvos":
66 // Only 64-bit support.
67 return []string{"arm64", "amd64"}
68 case "android":
69 return []string{"arm", "arm64", "386", "amd64"}
70 case "windows":
71 goarch := os.Getenv("GOARCH")
72 if goarch == "" {
73 goarch = runtime.GOARCH
74 }
75 return []string{goarch}
76 default:
77 // TODO: Add flag tests.
78 panic("The target value has already been validated, this will never execute.")
79 }
80 }
81
82 func getLdFlags(appID string) string {
83 var ldflags []string
84 if extra := *extraLdflags; extra != "" {
85 ldflags = append(ldflags, strings.Split(extra, " ")...)
86 }
87 // Pass appID along, to be used for logging on platforms like Android.
88 ldflags = append(ldflags, fmt.Sprintf("-X github.com/p9c/p9/pkg/gel/gio/app/internal/log.appID=%s", appID))
89 // Pass along all remaining arguments to the app.
90 if appArgs := flag.Args()[1:]; len(appArgs) > 0 {
91 ldflags = append(ldflags, fmt.Sprintf("-X github.com/p9c/p9/pkg/gel/gio/app.extraArgs=%s", strings.Join(appArgs, "|")))
92 }
93 if m := *linkMode; m != "" {
94 ldflags = append(ldflags, "-linkmode="+m)
95 }
96 return strings.Join(ldflags, " ")
97 }
98
99 type packageMetadata struct {
100 PkgPath string
101 Dir string
102 }
103
104 func getPkgMetadata(pkgPath string) (*packageMetadata, error) {
105 pkgImportPath, err := runCmd(exec.Command("go", "list", "-f", "{{.ImportPath}}", pkgPath))
106 if err != nil {
107 return nil, err
108 }
109 pkgDir, err := runCmd(exec.Command("go", "list", "-f", "{{.Dir}}", pkgPath))
110 if err != nil {
111 return nil, err
112 }
113 return &packageMetadata{
114 PkgPath: pkgImportPath,
115 Dir: pkgDir,
116 }, nil
117 }
118
119 func getAppID(pkgMetadata *packageMetadata) string {
120 if *appID != "" {
121 return *appID
122 }
123 elems := strings.Split(pkgMetadata.PkgPath, "/")
124 domain := strings.Split(elems[0], ".")
125 name := ""
126 if len(elems) > 1 {
127 name = "." + elems[len(elems)-1]
128 }
129 if len(elems) < 2 && len(domain) < 2 {
130 name = "." + domain[0]
131 domain[0] = "localhost"
132 } else {
133 for i := 0; i < len(domain)/2; i++ {
134 opp := len(domain) - 1 - i
135 domain[i], domain[opp] = domain[opp], domain[i]
136 }
137 }
138
139 pkgDomain := strings.Join(domain, ".")
140 appid := []rune(pkgDomain + name)
141
142 // a Java-language-style package name may contain upper- and lower-case
143 // letters and underscores with individual parts separated by '.'.
144 // https://developer.android.com/guide/topics/manifest/manifest-element
145 for i, c := range appid {
146 if !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' ||
147 c == '_' || c == '.') {
148 appid[i] = '_'
149 }
150 }
151 return string(appid)
152 }
153
154 func getPkgName(pkgMetadata *packageMetadata) string {
155 return path.Base(pkgMetadata.PkgPath)
156 }
157