~eliasnaur/gio

cmd/gio: generate appID if not specified v1 PROPOSED

Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~eliasnaur/gio/%3C20190910220301.71325-1-gmp.gio%40wow.st%3E/mbox | git am -3
Learn more about email & git

[PATCH] cmd/gio: generate appID if not specified Export this patch

Greg Pomerantz
Use the Go source path to create an appID based on the domain name
plus the last directory location in the source path.
Signed-off-by: Greg Pomerantz <gmp.gio@wow.st>
---
 cmd/gio/androidbuild.go |  7 ++-----
 cmd/gio/gio.go          | 46 ++++++++++++++++++++++++++++++++++++++++++----
 cmd/gio/gio_test.go     | 42 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 86 insertions(+), 9 deletions(-)
 create mode 100644 cmd/gio/gio_test.go

diff --git a/cmd/gio/androidbuild.go b/cmd/gio/androidbuild.go
index 8ff7b7e..5802fa8 100644
--- a/cmd/gio/androidbuild.go
+++ b/cmd/gio/androidbuild.go
@@ -176,10 +176,10 @@ func archiveAndroid(tmpDir string, bi *buildInfo) (err error) {
 	aarw.Create("R.txt")
 	aarw.Create("res/")
 	manifest := aarw.Create("AndroidManifest.xml")
-	manifest.Write([]byte(`<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.gioui.app">
+	manifest.Write([]byte(fmt.Sprintf(`<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="%s">
 	<uses-sdk android:minSdkVersion="16"/>
 	<uses-feature android:glEsVersion="0x00030000" android:required="true" />
-</manifest>`))
+</manifest>`, bi.appID)))
 	proguard := aarw.Create("proguard.txt")
 	proguard.Write([]byte(`-keep class org.gioui.** { *; }`))
 
@@ -200,9 +200,6 @@ func archiveAndroid(tmpDir string, bi *buildInfo) (err error) {
 }
 
 func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err error) {
-	if bi.appID == "" {
-		return errors.New("app id is empty; use -appid to set it")
-	}
 	classes := filepath.Join(tmpDir, "classes")
 	var classFiles []string
 	err = filepath.Walk(classes, func(path string, f os.FileInfo, err error) error {
diff --git a/cmd/gio/gio.go b/cmd/gio/gio.go
index f23ff38..1fa80c4 100644
--- a/cmd/gio/gio.go
+++ b/cmd/gio/gio.go
@@ -14,7 +14,6 @@ import (
 	"io/ioutil"
 	"os"
 	"os/exec"
-	"path"
 	"path/filepath"
 	"strings"
 
@@ -27,7 +26,7 @@ var (
 	archNames     = flag.String("arch", "", "specify architecture(s) to include (arm, arm64, amd64).")
 	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", "org.gioui.app", "app identifier (for -buildmode=exe)")
+	appID         = flag.String("appid", "", "app identifier (for -buildmode=exe)")
 	version       = flag.Int("version", 1, "app version (for -buildmode=exe)")
 	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.")
@@ -75,15 +74,16 @@ func mainErr() error {
 		return fmt.Errorf("invalid -buildmode %s\n", *buildMode)
 	}
 	// Find package name.
-	name, err := runCmd(exec.Command("go", "list", "-f", "{{.ImportPath}}", pkg))
+	pkgname, err := runCmd(exec.Command("go", "list", "-f", "{{.ImportPath}}", pkg))
 	if err != nil {
 		return fmt.Errorf("gio: %v", err)
 	}
-	name = path.Base(name)
 	dir, err := runCmd(exec.Command("go", "list", "-f", "{{.Dir}}", pkg))
 	if err != nil {
 		return fmt.Errorf("gio: %v", err)
 	}
+	elems := strings.Split(pkgname, "/")
+	name := elems[len(elems)-1]
 	bi := &buildInfo{
 		name:    name,
 		pkg:     pkg,
@@ -92,6 +92,13 @@ func mainErr() error {
 		dir:     dir,
 		version: *version,
 	}
+	if bi.appID == "" {
+		bi.appID, err = appIDFromPackage(pkgname)
+		if err != nil {
+			return err
+		}
+	}
+
 	switch *target {
 	case "js":
 		bi.archs = []string{"wasm"}
@@ -114,6 +121,37 @@ func mainErr() error {
 	return nil
 }
 
+func appIDFromPackage(pkgname string) (string, error) {
+	elems := strings.Split(pkgname, "/")
+	domain := strings.Split(elems[0], ".")
+	name := ""
+	if len(elems) > 1 {
+		name = "." + elems[len(elems)-1]
+	}
+	// the apk will not build if appID does not contain at least one '.'.
+	if len(elems) < 2 && len(domain) < 2 {
+		return "", fmt.Errorf("gio: Invalid appID. Use -appid to set it.")
+	}
+	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), nil
+}
+
 func build(bi *buildInfo) error {
 	tmpDir, err := ioutil.TempDir("", "gio-")
 	if err != nil {
diff --git a/cmd/gio/gio_test.go b/cmd/gio/gio_test.go
new file mode 100644
index 0000000..b00d6a4
--- /dev/null
+++ b/cmd/gio/gio_test.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+	"testing"
+)
+
+type expval struct {
+	in, out, err string
+}
+
+func TestAppID(t *testing.T) {
+	tests := []expval{
+		{"example", "", "gio: Invalid appID. Use -appid to set it."},
+		{"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, err := appIDFromPackage(test.in)
+		if exp := test.out; got != exp {
+			t.Errorf("(%d): expected '%s', got '%s'", i, exp, got)
+		}
+		if test.err == "" && err == nil {
+			continue
+		}
+		if err == nil {
+			t.Errorf("(%d): expected nil, got '%s'", i, test.err)
+			continue
+		}
+		if exp := test.err; err.Error() != exp {
+			t.Errorf("(%d): expected '%s', got '%s'", i, exp, err)
+		}
+	}
+}
-- 
2.16.2
View this thread in the archives