aboutsummaryrefslogtreecommitdiff
path: root/test/linkobj.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2016-04-26 21:50:59 -0400
committerRuss Cox <rsc@golang.org>2016-05-09 17:31:45 +0000
commitfeb6131b1a4c0da098821c516e06499add886182 (patch)
tree1e5eca4df54a33267f31a9fa25e0a611012dc255 /test/linkobj.go
parentaeecee8ce4cf1821dcb6b5e37e20f40696278498 (diff)
downloadgo-feb6131b1a4c0da098821c516e06499add886182.tar.gz
go-feb6131b1a4c0da098821c516e06499add886182.zip
cmd/compile: add -linkobj flag to allow writing object file in two parts
This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
Diffstat (limited to 'test/linkobj.go')
-rw-r--r--test/linkobj.go155
1 files changed, 155 insertions, 0 deletions
diff --git a/test/linkobj.go b/test/linkobj.go
new file mode 100644
index 0000000000..8a86aa872f
--- /dev/null
+++ b/test/linkobj.go
@@ -0,0 +1,155 @@
+// +build !nacl
+// run
+
+// Copyright 2016 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.
+
+// Test the compiler -linkobj flag.
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+var pwd, tmpdir string
+
+func main() {
+ dir, err := ioutil.TempDir("", "go-test-linkobj-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ pwd, err = os.Getwd()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if err := os.Chdir(dir); err != nil {
+ os.RemoveAll(dir)
+ log.Fatal(err)
+ }
+ tmpdir = dir
+
+ writeFile("p1.go", `
+ package p1
+
+ func F() {
+ println("hello from p1")
+ }
+ `)
+ writeFile("p2.go", `
+ package p2
+
+ import "./p1"
+
+ func F() {
+ p1.F()
+ println("hello from p2")
+ }
+
+ func main() {}
+ `)
+ writeFile("p3.go", `
+ package main
+
+ import "./p2"
+
+ func main() {
+ p2.F()
+ println("hello from main")
+ }
+ `)
+
+ // two rounds: once using normal objects, again using .a files (compile -pack).
+ for round := 0; round < 2; round++ {
+ pkg := "-pack=" + fmt.Sprint(round)
+
+ // The compiler expects the files being read to have the right suffix.
+ o := "o"
+ if round == 1 {
+ o = "a"
+ }
+
+ // inlining is disabled to make sure that the link objects contain needed code.
+ run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p1."+o, "-linkobj", "p1.lo", "p1.go")
+ run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p2."+o, "-linkobj", "p2.lo", "p2.go")
+ run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p3."+o, "-linkobj", "p3.lo", "p3.go")
+
+ cp("p1."+o, "p1.oo")
+ cp("p2."+o, "p2.oo")
+ cp("p3."+o, "p3.oo")
+ cp("p1.lo", "p1."+o)
+ cp("p2.lo", "p2."+o)
+ cp("p3.lo", "p3."+o)
+ out := runFail("go", "tool", "link", "p2."+o)
+ if !strings.Contains(out, "not package main") {
+ fatalf("link p2.o failed but not for package main:\n%s", out)
+ }
+
+ run("go", "tool", "link", "-L", ".", "-o", "a.out.exe", "p3."+o)
+ out = run("./a.out.exe")
+ if !strings.Contains(out, "hello from p1\nhello from p2\nhello from main\n") {
+ fatalf("running main, incorrect output:\n%s", out)
+ }
+
+ // ensure that mistaken future round can't use these
+ os.Remove("p1.o")
+ os.Remove("a.out.exe")
+ }
+
+ cleanup()
+}
+
+func run(args ...string) string {
+ out, err := exec.Command(args[0], args[1:]...).CombinedOutput()
+ if err != nil {
+ fatalf("run %v: %s\n%s", args, err, out)
+ }
+ return string(out)
+}
+
+func runFail(args ...string) string {
+ out, err := exec.Command(args[0], args[1:]...).CombinedOutput()
+ if err == nil {
+ fatalf("runFail %v: unexpected success!\n%s", args, err, out)
+ }
+ return string(out)
+}
+
+func cp(src, dst string) {
+ data, err := ioutil.ReadFile(src)
+ if err != nil {
+ fatalf("%v", err)
+ }
+ err = ioutil.WriteFile(dst, data, 0666)
+ if err != nil {
+ fatalf("%v", err)
+ }
+}
+
+func writeFile(name, data string) {
+ err := ioutil.WriteFile(name, []byte(data), 0666)
+ if err != nil {
+ fatalf("%v", err)
+ }
+}
+
+func cleanup() {
+ const debug = false
+ if debug {
+ println("TMPDIR:", tmpdir)
+ return
+ }
+ os.Chdir(pwd) // get out of tmpdir before removing it
+ os.RemoveAll(tmpdir)
+}
+
+func fatalf(format string, args ...interface{}) {
+ cleanup()
+ log.Fatalf(format, args...)
+}