deps_test.go 11.2 KB
Newer Older
Russ Cox's avatar
Russ Cox committed
1 2 3 4 5 6 7
// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file exercises the import parser but also checks that
// some low-level packages do not have new dependencies added.

8
package build
Russ Cox's avatar
Russ Cox committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

import (
	"sort"
	"testing"
)

// pkgDeps defines the expected dependencies between packages in
// the Go source tree.  It is a statement of policy.
// Changes should not be made to this map without prior discussion.
//
// The map contains two kinds of entries:
// 1) Lower-case keys are standard import paths and list the
// allowed imports in that package.
// 2) Upper-case keys define aliases for package sets, which can then
// be used as dependencies by other rules.
//
// DO NOT CHANGE THIS DATA TO FIX BUILDS.
26
//
Russ Cox's avatar
Russ Cox committed
27 28 29 30 31
var pkgDeps = map[string][]string{
	// L0 is the lowest level, core, nearly unavoidable packages.
	"errors":      {},
	"io":          {"errors", "sync"},
	"runtime":     {"unsafe"},
Dmitriy Vyukov's avatar
Dmitriy Vyukov committed
32
	"sync":        {"sync/atomic", "unsafe"},
Russ Cox's avatar
Russ Cox committed
33 34 35 36 37 38 39 40 41 42 43 44
	"sync/atomic": {"unsafe"},
	"unsafe":      {},

	"L0": {
		"errors",
		"io",
		"runtime",
		"sync",
		"sync/atomic",
		"unsafe",
	},

Russ Cox's avatar
Russ Cox committed
45 46
	// L1 adds simple functions and strings processing,
	// but not Unicode tables.
Russ Cox's avatar
Russ Cox committed
47 48 49
	"math":          {"unsafe"},
	"math/cmplx":    {"math"},
	"math/rand":     {"L0", "math"},
50
	"sort":          {},
51
	"strconv":       {"L0", "unicode/utf8", "math"},
Russ Cox's avatar
Russ Cox committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65
	"unicode/utf16": {},
	"unicode/utf8":  {},

	"L1": {
		"L0",
		"math",
		"math/cmplx",
		"math/rand",
		"sort",
		"strconv",
		"unicode/utf16",
		"unicode/utf8",
	},

Russ Cox's avatar
Russ Cox committed
66 67 68 69 70 71
	// L2 adds Unicode and strings processing.
	"bufio":   {"L0", "unicode/utf8", "bytes"},
	"bytes":   {"L0", "unicode", "unicode/utf8"},
	"path":    {"L0", "unicode/utf8", "strings"},
	"strings": {"L0", "unicode", "unicode/utf8"},
	"unicode": {},
Russ Cox's avatar
Russ Cox committed
72 73 74

	"L2": {
		"L1",
Russ Cox's avatar
Russ Cox committed
75 76 77 78 79 80 81 82 83 84
		"bufio",
		"bytes",
		"path",
		"strings",
		"unicode",
	},

	// L3 adds reflection and some basic utility packages
	// and interface definitions, but nothing that makes
	// system calls.
Adam Langley's avatar
Adam Langley committed
85 86 87
	"crypto":          {"L2", "hash"},          // interfaces
	"crypto/cipher":   {"L2", "crypto/subtle"}, // interfaces
	"crypto/subtle":   {},
Russ Cox's avatar
Russ Cox committed
88 89 90 91 92 93 94 95 96 97 98 99 100 101
	"encoding/base32": {"L2"},
	"encoding/base64": {"L2"},
	"encoding/binary": {"L2", "reflect"},
	"hash":            {"L2"}, // interfaces
	"hash/adler32":    {"L2", "hash"},
	"hash/crc32":      {"L2", "hash"},
	"hash/crc64":      {"L2", "hash"},
	"hash/fnv":        {"L2", "hash"},
	"image":           {"L2", "image/color"}, // interfaces
	"image/color":     {"L2"},                // interfaces
	"reflect":         {"L2"},

	"L3": {
		"L2",
Russ Cox's avatar
Russ Cox committed
102 103
		"crypto",
		"crypto/cipher",
Adam Langley's avatar
Adam Langley committed
104
		"crypto/subtle",
Russ Cox's avatar
Russ Cox committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
		"encoding/base32",
		"encoding/base64",
		"encoding/binary",
		"hash",
		"hash/adler32",
		"hash/crc32",
		"hash/crc64",
		"hash/fnv",
		"image",
		"image/color",
		"reflect",
	},

	// End of linear dependency definitions.

	// Operating system access.
	"syscall":       {"L0", "unicode/utf16"},
	"time":          {"L0", "syscall"},
Russ Cox's avatar
Russ Cox committed
123
	"os":            {"L1", "os", "syscall", "time"},
124
	"path/filepath": {"L2", "os", "syscall"},
Russ Cox's avatar
Russ Cox committed
125 126 127
	"io/ioutil":     {"L2", "os", "path/filepath", "time"},
	"os/exec":       {"L2", "os", "syscall"},
	"os/signal":     {"L2", "os", "syscall"},
Russ Cox's avatar
Russ Cox committed
128 129 130 131 132 133 134 135 136 137 138

	// OS enables basic operating system functionality,
	// but not direct use of package syscall, nor os/signal.
	"OS": {
		"io/ioutil",
		"os",
		"os/exec",
		"path/filepath",
		"time",
	},

Russ Cox's avatar
Russ Cox committed
139 140 141 142 143 144 145
	// Formatted I/O: few dependencies (L1) but we must add reflect.
	"fmt": {"L1", "os", "reflect"},
	"log": {"L1", "os", "fmt", "time"},

	// Packages used by testing must be low-level (L2+fmt).
	"regexp":         {"L2", "regexp/syntax"},
	"regexp/syntax":  {"L2"},
146
	"runtime/debug":  {"L2", "fmt", "io/ioutil", "os", "time"},
Russ Cox's avatar
Russ Cox committed
147 148 149 150 151 152 153 154 155 156 157
	"runtime/pprof":  {"L2", "fmt", "text/tabwriter"},
	"text/tabwriter": {"L2"},

	"testing":        {"L2", "flag", "fmt", "os", "runtime/pprof", "time"},
	"testing/iotest": {"L2", "log"},
	"testing/quick":  {"L2", "flag", "fmt", "reflect"},

	// L4 is defined as L3+fmt+log+time, because in general once
	// you're using L3 packages, use of fmt, log, or time is not a big deal.
	"L4": {
		"L3",
Russ Cox's avatar
Russ Cox committed
158 159 160 161 162 163
		"fmt",
		"log",
		"time",
	},

	// Go parser.
Russ Cox's avatar
Russ Cox committed
164 165 166 167 168 169
	"go/ast":     {"L4", "OS", "go/scanner", "go/token"},
	"go/doc":     {"L4", "go/ast", "go/token", "regexp", "text/template"},
	"go/parser":  {"L4", "OS", "go/ast", "go/scanner", "go/token"},
	"go/printer": {"L4", "OS", "go/ast", "go/scanner", "go/token", "text/tabwriter"},
	"go/scanner": {"L4", "OS", "go/token"},
	"go/token":   {"L4"},
Russ Cox's avatar
Russ Cox committed
170 171 172 173 174 175 176 177 178 179 180

	"GOPARSER": {
		"go/ast",
		"go/doc",
		"go/parser",
		"go/printer",
		"go/scanner",
		"go/token",
	},

	// One of a kind.
181
	"archive/tar":         {"L4", "OS", "syscall"},
Russ Cox's avatar
Russ Cox committed
182 183 184 185 186 187 188 189 190 191 192 193 194
	"archive/zip":         {"L4", "OS", "compress/flate"},
	"compress/bzip2":      {"L4"},
	"compress/flate":      {"L4"},
	"compress/gzip":       {"L4", "compress/flate"},
	"compress/lzw":        {"L4"},
	"compress/zlib":       {"L4", "compress/flate"},
	"database/sql":        {"L4", "database/sql/driver"},
	"database/sql/driver": {"L4", "time"},
	"debug/dwarf":         {"L4"},
	"debug/elf":           {"L4", "OS", "debug/dwarf"},
	"debug/gosym":         {"L4"},
	"debug/macho":         {"L4", "OS", "debug/dwarf"},
	"debug/pe":            {"L4", "OS", "debug/dwarf"},
Russ Cox's avatar
Russ Cox committed
195
	"encoding":            {"L4"},
Russ Cox's avatar
Russ Cox committed
196 197 198
	"encoding/ascii85":    {"L4"},
	"encoding/asn1":       {"L4", "math/big"},
	"encoding/csv":        {"L4"},
Russ Cox's avatar
Russ Cox committed
199
	"encoding/gob":        {"L4", "OS", "encoding"},
Russ Cox's avatar
Russ Cox committed
200
	"encoding/hex":        {"L4"},
Russ Cox's avatar
Russ Cox committed
201
	"encoding/json":       {"L4", "encoding"},
Russ Cox's avatar
Russ Cox committed
202
	"encoding/pem":        {"L4"},
203
	"encoding/xml":        {"L4", "encoding"},
Russ Cox's avatar
Russ Cox committed
204 205 206 207
	"flag":                {"L4", "OS"},
	"go/build":            {"L4", "OS", "GOPARSER"},
	"html":                {"L4"},
	"image/draw":          {"L4"},
208
	"image/gif":           {"L4", "compress/lzw", "image/draw"},
Russ Cox's avatar
Russ Cox committed
209 210 211 212 213 214 215 216
	"image/jpeg":          {"L4"},
	"image/png":           {"L4", "compress/zlib"},
	"index/suffixarray":   {"L4", "regexp"},
	"math/big":            {"L4"},
	"mime":                {"L4", "OS", "syscall"},
	"net/url":             {"L4"},
	"text/scanner":        {"L4", "OS"},
	"text/template/parse": {"L4"},
Russ Cox's avatar
Russ Cox committed
217 218

	"html/template": {
Russ Cox's avatar
Russ Cox committed
219
		"L4", "OS", "encoding/json", "html", "text/template",
Russ Cox's avatar
Russ Cox committed
220 221 222
		"text/template/parse",
	},
	"text/template": {
Russ Cox's avatar
Russ Cox committed
223
		"L4", "OS", "net/url", "text/template/parse",
Russ Cox's avatar
Russ Cox committed
224 225 226 227 228 229 230 231 232 233
	},

	// Cgo.
	"runtime/cgo": {"L0", "C"},
	"CGO":         {"C", "runtime/cgo"},

	// Fake entry to satisfy the pseudo-import "C"
	// that shows up in programs that use cgo.
	"C": {},

Russ Cox's avatar
Russ Cox committed
234
	"os/user": {"L4", "CGO", "syscall"},
Russ Cox's avatar
Russ Cox committed
235 236

	// Basic networking.
Russ Cox's avatar
Russ Cox committed
237 238 239
	// Because net must be used by any package that wants to
	// do networking portably, it must have a small dependency set: just L1+basic os.
	"net": {"L1", "CGO", "os", "syscall", "time"},
Russ Cox's avatar
Russ Cox committed
240 241 242 243 244 245 246 247 248 249

	// NET enables use of basic network-related packages.
	"NET": {
		"net",
		"mime",
		"net/textproto",
		"net/url",
	},

	// Uses of networking.
Russ Cox's avatar
Russ Cox committed
250 251 252
	"log/syslog":    {"L4", "OS", "net"},
	"net/mail":      {"L4", "NET", "OS"},
	"net/textproto": {"L4", "OS", "net"},
Russ Cox's avatar
Russ Cox committed
253 254

	// Core crypto.
Russ Cox's avatar
Russ Cox committed
255 256
	"crypto/aes":    {"L3"},
	"crypto/des":    {"L3"},
Adam Langley's avatar
Adam Langley committed
257
	"crypto/hmac":   {"L3"},
Russ Cox's avatar
Russ Cox committed
258 259 260 261 262
	"crypto/md5":    {"L3"},
	"crypto/rc4":    {"L3"},
	"crypto/sha1":   {"L3"},
	"crypto/sha256": {"L3"},
	"crypto/sha512": {"L3"},
Russ Cox's avatar
Russ Cox committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

	"CRYPTO": {
		"crypto/aes",
		"crypto/des",
		"crypto/hmac",
		"crypto/md5",
		"crypto/rc4",
		"crypto/sha1",
		"crypto/sha256",
		"crypto/sha512",
	},

	// Random byte, number generation.
	// This would be part of core crypto except that it imports
	// math/big, which imports fmt.
Russ Cox's avatar
Russ Cox committed
278
	"crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall"},
Russ Cox's avatar
Russ Cox committed
279

Russ Cox's avatar
Russ Cox committed
280
	// Mathematical crypto: dependencies on fmt (L4) and math/big.
Russ Cox's avatar
Russ Cox committed
281
	// We could avoid some of the fmt, but math/big imports fmt anyway.
Russ Cox's avatar
Russ Cox committed
282 283 284 285
	"crypto/dsa":      {"L4", "CRYPTO", "math/big"},
	"crypto/ecdsa":    {"L4", "CRYPTO", "crypto/elliptic", "math/big"},
	"crypto/elliptic": {"L4", "CRYPTO", "math/big"},
	"crypto/rsa":      {"L4", "CRYPTO", "crypto/rand", "math/big"},
Russ Cox's avatar
Russ Cox committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299

	"CRYPTO-MATH": {
		"CRYPTO",
		"crypto/dsa",
		"crypto/ecdsa",
		"crypto/elliptic",
		"crypto/rand",
		"crypto/rsa",
		"encoding/asn1",
		"math/big",
	},

	// SSL/TLS.
	"crypto/tls": {
Russ Cox's avatar
Russ Cox committed
300
		"L4", "CRYPTO-MATH", "CGO", "OS",
Russ Cox's avatar
Russ Cox committed
301 302
		"crypto/x509", "encoding/pem", "net", "syscall",
	},
303 304
	"crypto/x509": {
		"L4", "CRYPTO-MATH", "OS", "CGO",
Adam Langley's avatar
Adam Langley committed
305
		"crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "syscall",
306
	},
Russ Cox's avatar
Russ Cox committed
307
	"crypto/x509/pkix": {"L4", "CRYPTO-MATH"},
Russ Cox's avatar
Russ Cox committed
308 309

	// Simple net+crypto-aware packages.
Russ Cox's avatar
Russ Cox committed
310 311
	"mime/multipart": {"L4", "OS", "mime", "crypto/rand", "net/textproto"},
	"net/smtp":       {"L4", "CRYPTO", "NET", "crypto/tls"},
Russ Cox's avatar
Russ Cox committed
312 313 314

	// HTTP, kingpin of dependencies.
	"net/http": {
Russ Cox's avatar
Russ Cox committed
315
		"L4", "NET", "OS",
Russ Cox's avatar
Russ Cox committed
316 317 318 319
		"compress/gzip", "crypto/tls", "mime/multipart", "runtime/debug",
	},

	// HTTP-using packages.
Russ Cox's avatar
Russ Cox committed
320 321 322 323 324 325 326 327
	"expvar":            {"L4", "OS", "encoding/json", "net/http"},
	"net/http/cgi":      {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"},
	"net/http/fcgi":     {"L4", "NET", "OS", "net/http", "net/http/cgi"},
	"net/http/httptest": {"L4", "NET", "OS", "crypto/tls", "flag", "net/http"},
	"net/http/httputil": {"L4", "NET", "OS", "net/http"},
	"net/http/pprof":    {"L4", "OS", "html/template", "net/http", "runtime/pprof"},
	"net/rpc":           {"L4", "NET", "encoding/gob", "net/http", "text/template"},
	"net/rpc/jsonrpc":   {"L4", "NET", "encoding/json", "net/rpc"},
Russ Cox's avatar
Russ Cox committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
}

// isMacro reports whether p is a package dependency macro
// (uppercase name).
func isMacro(p string) bool {
	return 'A' <= p[0] && p[0] <= 'Z'
}

func allowed(pkg string) map[string]bool {
	m := map[string]bool{}
	var allow func(string)
	allow = func(p string) {
		if m[p] {
			return
		}
		m[p] = true // set even for macros, to avoid loop on cycle

		// Upper-case names are macro-expanded.
		if isMacro(p) {
			for _, pp := range pkgDeps[p] {
				allow(pp)
			}
		}
	}
	for _, pp := range pkgDeps[pkg] {
		allow(pp)
	}
	return m
}

var bools = []bool{false, true}
var geese = []string{"darwin", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"}
var goarches = []string{"386", "amd64", "arm"}

362 363 364 365 366 367 368 369 370 371 372
type osPkg struct {
	goos, pkg string
}

// allowedErrors are the operating systems and packages known to contain errors
// (currently just "no Go source files")
var allowedErrors = map[osPkg]bool{
	osPkg{"windows", "log/syslog"}: true,
	osPkg{"plan9", "log/syslog"}:   true,
}

Russ Cox's avatar
Russ Cox committed
373 374 375 376 377 378 379 380
func TestDependencies(t *testing.T) {
	var all []string

	for k := range pkgDeps {
		all = append(all, k)
	}
	sort.Strings(all)

381
	ctxt := Default
Russ Cox's avatar
Russ Cox committed
382 383 384 385 386 387 388
	test := func(mustImport bool) {
		for _, pkg := range all {
			if isMacro(pkg) {
				continue
			}
			p, err := ctxt.Import(pkg, "", 0)
			if err != nil {
389 390 391
				if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
					continue
				}
Russ Cox's avatar
Russ Cox committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
				// Some of the combinations we try might not
				// be reasonable (like arm,plan9,cgo), so ignore
				// errors for the auto-generated combinations.
				if !mustImport {
					continue
				}
				t.Errorf("%s/%s/cgo=%v %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, err)
				continue
			}
			ok := allowed(pkg)
			var bad []string
			for _, imp := range p.Imports {
				if !ok[imp] {
					bad = append(bad, imp)
				}
			}
			if bad != nil {
				t.Errorf("%s/%s/cgo=%v unexpected dependency: %s imports %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, pkg, bad)
			}
		}
	}
	test(true)

	if testing.Short() {
		t.Logf("skipping other systems")
		return
	}

	for _, ctxt.GOOS = range geese {
		for _, ctxt.GOARCH = range goarches {
			for _, ctxt.CgoEnabled = range bools {
				test(false)
			}
		}
	}
}