@@ -17,7 +17,8 @@ import (
17
17
18
18
// Ctx defines the supporting context of the tool.
19
19
type Ctx struct {
20
- GOPATH string // Go path
20
+ GOPATH string // Selected Go path
21
+ GOPATHS []string // Other Go paths
21
22
}
22
23
23
24
// NewContext creates a struct with the project's GOPATH. It assumes
@@ -26,18 +27,28 @@ func NewContext() (*Ctx, error) {
26
27
// this way we get the default GOPATH that was added in 1.8
27
28
buildContext := build .Default
28
29
wd , err := os .Getwd ()
30
+
29
31
if err != nil {
30
32
return nil , errors .Wrap (err , "getting work directory" )
31
33
}
32
34
wd = filepath .FromSlash (wd )
35
+ ctx := & Ctx {}
36
+
33
37
for _ , gp := range filepath .SplitList (buildContext .GOPATH ) {
34
38
gp = filepath .FromSlash (gp )
39
+
35
40
if filepath .HasPrefix (wd , gp ) {
36
- return & Ctx { GOPATH : gp }, nil
41
+ ctx . GOPATH = gp
37
42
}
43
+
44
+ ctx .GOPATHS = append (ctx .GOPATHS , gp )
38
45
}
39
46
40
- return nil , errors .New ("project not in a GOPATH" )
47
+ if ctx .GOPATH == "" {
48
+ return nil , errors .New ("project not in a GOPATH" )
49
+ }
50
+
51
+ return ctx , nil
41
52
}
42
53
43
54
func (c * Ctx ) SourceManager () (* gps.SourceMgr , error ) {
@@ -74,6 +85,13 @@ func (c *Ctx) LoadProject(path string) (*Project, error) {
74
85
return nil , err
75
86
}
76
87
88
+ // The path may lie within a symlinked directory, resolve the path
89
+ // before moving forward
90
+ p .AbsRoot , err = c .resolveProjectRoot (p .AbsRoot )
91
+ if err != nil {
92
+ return nil , errors .Wrapf (err , "resolve project root" )
93
+ }
94
+
77
95
ip , err := c .SplitAbsoluteProjectRoot (p .AbsRoot )
78
96
if err != nil {
79
97
return nil , errors .Wrap (err , "split absolute project root" )
@@ -117,6 +135,43 @@ func (c *Ctx) LoadProject(path string) (*Project, error) {
117
135
return p , nil
118
136
}
119
137
138
+ // resolveProjectRoot evaluates the root directory and does the following:
139
+ //
140
+ // If the passed path is a symlink outside GOPATH to a directory within a
141
+ // GOPATH, the resolved full real path is returned.
142
+ //
143
+ // If the passed path is a symlink within a GOPATH, we return an error.
144
+ //
145
+ // If the passed path isn't a symlink at all, we just pass through.
146
+ func (c * Ctx ) resolveProjectRoot (path string ) (string , error ) {
147
+ // Determine if this path is a Symlink
148
+ l , err := os .Lstat (path )
149
+ if err != nil {
150
+ return "" , errors .Wrap (err , "resolveProjectRoot" )
151
+ }
152
+
153
+ // Pass through if not
154
+ if l .Mode ()& os .ModeSymlink == 0 {
155
+ return path , nil
156
+ }
157
+
158
+ // Resolve path
159
+ resolved , err := filepath .EvalSymlinks (path )
160
+ if err != nil {
161
+ return "" , errors .Wrap (err , "resolveProjectRoot" )
162
+ }
163
+
164
+ // Determine if the symlink is within any of the GOPATHs, in which case we're not
165
+ // sure how to resolve it.
166
+ for _ , gp := range c .GOPATHS {
167
+ if filepath .HasPrefix (path , gp ) {
168
+ return "" , errors .Errorf ("'%s' is linked to another path within a GOPATH (%s)" , path , gp )
169
+ }
170
+ }
171
+
172
+ return resolved , nil
173
+ }
174
+
120
175
// SplitAbsoluteProjectRoot takes an absolute path and compares it against declared
121
176
// GOPATH(s) to determine what portion of the input path should be treated as an
122
177
// import path - as a project root.
0 commit comments