dll_windows.go 6.3 KB

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