Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
voxeljorge committed Oct 23, 2024
1 parent b1a4cc1 commit c322537
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 11 deletions.
10 changes: 10 additions & 0 deletions go/tools/builders/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ go_test(
],
)

go_test(
name = "env_test",
size = "small",
srcs = [
"env.go",
"env_test.go",
"flags.go",
],
)

filegroup(
name = "builder_srcs",
srcs = [
Expand Down
21 changes: 16 additions & 5 deletions go/tools/builders/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,27 @@ func main() {
if err != nil {
log.Fatal(err)
}
if len(args) == 0 {

verb := verbFromName(os.Args[0])
if verb == "" && len(args) == 0 {
log.Fatalf("usage: %s verb options...", os.Args[0])
}
verb, rest := args[0], args[1:]

var rest []string
if verb == "" {
verb, rest = args[0], args[1:]
} else {
rest = args
}

var action func(args []string) error
switch verb {
case "compilepkg": action = compilePkg
case "nogo": action = nogo
case "nogovalidation": action = nogoValidation
case "compilepkg":
action = compilePkg
case "nogo":
action = nogo
case "nogovalidation":
action = nogoValidation
case "filterbuildid":
action = filterBuildID
case "gentestmain":
Expand Down
13 changes: 7 additions & 6 deletions go/tools/builders/cc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@ import (
func cc(args []string) error {
cc := os.Getenv("GO_CC")
if cc == "" {
errors.New("GO_CC environment variable not set")
return errors.New("GO_CC environment variable not set")
}
ccroot := os.Getenv("GO_CC_ROOT")
if ccroot == "" {
errors.New("GO_CC_ROOT environment variable not set")
return errors.New("GO_CC_ROOT environment variable not set")
}

normalized := []string{cc}
normalized = append(normalized, args...)
transformArgs(normalized, cgoAbsEnvFlags, func(s string) string {
if strings.HasPrefix(s, cgoAbsPlaceholder) {
trimmed := strings.TrimPrefix(s, cgoAbsPlaceholder)
abspath := filepath.Join(ccroot, trimmed)
if _, err := os.Stat(abspath); err == nil {
// Only return the abspath if it exists, otherwise it
// means that either it won't have any effect or the original
// value was not a relpath (e.g. a path with a XCODE placehold from
// macos cc_wrapper)
// Only return the abspath if it exists, otherwise it
// means that either it won't have any effect or the original
// value was not a relpath (e.g. a path with a XCODE placehold from
// macos cc_wrapper)
return abspath
}
return trimmed
Expand Down
81 changes: 81 additions & 0 deletions go/tools/builders/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
Expand Down Expand Up @@ -162,6 +163,86 @@ func (e *env) runCommandToFile(out, err io.Writer, args []string) error {
return runAndLogCommand(cmd, e.verbose)
}

func symlinkBuilderCC() (func(), string, error) {
absPath, err := filepath.Abs(os.Args[0])
if err != nil {
return nil, "", err
}

dirname, err := os.MkdirTemp("", "")
if err != nil {
return nil, "", err
}

// re-use the file extension if there is one (mostly for .exe)
builderPath := filepath.Join(dirname, "builder-cc"+filepath.Ext(os.Args[0]))

if err := os.Symlink(absPath, builderPath); err != nil {
_ = os.RemoveAll(dirname)
return nil, "", err
}

return func() { _ = os.RemoveAll(dirname) }, builderPath, nil
}

var symlinkBuilderName = regexp.MustCompile(`.*builder-(\w+)(\.exe)?$`)

// verbFromName parses the builder verb from the builder name so
// that the builder+verb can be invoked in a way that allows the verb
// to be part of the program name, rather than requring it to be in the
// arg list. this makes it easier to set a program like `builder cc` as
// an environment variable in a way that is more compatible
func verbFromName(arg0 string) string {
matches := symlinkBuilderName.FindAllStringSubmatch(arg0, 1)
if len(matches) != 1 {
return ""
}

if len(matches[0]) < 2 {
return ""
}

return matches[0][1]
}

// absCCLinker sets the target of the `-extld` to a name which `verbFromName`
// can extract correctly, allowing go tool link to be able to use the `builder cc` wrapper
func absCCLinker(argList []string) (func(), error) {
extldIndex := -1
for i, arg := range argList {
if arg == "-extld" && i+1 < len(argList) {
extldIndex = i + 1
break
}
}
if extldIndex < 0 {
// if we don't find extld just return
// a noop cleanup function
return func() {}, nil
}

cleanup, buildercc, err := symlinkBuilderCC()
if err != nil {
return nil, err
}

err = os.Setenv("GO_CC", argList[extldIndex])
if err != nil {
cleanup()
return nil, err
}

err = os.Setenv("GO_CC_ROOT", abs("."))
if err != nil {
cleanup()
return nil, err
}

argList[extldIndex] = buildercc

return cleanup, nil
}

// absCCCompiler modifies CGO flags to workaround relative paths.
// Because go is having its own sandbox, all CGO flags should use
// absolute paths. However, CGO flags are embedded in the output
Expand Down
26 changes: 26 additions & 0 deletions go/tools/builders/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//go:build !windows

package main

import "testing"

func TestVerbFromName(t *testing.T) {
testCases := []struct {
name string
verb string
}{
{"/a/b/c/d/builder", ""},
{"builder", ""},
{"/a/b/c/d/builder-cc", "cc"},
{"builder-ld", "ld"},
{"c:\\builder\\builder.exe", ""},
{"c:\\builder with spaces\\builder-cc.exe", "cc"},
}

for _, tc := range testCases {
result := verbFromName(tc.name)
if result != tc.verb {
t.Fatalf("retrieved invalid verb %q from name %q", result, tc.name)
}
}
}
13 changes: 13 additions & 0 deletions go/tools/builders/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ func link(args []string) error {
}
goargs = append(goargs, "-o", *outFile)

// substitute `builder cc` for the linker with a symlink to builder called `builder-cc`.
// unfortunately we can't just set an environment variable to `builder cc` because
// in `go tool link` the `linkerFlagSupported` [1][2] call sites used to determine
// if a linker supports various flags all appear to use the first arg after splitting
// so the `cc` would be left off of `builder cc`
//
// [1]: https://cs.opensource.google/go/go/+/ad7f736d8f51ea03166b698256385c869968ae3e:src/cmd/link/internal/ld/lib.go;l=1739
// [2]: https://cs.opensource.google/go/go/+/master:src/cmd/link/internal/ld/lib.go;drc=c6531fae589cf3f9475f3567a5beffb4336fe1d6;l=1429?q=linkerFlagSupported&ss=go%2Fgo
linkerCleanup, err := absCCLinker(toolArgs)
if err != nil {
return err
}
defer linkerCleanup()
// add in the unprocess pass through options
goargs = append(goargs, toolArgs...)
goargs = append(goargs, *main)
Expand Down
5 changes: 5 additions & 0 deletions tests/core/cgo/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,11 @@ go_test(
cgo = True,
)

go_bazel_test(
name = "cgo_abs_paths_test",
srcs = ["cgo_abs_paths_test.go"],
)

cc_library(
name = "cgo_link_dep",
srcs = ["cgo_link_dep.c"],
Expand Down
82 changes: 82 additions & 0 deletions tests/core/cgo/cgo_abs_paths_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cgo_abs_paths_test

import (
"strings"
"testing"

"github.com/bazelbuild/rules_go/go/tools/bazel_testing"
)

func TestMain(m *testing.M) {
bazel_testing.TestMain(m, bazel_testing.Args{
Main: `
-- main.go --
package main
/*
#include <stdio.h>
void hello() {
printf("Hello, world!\n");
}
*/
import "C"
func main() {
C.hello()
}
-- BUILD.bazel --
load("@io_bazel_rules_go//go:def.bzl", "go_test", "go_library")
go_library(
name = "example_lib",
srcs = ["main.go"],
cgo = True,
importpath = "example.com/cmd/example",
)
go_test(
name = "example",
embed = [":example_lib"],
pure = "off",
)
`,
SetUp: func() error {
// we have to force a bazel clean because this test needs to
// fully execute every time so that we can actually scan the output
return bazel_testing.RunBazel("clean")
},
})
}

func TestCgoAbsolutePaths(t *testing.T) {
_, stderrBytes, err := bazel_testing.BazelOutputWithInput(nil,
"build",
"--linkopt=-Lan/imaginary/lib",
"--linkopt=-v",
"--copt=-Ian/imaginary/include",
"--copt=-v",
"-s",
"//:example",
)
if err != nil {
t.Fatal(err)
}

stderr := string(stderrBytes)

if strings.Contains(stderr, "__GO_BAZEL_CC_PLACEHOLDER__") {
t.Log(stderr)
t.Fatal("Found absolute path placeholder string in go linker command output")
}

if !strings.Contains(stderr, "-Lan/imaginary/lib") {
t.Log(stderr)
t.Fatal("Could not find --linkopt in go linker command output")
}

if !strings.Contains(stderr, "-Ian/imaginary/include") {
t.Log(stderr)
t.Fatal("Could not find --copt in go compiler command output")
}
}

0 comments on commit c322537

Please sign in to comment.