Skip to content

Commit

Permalink
compilepkg: fix race when run without sandbox
Browse files Browse the repository at this point in the history
On platforms without sandbox execution support such as Windows, multiple
go_test targets declared in the same package would cause a race condition.

```
[
    go_test(
        name = analyzer + "_test",
        srcs = ["analyzer_test.go"],
        embed = [analyzer],
    )
    for analyzer in ANALYZERS
]
```

For each of go_test targets, there would be internal archive and
external archive that needs to compile from source files.  However, due
to testfilter, external archive would end up without any Go source and
thus compilepkg needs to generate a dummy source file `_empty.go` to
trick 'go tool compile'.

This `_empty.go` file is generated in the output path of the Bazel
package thus multiple go_test targets in the same package would generate
this file using the same path.  On a sandbox supported platform,
this is not a problem as the full path is prefixed with the sandbox root
dir.  However, without sandbox, the full path is the same for each
go_test's external archive compilation, leading to a race condition
where multiple compilations running in parallel would compete to
create/delete the same file.

Fix this by using the unique importPath to create a directory to store
the _empty.go file.  This would effectively give each go_test compilation
it's own _empty.go file in non-sandbox environment.
  • Loading branch information
sluongng committed May 11, 2022
1 parent d10b680 commit e1a17c4
Showing 1 changed file with 13 additions and 3 deletions.
16 changes: 13 additions & 3 deletions go/tools/builders/compilepkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,19 +188,29 @@ func compileArchive(
// We need to run the compiler to create a valid archive, even if there's
// nothing in it. GoPack will complain if we try to add assembly or cgo
// objects.
//
// _empty.go needs to be in a deterministic location (not tmpdir) in order
// to ensure deterministic output
emptyPath := filepath.Join(filepath.Dir(outPath), "_empty.go")
// to ensure deterministic output. The location also needs to be unique
// otherwise platforms without sandbox support may race to create/remove
// the file during parallel compilation.
emptyDir := filepath.Join(filepath.Dir(outPath), sanitizePathForIdentifier(importPath))
err := os.Mkdir(emptyDir, 0700)
if err != nil {
return err
}
defer os.RemoveAll(emptyDir)
emptyPath := filepath.Join(emptyDir, "_empty.go")
if err := ioutil.WriteFile(emptyPath, []byte("package empty\n"), 0666); err != nil {
return err
}
defer os.Remove(emptyPath)

srcs.goSrcs = append(srcs.goSrcs, fileInfo{
filename: emptyPath,
ext: goExt,
matched: true,
pkg: "empty",
})
defer os.Remove(emptyPath)
}
packageName := srcs.goSrcs[0].pkg
var goSrcs, cgoSrcs []string
Expand Down

0 comments on commit e1a17c4

Please sign in to comment.