1// Copyright 2011 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 5package build 6 7import ( 8 "flag" 9 "internal/testenv" 10 "io" 11 "os" 12 "path/filepath" 13 "reflect" 14 "runtime" 15 "strings" 16 "testing" 17) 18 19func TestMain(m *testing.M) { 20 flag.Parse() 21 if goTool, err := testenv.GoTool(); err == nil { 22 os.Setenv("PATH", filepath.Dir(goTool)+string(os.PathListSeparator)+os.Getenv("PATH")) 23 } 24 os.Exit(m.Run()) 25} 26 27func TestMatch(t *testing.T) { 28 ctxt := Default 29 what := "default" 30 match := func(tag string, want map[string]bool) { 31 t.Helper() 32 m := make(map[string]bool) 33 if !ctxt.matchAuto(tag, m) { 34 t.Errorf("%s context should match %s, does not", what, tag) 35 } 36 if !reflect.DeepEqual(m, want) { 37 t.Errorf("%s tags = %v, want %v", tag, m, want) 38 } 39 } 40 nomatch := func(tag string, want map[string]bool) { 41 t.Helper() 42 m := make(map[string]bool) 43 if ctxt.matchAuto(tag, m) { 44 t.Errorf("%s context should NOT match %s, does", what, tag) 45 } 46 if !reflect.DeepEqual(m, want) { 47 t.Errorf("%s tags = %v, want %v", tag, m, want) 48 } 49 } 50 51 match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true}) 52 match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) 53 nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) 54 55 what = "modified" 56 ctxt.BuildTags = []string{"foo"} 57 match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true}) 58 match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) 59 nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) 60 match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true}) 61 nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true}) 62} 63 64func TestDotSlashImport(t *testing.T) { 65 p, err := ImportDir("testdata/other", 0) 66 if err != nil { 67 t.Fatal(err) 68 } 69 if len(p.Imports) != 1 || p.Imports[0] != "./file" { 70 t.Fatalf("testdata/other: Imports=%v, want [./file]", p.Imports) 71 } 72 73 p1, err := Import("./file", "testdata/other", 0) 74 if err != nil { 75 t.Fatal(err) 76 } 77 if p1.Name != "file" { 78 t.Fatalf("./file: Name=%q, want %q", p1.Name, "file") 79 } 80 dir := filepath.Clean("testdata/other/file") // Clean to use \ on Windows 81 if p1.Dir != dir { 82 t.Fatalf("./file: Dir=%q, want %q", p1.Name, dir) 83 } 84} 85 86func TestEmptyImport(t *testing.T) { 87 p, err := Import("", Default.GOROOT, FindOnly) 88 if err == nil { 89 t.Fatal(`Import("") returned nil error.`) 90 } 91 if p == nil { 92 t.Fatal(`Import("") returned nil package.`) 93 } 94 if p.ImportPath != "" { 95 t.Fatalf("ImportPath=%q, want %q.", p.ImportPath, "") 96 } 97} 98 99func TestEmptyFolderImport(t *testing.T) { 100 _, err := Import(".", "testdata/empty", 0) 101 if _, ok := err.(*NoGoError); !ok { 102 t.Fatal(`Import("testdata/empty") did not return NoGoError.`) 103 } 104} 105 106func TestMultiplePackageImport(t *testing.T) { 107 pkg, err := Import(".", "testdata/multi", 0) 108 109 mpe, ok := err.(*MultiplePackageError) 110 if !ok { 111 t.Fatal(`Import("testdata/multi") did not return MultiplePackageError.`) 112 } 113 want := &MultiplePackageError{ 114 Dir: filepath.FromSlash("testdata/multi"), 115 Packages: []string{"main", "test_package"}, 116 Files: []string{"file.go", "file_appengine.go"}, 117 } 118 if !reflect.DeepEqual(mpe, want) { 119 t.Errorf("err = %#v; want %#v", mpe, want) 120 } 121 122 // TODO(#45999): Since the name is ambiguous, pkg.Name should be left empty. 123 if wantName := "main"; pkg.Name != wantName { 124 t.Errorf("pkg.Name = %q; want %q", pkg.Name, wantName) 125 } 126 127 if wantGoFiles := []string{"file.go", "file_appengine.go"}; !reflect.DeepEqual(pkg.GoFiles, wantGoFiles) { 128 t.Errorf("pkg.GoFiles = %q; want %q", pkg.GoFiles, wantGoFiles) 129 } 130 131 if wantInvalidFiles := []string{"file_appengine.go"}; !reflect.DeepEqual(pkg.InvalidGoFiles, wantInvalidFiles) { 132 t.Errorf("pkg.InvalidGoFiles = %q; want %q", pkg.InvalidGoFiles, wantInvalidFiles) 133 } 134} 135 136func TestLocalDirectory(t *testing.T) { 137 if runtime.GOOS == "ios" { 138 t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH) 139 } 140 141 cwd, err := os.Getwd() 142 if err != nil { 143 t.Fatal(err) 144 } 145 146 p, err := ImportDir(cwd, 0) 147 if err != nil { 148 t.Fatal(err) 149 } 150 if p.ImportPath != "go/build" { 151 t.Fatalf("ImportPath=%q, want %q", p.ImportPath, "go/build") 152 } 153} 154 155var shouldBuildTests = []struct { 156 name string 157 content string 158 tags map[string]bool 159 binaryOnly bool 160 shouldBuild bool 161 err error 162}{ 163 { 164 name: "Yes", 165 content: "// +build yes\n\n" + 166 "package main\n", 167 tags: map[string]bool{"yes": true}, 168 shouldBuild: true, 169 }, 170 { 171 name: "Yes2", 172 content: "//go:build yes\n" + 173 "package main\n", 174 tags: map[string]bool{"yes": true}, 175 shouldBuild: true, 176 }, 177 { 178 name: "Or", 179 content: "// +build no yes\n\n" + 180 "package main\n", 181 tags: map[string]bool{"yes": true, "no": true}, 182 shouldBuild: true, 183 }, 184 { 185 name: "Or2", 186 content: "//go:build no || yes\n" + 187 "package main\n", 188 tags: map[string]bool{"yes": true, "no": true}, 189 shouldBuild: true, 190 }, 191 { 192 name: "And", 193 content: "// +build no,yes\n\n" + 194 "package main\n", 195 tags: map[string]bool{"yes": true, "no": true}, 196 shouldBuild: false, 197 }, 198 { 199 name: "And2", 200 content: "//go:build no && yes\n" + 201 "package main\n", 202 tags: map[string]bool{"yes": true, "no": true}, 203 shouldBuild: false, 204 }, 205 { 206 name: "Cgo", 207 content: "// +build cgo\n\n" + 208 "// Copyright The Go Authors.\n\n" + 209 "// This package implements parsing of tags like\n" + 210 "// +build tag1\n" + 211 "package build", 212 tags: map[string]bool{"cgo": true}, 213 shouldBuild: false, 214 }, 215 { 216 name: "Cgo2", 217 content: "//go:build cgo\n" + 218 "// Copyright The Go Authors.\n\n" + 219 "// This package implements parsing of tags like\n" + 220 "// +build tag1\n" + 221 "package build", 222 tags: map[string]bool{"cgo": true}, 223 shouldBuild: false, 224 }, 225 { 226 name: "AfterPackage", 227 content: "// Copyright The Go Authors.\n\n" + 228 "package build\n\n" + 229 "// shouldBuild checks tags given by lines of the form\n" + 230 "// +build tag\n" + 231 "//go:build tag\n" + 232 "func shouldBuild(content []byte)\n", 233 tags: map[string]bool{}, 234 shouldBuild: true, 235 }, 236 { 237 name: "TooClose", 238 content: "// +build yes\n" + 239 "package main\n", 240 tags: map[string]bool{}, 241 shouldBuild: true, 242 }, 243 { 244 name: "TooClose2", 245 content: "//go:build yes\n" + 246 "package main\n", 247 tags: map[string]bool{"yes": true}, 248 shouldBuild: true, 249 }, 250 { 251 name: "TooCloseNo", 252 content: "// +build no\n" + 253 "package main\n", 254 tags: map[string]bool{}, 255 shouldBuild: true, 256 }, 257 { 258 name: "TooCloseNo2", 259 content: "//go:build no\n" + 260 "package main\n", 261 tags: map[string]bool{"no": true}, 262 shouldBuild: false, 263 }, 264 { 265 name: "BinaryOnly", 266 content: "//go:binary-only-package\n" + 267 "// +build yes\n" + 268 "package main\n", 269 tags: map[string]bool{}, 270 binaryOnly: true, 271 shouldBuild: true, 272 }, 273 { 274 name: "BinaryOnly2", 275 content: "//go:binary-only-package\n" + 276 "//go:build no\n" + 277 "package main\n", 278 tags: map[string]bool{"no": true}, 279 binaryOnly: true, 280 shouldBuild: false, 281 }, 282 { 283 name: "ValidGoBuild", 284 content: "// +build yes\n\n" + 285 "//go:build no\n" + 286 "package main\n", 287 tags: map[string]bool{"no": true}, 288 shouldBuild: false, 289 }, 290 { 291 name: "MissingBuild2", 292 content: "/* */\n" + 293 "// +build yes\n\n" + 294 "//go:build no\n" + 295 "package main\n", 296 tags: map[string]bool{"no": true}, 297 shouldBuild: false, 298 }, 299 { 300 name: "Comment1", 301 content: "/*\n" + 302 "//go:build no\n" + 303 "*/\n\n" + 304 "package main\n", 305 tags: map[string]bool{}, 306 shouldBuild: true, 307 }, 308 { 309 name: "Comment2", 310 content: "/*\n" + 311 "text\n" + 312 "*/\n\n" + 313 "//go:build no\n" + 314 "package main\n", 315 tags: map[string]bool{"no": true}, 316 shouldBuild: false, 317 }, 318 { 319 name: "Comment3", 320 content: "/*/*/ /* hi *//* \n" + 321 "text\n" + 322 "*/\n\n" + 323 "//go:build no\n" + 324 "package main\n", 325 tags: map[string]bool{"no": true}, 326 shouldBuild: false, 327 }, 328 { 329 name: "Comment4", 330 content: "/**///go:build no\n" + 331 "package main\n", 332 tags: map[string]bool{}, 333 shouldBuild: true, 334 }, 335 { 336 name: "Comment5", 337 content: "/**/\n" + 338 "//go:build no\n" + 339 "package main\n", 340 tags: map[string]bool{"no": true}, 341 shouldBuild: false, 342 }, 343} 344 345func TestShouldBuild(t *testing.T) { 346 for _, tt := range shouldBuildTests { 347 t.Run(tt.name, func(t *testing.T) { 348 ctx := &Context{BuildTags: []string{"yes"}} 349 tags := map[string]bool{} 350 shouldBuild, binaryOnly, err := ctx.shouldBuild([]byte(tt.content), tags) 351 if shouldBuild != tt.shouldBuild || binaryOnly != tt.binaryOnly || !reflect.DeepEqual(tags, tt.tags) || err != tt.err { 352 t.Errorf("mismatch:\n"+ 353 "have shouldBuild=%v, binaryOnly=%v, tags=%v, err=%v\n"+ 354 "want shouldBuild=%v, binaryOnly=%v, tags=%v, err=%v", 355 shouldBuild, binaryOnly, tags, err, 356 tt.shouldBuild, tt.binaryOnly, tt.tags, tt.err) 357 } 358 }) 359 } 360} 361 362func TestGoodOSArchFile(t *testing.T) { 363 ctx := &Context{BuildTags: []string{"linux"}, GOOS: "darwin"} 364 m := map[string]bool{} 365 want := map[string]bool{"linux": true} 366 if !ctx.goodOSArchFile("hello_linux.go", m) { 367 t.Errorf("goodOSArchFile(hello_linux.go) = false, want true") 368 } 369 if !reflect.DeepEqual(m, want) { 370 t.Errorf("goodOSArchFile(hello_linux.go) tags = %v, want %v", m, want) 371 } 372} 373 374type readNopCloser struct { 375 io.Reader 376} 377 378func (r readNopCloser) Close() error { 379 return nil 380} 381 382var ( 383 ctxtP9 = Context{GOARCH: "arm", GOOS: "plan9"} 384 ctxtAndroid = Context{GOARCH: "arm", GOOS: "android"} 385) 386 387var matchFileTests = []struct { 388 ctxt Context 389 name string 390 data string 391 match bool 392}{ 393 {ctxtP9, "foo_arm.go", "", true}, 394 {ctxtP9, "foo1_arm.go", "// +build linux\n\npackage main\n", false}, 395 {ctxtP9, "foo_darwin.go", "", false}, 396 {ctxtP9, "foo.go", "", true}, 397 {ctxtP9, "foo1.go", "// +build linux\n\npackage main\n", false}, 398 {ctxtP9, "foo.badsuffix", "", false}, 399 {ctxtAndroid, "foo_linux.go", "", true}, 400 {ctxtAndroid, "foo_android.go", "", true}, 401 {ctxtAndroid, "foo_plan9.go", "", false}, 402 {ctxtAndroid, "android.go", "", true}, 403 {ctxtAndroid, "plan9.go", "", true}, 404 {ctxtAndroid, "plan9_test.go", "", true}, 405 {ctxtAndroid, "arm.s", "", true}, 406 {ctxtAndroid, "amd64.s", "", true}, 407} 408 409func TestMatchFile(t *testing.T) { 410 for _, tt := range matchFileTests { 411 ctxt := tt.ctxt 412 ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) { 413 if path != "x+"+tt.name { 414 t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name) 415 } 416 return &readNopCloser{strings.NewReader(tt.data)}, nil 417 } 418 ctxt.JoinPath = func(elem ...string) string { 419 return strings.Join(elem, "+") 420 } 421 match, err := ctxt.MatchFile("x", tt.name) 422 if match != tt.match || err != nil { 423 t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match) 424 } 425 } 426} 427 428func TestImportCmd(t *testing.T) { 429 if runtime.GOOS == "ios" { 430 t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH) 431 } 432 433 p, err := Import("cmd/internal/objfile", "", 0) 434 if err != nil { 435 t.Fatal(err) 436 } 437 if !strings.HasSuffix(filepath.ToSlash(p.Dir), "src/cmd/internal/objfile") { 438 t.Fatalf("Import cmd/internal/objfile returned Dir=%q, want %q", filepath.ToSlash(p.Dir), ".../src/cmd/internal/objfile") 439 } 440} 441 442var ( 443 expandSrcDirPath = filepath.Join(string(filepath.Separator)+"projects", "src", "add") 444) 445 446var expandSrcDirTests = []struct { 447 input, expected string 448}{ 449 {"-L ${SRCDIR}/libs -ladd", "-L /projects/src/add/libs -ladd"}, 450 {"${SRCDIR}/add_linux_386.a -pthread -lstdc++", "/projects/src/add/add_linux_386.a -pthread -lstdc++"}, 451 {"Nothing to expand here!", "Nothing to expand here!"}, 452 {"$", "$"}, 453 {"$$", "$$"}, 454 {"${", "${"}, 455 {"$}", "$}"}, 456 {"$FOO ${BAR}", "$FOO ${BAR}"}, 457 {"Find me the $SRCDIRECTORY.", "Find me the $SRCDIRECTORY."}, 458 {"$SRCDIR is missing braces", "$SRCDIR is missing braces"}, 459} 460 461func TestExpandSrcDir(t *testing.T) { 462 for _, test := range expandSrcDirTests { 463 output, _ := expandSrcDir(test.input, expandSrcDirPath) 464 if output != test.expected { 465 t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected) 466 } else { 467 t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath) 468 } 469 } 470} 471 472func TestShellSafety(t *testing.T) { 473 tests := []struct { 474 input, srcdir, expected string 475 result bool 476 }{ 477 {"-I${SRCDIR}/../include", "/projects/src/issue 11868", "-I/projects/src/issue 11868/../include", true}, 478 {"-I${SRCDIR}", "~wtf$@%^", "-I~wtf$@%^", true}, 479 {"-X${SRCDIR}/1,${SRCDIR}/2", "/projects/src/issue 11868", "-X/projects/src/issue 11868/1,/projects/src/issue 11868/2", true}, 480 {"-I/tmp -I/tmp", "/tmp2", "-I/tmp -I/tmp", true}, 481 {"-I/tmp", "/tmp/[0]", "-I/tmp", true}, 482 {"-I${SRCDIR}/dir", "/tmp/[0]", "-I/tmp/[0]/dir", false}, 483 {"-I${SRCDIR}/dir", "/tmp/go go", "-I/tmp/go go/dir", true}, 484 {"-I${SRCDIR}/dir dir", "/tmp/go", "-I/tmp/go/dir dir", true}, 485 } 486 for _, test := range tests { 487 output, ok := expandSrcDir(test.input, test.srcdir) 488 if ok != test.result { 489 t.Errorf("Expected %t while %q expands to %q with SRCDIR=%q; got %t", test.result, test.input, output, test.srcdir, ok) 490 } 491 if output != test.expected { 492 t.Errorf("Expected %q while %q expands with SRCDIR=%q; got %q", test.expected, test.input, test.srcdir, output) 493 } 494 } 495} 496 497// Want to get a "cannot find package" error when directory for package does not exist. 498// There should be valid partial information in the returned non-nil *Package. 499func TestImportDirNotExist(t *testing.T) { 500 testenv.MustHaveGoBuild(t) // really must just have source 501 ctxt := Default 502 503 emptyDir := t.TempDir() 504 505 ctxt.GOPATH = emptyDir 506 ctxt.Dir = emptyDir 507 508 tests := []struct { 509 label string 510 path, srcDir string 511 mode ImportMode 512 }{ 513 {"Import(full, 0)", "go/build/doesnotexist", "", 0}, 514 {"Import(local, 0)", "./doesnotexist", filepath.Join(ctxt.GOROOT, "src/go/build"), 0}, 515 {"Import(full, FindOnly)", "go/build/doesnotexist", "", FindOnly}, 516 {"Import(local, FindOnly)", "./doesnotexist", filepath.Join(ctxt.GOROOT, "src/go/build"), FindOnly}, 517 } 518 519 defer os.Setenv("GO111MODULE", os.Getenv("GO111MODULE")) 520 521 for _, GO111MODULE := range []string{"off", "on"} { 522 t.Run("GO111MODULE="+GO111MODULE, func(t *testing.T) { 523 os.Setenv("GO111MODULE", GO111MODULE) 524 525 for _, test := range tests { 526 p, err := ctxt.Import(test.path, test.srcDir, test.mode) 527 528 errOk := (err != nil && strings.HasPrefix(err.Error(), "cannot find package")) 529 wantErr := `"cannot find package" error` 530 if test.srcDir == "" { 531 if err != nil && strings.Contains(err.Error(), "is not in GOROOT") { 532 errOk = true 533 } 534 wantErr = `"cannot find package" or "is not in GOROOT" error` 535 } 536 if !errOk { 537 t.Errorf("%s got error: %q, want %s", test.label, err, wantErr) 538 } 539 // If an error occurs, build.Import is documented to return 540 // a non-nil *Package containing partial information. 541 if p == nil { 542 t.Fatalf(`%s got nil p, want non-nil *Package`, test.label) 543 } 544 // Verify partial information in p. 545 if p.ImportPath != "go/build/doesnotexist" { 546 t.Errorf(`%s got p.ImportPath: %q, want "go/build/doesnotexist"`, test.label, p.ImportPath) 547 } 548 } 549 }) 550 } 551} 552 553func TestImportVendor(t *testing.T) { 554 testenv.MustHaveGoBuild(t) // really must just have source 555 556 t.Setenv("GO111MODULE", "off") 557 558 ctxt := Default 559 wd, err := os.Getwd() 560 if err != nil { 561 t.Fatal(err) 562 } 563 ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor") 564 p, err := ctxt.Import("c/d", filepath.Join(ctxt.GOPATH, "src/a/b"), 0) 565 if err != nil { 566 t.Fatalf("cannot find vendored c/d from testdata src/a/b directory: %v", err) 567 } 568 want := "a/vendor/c/d" 569 if p.ImportPath != want { 570 t.Fatalf("Import succeeded but found %q, want %q", p.ImportPath, want) 571 } 572} 573 574func TestImportVendorFailure(t *testing.T) { 575 testenv.MustHaveGoBuild(t) // really must just have source 576 577 t.Setenv("GO111MODULE", "off") 578 579 ctxt := Default 580 wd, err := os.Getwd() 581 if err != nil { 582 t.Fatal(err) 583 } 584 ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor") 585 p, err := ctxt.Import("x.com/y/z", filepath.Join(ctxt.GOPATH, "src/a/b"), 0) 586 if err == nil { 587 t.Fatalf("found made-up package x.com/y/z in %s", p.Dir) 588 } 589 590 e := err.Error() 591 if !strings.Contains(e, " (vendor tree)") { 592 t.Fatalf("error on failed import does not mention GOROOT/src/vendor directory:\n%s", e) 593 } 594} 595 596func TestImportVendorParentFailure(t *testing.T) { 597 testenv.MustHaveGoBuild(t) // really must just have source 598 599 t.Setenv("GO111MODULE", "off") 600 601 ctxt := Default 602 wd, err := os.Getwd() 603 if err != nil { 604 t.Fatal(err) 605 } 606 ctxt.GOPATH = filepath.Join(wd, "testdata/withvendor") 607 // This import should fail because the vendor/c directory has no source code. 608 p, err := ctxt.Import("c", filepath.Join(ctxt.GOPATH, "src/a/b"), 0) 609 if err == nil { 610 t.Fatalf("found empty parent in %s", p.Dir) 611 } 612 if p != nil && p.Dir != "" { 613 t.Fatalf("decided to use %s", p.Dir) 614 } 615 e := err.Error() 616 if !strings.Contains(e, " (vendor tree)") { 617 t.Fatalf("error on failed import does not mention GOROOT/src/vendor directory:\n%s", e) 618 } 619} 620 621// Check that a package is loaded in module mode if GO111MODULE=on, even when 622// no go.mod file is present. It should fail to resolve packages outside std. 623// Verifies golang.org/issue/34669. 624func TestImportPackageOutsideModule(t *testing.T) { 625 testenv.MustHaveGoBuild(t) 626 627 // Disable module fetching for this test so that 'go list' fails quickly 628 // without trying to find the latest version of a module. 629 t.Setenv("GOPROXY", "off") 630 631 // Create a GOPATH in a temporary directory. We don't use testdata 632 // because it's in GOROOT, which interferes with the module heuristic. 633 gopath := t.TempDir() 634 if err := os.MkdirAll(filepath.Join(gopath, "src/example.com/p"), 0777); err != nil { 635 t.Fatal(err) 636 } 637 if err := os.WriteFile(filepath.Join(gopath, "src/example.com/p/p.go"), []byte("package p"), 0666); err != nil { 638 t.Fatal(err) 639 } 640 641 t.Setenv("GO111MODULE", "on") 642 t.Setenv("GOPATH", gopath) 643 ctxt := Default 644 ctxt.GOPATH = gopath 645 ctxt.Dir = filepath.Join(gopath, "src/example.com/p") 646 647 want := "go.mod file not found in current directory or any parent directory" 648 if _, err := ctxt.Import("example.com/p", gopath, FindOnly); err == nil { 649 t.Fatal("importing package when no go.mod is present succeeded unexpectedly") 650 } else if errStr := err.Error(); !strings.Contains(errStr, want) { 651 t.Fatalf("error when importing package when no go.mod is present: got %q; want %q", errStr, want) 652 } else { 653 t.Logf(`ctxt.Import("example.com/p", _, FindOnly): %v`, err) 654 } 655} 656 657func TestImportDirTarget(t *testing.T) { 658 testenv.MustHaveGoBuild(t) // really must just have source 659 ctxt := Default 660 ctxt.GOPATH = "" 661 p, err := ctxt.ImportDir(filepath.Join(ctxt.GOROOT, "src/path"), 0) 662 if err != nil { 663 t.Fatal(err) 664 } 665 if p.PkgTargetRoot == "" || p.PkgObj == "" { 666 t.Errorf("p.PkgTargetRoot == %q, p.PkgObj == %q, want non-empty", p.PkgTargetRoot, p.PkgObj) 667 } 668} 669 670// TestIssue23594 prevents go/build from regressing and populating Package.Doc 671// from comments in test files. 672func TestIssue23594(t *testing.T) { 673 // Package testdata/doc contains regular and external test files 674 // with comments attached to their package declarations. The names of the files 675 // ensure that we see the comments from the test files first. 676 p, err := ImportDir("testdata/doc", 0) 677 if err != nil { 678 t.Fatalf("could not import testdata: %v", err) 679 } 680 681 if p.Doc != "Correct" { 682 t.Fatalf("incorrectly set .Doc to %q", p.Doc) 683 } 684} 685 686// TestMissingImportErrorRepetition checks that when an unknown package is 687// imported, the package path is only shown once in the error. 688// Verifies golang.org/issue/34752. 689func TestMissingImportErrorRepetition(t *testing.T) { 690 testenv.MustHaveGoBuild(t) // need 'go list' internally 691 tmp := t.TempDir() 692 if err := os.WriteFile(filepath.Join(tmp, "go.mod"), []byte("module m"), 0666); err != nil { 693 t.Fatal(err) 694 } 695 t.Setenv("GO111MODULE", "on") 696 t.Setenv("GOPROXY", "off") 697 t.Setenv("GONOPROXY", "none") 698 699 ctxt := Default 700 ctxt.Dir = tmp 701 702 pkgPath := "example.com/hello" 703 _, err := ctxt.Import(pkgPath, tmp, FindOnly) 704 if err == nil { 705 t.Fatal("unexpected success") 706 } 707 708 // Don't count the package path with a URL like https://...?go-get=1. 709 // See golang.org/issue/35986. 710 errStr := strings.ReplaceAll(err.Error(), "://"+pkgPath+"?go-get=1", "://...?go-get=1") 711 712 // Also don't count instances in suggested "go get" or similar commands 713 // (see https://golang.org/issue/41576). The suggested command typically 714 // follows a semicolon. 715 errStr, _, _ = strings.Cut(errStr, ";") 716 717 if n := strings.Count(errStr, pkgPath); n != 1 { 718 t.Fatalf("package path %q appears in error %d times; should appear once\nerror: %v", pkgPath, n, err) 719 } 720} 721 722// TestCgoImportsIgnored checks that imports in cgo files are not included 723// in the imports list when cgo is disabled. 724// Verifies golang.org/issue/35946. 725func TestCgoImportsIgnored(t *testing.T) { 726 ctxt := Default 727 ctxt.CgoEnabled = false 728 p, err := ctxt.ImportDir("testdata/cgo_disabled", 0) 729 if err != nil { 730 t.Fatal(err) 731 } 732 for _, path := range p.Imports { 733 if path == "should/be/ignored" { 734 t.Errorf("found import %q in ignored cgo file", path) 735 } 736 } 737} 738