Jess Frazelle | 70b94ea | 2017-01-17 22:07:50 | [diff] [blame] | 1 | // 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 Molina | 0acfd38 | 2017-01-27 20:39:07 | [diff] [blame] | 5 | package dep |
Jess Frazelle | 229c947 | 2016-12-05 22:04:46 | [diff] [blame] | 6 | |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 7 | import ( |
Jordan Krage | cbded81 | 2017-05-10 04:57:14 | [diff] [blame] | 8 | "log" |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 9 | "os" |
| 10 | "path/filepath" |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 11 | "runtime" |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 12 | |
Ibrahim AshShohail | f87d614 | 2017-05-10 16:21:00 | [diff] [blame] | 13 | "github.com/golang/dep/internal/fs" |
Dave Cheney | 9ea8489 | 2017-05-10 04:13:22 | [diff] [blame] | 14 | "github.com/golang/dep/internal/gps" |
Kris Nova | 0561ca9 | 2017-04-25 17:13:14 | [diff] [blame] | 15 | "github.com/pkg/errors" |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 16 | ) |
Jess Frazelle | 229c947 | 2016-12-05 22:04:46 | [diff] [blame] | 17 | |
Ibrahim AshShohail | 138cebb | 2017-06-17 00:47:22 | [diff] [blame] | 18 | // 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 Molina | 0acfd38 | 2017-01-27 20:39:07 | [diff] [blame] | 36 | type Ctx struct { |
Jordan Krage | 2fd3763 | 2017-05-28 16:49:26 | [diff] [blame] | 37 | WorkingDir string // Where to execute. |
| 38 | GOPATH string // Selected Go path, containing WorkingDir. |
Ibrahim AshShohail | b7048cc | 2017-06-12 19:21:28 | [diff] [blame] | 39 | GOPATHs []string // Other Go paths. |
Jordan Krage | 2fd3763 | 2017-05-28 16:49:26 | [diff] [blame] | 40 | Out, Err *log.Logger // Required loggers. |
| 41 | Verbose bool // Enables more verbose logging. |
Jordan Krage | cbded81 | 2017-05-10 04:57:14 | [diff] [blame] | 42 | } |
| 43 | |
Jordan Krage | ac8ec85 | 2017-06-25 17:21:35 | [diff] [blame] | 44 | // 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 AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 46 | func (c *Ctx) SetPaths(wd string, GOPATHs ...string) error { |
| 47 | if wd == "" { |
| 48 | return errors.New("cannot set Ctx.WorkingDir to an empty path") |
Jess Frazelle | 229c947 | 2016-12-05 22:04:46 | [diff] [blame] | 49 | } |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 50 | c.WorkingDir = wd |
| 51 | |
| 52 | if len(GOPATHs) == 0 { |
Jordan Krage | 3fe0397 | 2017-06-22 12:51:47 | [diff] [blame] | 53 | GOPATH := os.Getenv("GOPATH") |
| 54 | if GOPATH == "" { |
| 55 | GOPATH = defaultGOPATH() |
| 56 | } |
| 57 | GOPATHs = filepath.SplitList(GOPATH) |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 58 | } |
Ibrahim AshShohail | 6e2930f | 2017-06-20 22:29:39 | [diff] [blame] | 59 | |
| 60 | c.GOPATHs = append(c.GOPATHs, GOPATHs...) |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 61 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 62 | return nil |
Jess Frazelle | 229c947 | 2016-12-05 22:04:46 | [diff] [blame] | 63 | } |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 64 | |
Ibrahim AshShohail | 5a3291a | 2017-06-14 23:58:25 | [diff] [blame] | 65 | // defaultGOPATH gets the default GOPATH that was added in 1.8 |
| 66 | // copied from go/build/build.go |
| 67 | func 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 Krage | e50a643 | 2017-05-06 13:23:31 | [diff] [blame] | 86 | func (c *Ctx) SourceManager() (*gps.SourceMgr, error) { |
| 87 | return gps.NewSourceManager(filepath.Join(c.GOPATH, "pkg", "dep")) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 88 | } |
| 89 | |
Jordan Krage | f7ce515 | 2017-05-30 14:07:23 | [diff] [blame] | 90 | // 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. |
tro3 | 6411f9e | 2017-01-31 18:53:31 | [diff] [blame] | 93 | // |
| 94 | // The Project contains the parsed manifest as well as a parsed lock file, if |
tro3 | 1b5ef14 | 2017-01-31 20:06:10 | [diff] [blame] | 95 | // present. The import path is calculated as the remaining path segment |
| 96 | // below Ctx.GOPATH/src. |
Jordan Krage | f7ce515 | 2017-05-30 14:07:23 | [diff] [blame] | 97 | func (c *Ctx) LoadProject() (*Project, error) { |
Ibrahim AshShohail | 71a2607 | 2017-06-14 22:30:25 | [diff] [blame] | 98 | root, err := findProjectRoot(c.WorkingDir) |
| 99 | if err != nil { |
| 100 | return nil, err |
| 101 | } |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 102 | |
Ibrahim AshShohail | 9166034 | 2017-06-14 23:36:01 | [diff] [blame] | 103 | p := new(Project) |
| 104 | |
| 105 | if err = p.SetRoot(root); err != nil { |
Shintaro Kaneko | 691a2d6 | 2017-01-27 17:23:14 | [diff] [blame] | 106 | return nil, err |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 107 | } |
| 108 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 109 | c.GOPATH, err = c.DetectProjectGOPATH(p) |
Brian Starke | 0047ea1 | 2017-02-21 18:22:38 | [diff] [blame] | 110 | if err != nil { |
Ibrahim AshShohail | 71a2607 | 2017-06-14 22:30:25 | [diff] [blame] | 111 | return nil, err |
Brian Starke | 0047ea1 | 2017-02-21 18:22:38 | [diff] [blame] | 112 | } |
| 113 | |
Jordan Krage | eb972b2 | 2017-05-31 01:47:18 | [diff] [blame] | 114 | ip, err := c.ImportForAbs(p.AbsRoot) |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 115 | if err != nil { |
Jordan Krage | eb972b2 | 2017-05-31 01:47:18 | [diff] [blame] | 116 | return nil, errors.Wrap(err, "root project import") |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 117 | } |
Miguel Molina | 0acfd38 | 2017-01-27 20:39:07 | [diff] [blame] | 118 | p.ImportRoot = gps.ProjectRoot(ip) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 119 | |
Miguel Molina | 0acfd38 | 2017-01-27 20:39:07 | [diff] [blame] | 120 | mp := filepath.Join(p.AbsRoot, ManifestName) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 121 | mf, err := os.Open(mp) |
| 122 | if err != nil { |
| 123 | if os.IsNotExist(err) { |
Andrew Gerrand | f91353d | 2017-01-19 00:11:38 | [diff] [blame] | 124 | // TODO: list possible solutions? (dep init, cd $project) |
Carolyn Van Slyck | cfc2c94 | 2017-03-22 15:58:50 | [diff] [blame] | 125 | return nil, errors.Errorf("no %v found in project root %v", ManifestName, p.AbsRoot) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 126 | } |
| 127 | // Unable to read the manifest file |
| 128 | return nil, err |
| 129 | } |
| 130 | defer mf.Close() |
| 131 | |
Jordan Krage | cbded81 | 2017-05-10 04:57:14 | [diff] [blame] | 132 | var warns []error |
| 133 | p.Manifest, warns, err = readManifest(mf) |
| 134 | for _, warn := range warns { |
Jordan Krage | 2fd3763 | 2017-05-28 16:49:26 | [diff] [blame] | 135 | c.Err.Printf("dep: WARNING: %v\n", warn) |
Jordan Krage | cbded81 | 2017-05-10 04:57:14 | [diff] [blame] | 136 | } |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 137 | if err != nil { |
Carolyn Van Slyck | cfc2c94 | 2017-03-22 15:58:50 | [diff] [blame] | 138 | return nil, errors.Errorf("error while parsing %s: %s", mp, err) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 139 | } |
| 140 | |
tro3 | 6ea5ba0 | 2017-01-31 04:44:03 | [diff] [blame] | 141 | lp := filepath.Join(p.AbsRoot, LockName) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 142 | 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 Slyck | cfc2c94 | 2017-03-22 15:58:50 | [diff] [blame] | 149 | return nil, errors.Errorf("could not open %s: %s", lp, err) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 150 | } |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 151 | defer lf.Close() |
Shintaro Kaneko | f3958be | 2017-01-27 17:34:22 | [diff] [blame] | 152 | |
Miguel Molina | 0acfd38 | 2017-01-27 20:39:07 | [diff] [blame] | 153 | p.Lock, err = readLock(lf) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 154 | if err != nil { |
Carolyn Van Slyck | cfc2c94 | 2017-03-22 15:58:50 | [diff] [blame] | 155 | return nil, errors.Errorf("error while parsing %s: %s", lp, err) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | return p, nil |
| 159 | } |
| 160 | |
Ibrahim AshShohail | 138cebb | 2017-06-17 00:47:22 | [diff] [blame] | 161 | // DetectProjectGOPATH attempt to find the GOPATH containing the project. |
| 162 | // |
Ibrahim AshShohail | f4b9f49 | 2017-06-17 00:54:25 | [diff] [blame] | 163 | // 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 AshShohail | 138cebb | 2017-06-17 00:47:22 | [diff] [blame] | 165 | // |
| 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 AshShohail | f4b9f49 | 2017-06-17 00:54:25 | [diff] [blame] | 170 | // If p.AbsRoot is not a symlink and is not within any known GOPATH. |
Ibrahim AshShohail | a69d6ae | 2017-06-17 01:00:51 | [diff] [blame] | 171 | // If neither p.AbsRoot nor p.ResolvedAbsRoot are within a known GOPATH. |
Ibrahim AshShohail | f4b9f49 | 2017-06-17 00:54:25 | [diff] [blame] | 172 | // 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 AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 174 | func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) { |
Ibrahim AshShohail | 138cebb | 2017-06-17 00:47:22 | [diff] [blame] | 175 | if p.AbsRoot == "" || p.ResolvedAbsRoot == "" { |
| 176 | return "", errors.New("project AbsRoot and ResolvedAbsRoot must be set to detect GOPATH") |
| 177 | } |
| 178 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 179 | pGOPATH, perr := c.detectGOPATH(p.AbsRoot) |
Ibrahim AshShohail | 91ac4d2 | 2017-06-03 19:15:21 | [diff] [blame] | 180 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 181 | // 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 AshShohail | 91ac4d2 | 2017-06-03 19:15:21 | [diff] [blame] | 184 | } |
| 185 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 186 | 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 Starke | ab79f83 | 2017-03-14 18:58:47 | [diff] [blame] | 191 | } |
| 192 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 193 | // 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 Starke | ab79f83 | 2017-03-14 18:58:47 | [diff] [blame] | 196 | } |
| 197 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 198 | if pGOPATH != "" && rGOPATH != "" { |
| 199 | return "", errors.Errorf("%s and %s are both in different GOPATHs", p.AbsRoot, p.ResolvedAbsRoot) |
Brian Starke | c922a91 | 2017-03-11 23:41:53 | [diff] [blame] | 200 | } |
| 201 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 202 | // Otherwise, either the p.AbsRoot or p.ResolvedAbsRoot is within a GOPATH. |
| 203 | if pGOPATH == "" { |
| 204 | return rGOPATH, nil |
Ibrahim AshShohail | 91ac4d2 | 2017-06-03 19:15:21 | [diff] [blame] | 205 | } |
| 206 | |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 207 | return pGOPATH, nil |
Ibrahim AshShohail | 91ac4d2 | 2017-06-03 19:15:21 | [diff] [blame] | 208 | } |
| 209 | |
Ibrahim AshShohail | b7048cc | 2017-06-12 19:21:28 | [diff] [blame] | 210 | // detectGOPATH detects the GOPATH for a given path from ctx.GOPATHs. |
Ibrahim AshShohail | 6a464ac | 2017-06-06 03:25:42 | [diff] [blame] | 211 | func (c *Ctx) detectGOPATH(path string) (string, error) { |
Ibrahim AshShohail | b7048cc | 2017-06-12 19:21:28 | [diff] [blame] | 212 | for _, gp := range c.GOPATHs { |
Ibrahim AshShohail | 6e2930f | 2017-06-20 22:29:39 | [diff] [blame] | 213 | if fs.HasFilepathPrefix(path, gp) { |
Ibrahim AshShohail | 91ac4d2 | 2017-06-03 19:15:21 | [diff] [blame] | 214 | return gp, nil |
Brian Starke | 4e3205d | 2017-04-06 20:46:03 | [diff] [blame] | 215 | } |
Brian Starke | c922a91 | 2017-03-11 23:41:53 | [diff] [blame] | 216 | } |
Ibrahim AshShohail | 71a2607 | 2017-06-14 22:30:25 | [diff] [blame] | 217 | return "", errors.Errorf("%s is not within a known GOPATH", path) |
Brian Starke | c922a91 | 2017-03-11 23:41:53 | [diff] [blame] | 218 | } |
| 219 | |
Jordan Krage | eb972b2 | 2017-05-31 01:47:18 | [diff] [blame] | 220 | // 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. |
| 222 | func (c *Ctx) ImportForAbs(path string) (string, error) { |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 223 | srcprefix := filepath.Join(c.GOPATH, "src") + string(filepath.Separator) |
Ibrahim AshShohail | f87d614 | 2017-05-10 16:21:00 | [diff] [blame] | 224 | if fs.HasFilepathPrefix(path, srcprefix) { |
Ibrahim AshShohail | 938475b | 2017-05-08 21:48:28 | [diff] [blame] | 225 | if len(path) <= len(srcprefix) { |
Ibrahim AshShohail | cd9bea6 | 2017-06-14 22:34:20 | [diff] [blame] | 226 | return "", errors.New("dep does not currently support using GOPATH/src as the project root") |
Ibrahim AshShohail | a11483a | 2017-05-08 21:35:18 | [diff] [blame] | 227 | } |
| 228 | |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 229 | // filepath.ToSlash because we're dealing with an import path now, |
| 230 | // not an fs path |
Yasuhiro Matsumoto | 9a14f12 | 2017-02-13 02:45:20 | [diff] [blame] | 231 | return filepath.ToSlash(path[len(srcprefix):]), nil |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 232 | } |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 233 | |
Jordan Krage | eb972b2 | 2017-05-31 01:47:18 | [diff] [blame] | 234 | return "", errors.Errorf("%s not in GOPATH", path) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 235 | } |
| 236 | |
Jordan Krage | c89cb10 | 2017-05-31 15:52:27 | [diff] [blame] | 237 | // AbsForImport returns the absolute path for the project root |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 238 | // including the $GOPATH. This will not work with stdlib packages and the |
| 239 | // package directory needs to exist. |
Jordan Krage | c89cb10 | 2017-05-31 15:52:27 | [diff] [blame] | 240 | func (c *Ctx) AbsForImport(path string) (string, error) { |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 241 | posspath := filepath.Join(c.GOPATH, "src", path) |
Ibrahim AshShohail | f87d614 | 2017-05-10 16:21:00 | [diff] [blame] | 242 | dirOK, err := fs.IsDir(posspath) |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 243 | if err != nil { |
| 244 | return "", errors.Wrapf(err, "checking if %s is a directory", posspath) |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 245 | } |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 246 | if !dirOK { |
Carolyn Van Slyck | cfc2c94 | 2017-03-22 15:58:50 | [diff] [blame] | 247 | return "", errors.Errorf("%s does not exist", posspath) |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 248 | } |
Jess Frazelle | 48c329b | 2016-12-07 00:33:28 | [diff] [blame] | 249 | return posspath, nil |
Jess Frazelle | 35349ab | 2016-12-06 01:58:00 | [diff] [blame] | 250 | } |