From 3c739323cb4fbe49219530fa9594ccffce025c89 Mon Sep 17 00:00:00 2001 From: Walter Werner SCHNEIDER Date: Wed, 11 Nov 2020 23:16:33 +0200 Subject: [PATCH] cmd/gogio: group buildInfo related code Signed-off-by: Walter Werner SCHNEIDER --- cmd/gogio/androidbuild.go | 6 +- cmd/gogio/build_info.go | 143 +++++++++++++++++++++++++++++++++++ cmd/gogio/build_info_test.go | 32 ++++++++ cmd/gogio/iosbuild.go | 4 +- cmd/gogio/jsbuild.go | 2 +- cmd/gogio/main.go | 119 ++++------------------------- cmd/gogio/main_test.go | 29 ------- 7 files changed, 197 insertions(+), 138 deletions(-) create mode 100644 cmd/gogio/build_info.go create mode 100644 cmd/gogio/build_info_test.go diff --git a/cmd/gogio/androidbuild.go b/cmd/gogio/androidbuild.go index 69299614..112d79a7 100644 --- a/cmd/gogio/androidbuild.go +++ b/cmd/gogio/androidbuild.go @@ -111,7 +111,7 @@ func buildAndroid(tmpDir string, bi *buildInfo) error { "CGO_ENABLED=1", ), } - pkgs, err := packages.Load(cfg, bi.pkg) + pkgs, err := packages.Load(cfg, bi.pkgPath) if err != nil { return err } @@ -205,7 +205,7 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro "-buildmode=c-shared", "-tags", bi.tags, "-o", libFile, - bi.pkg, + bi.pkgPath, ) cmd.Env = append( os.Environ(), @@ -352,7 +352,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe } icon := *iconPath if icon == "" { - icon = filepath.Join(bi.dir, "appicon.png") + icon = filepath.Join(bi.pkgDir, "appicon.png") } iconSnip := "" if _, err := os.Stat(icon); err == nil { diff --git a/cmd/gogio/build_info.go b/cmd/gogio/build_info.go new file mode 100644 index 00000000..a2120bfb --- /dev/null +++ b/cmd/gogio/build_info.go @@ -0,0 +1,143 @@ +package main + +import ( + "flag" + "fmt" + "os/exec" + "path" + "path/filepath" + "strings" +) + +type buildInfo struct { + appID string + archs []string + ldflags string + minsdk int + name string + pkgDir string + pkgPath string + tags string + target string + version int +} + +func newBuildInfo(pkgAbsPath string) (*buildInfo, error) { + pkgMetadata, err := getPkgMetadata(pkgAbsPath) + if err != nil { + return nil, err + } + appID := getAppID(pkgMetadata) + bi := &buildInfo{ + appID: appID, + archs: getArchs(), + ldflags: getLdFlags(appID), + minsdk: *minsdk, + name: getPkgName(pkgMetadata), + pkgDir: pkgMetadata.Dir, + pkgPath: pkgAbsPath, + tags: *extraTags, + target: *target, + version: *version, + } + return bi, nil +} + +func getPkgAbsPath() string { + absPath, _ := filepath.Abs(flag.Arg(0)) + return absPath +} + +func getArchs() []string { + if *archNames != "" { + return strings.Split(*archNames, ",") + } + switch *target { + case "js": + return []string{"wasm"} + case "ios", "tvos": + // Only 64-bit support. + return []string{"arm64", "amd64"} + case "android": + return []string{"arm", "arm64", "386", "amd64"} + default: + // TODO: Add flag tests. + panic("The target value has already been validated, this will never execute.") + } +} + +func getLdFlags(appID string) string { + var ldflags []string + if extra := *extraLdflags; extra != "" { + ldflags = append(ldflags, strings.Split(extra, " ")...) + } + // Pass appID along, to be used for logging on platforms like Android. + ldflags = append(ldflags, fmt.Sprintf("-X gioui.org/app/internal/log.appID=%s", appID)) + // Pass along all remaining arguments to the app. + if appArgs := flag.Args()[1:]; len(appArgs) > 0 { + ldflags = append(ldflags, fmt.Sprintf("-X gioui.org/app.extraArgs=%s", strings.Join(appArgs, "|"))) + } + if m := *linkMode; m != "" { + ldflags = append(ldflags, "-linkmode="+m) + } + return strings.Join(ldflags, " ") +} + +type packageMetadata struct { + PkgPath string + Dir string +} + +func getPkgMetadata(absPath string) (*packageMetadata, error) { + pkgImportPath, err := runCmd(exec.Command("go", "list", "-f", "{{.ImportPath}}", absPath)) + if err != nil { + return nil, err + } + pkgDir, err := runCmd(exec.Command("go", "list", "-f", "{{.Dir}}", absPath)) + if err != nil { + return nil, err + } + return &packageMetadata{ + PkgPath: pkgImportPath, + Dir: pkgDir, + }, nil +} + +func getAppID(pkgMetadata *packageMetadata) string { + if *appID != "" { + return *appID + } + elems := strings.Split(pkgMetadata.PkgPath, "/") + domain := strings.Split(elems[0], ".") + name := "" + if len(elems) > 1 { + name = "." + elems[len(elems)-1] + } + if len(elems) < 2 && len(domain) < 2 { + name = "." + domain[0] + domain[0] = "localhost" + } else { + for i := 0; i < len(domain)/2; i++ { + opp := len(domain) - 1 - i + domain[i], domain[opp] = domain[opp], domain[i] + } + } + + pkgDomain := strings.Join(domain, ".") + appid := []rune(pkgDomain + name) + + // a Java-language-style package name may contain upper- and lower-case + // letters and underscores with individual parts separated by '.'. + // https://developer.android.com/guide/topics/manifest/manifest-element + for i, c := range appid { + if !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || + c == '_' || c == '.') { + appid[i] = '_' + } + } + return string(appid) +} + +func getPkgName(pkgMetadata *packageMetadata) string { + return path.Base(pkgMetadata.PkgPath) +} diff --git a/cmd/gogio/build_info_test.go b/cmd/gogio/build_info_test.go new file mode 100644 index 00000000..397e2a3c --- /dev/null +++ b/cmd/gogio/build_info_test.go @@ -0,0 +1,32 @@ +package main + +import "testing" + +type expval struct { + in, out string +} + +func TestAppID(t *testing.T) { + t.Parallel() + + tests := []expval{ + {"example", "localhost.example"}, + {"example.com", "com.example"}, + {"www.example.com", "com.example.www"}, + {"examplecom/app", "examplecom.app"}, + {"example.com/app", "com.example.app"}, + {"www.example.com/app", "com.example.www.app"}, + {"www.en.example.com/app", "com.example.en.www.app"}, + {"example.com/dir/app", "com.example.app"}, + {"example.com/dir.ext/app", "com.example.app"}, + {"example.com/dir/app.ext", "com.example.app.ext"}, + {"example-com.net/dir/app", "net.example_com.app"}, + } + + for i, test := range tests { + got := getAppID(&packageMetadata{PkgPath: test.in}) + if exp := test.out; got != exp { + t.Errorf("(%d): expected '%s', got '%s'", i, exp, got) + } + } +} diff --git a/cmd/gogio/iosbuild.go b/cmd/gogio/iosbuild.go index aac680ee..ae68a369 100644 --- a/cmd/gogio/iosbuild.go +++ b/cmd/gogio/iosbuild.go @@ -224,7 +224,7 @@ int main(int argc, char * argv[]) { } icon := *iconPath if icon == "" { - icon = filepath.Join(bi.dir, "appicon.png") + icon = filepath.Join(bi.pkgDir, "appicon.png") } if _, err := os.Stat(icon); err == nil { assetPlist, err := iosIcons(bi, tmpDir, app, icon) @@ -440,7 +440,7 @@ func archiveIOS(tmpDir, target, frameworkRoot string, bi *buildInfo) error { "-buildmode=c-archive", "-o", lib, "-tags", tags, - bi.pkg, + bi.pkgPath, ) lipo.Args = append(lipo.Args, lib) cflagsLine := strings.Join(cflags, " ") diff --git a/cmd/gogio/jsbuild.go b/cmd/gogio/jsbuild.go index 733afee3..63bf07b3 100644 --- a/cmd/gogio/jsbuild.go +++ b/cmd/gogio/jsbuild.go @@ -24,7 +24,7 @@ func buildJS(bi *buildInfo) error { "-ldflags="+bi.ldflags, "-tags="+bi.tags, "-o", filepath.Join(out, "main.wasm"), - bi.pkg, + bi.pkgPath, ) cmd.Env = append( os.Environ(), diff --git a/cmd/gogio/main.go b/cmd/gogio/main.go index cc04cb5d..34786b3a 100644 --- a/cmd/gogio/main.go +++ b/cmd/gogio/main.go @@ -37,36 +37,35 @@ var ( iconPath = flag.String("icon", "", "Specify an icon for iOS and Android") ) -type buildInfo struct { - name string - pkg string - ldflags string - tags string - target string - appID string - version int - dir string - archs []string - minsdk int -} - func main() { flag.Usage = func() { fmt.Fprint(os.Stderr, mainUsage) } flag.Parse() - if err := mainErr(); err != nil { + if err := flagValidate(); err != nil { + fmt.Fprintf(os.Stderr, "gogio: %v\n", err) + os.Exit(1) + } + buildInfo, err := newBuildInfo(getPkgAbsPath()) + 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 mainErr() error { - pkg := flag.Arg(0) - if pkg == "" { +func flagValidate() error { + pkgPathArg := flag.Arg(0) + if pkgPathArg == "" { return errors.New("specify a package") } + if _, err := filepath.Abs(pkgPathArg); err != nil { + return err + } if *target == "" { return errors.New("please specify -target") } @@ -80,95 +79,9 @@ func mainErr() error { default: return fmt.Errorf("invalid -buildmode %s", *buildMode) } - // Find package name. - pkgPath, err := runCmd(exec.Command("go", "list", "-f", "{{.ImportPath}}", pkg)) - if err != nil { - return err - } - dir, err := runCmd(exec.Command("go", "list", "-f", "{{.Dir}}", pkg)) - if err != nil { - return err - } - elems := strings.Split(pkgPath, "/") - name := elems[len(elems)-1] - bi := &buildInfo{ - name: name, - pkg: pkg, - target: *target, - appID: *appID, - dir: dir, - version: *version, - minsdk: *minsdk, - tags: *extraTags, - } - if bi.appID == "" { - bi.appID = appIDFromPackage(pkgPath) - } - var ldflags []string - if extra := *extraLdflags; extra != "" { - ldflags = append(ldflags, strings.Split(extra, " ")...) - } - // Pass appID along, to be used for logging on platforms like Android. - ldflags = append(ldflags, fmt.Sprintf("-X gioui.org/app/internal/log.appID=%s", bi.appID)) - - switch *target { - case "js": - bi.archs = []string{"wasm"} - case "ios", "tvos": - // Only 64-bit support. - bi.archs = []string{"arm64", "amd64"} - case "android": - bi.archs = []string{"arm", "arm64", "386", "amd64"} - } - if *archNames != "" { - bi.archs = strings.Split(*archNames, ",") - } - if appArgs := flag.Args()[1:]; len(appArgs) > 0 { - // Pass along arguments to the app. - ldflags = append(ldflags, fmt.Sprintf("-X gioui.org/app.extraArgs=%s", strings.Join(appArgs, "|"))) - } - if m := *linkMode; m != "" { - ldflags = append(ldflags, "-linkmode="+m) - } - bi.ldflags = strings.Join(ldflags, " ") - if err := build(bi); err != nil { - return err - } return nil } -func appIDFromPackage(pkgPath string) string { - elems := strings.Split(pkgPath, "/") - domain := strings.Split(elems[0], ".") - name := "" - if len(elems) > 1 { - name = "." + elems[len(elems)-1] - } - if len(elems) < 2 && len(domain) < 2 { - name = "." + domain[0] - domain[0] = "localhost" - } else { - for i := 0; i < len(domain)/2; i++ { - opp := len(domain) - 1 - i - domain[i], domain[opp] = domain[opp], domain[i] - } - } - - pkgDomain := strings.Join(domain, ".") - appid := []rune(pkgDomain + name) - - // a Java-language-style package name may contain upper- and lower-case - // letters and underscores with individual parts separated by '.'. - // https://developer.android.com/guide/topics/manifest/manifest-element - for i, c := range appid { - if !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || - c == '_' || c == '.') { - appid[i] = '_' - } - } - return string(appid) -} - func build(bi *buildInfo) error { tmpDir, err := ioutil.TempDir("", "gogio-") if err != nil { diff --git a/cmd/gogio/main_test.go b/cmd/gogio/main_test.go index 9e072180..98dcb273 100644 --- a/cmd/gogio/main_test.go +++ b/cmd/gogio/main_test.go @@ -15,32 +15,3 @@ func TestMain(m *testing.M) { } os.Exit(m.Run()) } - -type expval struct { - in, out string -} - -func TestAppID(t *testing.T) { - t.Parallel() - - tests := []expval{ - {"example", "localhost.example"}, - {"example.com", "com.example"}, - {"www.example.com", "com.example.www"}, - {"examplecom/app", "examplecom.app"}, - {"example.com/app", "com.example.app"}, - {"www.example.com/app", "com.example.www.app"}, - {"www.en.example.com/app", "com.example.en.www.app"}, - {"example.com/dir/app", "com.example.app"}, - {"example.com/dir.ext/app", "com.example.app"}, - {"example.com/dir/app.ext", "com.example.app.ext"}, - {"example-com.net/dir/app", "net.example_com.app"}, - } - - for i, test := range tests { - got := appIDFromPackage(test.in) - if exp := test.out; got != exp { - t.Errorf("(%d): expected '%s', got '%s'", i, exp, got) - } - } -}