github.com/acrespo/mobile@v0.0.0-20190107162257-dc0771356504/gl/dll_windows.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 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  }