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