gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/golang.org/x/tools/go/packages/packagestest/modules.go (about)

     1  // Copyright 2018 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 packagestest
     6  
     7  import (
     8  	"archive/zip"
     9  	"bytes"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path"
    15  	"path/filepath"
    16  	"regexp"
    17  
    18  	"golang.org/x/tools/go/packages"
    19  )
    20  
    21  // Modules is the exporter that produces module layouts.
    22  // Each "repository" is put in it's own module, and the module file generated
    23  // will have replace directives for all other modules.
    24  // Given the two files
    25  //     golang.org/repoa#a/a.go
    26  //     golang.org/repob#b/b.go
    27  // You would get the directory layout
    28  //     /sometemporarydirectory
    29  //     ├── repoa
    30  //     │   ├── a
    31  //     │   │   └── a.go
    32  //     │   └── go.mod
    33  //     └── repob
    34  //         ├── b
    35  //         │   └── b.go
    36  //         └── go.mod
    37  // and the working directory would be
    38  //     /sometemporarydirectory/repoa
    39  var Modules = modules{}
    40  
    41  type modules struct{}
    42  
    43  func (modules) Name() string {
    44  	return "Modules"
    45  }
    46  
    47  func (modules) Filename(exported *Exported, module, fragment string) string {
    48  	if module == exported.primary {
    49  		return filepath.Join(primaryDir(exported), fragment)
    50  	}
    51  	return filepath.Join(moduleDir(exported, module), fragment)
    52  }
    53  
    54  func (modules) Finalize(exported *Exported) error {
    55  	// Write out the primary module. This module can use symlinks and
    56  	// other weird stuff, and will be the working dir for the go command.
    57  	// It depends on all the other modules.
    58  	primaryDir := primaryDir(exported)
    59  	exported.Config.Dir = primaryDir
    60  	exported.written[exported.primary]["go.mod"] = filepath.Join(primaryDir, "go.mod")
    61  	primaryGomod := "module " + exported.primary + "\nrequire (\n"
    62  	for other := range exported.written {
    63  		if other == exported.primary {
    64  			continue
    65  		}
    66  		primaryGomod += fmt.Sprintf("\t%v %v\n", other, moduleVersion(other))
    67  	}
    68  	primaryGomod += ")\n"
    69  	if err := ioutil.WriteFile(filepath.Join(primaryDir, "go.mod"), []byte(primaryGomod), 0644); err != nil {
    70  		return err
    71  	}
    72  
    73  	// Create the mod cache so we can rename it later, even if we don't need it.
    74  	if err := os.MkdirAll(modCache(exported), 0755); err != nil {
    75  		return err
    76  	}
    77  
    78  	// Write out the go.mod files for the other modules.
    79  	for module, files := range exported.written {
    80  		if module == exported.primary {
    81  			continue
    82  		}
    83  		dir := moduleDir(exported, module)
    84  
    85  		modfile := filepath.Join(dir, "go.mod")
    86  		if err := ioutil.WriteFile(modfile, []byte("module "+module+"\n"), 0644); err != nil {
    87  			return err
    88  		}
    89  		files["go.mod"] = modfile
    90  	}
    91  
    92  	// Zip up all the secondary modules into the proxy dir.
    93  	proxyDir := filepath.Join(exported.temp, "modproxy")
    94  	for module, files := range exported.written {
    95  		if module == exported.primary {
    96  			continue
    97  		}
    98  		dir := filepath.Join(proxyDir, module, "@v")
    99  
   100  		if err := writeModuleProxy(dir, module, files); err != nil {
   101  			return fmt.Errorf("creating module proxy dir for %v: %v", module, err)
   102  		}
   103  	}
   104  
   105  	// Discard the original mod cache dir, which contained the files written
   106  	// for us by Export.
   107  	if err := os.Rename(modCache(exported), modCache(exported)+".orig"); err != nil {
   108  		return err
   109  	}
   110  	exported.Config.Env = append(exported.Config.Env,
   111  		"GO111MODULE=on",
   112  		"GOPATH="+filepath.Join(exported.temp, "modcache"),
   113  		"GOPROXY=file://"+filepath.ToSlash(proxyDir))
   114  
   115  	// Run go mod download to recreate the mod cache dir with all the extra
   116  	// stuff in cache. All the files created by Export should be recreated.
   117  	if err := invokeGo(exported.Config, "mod", "download"); err != nil {
   118  		return err
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  // writeModuleProxy creates a directory in the proxy dir for a module.
   125  func writeModuleProxy(dir, module string, files map[string]string) error {
   126  	ver := moduleVersion(module)
   127  	if err := os.MkdirAll(dir, 0755); err != nil {
   128  		return err
   129  	}
   130  
   131  	// list file. Just the single version.
   132  	if err := ioutil.WriteFile(filepath.Join(dir, "list"), []byte(ver+"\n"), 0644); err != nil {
   133  		return err
   134  	}
   135  
   136  	// go.mod, copied from the file written in Finalize.
   137  	modContents, err := ioutil.ReadFile(files["go.mod"])
   138  	if err != nil {
   139  		return err
   140  	}
   141  	if err := ioutil.WriteFile(filepath.Join(dir, ver+".mod"), modContents, 0644); err != nil {
   142  		return err
   143  	}
   144  
   145  	// info file, just the bare bones.
   146  	infoContents := []byte(fmt.Sprintf(`{"Version": "%v", "Time":"2017-12-14T13:08:43Z"}`, ver))
   147  	if err := ioutil.WriteFile(filepath.Join(dir, ver+".info"), infoContents, 0644); err != nil {
   148  		return err
   149  	}
   150  
   151  	// zip of all the source files.
   152  	f, err := os.OpenFile(filepath.Join(dir, ver+".zip"), os.O_CREATE|os.O_WRONLY, 0644)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	z := zip.NewWriter(f)
   157  	for name, path := range files {
   158  		zf, err := z.Create(module + "@" + ver + "/" + name)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		contents, err := ioutil.ReadFile(path)
   163  		if err != nil {
   164  			return err
   165  		}
   166  		if _, err := zf.Write(contents); err != nil {
   167  			return err
   168  		}
   169  	}
   170  	if err := z.Close(); err != nil {
   171  		return err
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func invokeGo(cfg *packages.Config, args ...string) error {
   178  	stdout := new(bytes.Buffer)
   179  	stderr := new(bytes.Buffer)
   180  	cmd := exec.Command("go", args...)
   181  	cmd.Env = append(append([]string{}, cfg.Env...), "PWD="+cfg.Dir)
   182  	cmd.Dir = cfg.Dir
   183  	cmd.Stdout = stdout
   184  	cmd.Stderr = stderr
   185  	if err := cmd.Run(); err != nil {
   186  		return fmt.Errorf("go %v: %s: %s", args, err, stderr)
   187  	}
   188  	return nil
   189  }
   190  
   191  func modCache(exported *Exported) string {
   192  	return filepath.Join(exported.temp, "modcache/pkg/mod")
   193  }
   194  
   195  func primaryDir(exported *Exported) string {
   196  	return filepath.Join(exported.temp, "primarymod", path.Base(exported.primary))
   197  }
   198  
   199  func moduleDir(exported *Exported, module string) string {
   200  	return filepath.Join(modCache(exported), path.Dir(module), path.Base(module)+"@"+moduleVersion(module))
   201  }
   202  
   203  var versionSuffixRE = regexp.MustCompile(`v\d+`)
   204  
   205  func moduleVersion(module string) string {
   206  	if versionSuffixRE.MatchString(path.Base(module)) {
   207  		return path.Base(module) + ".0.0"
   208  	}
   209  	return "v1.0.0"
   210  }