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