Skip to content

Commit 9c1568a

Browse files
authored
go_test: change directory before use init() functions run (bazel-contrib#2696)
Forces a new package, go/tools/test_init to have its package initializer run before any Go user code can be initialized. This package sets the correct working directory, PWD, and TMPDIR to match bazel standards. Fixes bazel-contrib#1918
1 parent 8562042 commit 9c1568a

File tree

6 files changed

+89
-28
lines changed

6 files changed

+89
-28
lines changed

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ load(
3333
# gazelle:exclude go/tools/coverdata
3434
# gazelle:exclude go/tools/fetch_repo
3535
# gazelle:exclude go/tools/windows-testrunner
36+
# gazelle:exclude go/tools/testinit
3637
# gazelle:exclude go/tools/testwrapper
3738
# gazelle:go_naming_convention import_alias
3839

go/private/rules/test.bzl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ def _go_test_impl(ctx):
9393

9494
main_go = go.declare_file(go, path = "testmain.go")
9595
arguments = go.builder_args(go, "gentestmain")
96-
arguments.add("-rundir", run_dir)
9796
arguments.add("-output", main_go)
9897
if ctx.configuration.coverage_enabled:
9998
arguments.add("-coverage")
@@ -115,16 +114,16 @@ def _go_test_impl(ctx):
115114
mnemonic = "GoTestGenTest",
116115
executable = go.toolchain._builder,
117116
arguments = [arguments],
118-
env = {
119-
"RUNDIR": ctx.label.package,
120-
},
121117
)
122118

123119
test_gc_linkopts = gc_linkopts(ctx)
124120
if not go.mode.debug:
125121
# Disable symbol table and DWARF generation for test binaries.
126122
test_gc_linkopts.extend(["-s", "-w"])
127123

124+
# Link in the run_dir global for testinit
125+
test_gc_linkopts.extend(["-X", "github.com/bazelbuild/rules_go/go/tools/testinit.RunDir=" + run_dir])
126+
128127
# Now compile the test binary itself
129128
test_library = GoLibrary(
130129
name = go._ctx.label.name + "~testmain",
@@ -136,7 +135,7 @@ def _go_test_impl(ctx):
136135
is_main = True,
137136
resolve = None,
138137
)
139-
test_deps = external_archive.direct + [external_archive]
138+
test_deps = external_archive.direct + [external_archive] + ctx.attr._testmain_additional_deps
140139
if ctx.configuration.coverage_enabled:
141140
test_deps.append(go.coverdata)
142141
test_source = go.library_to_source(go, struct(
@@ -200,6 +199,10 @@ _go_test_kwargs = {
200199
default = ["@io_bazel_rules_go//go/tools/testwrapper:srcs"],
201200
allow_files = go_exts,
202201
),
202+
"_testmain_additional_deps": attr.label_list(
203+
providers = [GoLibrary],
204+
default = ["@io_bazel_rules_go//go/tools/testinit"],
205+
),
203206
# Workaround for bazelbuild/bazel#6293. See comment in lcov_merger.sh.
204207
"_lcov_merger": attr.label(
205208
executable = True,

go/tools/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ filegroup(
66
"//go/tools/bazel_testing:all_files",
77
"//go/tools/builders:all_files",
88
"//go/tools/coverdata:all_files",
9+
"//go/tools/testinit:all_files",
910
"//go/tools/testwrapper:all_files",
1011
],
1112
visibility = ["//visibility:public"],

go/tools/builders/generate_test_main.go

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"go/parser"
2626
"go/token"
2727
"os"
28-
"path/filepath"
2928
"sort"
3029
"strings"
3130
"text/template"
@@ -50,7 +49,6 @@ type Example struct {
5049

5150
// Cases holds template data.
5251
type Cases struct {
53-
RunDir string
5452
Imports []*Import
5553
Tests []TestCase
5654
Benchmarks []TestCase
@@ -62,13 +60,19 @@ type Cases struct {
6260

6361
const testMainTpl = `
6462
package main
63+
// This package must be initialized before packages being tested.
64+
// NOTE: this relies on the order of package initialization, which is the spec
65+
// is somewhat unclear about-- it only clearly guarantees that imported packages
66+
// are initialized before their importers, though in practice (and implied) it
67+
// also respects declaration order, which we're relying on here.
68+
import (
69+
_ "github.com/bazelbuild/rules_go/go/tools/testinit"
70+
)
6571
import (
6672
"flag"
6773
"log"
6874
"os"
6975
"os/exec"
70-
"path/filepath"
71-
"runtime"
7276
"strconv"
7377
"testing"
7478
"testing/internal/testdeps"
@@ -131,23 +135,6 @@ func main() {
131135
}
132136
}
133137
134-
// Check if we're being run by Bazel and change directories if so.
135-
// TEST_SRCDIR and TEST_WORKSPACE are set by the Bazel test runner, so that makes a decent proxy.
136-
testSrcdir := os.Getenv("TEST_SRCDIR")
137-
testWorkspace := os.Getenv("TEST_WORKSPACE")
138-
if testSrcdir != "" && testWorkspace != "" {
139-
abs := filepath.Join(testSrcdir, testWorkspace, {{printf "%q" .RunDir}})
140-
err := os.Chdir(abs)
141-
// Ignore the Chdir err when on Windows, since it might have have runfiles symlinks.
142-
// https://github.com/bazelbuild/rules_go/pull/1721#issuecomment-422145904
143-
if err != nil && runtime.GOOS != "windows" {
144-
log.Fatalf("could not change to test directory: %v", err)
145-
}
146-
if err == nil {
147-
os.Setenv("PWD", abs)
148-
}
149-
}
150-
151138
m := testing.MainStart(testdeps.TestDeps{}, testsInShard(), benchmarks, examples)
152139
153140
if filter := os.Getenv("TESTBRIDGE_TEST_ONLY"); filter != "" {
@@ -183,7 +170,6 @@ func genTestMain(args []string) error {
183170
sources := multiFlag{}
184171
flags := flag.NewFlagSet("GoTestGenTest", flag.ExitOnError)
185172
goenv := envFlags(flags)
186-
runDir := flags.String("rundir", ".", "Path to directory where tests should run.")
187173
out := flags.String("output", "", "output file to write. Defaults to stdout.")
188174
coverage := flags.Bool("coverage", false, "whether coverage is supported")
189175
pkgname := flags.String("pkgname", "", "package name of test")
@@ -235,7 +221,6 @@ func genTestMain(args []string) error {
235221
}
236222

237223
cases := Cases{
238-
RunDir: strings.Replace(filepath.FromSlash(*runDir), `\`, `\\`, -1),
239224
Coverage: *coverage,
240225
Pkgname: *pkgname,
241226
}

go/tools/testinit/BUILD.bazel

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("@io_bazel_rules_go//go/private/rules:library.bzl", "go_tool_library")
2+
3+
go_tool_library(
4+
name = "testinit",
5+
srcs = ["testinit.go"],
6+
importpath = "github.com/bazelbuild/rules_go/go/tools/testinit",
7+
visibility = ["//visibility:public"],
8+
)
9+
10+
filegroup(
11+
name = "all_files",
12+
testonly = True,
13+
srcs = glob(["**"]),
14+
visibility = ["//visibility:public"],
15+
)

go/tools/testinit/testinit.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2020 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package testinit
16+
17+
// This package must have no deps beyond Go SDK.
18+
import (
19+
"log"
20+
"os"
21+
"path/filepath"
22+
"runtime"
23+
)
24+
25+
var (
26+
// Initialized by linker.
27+
RunDir string
28+
)
29+
30+
// This initializer runs before any user packages.
31+
func init() {
32+
// Check if we're being run by Bazel and change directories if so.
33+
// TEST_SRCDIR and TEST_WORKSPACE are set by the Bazel test runner, so that makes a decent proxy.
34+
testSrcdir, hasSrcDir := os.LookupEnv("TEST_SRCDIR")
35+
testWorkspace, hasWorkspace := os.LookupEnv("TEST_WORKSPACE")
36+
if hasSrcDir && hasWorkspace && RunDir != "" {
37+
abs := RunDir
38+
if !filepath.IsAbs(RunDir) {
39+
abs = filepath.Join(testSrcdir, testWorkspace, RunDir)
40+
}
41+
err := os.Chdir(abs)
42+
// Ignore the Chdir err when on Windows, since it might have have runfiles symlinks.
43+
// https://github.com/bazelbuild/rules_go/pull/1721#issuecomment-422145904
44+
if err != nil && runtime.GOOS != "windows" {
45+
log.Fatalf("could not change to test directory: %v", err)
46+
}
47+
if err == nil {
48+
_ = os.Setenv("PWD", abs)
49+
}
50+
}
51+
52+
// Setup the bazel tmpdir as the go tmpdir.
53+
if tmpDir, ok := os.LookupEnv("TEST_TMPDIR"); ok {
54+
_ = os.Setenv("TMPDIR", tmpDir)
55+
}
56+
}

0 commit comments

Comments
 (0)