github.com/cyrilou242/gomobile-java@v0.0.0-20220215185836-09daef25a210/cmd/gomobile/init.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "runtime" 17 "strings" 18 "time" 19 ) 20 21 var ( 22 goos = runtime.GOOS 23 goarch = runtime.GOARCH 24 ) 25 26 var cmdInit = &command{ 27 run: runInit, 28 Name: "init", 29 Usage: "[-openal dir]", 30 Short: "build OpenAL for Android", 31 Long: ` 32 If a OpenAL source directory is specified with -openal, init will 33 build an Android version of OpenAL for use with gomobile build 34 and gomobile install. 35 `, 36 } 37 38 var initOpenAL string // -openal 39 40 func init() { 41 cmdInit.flag.StringVar(&initOpenAL, "openal", "", "OpenAL source path") 42 } 43 44 func runInit(cmd *command) error { 45 gopaths := filepath.SplitList(goEnv("GOPATH")) 46 if len(gopaths) == 0 { 47 return fmt.Errorf("GOPATH is not set") 48 } 49 gomobilepath = filepath.Join(gopaths[0], "pkg/gomobile") 50 51 if buildX || buildN { 52 fmt.Fprintln(xout, "GOMOBILE="+gomobilepath) 53 } 54 removeAll(gomobilepath) 55 56 if err := mkdir(gomobilepath); err != nil { 57 return err 58 } 59 60 if buildN { 61 tmpdir = filepath.Join(gomobilepath, "work") 62 } else { 63 var err error 64 tmpdir, err = ioutil.TempDir(gomobilepath, "work-") 65 if err != nil { 66 return err 67 } 68 } 69 if buildX || buildN { 70 fmt.Fprintln(xout, "WORK="+tmpdir) 71 } 72 defer func() { 73 if buildWork { 74 fmt.Printf("WORK=%s\n", tmpdir) 75 return 76 } 77 removeAll(tmpdir) 78 }() 79 80 // Make sure gobind is up to date. 81 if err := goInstall([]string{"github.com/cyrilou242/gomobile-java/cmd/gobind"}, nil); err != nil { 82 return err 83 } 84 85 if buildN { 86 initOpenAL = "$OPENAL_PATH" 87 } else { 88 if initOpenAL != "" { 89 var err error 90 if initOpenAL, err = filepath.Abs(initOpenAL); err != nil { 91 return err 92 } 93 } 94 } 95 if err := envInit(); err != nil { 96 return err 97 } 98 99 start := time.Now() 100 101 if err := installOpenAL(gomobilepath); err != nil { 102 return err 103 } 104 105 if buildV { 106 took := time.Since(start) / time.Second * time.Second 107 fmt.Fprintf(os.Stderr, "\nDone, build took %s.\n", took) 108 } 109 return nil 110 } 111 112 func installOpenAL(gomobilepath string) error { 113 if initOpenAL == "" { 114 return nil 115 } 116 ndkRoot, err := ndkRoot() 117 if err != nil { 118 return err 119 } 120 121 var cmake string 122 if buildN { 123 cmake = "cmake" 124 } else { 125 sdkRoot := os.Getenv("ANDROID_HOME") 126 if sdkRoot == "" { 127 return nil 128 } 129 var err error 130 cmake, err = exec.LookPath("cmake") 131 if err != nil { 132 cmakePath := filepath.Join(sdkRoot, "cmake") 133 cmakeDir, err := os.Open(cmakePath) 134 if err != nil { 135 if os.IsNotExist(err) { 136 // Skip OpenAL install if the cmake package is not installed. 137 return errors.New("cmake was not found in the PATH. Please install it through the Android SDK manager.") 138 } 139 return err 140 } 141 defer cmakeDir.Close() 142 // There might be multiple versions of CMake installed. Use any one for now. 143 cmakeVers, err := cmakeDir.Readdirnames(1) 144 if err != nil || len(cmakeVers) == 0 { 145 return errors.New("cmake was not found in the PATH. Please install it through the Android SDK manager.") 146 } 147 cmake = filepath.Join(cmakePath, cmakeVers[0], "bin", "cmake") 148 } 149 } 150 var alTmpDir string 151 if buildN { 152 alTmpDir = filepath.Join(gomobilepath, "work") 153 } else { 154 var err error 155 alTmpDir, err = ioutil.TempDir(gomobilepath, "openal-release-") 156 if err != nil { 157 return err 158 } 159 defer removeAll(alTmpDir) 160 } 161 162 for _, f := range []string{"include/AL/al.h", "include/AL/alc.h"} { 163 dst := filepath.Join(gomobilepath, f) 164 src := filepath.Join(initOpenAL, f) 165 if err := copyFile(dst, src); err != nil { 166 return err 167 } 168 } 169 170 for _, arch := range platformArchs("android") { 171 t := ndk[arch] 172 abi := t.arch 173 if abi == "arm" { 174 abi = "armeabi" 175 } 176 make := filepath.Join(ndkRoot, "prebuilt", archNDK(), "bin", "make") 177 // Split android-XX to get the api version. 178 buildDir := alTmpDir + "/build/" + abi 179 if err := mkdir(buildDir); err != nil { 180 return err 181 } 182 cmd := exec.Command(cmake, 183 initOpenAL, 184 "-DCMAKE_TOOLCHAIN_FILE="+initOpenAL+"/XCompile-Android.txt", 185 "-DHOST="+t.ClangPrefix()) 186 cmd.Dir = buildDir 187 tcPath := filepath.Join(ndkRoot, "toolchains", "llvm", "prebuilt", archNDK(), "bin") 188 if !buildN { 189 orgPath := os.Getenv("PATH") 190 cmd.Env = []string{"PATH=" + tcPath + string(os.PathListSeparator) + orgPath} 191 } 192 if err := runCmd(cmd); err != nil { 193 return err 194 } 195 196 cmd = exec.Command(make) 197 cmd.Dir = buildDir 198 if err := runCmd(cmd); err != nil { 199 return err 200 } 201 202 dst := filepath.Join(gomobilepath, "lib", t.abi, "libopenal.so") 203 src := filepath.Join(alTmpDir, "build", abi, "libopenal.so") 204 if err := copyFile(dst, src); err != nil { 205 return err 206 } 207 } 208 return nil 209 } 210 211 var commonPkgs = []string{ 212 "github.com/cyrilou242/gomobile-java/gl", 213 "github.com/cyrilou242/gomobile-java/app", 214 "github.com/cyrilou242/gomobile-java/exp/app/debug", 215 } 216 217 func mkdir(dir string) error { 218 if buildX || buildN { 219 printcmd("mkdir -p %s", dir) 220 } 221 if buildN { 222 return nil 223 } 224 return os.MkdirAll(dir, 0755) 225 } 226 227 func symlink(src, dst string) error { 228 if buildX || buildN { 229 printcmd("ln -s %s %s", src, dst) 230 } 231 if buildN { 232 return nil 233 } 234 if goos == "windows" { 235 return doCopyAll(dst, src) 236 } 237 return os.Symlink(src, dst) 238 } 239 240 func rm(name string) error { 241 if buildX || buildN { 242 printcmd("rm %s", name) 243 } 244 if buildN { 245 return nil 246 } 247 return os.Remove(name) 248 } 249 250 func doCopyAll(dst, src string) error { 251 return filepath.Walk(src, func(path string, info os.FileInfo, errin error) (err error) { 252 if errin != nil { 253 return errin 254 } 255 prefixLen := len(src) 256 if len(path) > prefixLen { 257 prefixLen++ // file separator 258 } 259 outpath := filepath.Join(dst, path[prefixLen:]) 260 if info.IsDir() { 261 return os.Mkdir(outpath, 0755) 262 } 263 in, err := os.Open(path) 264 if err != nil { 265 return err 266 } 267 defer in.Close() 268 out, err := os.OpenFile(outpath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode()) 269 if err != nil { 270 return err 271 } 272 defer func() { 273 if errc := out.Close(); err == nil { 274 err = errc 275 } 276 }() 277 _, err = io.Copy(out, in) 278 return err 279 }) 280 } 281 282 func removeAll(path string) error { 283 if buildX || buildN { 284 printcmd(`rm -r -f "%s"`, path) 285 } 286 if buildN { 287 return nil 288 } 289 290 // os.RemoveAll behaves differently in windows. 291 // http://golang.org/issues/9606 292 if goos == "windows" { 293 resetReadOnlyFlagAll(path) 294 } 295 296 return os.RemoveAll(path) 297 } 298 299 func resetReadOnlyFlagAll(path string) error { 300 fi, err := os.Stat(path) 301 if err != nil { 302 return err 303 } 304 if !fi.IsDir() { 305 return os.Chmod(path, 0666) 306 } 307 fd, err := os.Open(path) 308 if err != nil { 309 return err 310 } 311 defer fd.Close() 312 313 names, _ := fd.Readdirnames(-1) 314 for _, name := range names { 315 resetReadOnlyFlagAll(path + string(filepath.Separator) + name) 316 } 317 return nil 318 } 319 320 func goEnv(name string) string { 321 if val := os.Getenv(name); val != "" { 322 return val 323 } 324 val, err := exec.Command("go", "env", name).Output() 325 if err != nil { 326 panic(err) // the Go tool was tested to work earlier 327 } 328 return strings.TrimSpace(string(val)) 329 } 330 331 func runCmd(cmd *exec.Cmd) error { 332 if buildX || buildN { 333 dir := "" 334 if cmd.Dir != "" { 335 dir = "PWD=" + cmd.Dir + " " 336 } 337 env := strings.Join(cmd.Env, " ") 338 if env != "" { 339 env += " " 340 } 341 printcmd("%s%s%s", dir, env, strings.Join(cmd.Args, " ")) 342 } 343 344 buf := new(bytes.Buffer) 345 buf.WriteByte('\n') 346 if buildV { 347 cmd.Stdout = os.Stdout 348 cmd.Stderr = os.Stderr 349 } else { 350 cmd.Stdout = buf 351 cmd.Stderr = buf 352 } 353 354 if buildWork { 355 if goos == "windows" { 356 cmd.Env = append(cmd.Env, `TEMP=`+tmpdir) 357 cmd.Env = append(cmd.Env, `TMP=`+tmpdir) 358 } else { 359 cmd.Env = append(cmd.Env, `TMPDIR=`+tmpdir) 360 } 361 } 362 363 if !buildN { 364 cmd.Env = environ(cmd.Env) 365 if err := cmd.Run(); err != nil { 366 return fmt.Errorf("%s failed: %v%s", strings.Join(cmd.Args, " "), err, buf) 367 } 368 } 369 return nil 370 }