myContext := build.Default
myContext.GOROOT = goroot
myContext.GOPATH = gopath
- runtimeP, err := myContext.Import("runtime", ".", build.ImportComment)
- if err != nil {
- return 0, fmt.Errorf("import failed: %v", err)
- }
- gorootInstallDir = runtimeP.PkgTargetRoot + "_dynlink"
// All tests depend on runtime being built into a shared library. Because
// that takes a few seconds, do it here and have all tests use the version
// built here.
goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...)
+ shlib := goCmd(nil, "list", "-linkshared", "-f={{.Shlib}}", "runtime")
+ if shlib != "" {
+ gorootInstallDir = filepath.Dir(shlib)
+ }
+
myContext.InstallSuffix = "_dynlink"
depP, err := myContext.Import("./depBase", ".", build.ImportComment)
if err != nil {
}
} else {
noext = strings.TrimSuffix(bp.PkgObj, ".a")
- id = bp.ImportPath
}
+ id = bp.ImportPath
case build.IsLocalImport(path):
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
// Instead, we can just check that it is not stale, which may be less
// expensive (and is also more likely to catch bugs in the builder
// implementation).
+ // The cache used by dist when building is different from that used when
+ // running dist test, so rebuild (but don't install) std and cmd to make
+ // sure packages without install targets are cached so they are not stale.
+ goCmd("go", "build", "std", "cmd") // make sure dependencies of targets are cached
checkNotStale("go", "std", "cmd")
}
}
func TestGoInstallPkgdir(t *testing.T) {
skipIfGccgo(t, "gccgo has no standard packages")
+ if !canCgo {
+ // Only the stdlib packages that use cgo have install
+ // targets, (we're using net below) so cgo is required
+ // for the install.
+ t.Skip("skipping because cgo not enabled")
+ }
tooSlow(t)
tg := testgo(t)
tg.parallel()
+ tg.setenv("GODEBUG", "installgoroot=all")
defer tg.cleanup()
tg.makeTempdir()
pkg := tg.path(".")
- tg.run("install", "-pkgdir", pkg, "sync")
- tg.mustExist(filepath.Join(pkg, "sync.a"))
- tg.mustNotExist(filepath.Join(pkg, "sync/atomic.a"))
+ tg.run("install", "-pkgdir", pkg, "net")
+ tg.mustExist(filepath.Join(pkg, "net.a"))
+ tg.mustNotExist(filepath.Join(pkg, "runtime/cgo.a"))
}
// For issue 14337.
old := pp.PkgTargetRoot
pp.PkgRoot = cfg.BuildPkgdir
pp.PkgTargetRoot = cfg.BuildPkgdir
- pp.PkgObj = filepath.Join(cfg.BuildPkgdir, strings.TrimPrefix(pp.PkgObj, old))
+ if pp.PkgObj != "" {
+ pp.PkgObj = filepath.Join(cfg.BuildPkgdir, strings.TrimPrefix(pp.PkgObj, old))
+ }
}
p.Dir = pp.Dir
p.Target = ""
} else {
p.Target = p.Internal.Build.PkgObj
- if cfg.BuildLinkshared && p.Target != "" {
- // TODO(bcmills): The reliance on p.Target implies that -linkshared does
- // not work for any package that lacks a Target — such as a non-main
+ if cfg.BuildBuildmode == "shared" && p.Internal.Build.PkgTargetRoot != "" {
+ // TODO(matloob): This shouldn't be necessary, but the misc/cgo/testshared
+ // test fails without Target set for this condition. Figure out why and
+ // fix it.
+ p.Target = filepath.Join(p.Internal.Build.PkgTargetRoot, p.ImportPath+".a")
+ }
+ if cfg.BuildLinkshared && p.Internal.Build.PkgTargetRoot != "" {
+ // TODO(bcmills): The reliance on PkgTargetRoot implies that -linkshared does
+ // not work for any package that lacks a PkgTargetRoot — such as a non-main
// package in module mode. We should probably fix that.
- shlibnamefile := p.Target[:len(p.Target)-2] + ".shlibname"
+ targetPrefix := filepath.Join(p.Internal.Build.PkgTargetRoot, p.ImportPath)
+ p.Target = targetPrefix + ".a"
+ shlibnamefile := targetPrefix + ".shlibname"
shlib, err := os.ReadFile(shlibnamefile)
if err != nil && !os.IsNotExist(err) {
base.Fatalf("reading shlibname: %v", err)
if err != nil {
t.Fatal(err)
}
- bp1, err := build.Default.Import(pkg, filepath.Join(src, pkg), build.ImportComment)
+ bp1, err := build.Default.Import(".", filepath.Join(src, pkg), build.ImportComment)
if err != nil {
t.Fatal(err)
}
"go/build"
"go/build/constraint"
"go/token"
+ "internal/buildinternal"
"internal/godebug"
"internal/goroot"
"path"
p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
p.BinDir = ctxt.joinPath(p.Root, "bin")
if pkga != "" {
+ // Always set PkgTargetRoot. It might be used when building in shared
+ // mode.
p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
- p.PkgObj = ctxt.joinPath(p.Root, pkga)
+
+ // Set the install target if applicable.
+ if strings.ToLower(godebug.Get("installgoroot")) == "all" ||
+ !p.Goroot || buildinternal.NeedsInstalledDotA(p.ImportPath) {
+ p.PkgObj = ctxt.joinPath(p.Root, pkga)
+ }
}
}
}
vetOnly := mode&ModeVetOnly != 0
mode &^= ModeVetOnly
- if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" {
- // Imported via local path or using modules. No permanent target.
+ if mode != ModeBuild && p.Target == "" {
+ // No permanent target.
mode = ModeBuild
}
if mode != ModeBuild && p.Name == "main" {
}
for _, a2 := range buildAction.Deps[0].Deps {
p := a2.Package
- if p.Target == "" {
+ pkgTargetRoot := p.Internal.Build.PkgTargetRoot
+ if pkgTargetRoot == "" {
continue
}
a.Deps = append(a.Deps, &Action{
Mode: "shlibname",
Package: p,
Func: (*Builder).installShlibname,
- Target: strings.TrimSuffix(p.Target, ".a") + ".shlibname",
+ Target: filepath.Join(pkgTargetRoot, p.ImportPath+".shlibname"),
Deps: []*Action{a.Deps[0]},
})
}
"flag"
"fmt"
"go/build"
+ "internal/buildinternal"
"os"
"os/exec"
"path/filepath"
for _, p := range pkgs {
if p.Target == "" {
switch {
- case p.Standard && p.ImportPath == "unsafe":
- // unsafe is a built-in package, has no target
case p.Name != "main" && p.Internal.Local && p.ConflictDir == "":
// Non-executables outside GOPATH need not have a target:
// we can use the cache to hold the built package archive for use in future builds.
// or else something is wrong and worth reporting (like a ConflictDir).
case p.Name != "main" && p.Module != nil:
// Non-executables have no target (except the cache) when building with modules.
+ case p.Name != "main" && p.Standard && !buildinternal.NeedsInstalledDotA(p.ImportPath):
+ // Most packages in std do not need an installed .a, because they can be
+ // rebuilt and used directly from the build cache.
+ // A few targets (notably those using cgo) still do need to be installed
+ // in case the user's environment lacks a C compiler. case p.Internal.GobinSubdir:
case p.Internal.GobinSubdir:
base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set")
case p.Internal.CmdlineFiles:
// TODO: BuildN
a1 := a.Deps[0]
+ if err := b.Mkdir(filepath.Dir(a.Target)); err != nil {
+ return err
+ }
err := os.WriteFile(a.Target, []byte(filepath.Base(a1.Target)+"\n"), 0666)
if err != nil {
return err
--- /dev/null
+[short] skip
+[!cgo] skip
+
+# Most packages in std do not have an install target.
+go list -f '{{.Target}}' fmt
+! stdout .
+go list -export -f '{{.Export}}' fmt
+stdout $GOCACHE
+
+# Packages that use cgo still do.
+go list -f '{{.Target}}' runtime/cgo
+stdout .
+go list -export -f '{{.Export}}' runtime/cgo
+! stdout $GOCACHE
+stdout cgo\.a
+
+# With GODEBUG=installgoroot=all, fmt has a target.
+# (Though we can't try installing it without modifying goroot).
+env GODEBUG=installgoroot=all
+go list -f '{{.Target}}' fmt
+stdout fmt\.a
xObj := filepath.Join(tmpdir, "x.o")
pObj := filepath.Join(tmpdir, "p.o")
exe := filepath.Join(tmpdir, "x.exe")
+ importcfgfile := filepath.Join(tmpdir, "importcfg")
+ testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
if err != nil {
t.Fatalf("failed to write source file: %v", err)
if err != nil {
t.Fatalf("failed to write source file: %v", err)
}
- cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", pObj, pSrc) // without -p
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
}
- cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-p=main", "-o", xObj, xSrc)
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
}
- cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, xObj)
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
out, err = cmd.CombinedOutput()
if err == nil {
t.Fatalf("link did not fail")
}
// It is okay to omit -p for (only) main package.
- cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=p", "-o", pObj, pSrc)
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
}
- cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-o", xObj, xSrc) // without -p
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("compile failed: %v. output:\n%s", err, out)
}
- cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, xObj)
+
+ cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
out, err = cmd.CombinedOutput()
if err != nil {
t.Errorf("link failed: %v. output:\n%s", err, out)
"go/doc"
"go/token"
"internal/buildcfg"
+ "internal/buildinternal"
+ "internal/godebug"
"internal/goroot"
"internal/goversion"
"io"
p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
p.BinDir = ctxt.joinPath(p.Root, "bin")
if pkga != "" {
+ // Always set PkgTargetRoot. It might be used when building in shared
+ // mode.
p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
- p.PkgObj = ctxt.joinPath(p.Root, pkga)
+
+ // Set the install target if applicable.
+ if strings.ToLower(godebug.Get("installgoroot")) == "all" ||
+ !p.Goroot || buildinternal.NeedsInstalledDotA(p.ImportPath) {
+ p.PkgObj = ctxt.joinPath(p.Root, pkga)
+ }
}
}
testenv.MustHaveGoBuild(t) // really must just have source
ctxt := Default
ctxt.GOPATH = ""
- p, err := ctxt.ImportDir(filepath.Join(testenv.GOROOT(t), "src/path"), 0)
+ // In GOROOT only a handful of packages have install targets. Most stdlib packages will
+ // only be built and placed in the build cache.
+ p, err := ctxt.ImportDir(filepath.Join(testenv.GOROOT(t), "src/runtime/cgo"), 0)
if err != nil {
t.Fatal(err)
}
# No dependencies allowed for any of these packages.
NONE
< constraints, container/list, container/ring,
+ internal/buildinternal,
internal/cfg, internal/coverage, internal/coverage/rtcov,
internal/coverage/uleb128, internal/coverage/calloc,
internal/cpu, internal/goarch,
FMT, internal/goexperiment
< internal/buildcfg;
- go/build/constraint, go/doc, go/parser, internal/buildcfg, internal/goroot, internal/goversion
+ go/build/constraint, go/doc, go/parser, internal/buildcfg, internal/goroot, internal/goversion, internal/buildinternal
< go/build;
# databases
}
} else {
noext = strings.TrimSuffix(bp.PkgObj, ".a")
- id = bp.ImportPath
}
+ id = bp.ImportPath
case build.IsLocalImport(path):
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
// Only run where builders (build.golang.org) have
// access to compiled packages for import.
//
-//go:build !arm && !arm64
+//go:build !android && !ios && !js
package types_test
--- /dev/null
+// Copyright 2022 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.
+
+// Package buildinternal provides internal functions used by go/build
+// that need to be used by other packages too.
+package buildinternal
+
+// NeedsInstalledDotA returns true if the given stdlib package
+// needs an installed .a file in the stdlib.
+func NeedsInstalledDotA(importPath string) bool {
+ return importPath == "net" || importPath == "os/signal" || importPath == "os/user" || importPath == "plugin" ||
+ importPath == "runtime/cgo"
+}