Source file
src/go/build/deps_test.go
1
2
3
4
5
6
7
8 package build
9
10 import (
11 "bytes"
12 "fmt"
13 "go/token"
14 "internal/testenv"
15 "io/fs"
16 "os"
17 "path/filepath"
18 "runtime"
19 "sort"
20 "strings"
21 "testing"
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 var depsRules = `
71 # No dependencies allowed for any of these packages.
72 NONE
73 < container/list, container/ring,
74 internal/cfg, internal/cpu, internal/goexperiment,
75 internal/goversion, internal/nettrace,
76 unicode/utf8, unicode/utf16, unicode,
77 unsafe;
78
79 # These packages depend only on unsafe.
80 unsafe
81 < internal/abi;
82
83 # RUNTIME is the core runtime group of packages, all of them very light-weight.
84 internal/abi, internal/cpu, internal/goexperiment, unsafe
85 < internal/bytealg
86 < internal/itoa
87 < internal/unsafeheader
88 < runtime/internal/sys
89 < runtime/internal/atomic
90 < runtime/internal/math
91 < runtime
92 < sync/atomic
93 < internal/race
94 < sync
95 < internal/reflectlite
96 < errors
97 < internal/oserror, math/bits
98 < RUNTIME;
99
100 RUNTIME
101 < sort
102 < container/heap;
103
104 RUNTIME
105 < io;
106
107 syscall !< io;
108 reflect !< sort;
109
110 RUNTIME, unicode/utf8
111 < path;
112
113 unicode !< path;
114
115 # SYSCALL is RUNTIME plus the packages necessary for basic system calls.
116 RUNTIME, unicode/utf8, unicode/utf16
117 < internal/syscall/windows/sysdll, syscall/js
118 < syscall
119 < internal/syscall/unix, internal/syscall/windows, internal/syscall/windows/registry
120 < internal/syscall/execenv
121 < SYSCALL;
122
123 # TIME is SYSCALL plus the core packages about time, including context.
124 SYSCALL
125 < time/tzdata
126 < time
127 < context
128 < TIME;
129
130 TIME, io, path, sort
131 < io/fs;
132
133 # MATH is RUNTIME plus the basic math packages.
134 RUNTIME
135 < math
136 < MATH;
137
138 unicode !< math;
139
140 MATH
141 < math/cmplx;
142
143 MATH
144 < math/rand;
145
146 MATH
147 < runtime/metrics;
148
149 MATH, unicode/utf8
150 < strconv;
151
152 unicode !< strconv;
153
154 # STR is basic string and buffer manipulation.
155 RUNTIME, io, unicode/utf8, unicode/utf16, unicode
156 < bytes, strings
157 < bufio;
158
159 bufio, path, strconv
160 < STR;
161
162 # OS is basic OS access, including helpers (path/filepath, os/exec, etc).
163 # OS includes string routines, but those must be layered above package os.
164 # OS does not include reflection.
165 io/fs
166 < internal/testlog
167 < internal/poll
168 < os
169 < os/signal;
170
171 io/fs
172 < embed;
173
174 unicode, fmt !< os, os/signal;
175
176 os/signal, STR
177 < path/filepath
178 < io/ioutil, os/exec;
179
180 io/ioutil, os/exec, os/signal
181 < OS;
182
183 reflect !< OS;
184
185 OS
186 < golang.org/x/sys/cpu;
187
188 # FMT is OS (which includes string routines) plus reflect and fmt.
189 # It does not include package log, which should be avoided in core packages.
190 strconv, unicode
191 < reflect;
192
193 os, reflect
194 < internal/fmtsort
195 < fmt;
196
197 OS, fmt
198 < FMT;
199
200 log !< FMT;
201
202 OS, FMT
203 < internal/execabs;
204
205 OS, internal/execabs
206 < internal/goroot;
207
208 # Misc packages needing only FMT.
209 FMT
210 < flag,
211 html,
212 mime/quotedprintable,
213 net/internal/socktest,
214 net/url,
215 runtime/debug,
216 runtime/trace,
217 text/scanner,
218 text/tabwriter;
219
220 # encodings
221 # core ones do not use fmt.
222 io, strconv
223 < encoding;
224
225 encoding, reflect
226 < encoding/binary
227 < encoding/base32, encoding/base64;
228
229 fmt !< encoding/base32, encoding/base64;
230
231 FMT, encoding/base32, encoding/base64
232 < encoding/ascii85, encoding/csv, encoding/gob, encoding/hex,
233 encoding/json, encoding/pem, encoding/xml, mime;
234
235 # hashes
236 io
237 < hash
238 < hash/adler32, hash/crc32, hash/crc64, hash/fnv, hash/maphash;
239
240 # math/big
241 FMT, encoding/binary, math/rand
242 < math/big;
243
244 # compression
245 FMT, encoding/binary, hash/adler32, hash/crc32
246 < compress/bzip2, compress/flate, compress/lzw
247 < archive/zip, compress/gzip, compress/zlib;
248
249 # templates
250 FMT
251 < text/template/parse;
252
253 net/url, text/template/parse
254 < text/template
255 < internal/lazytemplate;
256
257 encoding/json, html, text/template
258 < html/template;
259
260 # regexp
261 FMT
262 < regexp/syntax
263 < regexp
264 < internal/lazyregexp;
265
266 # suffix array
267 encoding/binary, regexp
268 < index/suffixarray;
269
270 # executable parsing
271 FMT, encoding/binary, compress/zlib
272 < debug/dwarf
273 < debug/elf, debug/gosym, debug/macho, debug/pe, debug/plan9obj, internal/xcoff
274 < DEBUG;
275
276 # go parser and friends.
277 FMT
278 < go/token
279 < go/scanner
280 < go/ast
281 < go/internal/typeparams
282 < go/parser;
283
284 FMT
285 < go/build/constraint;
286
287 go/build/constraint, go/parser, text/tabwriter
288 < go/printer
289 < go/format;
290
291 go/parser, internal/lazyregexp, text/template
292 < go/doc;
293
294 math/big, go/token
295 < go/constant;
296
297 container/heap, go/constant, go/parser, regexp
298 < go/types;
299
300 FMT, internal/goexperiment
301 < internal/buildcfg;
302
303 go/build/constraint, go/doc, go/parser, internal/buildcfg, internal/goroot, internal/goversion
304 < go/build;
305
306 DEBUG, go/build, go/types, text/scanner
307 < go/internal/gcimporter, go/internal/gccgoimporter, go/internal/srcimporter
308 < go/importer;
309
310 # databases
311 FMT
312 < database/sql/internal
313 < database/sql/driver
314 < database/sql;
315
316 # images
317 FMT, compress/lzw, compress/zlib
318 < image/color
319 < image, image/color/palette
320 < image/internal/imageutil
321 < image/draw
322 < image/gif, image/jpeg, image/png;
323
324 # cgo, delayed as long as possible.
325 # If you add a dependency on CGO, you must add the package
326 # to cgoPackages in cmd/dist/test.go as well.
327 RUNTIME
328 < C
329 < runtime/cgo
330 < CGO
331 < runtime/race, runtime/msan;
332
333 # Bulk of the standard library must not use cgo.
334 # The prohibition stops at net and os/user.
335 C !< fmt, go/types, CRYPTO-MATH;
336
337 CGO, OS
338 < plugin;
339
340 CGO, FMT
341 < os/user
342 < archive/tar;
343
344 sync
345 < internal/singleflight;
346
347 os
348 < golang.org/x/net/dns/dnsmessage,
349 golang.org/x/net/lif,
350 golang.org/x/net/route;
351
352 # net is unavoidable when doing any networking,
353 # so large dependencies must be kept out.
354 # This is a long-looking list but most of these
355 # are small with few dependencies.
356 CGO,
357 golang.org/x/net/dns/dnsmessage,
358 golang.org/x/net/lif,
359 golang.org/x/net/route,
360 internal/nettrace,
361 internal/poll,
362 internal/singleflight,
363 internal/race,
364 os
365 < net;
366
367 fmt, unicode !< net;
368 math/rand !< net; # net uses runtime instead
369
370 # NET is net plus net-helper packages.
371 FMT, net
372 < net/textproto;
373
374 mime, net/textproto, net/url
375 < NET;
376
377 # logging - most packages should not import; http and up is allowed
378 FMT
379 < log;
380
381 log !< crypto/tls, database/sql, go/importer, testing;
382
383 FMT, log, net
384 < log/syslog;
385
386 NET, log
387 < net/mail;
388
389 # CRYPTO is core crypto algorithms - no cgo, fmt, net.
390 # Unfortunately, stuck with reflect via encoding/binary.
391 encoding/binary, golang.org/x/sys/cpu, hash
392 < crypto
393 < crypto/subtle
394 < crypto/internal/subtle
395 < crypto/elliptic/internal/fiat
396 < crypto/ed25519/internal/edwards25519/field
397 < crypto/ed25519/internal/edwards25519
398 < crypto/cipher
399 < crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4,
400 crypto/sha1, crypto/sha256, crypto/sha512
401 < CRYPTO;
402
403 CGO, fmt, net !< CRYPTO;
404
405 # CRYPTO-MATH is core bignum-based crypto - no cgo, net; fmt now ok.
406 CRYPTO, FMT, math/big
407 < crypto/rand
408 < crypto/internal/randutil
409 < crypto/ed25519
410 < encoding/asn1
411 < golang.org/x/crypto/cryptobyte/asn1
412 < golang.org/x/crypto/cryptobyte
413 < golang.org/x/crypto/curve25519
414 < crypto/dsa, crypto/elliptic, crypto/rsa
415 < crypto/ecdsa
416 < CRYPTO-MATH;
417
418 CGO, net !< CRYPTO-MATH;
419
420 # TLS, Prince of Dependencies.
421 CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem
422 < golang.org/x/crypto/internal/subtle
423 < golang.org/x/crypto/chacha20
424 < golang.org/x/crypto/poly1305
425 < golang.org/x/crypto/chacha20poly1305
426 < golang.org/x/crypto/hkdf
427 < crypto/x509/internal/macos
428 < crypto/x509/pkix
429 < crypto/x509
430 < crypto/tls;
431
432 # crypto-aware packages
433
434 NET, crypto/rand, mime/quotedprintable
435 < mime/multipart;
436
437 crypto/tls
438 < net/smtp;
439
440 # HTTP, King of Dependencies.
441
442 FMT
443 < golang.org/x/net/http2/hpack
444 < net/http/internal, net/http/internal/ascii, net/http/internal/testcert;
445
446 FMT, NET, container/list, encoding/binary, log
447 < golang.org/x/text/transform
448 < golang.org/x/text/unicode/norm
449 < golang.org/x/text/unicode/bidi
450 < golang.org/x/text/secure/bidirule
451 < golang.org/x/net/idna
452 < golang.org/x/net/http/httpguts, golang.org/x/net/http/httpproxy;
453
454 NET, crypto/tls
455 < net/http/httptrace;
456
457 compress/gzip,
458 golang.org/x/net/http/httpguts,
459 golang.org/x/net/http/httpproxy,
460 golang.org/x/net/http2/hpack,
461 net/http/internal,
462 net/http/internal/ascii,
463 net/http/internal/testcert,
464 net/http/httptrace,
465 mime/multipart,
466 log
467 < net/http;
468
469 # HTTP-aware packages
470
471 encoding/json, net/http
472 < expvar;
473
474 net/http, net/http/internal/ascii
475 < net/http/cookiejar, net/http/httputil;
476
477 net/http, flag
478 < net/http/httptest;
479
480 net/http, regexp
481 < net/http/cgi
482 < net/http/fcgi;
483
484 # Profiling
485 FMT, compress/gzip, encoding/binary, text/tabwriter
486 < runtime/pprof;
487
488 OS, compress/gzip, regexp
489 < internal/profile;
490
491 html, internal/profile, net/http, runtime/pprof, runtime/trace
492 < net/http/pprof;
493
494 # RPC
495 encoding/gob, encoding/json, go/token, html/template, net/http
496 < net/rpc
497 < net/rpc/jsonrpc;
498
499 # System Information
500 internal/cpu, sync
501 < internal/sysinfo;
502
503 # Test-only
504 log
505 < testing/iotest
506 < testing/fstest;
507
508 FMT, flag, math/rand
509 < testing/quick;
510
511 FMT, flag, runtime/debug, runtime/trace, internal/sysinfo, math/rand
512 < testing;
513
514 internal/testlog, runtime/pprof, regexp
515 < testing/internal/testdeps;
516
517 OS, flag, testing, internal/cfg
518 < internal/testenv;
519
520 OS, encoding/base64
521 < internal/obscuretestdata;
522
523 CGO, OS, fmt
524 < os/signal/internal/pty;
525
526 NET, testing, math/rand
527 < golang.org/x/net/nettest;
528
529 FMT, container/heap, math/rand
530 < internal/trace;
531 `
532
533
534 func listStdPkgs(goroot string) ([]string, error) {
535
536 var pkgs []string
537
538 src := filepath.Join(goroot, "src") + string(filepath.Separator)
539 walkFn := func(path string, d fs.DirEntry, err error) error {
540 if err != nil || !d.IsDir() || path == src {
541 return nil
542 }
543
544 base := filepath.Base(path)
545 if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_") || base == "testdata" {
546 return filepath.SkipDir
547 }
548
549 name := filepath.ToSlash(path[len(src):])
550 if name == "builtin" || name == "cmd" {
551 return filepath.SkipDir
552 }
553
554 pkgs = append(pkgs, strings.TrimPrefix(name, "vendor/"))
555 return nil
556 }
557 if err := filepath.WalkDir(src, walkFn); err != nil {
558 return nil, err
559 }
560 return pkgs, nil
561 }
562
563 func TestDependencies(t *testing.T) {
564 if !testenv.HasSrc() {
565
566
567 t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
568 }
569
570 ctxt := Default
571 all, err := listStdPkgs(ctxt.GOROOT)
572 if err != nil {
573 t.Fatal(err)
574 }
575 sort.Strings(all)
576
577 sawImport := map[string]map[string]bool{}
578 policy := depsPolicy(t)
579
580 for _, pkg := range all {
581 imports, err := findImports(pkg)
582 if err != nil {
583 t.Error(err)
584 continue
585 }
586 if sawImport[pkg] == nil {
587 sawImport[pkg] = map[string]bool{}
588 }
589 ok := policy[pkg]
590 var bad []string
591 for _, imp := range imports {
592 sawImport[pkg][imp] = true
593 if !ok[imp] {
594 bad = append(bad, imp)
595 }
596 }
597 if bad != nil {
598 t.Errorf("unexpected dependency: %s imports %v", pkg, bad)
599 }
600 }
601
602
603
604 var depPath func(string, string) string
605 depPath = func(from, to string) string {
606 if sawImport[from][to] {
607 return from + " => " + to
608 }
609 for pkg := range sawImport[from] {
610 if p := depPath(pkg, to); p != "" {
611 return from + " => " + p
612 }
613 }
614 return ""
615 }
616 }
617
618 var buildIgnore = []byte("\n// +build ignore")
619
620 func findImports(pkg string) ([]string, error) {
621 vpkg := pkg
622 if strings.HasPrefix(pkg, "golang.org") {
623 vpkg = "vendor/" + pkg
624 }
625 dir := filepath.Join(Default.GOROOT, "src", vpkg)
626 files, err := os.ReadDir(dir)
627 if err != nil {
628 return nil, err
629 }
630 var imports []string
631 var haveImport = map[string]bool{}
632 fset := token.NewFileSet()
633 for _, file := range files {
634 name := file.Name()
635 if name == "slice_go14.go" || name == "slice_go18.go" {
636
637 continue
638 }
639 if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
640 continue
641 }
642 info := fileInfo{
643 name: filepath.Join(dir, name),
644 fset: fset,
645 }
646 f, err := os.Open(info.name)
647 if err != nil {
648 return nil, err
649 }
650 err = readGoInfo(f, &info)
651 f.Close()
652 if err != nil {
653 return nil, fmt.Errorf("reading %v: %v", name, err)
654 }
655 if bytes.Contains(info.header, buildIgnore) {
656 continue
657 }
658 for _, imp := range info.imports {
659 path := imp.path
660 if !haveImport[path] {
661 haveImport[path] = true
662 imports = append(imports, path)
663 }
664 }
665 }
666 sort.Strings(imports)
667 return imports, nil
668 }
669
670
671 func depsPolicy(t *testing.T) map[string]map[string]bool {
672 allowed := map[string]map[string]bool{"NONE": {}}
673 disallowed := [][2][]string{}
674
675 parseDepsRules(t, func(deps []string, op string, users []string) {
676 if op == "!<" {
677 disallowed = append(disallowed, [2][]string{deps, users})
678 return
679 }
680 for _, u := range users {
681 if allowed[u] != nil {
682 t.Errorf("multiple deps lists for %s", u)
683 }
684 allowed[u] = make(map[string]bool)
685 for _, d := range deps {
686 if allowed[d] == nil {
687 t.Errorf("use of %s before its deps list", d)
688 }
689 allowed[u][d] = true
690 }
691 }
692 })
693
694
695 for _, deps := range allowed {
696 for d := range deps {
697 if allowed[d] == nil {
698 t.Errorf("missing deps list for %s", d)
699 }
700 }
701 }
702
703
704 for k := range allowed {
705 for i := range allowed {
706 for j := range allowed {
707 if i != k && k != j && allowed[i][k] && allowed[k][j] {
708 if i == j {
709
710
711
712 t.Errorf("deps policy cycle: %s < %s < %s", j, k, i)
713 }
714 allowed[i][j] = true
715 }
716 }
717 }
718 }
719
720
721 for _, bad := range disallowed {
722 deps, users := bad[0], bad[1]
723 for _, d := range deps {
724 for _, u := range users {
725 if allowed[u][d] {
726 t.Errorf("deps policy incorrect: assertion failed: %s !< %s", d, u)
727 }
728 }
729 }
730 }
731
732 if t.Failed() {
733 t.FailNow()
734 }
735
736 return allowed
737 }
738
739
740
741
742 func parseDepsRules(t *testing.T, save func(deps []string, op string, users []string)) {
743 p := &depsParser{t: t, lineno: 1, text: depsRules}
744
745 var prev []string
746 var op string
747 for {
748 list, tok := p.nextList()
749 if tok == "" {
750 if prev == nil {
751 break
752 }
753 p.syntaxError("unexpected EOF")
754 }
755 if prev != nil {
756 save(prev, op, list)
757 }
758 prev = list
759 if tok == ";" {
760 prev = nil
761 op = ""
762 continue
763 }
764 if tok != "<" && tok != "!<" {
765 p.syntaxError("missing <")
766 }
767 op = tok
768 }
769 }
770
771
772 type depsParser struct {
773 t *testing.T
774 lineno int
775 lastWord string
776 text string
777 }
778
779
780 func (p *depsParser) syntaxError(msg string) {
781 p.t.Fatalf("deps:%d: syntax error: %s near %s", p.lineno, msg, p.lastWord)
782 }
783
784
785 func (p *depsParser) nextList() (list []string, token string) {
786 for {
787 tok := p.nextToken()
788 switch tok {
789 case "":
790 if len(list) == 0 {
791 return nil, ""
792 }
793 fallthrough
794 case ",", "<", "!<", ";":
795 p.syntaxError("bad list syntax")
796 }
797 list = append(list, tok)
798
799 tok = p.nextToken()
800 if tok != "," {
801 return list, tok
802 }
803 }
804 }
805
806
807
808 func (p *depsParser) nextToken() string {
809 for {
810 if p.text == "" {
811 return ""
812 }
813 switch p.text[0] {
814 case ';', ',', '<':
815 t := p.text[:1]
816 p.text = p.text[1:]
817 return t
818
819 case '!':
820 if len(p.text) < 2 || p.text[1] != '<' {
821 p.syntaxError("unexpected token !")
822 }
823 p.text = p.text[2:]
824 return "!<"
825
826 case '#':
827 i := strings.Index(p.text, "\n")
828 if i < 0 {
829 i = len(p.text)
830 }
831 p.text = p.text[i:]
832 continue
833
834 case '\n':
835 p.lineno++
836 fallthrough
837 case ' ', '\t':
838 p.text = p.text[1:]
839 continue
840
841 default:
842 i := strings.IndexAny(p.text, "!;,<#\n \t")
843 if i < 0 {
844 i = len(p.text)
845 }
846 t := p.text[:i]
847 p.text = p.text[i:]
848 p.lastWord = t
849 return t
850 }
851 }
852 }
853
854
855
856 func TestStdlibLowercase(t *testing.T) {
857 if !testenv.HasSrc() {
858 t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
859 }
860
861 ctxt := Default
862 all, err := listStdPkgs(ctxt.GOROOT)
863 if err != nil {
864 t.Fatal(err)
865 }
866
867 for _, pkgname := range all {
868 if strings.ToLower(pkgname) != pkgname {
869 t.Errorf("package %q should not use upper-case path", pkgname)
870 }
871 }
872 }
873
874
875 func TestFindImports(t *testing.T) {
876 imports, err := findImports("go/build")
877 if err != nil {
878 t.Fatal(err)
879 }
880 t.Logf("go/build imports %q", imports)
881 want := []string{"bytes", "os", "path/filepath", "strings"}
882 wantLoop:
883 for _, w := range want {
884 for _, imp := range imports {
885 if imp == w {
886 continue wantLoop
887 }
888 }
889 t.Errorf("expected to find %q in import list", w)
890 }
891 }
892
View as plain text