init.go 7.4 KB

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