blob: 39b85b95a3c0b27feb1c28e6a4778d61eb819464 [file] [log] [blame]
Jess Frazelle70b94ea2017-01-17 22:07:501// Copyright 2017 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
Miguel Molina0acfd382017-01-27 20:39:075package dep
Jess Frazelle229c9472016-12-05 22:04:466
Jess Frazelle35349ab2016-12-06 01:58:007import (
Jordan Kragecbded812017-05-10 04:57:148 "log"
Jess Frazelle35349ab2016-12-06 01:58:009 "os"
10 "path/filepath"
Ibrahim AshShohailcd9bea62017-06-14 22:34:2011 "runtime"
Jess Frazelle35349ab2016-12-06 01:58:0012
Ibrahim AshShohailf87d6142017-05-10 16:21:0013 "github.com/golang/dep/internal/fs"
Dave Cheney9ea84892017-05-10 04:13:2214 "github.com/golang/dep/internal/gps"
Kris Nova0561ca92017-04-25 17:13:1415 "github.com/pkg/errors"
Jess Frazelle35349ab2016-12-06 01:58:0016)
Jess Frazelle229c9472016-12-05 22:04:4617
Ibrahim AshShohail138cebb2017-06-17 00:47:2218// Ctx defines the supporting context of dep.
19//
20// A properly initialized Ctx has a GOPATH containing the project root and non-nil Loggers.
21//
22// ctx := &dep.Ctx{
23// WorkingDir: GOPATH + "/src/project/root",
24// GOPATH: GOPATH,
25// Out: log.New(os.Stdout, "", 0),
26// Err: log.New(os.Stderr, "", 0),
27// }
28//
29// Ctx.DetectProjectGOPATH() helps with setting the containing GOPATH.
30//
31// ctx.GOPATH, err := Ctx.DetectProjectGOPATH(project)
32// if err != nil {
33// // Could not determine which GOPATH to use for the project.
34// }
35//
Miguel Molina0acfd382017-01-27 20:39:0736type Ctx struct {
Jordan Krage2fd37632017-05-28 16:49:2637 WorkingDir string // Where to execute.
38 GOPATH string // Selected Go path, containing WorkingDir.
Ibrahim AshShohailb7048cc2017-06-12 19:21:2839 GOPATHs []string // Other Go paths.
Jordan Krage2fd37632017-05-28 16:49:2640 Out, Err *log.Logger // Required loggers.
41 Verbose bool // Enables more verbose logging.
Jordan Kragecbded812017-05-10 04:57:1442}
43
Jordan Krageac8ec852017-06-25 17:21:3544// SetPaths sets the WorkingDir and GOPATHs fields. If GOPATHs is empty, then
45// the GOPATH environment variable (or the default GOPATH) is used instead.
Ibrahim AshShohailcd9bea62017-06-14 22:34:2046func (c *Ctx) SetPaths(wd string, GOPATHs ...string) error {
47 if wd == "" {
48 return errors.New("cannot set Ctx.WorkingDir to an empty path")
Jess Frazelle229c9472016-12-05 22:04:4649 }
Ibrahim AshShohailcd9bea62017-06-14 22:34:2050 c.WorkingDir = wd
51
52 if len(GOPATHs) == 0 {
Jordan Krage3fe03972017-06-22 12:51:4753 GOPATH := os.Getenv("GOPATH")
54 if GOPATH == "" {
55 GOPATH = defaultGOPATH()
56 }
57 GOPATHs = filepath.SplitList(GOPATH)
Ibrahim AshShohailcd9bea62017-06-14 22:34:2058 }
Ibrahim AshShohail6e2930f2017-06-20 22:29:3959
60 c.GOPATHs = append(c.GOPATHs, GOPATHs...)
Jess Frazelle48c329b2016-12-07 00:33:2861
Ibrahim AshShohailcd9bea62017-06-14 22:34:2062 return nil
Jess Frazelle229c9472016-12-05 22:04:4663}
Jess Frazelle35349ab2016-12-06 01:58:0064
Ibrahim AshShohail5a3291a2017-06-14 23:58:2565// defaultGOPATH gets the default GOPATH that was added in 1.8
66// copied from go/build/build.go
67func defaultGOPATH() string {
68 env := "HOME"
69 if runtime.GOOS == "windows" {
70 env = "USERPROFILE"
71 } else if runtime.GOOS == "plan9" {
72 env = "home"
73 }
74 if home := os.Getenv(env); home != "" {
75 def := filepath.Join(home, "go")
76 if def == runtime.GOROOT() {
77 // Don't set the default GOPATH to GOROOT,
78 // as that will trigger warnings from the go tool.
79 return ""
80 }
81 return def
82 }
83 return ""
84}
85
Jordan Kragee50a6432017-05-06 13:23:3186func (c *Ctx) SourceManager() (*gps.SourceMgr, error) {
87 return gps.NewSourceManager(filepath.Join(c.GOPATH, "pkg", "dep"))
Jess Frazelle35349ab2016-12-06 01:58:0088}
89
Jordan Kragef7ce5152017-05-30 14:07:2390// LoadProject starts from the current working directory and searches up the
91// directory tree for a project root. The search stops when a file with the name
92// ManifestName (Gopkg.toml, by default) is located.
tro36411f9e2017-01-31 18:53:3193//
94// The Project contains the parsed manifest as well as a parsed lock file, if
tro31b5ef142017-01-31 20:06:1095// present. The import path is calculated as the remaining path segment
96// below Ctx.GOPATH/src.
Jordan Kragef7ce5152017-05-30 14:07:2397func (c *Ctx) LoadProject() (*Project, error) {
Ibrahim AshShohail71a26072017-06-14 22:30:2598 root, err := findProjectRoot(c.WorkingDir)
99 if err != nil {
100 return nil, err
101 }
Jess Frazelle35349ab2016-12-06 01:58:00102
Ibrahim AshShohail91660342017-06-14 23:36:01103 p := new(Project)
104
105 if err = p.SetRoot(root); err != nil {
Shintaro Kaneko691a2d62017-01-27 17:23:14106 return nil, err
Jess Frazelle35349ab2016-12-06 01:58:00107 }
108
Ibrahim AshShohailcd9bea62017-06-14 22:34:20109 c.GOPATH, err = c.DetectProjectGOPATH(p)
Brian Starke0047ea12017-02-21 18:22:38110 if err != nil {
Ibrahim AshShohail71a26072017-06-14 22:30:25111 return nil, err
Brian Starke0047ea12017-02-21 18:22:38112 }
113
Jordan Krageeb972b22017-05-31 01:47:18114 ip, err := c.ImportForAbs(p.AbsRoot)
Jess Frazelle48c329b2016-12-07 00:33:28115 if err != nil {
Jordan Krageeb972b22017-05-31 01:47:18116 return nil, errors.Wrap(err, "root project import")
Jess Frazelle35349ab2016-12-06 01:58:00117 }
Miguel Molina0acfd382017-01-27 20:39:07118 p.ImportRoot = gps.ProjectRoot(ip)
Jess Frazelle35349ab2016-12-06 01:58:00119
Miguel Molina0acfd382017-01-27 20:39:07120 mp := filepath.Join(p.AbsRoot, ManifestName)
Jess Frazelle35349ab2016-12-06 01:58:00121 mf, err := os.Open(mp)
122 if err != nil {
123 if os.IsNotExist(err) {
Andrew Gerrandf91353d2017-01-19 00:11:38124 // TODO: list possible solutions? (dep init, cd $project)
Carolyn Van Slyckcfc2c942017-03-22 15:58:50125 return nil, errors.Errorf("no %v found in project root %v", ManifestName, p.AbsRoot)
Jess Frazelle35349ab2016-12-06 01:58:00126 }
127 // Unable to read the manifest file
128 return nil, err
129 }
130 defer mf.Close()
131
Jordan Kragecbded812017-05-10 04:57:14132 var warns []error
133 p.Manifest, warns, err = readManifest(mf)
134 for _, warn := range warns {
Jordan Krage2fd37632017-05-28 16:49:26135 c.Err.Printf("dep: WARNING: %v\n", warn)
Jordan Kragecbded812017-05-10 04:57:14136 }
Jess Frazelle35349ab2016-12-06 01:58:00137 if err != nil {
Carolyn Van Slyckcfc2c942017-03-22 15:58:50138 return nil, errors.Errorf("error while parsing %s: %s", mp, err)
Jess Frazelle35349ab2016-12-06 01:58:00139 }
140
tro36ea5ba02017-01-31 04:44:03141 lp := filepath.Join(p.AbsRoot, LockName)
Jess Frazelle35349ab2016-12-06 01:58:00142 lf, err := os.Open(lp)
143 if err != nil {
144 if os.IsNotExist(err) {
145 // It's fine for the lock not to exist
146 return p, nil
147 }
148 // But if a lock does exist and we can't open it, that's a problem
Carolyn Van Slyckcfc2c942017-03-22 15:58:50149 return nil, errors.Errorf("could not open %s: %s", lp, err)
Jess Frazelle35349ab2016-12-06 01:58:00150 }
Jess Frazelle35349ab2016-12-06 01:58:00151 defer lf.Close()
Shintaro Kanekof3958be2017-01-27 17:34:22152
Miguel Molina0acfd382017-01-27 20:39:07153 p.Lock, err = readLock(lf)
Jess Frazelle35349ab2016-12-06 01:58:00154 if err != nil {
Carolyn Van Slyckcfc2c942017-03-22 15:58:50155 return nil, errors.Errorf("error while parsing %s: %s", lp, err)
Jess Frazelle35349ab2016-12-06 01:58:00156 }
157
158 return p, nil
159}
160
Ibrahim AshShohail138cebb2017-06-17 00:47:22161// DetectProjectGOPATH attempt to find the GOPATH containing the project.
162//
Ibrahim AshShohailf4b9f492017-06-17 00:54:25163// If p.AbsRoot is not a symlink and is within a GOPATH, the GOPATH containing p.AbsRoot is returned.
164// If p.AbsRoot is a symlink and is not within any known GOPATH, the GOPATH containing p.ResolvedAbsRoot is returned.
Ibrahim AshShohail138cebb2017-06-17 00:47:22165//
166// p.AbsRoot is assumed to be a symlink if it is not the same as p.ResolvedAbsRoot.
167//
168// DetectProjectGOPATH will return an error in the following cases:
169//
Ibrahim AshShohailf4b9f492017-06-17 00:54:25170// If p.AbsRoot is not a symlink and is not within any known GOPATH.
Ibrahim AshShohaila69d6ae2017-06-17 01:00:51171// If neither p.AbsRoot nor p.ResolvedAbsRoot are within a known GOPATH.
Ibrahim AshShohailf4b9f492017-06-17 00:54:25172// If both p.AbsRoot and p.ResolvedAbsRoot are within the same GOPATH.
173// If p.AbsRoot and p.ResolvedAbsRoot are each within a different GOPATH.
Ibrahim AshShohailcd9bea62017-06-14 22:34:20174func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) {
Ibrahim AshShohail138cebb2017-06-17 00:47:22175 if p.AbsRoot == "" || p.ResolvedAbsRoot == "" {
176 return "", errors.New("project AbsRoot and ResolvedAbsRoot must be set to detect GOPATH")
177 }
178
Ibrahim AshShohailcd9bea62017-06-14 22:34:20179 pGOPATH, perr := c.detectGOPATH(p.AbsRoot)
Ibrahim AshShohail91ac4d22017-06-03 19:15:21180
Ibrahim AshShohailcd9bea62017-06-14 22:34:20181 // If p.AbsRoot is a not symlink, attempt to detect GOPATH for p.AbsRoot only.
182 if p.AbsRoot == p.ResolvedAbsRoot {
183 return pGOPATH, perr
Ibrahim AshShohail91ac4d22017-06-03 19:15:21184 }
185
Ibrahim AshShohailcd9bea62017-06-14 22:34:20186 rGOPATH, rerr := c.detectGOPATH(p.ResolvedAbsRoot)
187
188 // If detectGOPATH() failed for both p.AbsRoot and p.ResolvedAbsRoot, then both are not within any known GOPATHs.
189 if perr != nil && rerr != nil {
190 return "", errors.Errorf("both %s and %s are not within any known GOPATH", p.AbsRoot, p.ResolvedAbsRoot)
Brian Starkeab79f832017-03-14 18:58:47191 }
192
Ibrahim AshShohailcd9bea62017-06-14 22:34:20193 // If pGOPATH equals rGOPATH, then both are within the same GOPATH.
194 if pGOPATH == rGOPATH {
195 return "", errors.Errorf("both %s and %s are in the same GOPATH %s", p.AbsRoot, p.ResolvedAbsRoot, pGOPATH)
Brian Starkeab79f832017-03-14 18:58:47196 }
197
Ibrahim AshShohailcd9bea62017-06-14 22:34:20198 if pGOPATH != "" && rGOPATH != "" {
199 return "", errors.Errorf("%s and %s are both in different GOPATHs", p.AbsRoot, p.ResolvedAbsRoot)
Brian Starkec922a912017-03-11 23:41:53200 }
201
Ibrahim AshShohailcd9bea62017-06-14 22:34:20202 // Otherwise, either the p.AbsRoot or p.ResolvedAbsRoot is within a GOPATH.
203 if pGOPATH == "" {
204 return rGOPATH, nil
Ibrahim AshShohail91ac4d22017-06-03 19:15:21205 }
206
Ibrahim AshShohailcd9bea62017-06-14 22:34:20207 return pGOPATH, nil
Ibrahim AshShohail91ac4d22017-06-03 19:15:21208}
209
Ibrahim AshShohailb7048cc2017-06-12 19:21:28210// detectGOPATH detects the GOPATH for a given path from ctx.GOPATHs.
Ibrahim AshShohail6a464ac2017-06-06 03:25:42211func (c *Ctx) detectGOPATH(path string) (string, error) {
Ibrahim AshShohailb7048cc2017-06-12 19:21:28212 for _, gp := range c.GOPATHs {
Ibrahim AshShohail6e2930f2017-06-20 22:29:39213 if fs.HasFilepathPrefix(path, gp) {
Ibrahim AshShohail91ac4d22017-06-03 19:15:21214 return gp, nil
Brian Starke4e3205d2017-04-06 20:46:03215 }
Brian Starkec922a912017-03-11 23:41:53216 }
Ibrahim AshShohail71a26072017-06-14 22:30:25217 return "", errors.Errorf("%s is not within a known GOPATH", path)
Brian Starkec922a912017-03-11 23:41:53218}
219
Jordan Krageeb972b22017-05-31 01:47:18220// ImportForAbs returns the import path for an absolute project path by trimming the
221// `$GOPATH/src/` prefix. Returns an error for paths equal to, or without this prefix.
222func (c *Ctx) ImportForAbs(path string) (string, error) {
Jess Frazelle48c329b2016-12-07 00:33:28223 srcprefix := filepath.Join(c.GOPATH, "src") + string(filepath.Separator)
Ibrahim AshShohailf87d6142017-05-10 16:21:00224 if fs.HasFilepathPrefix(path, srcprefix) {
Ibrahim AshShohail938475b2017-05-08 21:48:28225 if len(path) <= len(srcprefix) {
Ibrahim AshShohailcd9bea62017-06-14 22:34:20226 return "", errors.New("dep does not currently support using GOPATH/src as the project root")
Ibrahim AshShohaila11483a2017-05-08 21:35:18227 }
228
Jess Frazelle48c329b2016-12-07 00:33:28229 // filepath.ToSlash because we're dealing with an import path now,
230 // not an fs path
Yasuhiro Matsumoto9a14f122017-02-13 02:45:20231 return filepath.ToSlash(path[len(srcprefix):]), nil
Jess Frazelle35349ab2016-12-06 01:58:00232 }
Jess Frazelle48c329b2016-12-07 00:33:28233
Jordan Krageeb972b22017-05-31 01:47:18234 return "", errors.Errorf("%s not in GOPATH", path)
Jess Frazelle35349ab2016-12-06 01:58:00235}
236
Jordan Kragec89cb102017-05-31 15:52:27237// AbsForImport returns the absolute path for the project root
Jess Frazelle35349ab2016-12-06 01:58:00238// including the $GOPATH. This will not work with stdlib packages and the
239// package directory needs to exist.
Jordan Kragec89cb102017-05-31 15:52:27240func (c *Ctx) AbsForImport(path string) (string, error) {
Jess Frazelle48c329b2016-12-07 00:33:28241 posspath := filepath.Join(c.GOPATH, "src", path)
Ibrahim AshShohailf87d6142017-05-10 16:21:00242 dirOK, err := fs.IsDir(posspath)
Jess Frazelle48c329b2016-12-07 00:33:28243 if err != nil {
244 return "", errors.Wrapf(err, "checking if %s is a directory", posspath)
Jess Frazelle35349ab2016-12-06 01:58:00245 }
Jess Frazelle48c329b2016-12-07 00:33:28246 if !dirOK {
Carolyn Van Slyckcfc2c942017-03-22 15:58:50247 return "", errors.Errorf("%s does not exist", posspath)
Jess Frazelle48c329b2016-12-07 00:33:28248 }
Jess Frazelle48c329b2016-12-07 00:33:28249 return posspath, nil
Jess Frazelle35349ab2016-12-06 01:58:00250}