-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathbench.go
More file actions
152 lines (134 loc) · 5.02 KB
/
bench.go
File metadata and controls
152 lines (134 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main
import (
"fmt"
"hash/fnv"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/pkg/errors"
)
const bazelRunScript = `#!/bin/bash
SCRIPT_PATH="$(realpath "$0")"
BAZEL_DIR="${SCRIPT_PATH}.bazel"
RUNFILES_DIR="${BAZEL_DIR}/%[1]s.runfiles"
"${BAZEL_DIR}/%[1]s" "$@"
`
// expandPackages expands the package filter into all of the packages that it
// references using `go list`.
func expandPackages(pkgFilter []string) ([]string, error) {
args := []string{"go", "list"}
args = append(args, pkgFilter...)
pkgs, err := capture(args...)
if err != nil {
return nil, errors.Wrap(err, "expanding packages")
}
return strings.Split(pkgs, "\n"), nil
}
// testDir returns the directory to store benchdiff artifacts and binaries for
// specified git ref.
func testDir(ref string) string {
return filepath.Join("benchdiff", ref)
}
// testArtifactsDir returns the directory to store benchdiff artifacts for
// specified git ref.
func testArtifactsDir(ref string) string {
return filepath.Join(testDir(ref), "artifacts")
}
func hash(s []string) string {
h := fnv.New32a()
for _, ss := range s {
h.Write([]byte(ss))
}
u := h.Sum32()
return strconv.Itoa(int(u))
}
// testArtifactsDir returns the directory to store benchdiff binaries for
// specified git ref.
func testBinDir(ref string, pkgFilter []string) string {
return filepath.Join(testDir(ref), "bin", hash(pkgFilter))
}
// pkgToTestBin translates a Go package name into a test binary name.
func pkgToTestBin(pkg string) string {
// Strip github.com prefix.
f := strings.TrimPrefix(pkg, "github.com")
// Turn forward-slashes into underscores.
f = strings.ReplaceAll(f, "/", "_")
// Trim leading underscores.
return strings.TrimLeft(f, "_")
}
// testBinToPkg translates a test binary name to a Go package name. This
// tranlation does not round-trip, but comes close enough.
func testBinToPkg(bin string) string {
return strings.ReplaceAll(bin, "_", "/")
}
// buildTestBinWithGo builds a test binary, using Go directly, for the specified
// package and moves it to the destination directory if successful.
func buildTestBinWithGo(pkg, dst string) (string, bool, error) {
dstFile := pkgToTestBin(pkg) // cockroachdb_cockroach_pkg_util_log
// Capture to silence warnings from pkgs with no test files.
if _, err := capture("go", "test", "-c", "-o", dstFile, pkg); err != nil {
return "", false, errors.Wrap(err, "building test binary")
}
// If there were no tests in the package, no file will have been created.
if _, err := os.Stat(dstFile); err != nil {
if os.IsNotExist(err) {
return "", false, nil
}
return "", false, errors.Wrap(err, "looking for test binary")
}
if err := spawn("mv", dstFile, filepath.Join(dst, dstFile)); err != nil {
return "", false, errors.Wrap(err, "moving test binary")
}
return dstFile, true, nil
}
// buildTestBinWithBazel builds a test binary, using Bazel, for the specified
// package. It creates an executable script inplace of a binary that will invoke
// the test binary with the correct runfiles, stored in a `<dst>.bazel`
// directory alongside it.
func buildTestBinWithBazel(pkg, dst string) (string, bool, error) {
dstBin := pkgToTestBin(pkg) // cockroachdb_cockroach_pkg_util_log
dstBazelDir := filepath.Join(dst, dstBin+".bazel")
relPkg := strings.TrimPrefix(pkg, "github.com/cockroachdb/cockroach/")
pathList := strings.Split(relPkg, string(filepath.Separator)) // ['pkg','util','log']
last := pathList[len(pathList)-1] // 'log'
// `bazel build //pkg/util/log:log_test`.
if _, err := capture("bazel", "build", "//"+relPkg+":"+last+"_test"); err != nil {
return "", false, errors.Wrap(err, "building test binary")
}
// `_bazel/bin/pkg/util/log/log_test_`.
outDir := append([]string{"_bazel", "bin"}, pathList...)
outDir = append(outDir, last+"_test_")
// `_bazel/bin/pkg/util/log/log_test_/log_test`.
srcBin := filepath.Join(filepath.Join(outDir...), last+"_test")
// `_bazel/bin/pkg/util/log/log_test_/log_test.runfiles`.
srcRunfilesDir := filepath.Join(filepath.Join(outDir...), filepath.Base(srcBin)+".runfiles")
// If there were no tests in the package, no test binary file will have been
// created.
if _, err := os.Stat(srcBin); err != nil {
if os.IsNotExist(err) {
return "", false, nil
}
return "", false, errors.Wrap(err, "looking for test binary")
}
if err := os.Mkdir(dstBazelDir, 0755); err != nil {
return "", false, errors.Wrap(err, "creating bazel binary directory")
}
if err := spawn("cp", "-rL", srcBin, srcRunfilesDir, dstBazelDir); err != nil {
return "", false, errors.Wrap(err, "copying binary and bazel runfiles")
}
runScript := fmt.Sprintf(bazelRunScript, filepath.Base(srcBin))
if err := writeExecutableScript(runScript, filepath.Join(dst, dstBin)); err != nil {
return "", false, errors.Wrap(err, "writing bazel binary script")
}
return dstBin, true, nil
}
func writeExecutableScript(script, path string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(script)
return err
}