dll_windows.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 gl
  5. import (
  6. "archive/tar"
  7. "compress/gzip"
  8. "debug/pe"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "log"
  13. "net/http"
  14. "os"
  15. "path/filepath"
  16. "runtime"
  17. )
  18. var debug = log.New(ioutil.Discard, "gl: ", log.LstdFlags)
  19. func downloadDLLs() (path string, err error) {
  20. url := "https://dl.google.com/go/mobile/angle-bd3f8780b-" + runtime.GOARCH + ".tgz"
  21. debug.Printf("downloading %s", url)
  22. resp, err := http.Get(url)
  23. if err != nil {
  24. return "", fmt.Errorf("gl: %v", err)
  25. }
  26. defer func() {
  27. err2 := resp.Body.Close()
  28. if err == nil && err2 != nil {
  29. err = fmt.Errorf("gl: error reading body from %v: %v", url, err2)
  30. }
  31. }()
  32. if resp.StatusCode != http.StatusOK {
  33. err := fmt.Errorf("gl: error fetching %v, status: %v", url, resp.Status)
  34. return "", err
  35. }
  36. r, err := gzip.NewReader(resp.Body)
  37. if err != nil {
  38. return "", fmt.Errorf("gl: error reading gzip from %v: %v", url, err)
  39. }
  40. tr := tar.NewReader(r)
  41. var bytesGLESv2, bytesEGL, bytesD3DCompiler []byte
  42. for {
  43. header, err := tr.Next()
  44. if err == io.EOF {
  45. break
  46. }
  47. if err != nil {
  48. return "", fmt.Errorf("gl: error reading tar from %v: %v", url, err)
  49. }
  50. switch header.Name {
  51. case "angle-" + runtime.GOARCH + "/libglesv2.dll":
  52. bytesGLESv2, err = ioutil.ReadAll(tr)
  53. case "angle-" + runtime.GOARCH + "/libegl.dll":
  54. bytesEGL, err = ioutil.ReadAll(tr)
  55. case "angle-" + runtime.GOARCH + "/d3dcompiler_47.dll":
  56. bytesD3DCompiler, err = ioutil.ReadAll(tr)
  57. default: // skip
  58. }
  59. if err != nil {
  60. return "", fmt.Errorf("gl: error reading %v from %v: %v", header.Name, url, err)
  61. }
  62. }
  63. if len(bytesGLESv2) == 0 || len(bytesEGL) == 0 || len(bytesD3DCompiler) == 0 {
  64. return "", fmt.Errorf("gl: did not find all DLLs in %v", url)
  65. }
  66. writeDLLs := func(path string) error {
  67. if err := ioutil.WriteFile(filepath.Join(path, "libglesv2.dll"), bytesGLESv2, 0755); err != nil {
  68. return fmt.Errorf("gl: cannot install ANGLE: %v", err)
  69. }
  70. if err := ioutil.WriteFile(filepath.Join(path, "libegl.dll"), bytesEGL, 0755); err != nil {
  71. return fmt.Errorf("gl: cannot install ANGLE: %v", err)
  72. }
  73. if err := ioutil.WriteFile(filepath.Join(path, "d3dcompiler_47.dll"), bytesD3DCompiler, 0755); err != nil {
  74. return fmt.Errorf("gl: cannot install ANGLE: %v", err)
  75. }
  76. return nil
  77. }
  78. // First, we attempt to install these DLLs in LOCALAPPDATA/Shiny.
  79. //
  80. // Traditionally we would use the system32 directory, but it is
  81. // no longer writable by normal programs.
  82. os.MkdirAll(appdataPath(), 0775)
  83. if err := writeDLLs(appdataPath()); err == nil {
  84. return appdataPath(), nil
  85. }
  86. debug.Printf("DLLs could not be written to %s", appdataPath())
  87. // Second, install in GOPATH/pkg if it exists.
  88. gopath := os.Getenv("GOPATH")
  89. gopathpkg := filepath.Join(gopath, "pkg")
  90. if _, err := os.Stat(gopathpkg); err == nil && gopath != "" {
  91. if err := writeDLLs(gopathpkg); err == nil {
  92. return gopathpkg, nil
  93. }
  94. }
  95. debug.Printf("DLLs could not be written to GOPATH")
  96. // Third, pick a temporary directory.
  97. tmp := os.TempDir()
  98. if err := writeDLLs(tmp); err != nil {
  99. return "", fmt.Errorf("gl: unable to install ANGLE DLLs: %v", err)
  100. }
  101. return tmp, nil
  102. }
  103. func appdataPath() string {
  104. return filepath.Join(os.Getenv("LOCALAPPDATA"), "GoGL", runtime.GOARCH)
  105. }
  106. func containsDLLs(dir string) bool {
  107. compatible := func(name string) bool {
  108. file, err := pe.Open(filepath.Join(dir, name))
  109. if err != nil {
  110. return false
  111. }
  112. defer file.Close()
  113. switch file.Machine {
  114. case pe.IMAGE_FILE_MACHINE_AMD64:
  115. return "amd64" == runtime.GOARCH
  116. case pe.IMAGE_FILE_MACHINE_ARM:
  117. return "arm" == runtime.GOARCH
  118. case pe.IMAGE_FILE_MACHINE_I386:
  119. return "386" == runtime.GOARCH
  120. }
  121. return false
  122. }
  123. return compatible("libglesv2.dll") && compatible("libegl.dll") && compatible("d3dcompiler_47.dll")
  124. }
  125. func chromePath() string {
  126. // dlls are stored in:
  127. // <BASE>/<VERSION>/libglesv2.dll
  128. var installdirs = []string{
  129. // Chrome User
  130. filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome", "Application"),
  131. // Chrome System
  132. filepath.Join(os.Getenv("ProgramFiles(x86)"), "Google", "Chrome", "Application"),
  133. // Chromium
  134. filepath.Join(os.Getenv("LOCALAPPDATA"), "Chromium", "Application"),
  135. // Chrome Canary
  136. filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome SxS", "Application"),
  137. }
  138. for _, installdir := range installdirs {
  139. versiondirs, err := ioutil.ReadDir(installdir)
  140. if err != nil {
  141. continue
  142. }
  143. for _, versiondir := range versiondirs {
  144. if !versiondir.IsDir() {
  145. continue
  146. }
  147. versionpath := filepath.Join(installdir, versiondir.Name())
  148. if containsDLLs(versionpath) {
  149. return versionpath
  150. }
  151. }
  152. }
  153. return ""
  154. }
  155. func findDLLs() (err error) {
  156. load := func(path string) (bool, error) {
  157. if path != "" {
  158. // don't try to start when one of the files is missing
  159. if !containsDLLs(path) {
  160. return false, nil
  161. }
  162. LibD3DCompiler.Name = filepath.Join(path, filepath.Base(LibD3DCompiler.Name))
  163. LibGLESv2.Name = filepath.Join(path, filepath.Base(LibGLESv2.Name))
  164. LibEGL.Name = filepath.Join(path, filepath.Base(LibEGL.Name))
  165. }
  166. if err := LibGLESv2.Load(); err == nil {
  167. if err := LibEGL.Load(); err != nil {
  168. return false, fmt.Errorf("gl: loaded libglesv2 but not libegl: %v", err)
  169. }
  170. if err := LibD3DCompiler.Load(); err != nil {
  171. return false, fmt.Errorf("gl: loaded libglesv2, libegl but not d3dcompiler: %v", err)
  172. }
  173. if path == "" {
  174. debug.Printf("DLLs found")
  175. } else {
  176. debug.Printf("DLLs found in: %q", path)
  177. }
  178. return true, nil
  179. }
  180. return false, nil
  181. }
  182. // Look in the system directory.
  183. if ok, err := load(""); ok || err != nil {
  184. return err
  185. }
  186. // Look in the AppData directory.
  187. if ok, err := load(appdataPath()); ok || err != nil {
  188. return err
  189. }
  190. // Look for a Chrome installation
  191. if dir := chromePath(); dir != "" {
  192. if ok, err := load(dir); ok || err != nil {
  193. return err
  194. }
  195. }
  196. // Look in GOPATH/pkg.
  197. if ok, err := load(filepath.Join(os.Getenv("GOPATH"), "pkg")); ok || err != nil {
  198. return err
  199. }
  200. // Look in temporary directory.
  201. if ok, err := load(os.TempDir()); ok || err != nil {
  202. return err
  203. }
  204. // Download the DLL binary.
  205. path, err := downloadDLLs()
  206. if err != nil {
  207. return err
  208. }
  209. debug.Printf("DLLs written to %s", path)
  210. if ok, err := load(path); !ok || err != nil {
  211. return fmt.Errorf("gl: unable to load ANGLE after installation: %v", err)
  212. }
  213. return nil
  214. }