Commit a1858e9c authored by Michael Hudson-Doyle's avatar Michael Hudson-Doyle Committed by Ian Lance Taylor

cmd/go: rebuild stale shared objects before linking against them.

This changes the action graph when shared libraries are involved to always have
an action for the shared library (which does nothing when the shared library
is up to date).

Change-Id: Ibbc70fd01cbb3f4e8c0ef96e62a151002d446144
Reviewed-on: https://go-review.googlesource.com/8934Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 21cb0625
package dep
var V int = 1
func F() int {
return V
}
package main
import "dep"
func main() {
dep.V = dep.F() + 1
}
......@@ -31,9 +31,10 @@ trap cleanup EXIT
mysuffix=$(echo $std_install_dir | sed -e 's/.*_\([^_]*\)_dynlink/\1/')
# This is the smallest set of packages we can link into a shared
# library. Check they are built into a library with the expected name.
minpkgs="runtime runtime/cgo sync/atomic"
soname=libruntime,runtime-cgo,sync-atomic.so
# library (runtime/cgo is built implicitly). Check they are built into
# a library with the expected name.
minpkgs="runtime sync/atomic"
soname=libruntime,sync-atomic.so
go install -installsuffix="$mysuffix" -buildmode=shared $minpkgs || die "install -buildmode=shared failed"
......@@ -42,9 +43,10 @@ if [ ! -f "$std_install_dir/$soname" ]; then
exit 1
fi
# The install command should have created a "shlibname" file for each
# package indicating the name of the shared library containing it.
for pkg in $minpkgs; do
# The install command should have created a "shlibname" file for the
# listed packages (and runtime/cgo) indicating the name of the shared
# library containing it.
for pkg in $minpkgs runtime/cgo; do
if [ ! -f "$std_install_dir/$pkg.shlibname" ]; then
die "no shlibname file for $pkg"
fi
......@@ -60,5 +62,49 @@ go install -installsuffix="$mysuffix" -linkshared trivial || die "build -linksha
# And check that it is actually dynamically linked against the library
# we hope it is linked against.
a="$(ldd ./bin/trivial)" || die "ldd ./bin/trivial failed: $a"
{ echo "$a" | grep -q "$std_install_dir/$soname"; } || die "trivial does not appear to be linked against $soname"
ensure_ldd () {
a="$(ldd $1)" || die "ldd $1 failed: $a"
{ echo "$a" | grep -q "$2"; } || die "$1 does not appear to be linked against $2"
}
ensure_ldd ./bin/trivial $std_install_dir/$soname
# Build a GOPATH package into a shared library that links against the above one.
rootdir="$(dirname $(go list -installsuffix="$mysuffix" -linkshared -f '{{.Target}}' dep))"
go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
ensure_ldd $rootdir/libdep.so $std_install_dir/$soname
# And exe that links against both
go install -installsuffix="$mysuffix" -linkshared exe
ensure_ldd ./bin/exe $rootdir/libdep.so
ensure_ldd ./bin/exe $std_install_dir/$soname
# Now, test rebuilding of shared libraries when they are stale.
will_check_rebuilt () {
for f in $@; do cp $f $f.bak; done
}
assert_rebuilt () {
find $1 -newer $1.bak | grep -q . || die "$1 was not rebuilt"
}
assert_not_rebuilt () {
find $1 -newer $1.bak | grep . && die "$1 was rebuilt" || true
}
# If the source is newer than both the .a file and the .so, both are rebuilt.
touch src/dep/dep.go
will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
go install -installsuffix="$mysuffix" -linkshared exe
assert_rebuilt $rootdir/dep.a
assert_rebuilt $rootdir/libdep.so
# If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
touch $rootdir/dep.a
will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
go install -installsuffix="$mysuffix" -linkshared exe
assert_not_rebuilt $rootdir/dep.a
assert_rebuilt $rootdir/libdep.so
......@@ -36,3 +36,7 @@ func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) {
func parseMetaGoImports(r io.Reader) ([]metaImport, error) {
panic("unreachable")
}
func readnote(a, b string, t int32) ([]byte, error) {
return nil, nil
}
This diff is collapsed.
......@@ -36,6 +36,7 @@ syntax of package template. The default output is equivalent to -f
Name string // package name
Doc string // package documentation string
Target string // install path
Shlib string // the shared library that contains this package (only set when -linkshared)
Goroot bool // is this package in the Go root?
Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package?
......
// Copyright 2015 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.
// +build !cmd_go_bootstrap
// This is not built when bootstrapping to avoid having go_bootstrap depend on
// debug/elf.
package main
import (
"debug/elf"
"encoding/binary"
"fmt"
"io"
)
func rnd(v int32, r int32) int32 {
if r <= 0 {
return v
}
v += r - 1
c := v % r
if c < 0 {
c += r
}
v -= c
return v
}
func readwithpad(r io.Reader, sz int32) ([]byte, error) {
full := rnd(sz, 4)
data := make([]byte, full)
_, err := io.ReadFull(r, data)
if err != nil {
return nil, err
}
data = data[:sz]
return data, nil
}
func readnote(filename, name string, typ int32) ([]byte, error) {
f, err := elf.Open(filename)
if err != nil {
return nil, err
}
for _, sect := range f.Sections {
if sect.Type != elf.SHT_NOTE {
continue
}
r := sect.Open()
for {
var namesize, descsize, noteType int32
err = binary.Read(r, f.ByteOrder, &namesize)
if err != nil {
if err == io.EOF {
break
}
return nil, fmt.Errorf("read namesize failed:", err)
}
err = binary.Read(r, f.ByteOrder, &descsize)
if err != nil {
return nil, fmt.Errorf("read descsize failed:", err)
}
err = binary.Read(r, f.ByteOrder, &noteType)
if err != nil {
return nil, fmt.Errorf("read type failed:", err)
}
noteName, err := readwithpad(r, namesize)
if err != nil {
return nil, fmt.Errorf("read name failed:", err)
}
desc, err := readwithpad(r, descsize)
if err != nil {
return nil, fmt.Errorf("read desc failed:", err)
}
if name == string(noteName) && typ == noteType {
return desc, nil
}
}
}
return nil, nil
}
......@@ -11,6 +11,7 @@ import (
"go/build"
"go/scanner"
"go/token"
"io/ioutil"
"os"
pathpkg "path"
"path/filepath"
......@@ -32,6 +33,7 @@ type Package struct {
Name string `json:",omitempty"` // package name
Doc string `json:",omitempty"` // package documentation string
Target string `json:",omitempty"` // install path
Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared)
Goroot bool `json:",omitempty"` // is this package found in the Go root?
Standard bool `json:",omitempty"` // is this package part of the standard Go library?
Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
......@@ -522,6 +524,15 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
p.target = ""
} else {
p.target = p.build.PkgObj
if buildLinkshared {
shlibnamefile := p.target[:len(p.target)-2] + ".shlibname"
shlib, err := ioutil.ReadFile(shlibnamefile)
if err == nil {
p.Shlib = strings.TrimSpace(string(shlib))
} else if !os.IsNotExist(err) {
fatalf("unexpected error reading %s: %v", shlibnamefile, err)
}
}
}
importPaths := p.Imports
......
......@@ -516,14 +516,8 @@ func loadlib() {
if Ctxt.Library[i].Shlib != "" {
ldshlibsyms(Ctxt.Library[i].Shlib)
} else {
// Because the linker always looks for runtime/cgo when
// -buildmode=shared is passed, the go tool never passes
// runtime/cgo on the command line. But runtime/cgo needs
// to end up in the package list if it is being built into
// the shared libarary.
if Buildmode == BuildmodeShared {
pkglistfornote = append(pkglistfornote, "runtime/cgo"...)
pkglistfornote = append(pkglistfornote, '\n')
if DynlinkingGo() {
Exitf("cannot implicitly include runtime/cgo in a shared library")
}
objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg)
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment