This repository has been archived by the owner on Mar 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 148
/
package.go
267 lines (234 loc) · 7.76 KB
/
package.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
package gb
import (
"fmt"
"go/build"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/pkg/errors"
)
// Package represents a resolved package from the Project with respect to the Context.
type Package struct {
*Context
*build.Package
TestScope bool
NotStale bool // this package _and_ all its dependencies are not stale
Main bool // is this a command
Imports []*Package
}
// newPackage creates a resolved Package without setting pkg.Stale.
func (ctx *Context) newPackage(p *build.Package) (*Package, error) {
pkg := &Package{
Context: ctx,
Package: p,
}
for _, i := range p.Imports {
dep, ok := ctx.pkgs[i]
if !ok {
return nil, errors.Errorf("newPackage(%q): could not locate dependant package %q ", p.Name, i)
}
pkg.Imports = append(pkg.Imports, dep)
}
return pkg, nil
}
func (p *Package) String() string {
return fmt.Sprintf("%s {Name:%s, Dir:%s}", p.ImportPath, p.Name, p.Dir)
}
func (p *Package) includePaths() []string {
includes := p.Context.includePaths()
switch {
case p.TestScope && p.Main:
ip := filepath.Dir(filepath.FromSlash(p.ImportPath))
return append([]string{filepath.Join(p.Context.Workdir(), ip, "_test")}, includes...)
case p.TestScope:
ip := strings.TrimSuffix(filepath.FromSlash(p.ImportPath), "_test")
return append([]string{filepath.Join(p.Context.Workdir(), ip, "_test")}, includes...)
default:
return includes
}
}
// complete indicates if this is a pure Go package
func (p *Package) complete() bool {
// If we're giving the compiler the entire package (no C etc files), tell it that,
// so that it can give good error messages about forward declarations.
// Exceptions: a few standard packages have forward declarations for
// pieces supplied behind-the-scenes by package runtime.
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Goroot {
switch p.ImportPath {
case "bytes", "internal/poll", "net", "os", "runtime/pprof", "sync", "syscall", "time":
extFiles++
}
}
return extFiles == 0
}
// Binfile returns the destination of the compiled target of this command.
func (pkg *Package) Binfile() string {
target := filepath.Join(pkg.bindir(), pkg.binname())
// if this is a cross compile or GOOS/GOARCH are both defined or there are build tags, add ctxString.
if pkg.isCrossCompile() || (os.Getenv("GOOS") != "" && os.Getenv("GOARCH") != "") {
target += "-" + pkg.ctxString()
} else if len(pkg.buildtags) > 0 {
target += "-" + strings.Join(pkg.buildtags, "-")
}
if pkg.gotargetos == "windows" {
target += ".exe"
}
return target
}
func (pkg *Package) bindir() string {
switch {
case pkg.TestScope:
return filepath.Join(pkg.Context.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test")
default:
return pkg.Context.bindir()
}
}
func (pkg *Package) Workdir() string {
path := filepath.FromSlash(pkg.ImportPath)
dir := filepath.Dir(path)
switch {
case pkg.TestScope:
ip := strings.TrimSuffix(path, "_test")
return filepath.Join(pkg.Context.Workdir(), ip, "_test", dir)
default:
return filepath.Join(pkg.Context.Workdir(), dir)
}
}
// objfile returns the name of the object file for this package
func (pkg *Package) objfile() string {
return filepath.Join(pkg.Workdir(), pkg.objname())
}
func (pkg *Package) objname() string {
return pkg.pkgname() + ".a"
}
func (pkg *Package) pkgname() string {
// TODO(dfc) use pkg path instead?
return filepath.Base(filepath.FromSlash(pkg.ImportPath))
}
func (pkg *Package) binname() string {
if !pkg.Main {
panic("binname called with non main package: " + pkg.ImportPath)
}
// TODO(dfc) use pkg path instead?
return filepath.Base(filepath.FromSlash(pkg.ImportPath))
}
// installpath returns the distination to cache this package's compiled .a file.
// pkgpath and installpath differ in that the former returns the location where you will find
// a previously cached .a file, the latter returns the location where an installed file
// will be placed.
//
// The difference is subtle. pkgpath must deal with the possibility that the file is from the
// standard library and is previously compiled. installpath will always return a path for the
// project's pkg/ directory in the case that the stdlib is out of date, or not compiled for
// a specific architecture.
func (pkg *Package) installpath() string {
if pkg.TestScope {
panic("installpath called with test scope")
}
return filepath.Join(pkg.Pkgdir(), filepath.FromSlash(pkg.ImportPath)+".a")
}
// pkgpath returns the destination for object cached for this Package.
func (pkg *Package) pkgpath() string {
importpath := filepath.FromSlash(pkg.ImportPath) + ".a"
switch {
case pkg.isCrossCompile():
return filepath.Join(pkg.Pkgdir(), importpath)
case pkg.Goroot && pkg.race:
// race enabled standard lib
return filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch+"_race", importpath)
case pkg.Goroot:
// standard lib
return filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch, importpath)
default:
return filepath.Join(pkg.Pkgdir(), importpath)
}
}
// isStale returns true if the source pkg is considered to be stale with
// respect to its installed version.
func (pkg *Package) isStale() bool {
switch pkg.ImportPath {
case "C", "unsafe":
// synthetic packages are never stale
return false
}
if !pkg.Goroot && pkg.Force {
return true
}
// tests are always stale, they are never installed
if pkg.TestScope {
return true
}
// Package is stale if completely unbuilt.
var built time.Time
if fi, err := os.Stat(pkg.pkgpath()); err == nil {
built = fi.ModTime()
}
if built.IsZero() {
pkg.debug("%s is missing", pkg.pkgpath())
return true
}
olderThan := func(file string) bool {
fi, err := os.Stat(file)
return err != nil || fi.ModTime().After(built)
}
newerThan := func(file string) bool {
fi, err := os.Stat(file)
return err != nil || fi.ModTime().Before(built)
}
// As a courtesy to developers installing new versions of the compiler
// frequently, define that packages are stale if they are
// older than the compiler, and commands if they are older than
// the linker. This heuristic will not work if the binaries are
// back-dated, as some binary distributions may do, but it does handle
// a very common case.
if !pkg.Goroot {
if olderThan(pkg.tc.compiler()) {
pkg.debug("%s is older than %s", pkg.pkgpath(), pkg.tc.compiler())
return true
}
if pkg.Main && olderThan(pkg.tc.linker()) {
pkg.debug("%s is older than %s", pkg.pkgpath(), pkg.tc.compiler())
return true
}
}
if pkg.Goroot && !pkg.isCrossCompile() {
// if this is a standard lib package, and we are not cross compiling
// then assume the package is up to date. This also works around
// golang/go#13769.
return false
}
// Package is stale if a dependency is newer.
for _, p := range pkg.Imports {
if p.ImportPath == "C" || p.ImportPath == "unsafe" {
continue // ignore stale imports of synthetic packages
}
if olderThan(p.pkgpath()) {
pkg.debug("%s is older than %s", pkg.pkgpath(), p.pkgpath())
return true
}
}
// if the main package is up to date but _newer_ than the binary (which
// could have been removed), then consider it stale.
if pkg.Main && newerThan(pkg.Binfile()) {
pkg.debug("%s is newer than %s", pkg.pkgpath(), pkg.Binfile())
return true
}
srcs := stringList(pkg.GoFiles, pkg.CFiles, pkg.CXXFiles, pkg.MFiles, pkg.HFiles, pkg.SFiles, pkg.CgoFiles, pkg.SysoFiles, pkg.SwigFiles, pkg.SwigCXXFiles)
for _, src := range srcs {
if olderThan(filepath.Join(pkg.Dir, src)) {
pkg.debug("%s is older than %s", pkg.pkgpath(), filepath.Join(pkg.Dir, src))
return true
}
}
return false
}
func stringList(args ...[]string) []string {
var l []string
for _, arg := range args {
l = append(l, arg...)
}
return l
}