@@ -9,32 +9,32 @@ import (
9
9
"bytes"
10
10
"debug/elf"
11
11
"encoding/binary"
12
- "errors"
13
12
"flag"
14
13
"fmt"
15
14
"go/build"
16
15
"io"
17
16
"io/ioutil"
18
17
"log"
19
- "math/rand"
20
18
"os"
21
19
"os/exec"
22
20
"path/filepath"
23
21
"regexp"
24
22
"runtime"
23
+ "sort"
25
24
"strings"
26
25
"testing"
27
26
"time"
28
27
)
29
28
30
- var gopathInstallDir , gorootInstallDir , suffix string
29
+ var gopathInstallDir , gorootInstallDir string
31
30
32
31
// This is the smallest set of packages we can link into a shared
33
32
// library (runtime/cgo is built implicitly).
34
33
var minpkgs = []string {"runtime" , "sync/atomic" }
35
34
var soname = "libruntime,sync-atomic.so"
36
35
37
36
var testX = flag .Bool ("testx" , false , "if true, pass -x to 'go' subcommands invoked by the test" )
37
+ var testWork = flag .Bool ("testwork" , false , "if true, log and do not delete the temporary working directory" )
38
38
39
39
// run runs a command and calls t.Errorf if it fails.
40
40
func run (t * testing.T , msg string , args ... string ) {
@@ -47,7 +47,7 @@ func run(t *testing.T, msg string, args ...string) {
47
47
// goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
48
48
// t.Fatalf if the command fails.
49
49
func goCmd (t * testing.T , args ... string ) string {
50
- newargs := []string {args [0 ], "-installsuffix=" + suffix }
50
+ newargs := []string {args [0 ]}
51
51
if * testX {
52
52
newargs = append (newargs , "-x" )
53
53
}
@@ -67,7 +67,8 @@ func goCmd(t *testing.T, args ...string) string {
67
67
t .Helper ()
68
68
t .Fatalf ("executing %s failed %v:\n %s" , strings .Join (c .Args , " " ), err , stderr )
69
69
} else {
70
- log .Fatalf ("executing %s failed %v:\n %s" , strings .Join (c .Args , " " ), err , stderr )
70
+ // Panic instead of using log.Fatalf so that deferred cleanup may run in testMain.
71
+ log .Panicf ("executing %s failed %v:\n %s" , strings .Join (c .Args , " " ), err , stderr )
71
72
}
72
73
}
73
74
if testing .Verbose () && t != nil {
@@ -81,73 +82,61 @@ func goCmd(t *testing.T, args ...string) string {
81
82
82
83
// TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
83
84
func testMain (m * testing.M ) (int , error ) {
84
- // Because go install -buildmode=shared $standard_library_package always
85
- // installs into $GOROOT, here are some gymnastics to come up with a unique
86
- // installsuffix to use in this test that we can clean up afterwards.
87
- myContext := build .Default
88
- runtimeP , err := myContext .Import ("runtime" , "." , build .ImportComment )
89
- if err != nil {
90
- return 0 , fmt .Errorf ("import failed: %v" , err )
91
- }
92
- for i := 0 ; i < 10000 ; i ++ {
93
- try := fmt .Sprintf ("%s_%d_dynlink" , runtimeP .PkgTargetRoot , rand .Int63 ())
94
- err = os .Mkdir (try , 0700 )
95
- if os .IsExist (err ) {
96
- continue
97
- }
98
- if err == nil {
99
- gorootInstallDir = try
100
- }
101
- break
102
- }
85
+ workDir , err := ioutil .TempDir ("" , "shared_test" )
103
86
if err != nil {
104
- return 0 , fmt . Errorf ( "can't create temporary directory: %v" , err )
87
+ return 0 , err
105
88
}
106
- if gorootInstallDir == "" {
107
- return 0 , errors . New ( "could not create temporary directory after 10000 tries" )
89
+ if * testWork || testing . Verbose () {
90
+ fmt . Printf ( "+ mkdir -p %s \n " , workDir )
108
91
}
109
- if testing . Verbose () {
110
- fmt . Printf ( "+ mkdir -p %s \n " , gorootInstallDir )
92
+ if ! * testWork {
93
+ defer os . RemoveAll ( workDir )
111
94
}
112
- defer os .RemoveAll (gorootInstallDir )
113
95
114
96
// Some tests need to edit the source in GOPATH, so copy this directory to a
115
97
// temporary directory and chdir to that.
116
- gopath , err := ioutil .TempDir ("" , "testshared" )
98
+ gopath := filepath .Join (workDir , "gopath" )
99
+ modRoot , err := cloneTestdataModule (gopath )
117
100
if err != nil {
118
- return 0 , fmt .Errorf ("TempDir failed: %v" , err )
119
- }
120
- if testing .Verbose () {
121
- fmt .Printf ("+ mkdir -p %s\n " , gopath )
122
- }
123
- defer os .RemoveAll (gopath )
124
-
125
- modRoot := filepath .Join (gopath , "src" , "testshared" )
126
- if err := overlayDir (modRoot , "testdata" ); err != nil {
127
101
return 0 , err
128
102
}
129
103
if testing .Verbose () {
104
+ fmt .Printf ("+ export GOPATH=%s\n " , gopath )
130
105
fmt .Printf ("+ cd %s\n " , modRoot )
131
106
}
107
+ os .Setenv ("GOPATH" , gopath )
132
108
os .Chdir (modRoot )
133
109
os .Setenv ("PWD" , modRoot )
134
- if err := ioutil .WriteFile ("go.mod" , []byte ("module testshared\n " ), 0666 ); err != nil {
110
+
111
+ // The test also needs to install libraries into GOROOT/pkg, so copy the
112
+ // subset of GOROOT that we need.
113
+ //
114
+ // TODO(golang.org/issue/28553): Rework -buildmode=shared so that it does not
115
+ // need to write to GOROOT.
116
+ goroot := filepath .Join (workDir , "goroot" )
117
+ if err := cloneGOROOTDeps (goroot ); err != nil {
135
118
return 0 , err
136
119
}
137
-
138
- os .Setenv ("GOPATH" , gopath )
139
120
if testing .Verbose () {
140
- fmt .Printf ( "+ export GOPATH =%s\n " , gopath )
121
+ fmt .Fprintf ( os . Stderr , "+ export GOROOT =%s\n " , goroot )
141
122
}
123
+ os .Setenv ("GOROOT" , goroot )
124
+
125
+ myContext := build .Default
126
+ myContext .GOROOT = goroot
142
127
myContext .GOPATH = gopath
128
+ runtimeP , err := myContext .Import ("runtime" , "." , build .ImportComment )
129
+ if err != nil {
130
+ return 0 , fmt .Errorf ("import failed: %v" , err )
131
+ }
132
+ gorootInstallDir = runtimeP .PkgTargetRoot + "_dynlink"
143
133
144
134
// All tests depend on runtime being built into a shared library. Because
145
135
// that takes a few seconds, do it here and have all tests use the version
146
136
// built here.
147
- suffix = strings .Split (filepath .Base (gorootInstallDir ), "_" )[2 ]
148
137
goCmd (nil , append ([]string {"install" , "-buildmode=shared" }, minpkgs ... )... )
149
138
150
- myContext .InstallSuffix = suffix + "_dynlink"
139
+ myContext .InstallSuffix = "_dynlink"
151
140
depP , err := myContext .Import ("./depBase" , "." , build .ImportComment )
152
141
if err != nil {
153
142
return 0 , fmt .Errorf ("import failed: %v" , err )
@@ -175,6 +164,75 @@ func TestMain(m *testing.M) {
175
164
os .Exit (exitCode )
176
165
}
177
166
167
+ // cloneTestdataModule clones the packages from src/testshared into gopath.
168
+ // It returns the directory within gopath at which the module root is located.
169
+ func cloneTestdataModule (gopath string ) (string , error ) {
170
+ modRoot := filepath .Join (gopath , "src" , "testshared" )
171
+ if err := overlayDir (modRoot , "testdata" ); err != nil {
172
+ return "" , err
173
+ }
174
+ if err := ioutil .WriteFile (filepath .Join (modRoot , "go.mod" ), []byte ("module testshared\n " ), 0644 ); err != nil {
175
+ return "" , err
176
+ }
177
+ return modRoot , nil
178
+ }
179
+
180
+ // cloneGOROOTDeps copies (or symlinks) the portions of GOROOT/src and
181
+ // GOROOT/pkg relevant to this test into the given directory.
182
+ // It must be run from within the testdata module.
183
+ func cloneGOROOTDeps (goroot string ) error {
184
+ oldGOROOT := strings .TrimSpace (goCmd (nil , "env" , "GOROOT" ))
185
+ if oldGOROOT == "" {
186
+ return fmt .Errorf ("go env GOROOT returned an empty string" )
187
+ }
188
+
189
+ // Before we clone GOROOT, figure out which packages we need to copy over.
190
+ listArgs := []string {
191
+ "list" ,
192
+ "-deps" ,
193
+ "-f" , "{{if and .Standard (not .ForTest)}}{{.ImportPath}}{{end}}" ,
194
+ }
195
+ stdDeps := goCmd (nil , append (listArgs , minpkgs ... )... )
196
+ testdataDeps := goCmd (nil , append (listArgs , "-test" , "./..." )... )
197
+
198
+ pkgs := append (strings .Split (strings .TrimSpace (stdDeps ), "\n " ),
199
+ strings .Split (strings .TrimSpace (testdataDeps ), "\n " )... )
200
+ sort .Strings (pkgs )
201
+ var pkgRoots []string
202
+ for _ , pkg := range pkgs {
203
+ parentFound := false
204
+ for _ , prev := range pkgRoots {
205
+ if strings .HasPrefix (pkg , prev ) {
206
+ // We will copy in the source for pkg when we copy in prev.
207
+ parentFound = true
208
+ break
209
+ }
210
+ }
211
+ if ! parentFound {
212
+ pkgRoots = append (pkgRoots , pkg )
213
+ }
214
+ }
215
+
216
+ gorootDirs := []string {
217
+ "pkg/tool" ,
218
+ "pkg/include" ,
219
+ }
220
+ for _ , pkg := range pkgRoots {
221
+ gorootDirs = append (gorootDirs , filepath .Join ("src" , pkg ))
222
+ }
223
+
224
+ for _ , dir := range gorootDirs {
225
+ if testing .Verbose () {
226
+ fmt .Fprintf (os .Stderr , "+ cp -r %s %s\n " , filepath .Join (goroot , dir ), filepath .Join (oldGOROOT , dir ))
227
+ }
228
+ if err := overlayDir (filepath .Join (goroot , dir ), filepath .Join (oldGOROOT , dir )); err != nil {
229
+ return err
230
+ }
231
+ }
232
+
233
+ return nil
234
+ }
235
+
178
236
// The shared library was built at the expected location.
179
237
func TestSOBuilt (t * testing.T ) {
180
238
_ , err := os .Stat (filepath .Join (gorootInstallDir , soname ))
@@ -223,6 +281,7 @@ func TestNoTextrel(t *testing.T) {
223
281
}
224
282
225
283
// The shared library does not contain symbols called ".dup"
284
+ // (See golang.org/issue/14841.)
226
285
func TestNoDupSymbols (t * testing.T ) {
227
286
sopath := filepath .Join (gorootInstallDir , soname )
228
287
f , err := elf .Open (sopath )
@@ -699,7 +758,7 @@ func resetFileStamps() {
699
758
}
700
759
reset := func (path string ) {
701
760
if err := filepath .Walk (path , chtime ); err != nil {
702
- log .Fatalf ("resetFileStamps failed: %v" , err )
761
+ log .Panicf ("resetFileStamps failed: %v" , err )
703
762
}
704
763
705
764
}
@@ -712,6 +771,7 @@ func resetFileStamps() {
712
771
// touch changes path and returns a function that changes it back.
713
772
// It also sets the time of the file, so that we can see if it is rewritten.
714
773
func touch (t * testing.T , path string ) (cleanup func ()) {
774
+ t .Helper ()
715
775
data , err := ioutil .ReadFile (path )
716
776
if err != nil {
717
777
t .Fatal (err )
@@ -740,14 +800,32 @@ func touch(t *testing.T, path string) (cleanup func()) {
740
800
// assume it's a text file
741
801
data = append (data , '\n' )
742
802
}
743
- if err := ioutil .WriteFile (path , data , 0666 ); err != nil {
803
+
804
+ // If the file is still a symlink from an overlay, delete it so that we will
805
+ // replace it with a regular file instead of overwriting the symlinked one.
806
+ fi , err := os .Lstat (path )
807
+ if err == nil && ! fi .Mode ().IsRegular () {
808
+ fi , err = os .Stat (path )
809
+ if err := os .Remove (path ); err != nil {
810
+ t .Fatal (err )
811
+ }
812
+ }
813
+ if err != nil {
814
+ t .Fatal (err )
815
+ }
816
+
817
+ // If we're replacing a symlink to a read-only file, make the new file
818
+ // user-writable.
819
+ perm := fi .Mode ().Perm () | 0200
820
+
821
+ if err := ioutil .WriteFile (path , data , perm ); err != nil {
744
822
t .Fatal (err )
745
823
}
746
824
if err := os .Chtimes (path , nearlyNew , nearlyNew ); err != nil {
747
825
t .Fatal (err )
748
826
}
749
827
return func () {
750
- if err := ioutil .WriteFile (path , old , 0666 ); err != nil {
828
+ if err := ioutil .WriteFile (path , old , perm ); err != nil {
751
829
t .Fatal (err )
752
830
}
753
831
}
0 commit comments