1 // +build !nacl 2 // run 3 4 // Copyright 2016 The Go Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 8 // Test the compiler -linkobj flag. 9 10 package main 11 12 import ( 13 "fmt" 14 "io/ioutil" 15 "log" 16 "os" 17 "os/exec" 18 "strings" 19 ) 20 21 var pwd, tmpdir string 22 23 func main() { 24 dir, err := ioutil.TempDir("", "go-test-linkobj-") 25 if err != nil { 26 log.Fatal(err) 27 } 28 pwd, err = os.Getwd() 29 if err != nil { 30 log.Fatal(err) 31 } 32 if err := os.Chdir(dir); err != nil { 33 os.RemoveAll(dir) 34 log.Fatal(err) 35 } 36 tmpdir = dir 37 38 writeFile("p1.go", ` 39 package p1 40 41 func F() { 42 println("hello from p1") 43 } 44 `) 45 writeFile("p2.go", ` 46 package p2 47 48 import "./p1" 49 50 func F() { 51 p1.F() 52 println("hello from p2") 53 } 54 55 func main() {} 56 `) 57 writeFile("p3.go", ` 58 package main 59 60 import "./p2" 61 62 func main() { 63 p2.F() 64 println("hello from main") 65 } 66 `) 67 68 // two rounds: once using normal objects, again using .a files (compile -pack). 69 for round := 0; round < 2; round++ { 70 pkg := "-pack=" + fmt.Sprint(round) 71 72 // The compiler expects the files being read to have the right suffix. 73 o := "o" 74 if round == 1 { 75 o = "a" 76 } 77 78 // inlining is disabled to make sure that the link objects contain needed code. 79 run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p1."+o, "-linkobj", "p1.lo", "p1.go") 80 run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p2."+o, "-linkobj", "p2.lo", "p2.go") 81 run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p3."+o, "-linkobj", "p3.lo", "p3.go") 82 83 cp("p1."+o, "p1.oo") 84 cp("p2."+o, "p2.oo") 85 cp("p3."+o, "p3.oo") 86 cp("p1.lo", "p1."+o) 87 cp("p2.lo", "p2."+o) 88 cp("p3.lo", "p3."+o) 89 out := runFail("go", "tool", "link", "p2."+o) 90 if !strings.Contains(out, "not package main") { 91 fatalf("link p2.o failed but not for package main:\n%s", out) 92 } 93 94 run("go", "tool", "link", "-L", ".", "-o", "a.out.exe", "p3."+o) 95 out = run("./a.out.exe") 96 if !strings.Contains(out, "hello from p1\nhello from p2\nhello from main\n") { 97 fatalf("running main, incorrect output:\n%s", out) 98 } 99 100 // ensure that mistaken future round can't use these 101 os.Remove("p1.o") 102 os.Remove("a.out.exe") 103 } 104 105 cleanup() 106 } 107 108 func run(args ...string) string { 109 out, err := exec.Command(args[0], args[1:]...).CombinedOutput() 110 if err != nil { 111 fatalf("run %v: %s\n%s", args, err, out) 112 } 113 return string(out) 114 } 115 116 func runFail(args ...string) string { 117 out, err := exec.Command(args[0], args[1:]...).CombinedOutput() 118 if err == nil { 119 fatalf("runFail %v: unexpected success!\n%s", args, err, out) 120 } 121 return string(out) 122 } 123 124 func cp(src, dst string) { 125 data, err := ioutil.ReadFile(src) 126 if err != nil { 127 fatalf("%v", err) 128 } 129 err = ioutil.WriteFile(dst, data, 0666) 130 if err != nil { 131 fatalf("%v", err) 132 } 133 } 134 135 func writeFile(name, data string) { 136 err := ioutil.WriteFile(name, []byte(data), 0666) 137 if err != nil { 138 fatalf("%v", err) 139 } 140 } 141 142 func cleanup() { 143 const debug = false 144 if debug { 145 println("TMPDIR:", tmpdir) 146 return 147 } 148 os.Chdir(pwd) // get out of tmpdir before removing it 149 os.RemoveAll(tmpdir) 150 } 151 152 func fatalf(format string, args ...interface{}) { 153 cleanup() 154 log.Fatalf(format, args...) 155 } 156