diff --git a/go/tools/builders/cgo2.go b/go/tools/builders/cgo2.go index 0294ec4c58..ca3de4954b 100644 --- a/go/tools/builders/cgo2.go +++ b/go/tools/builders/cgo2.go @@ -169,12 +169,12 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr } hdrIncludes = append(hdrIncludes, "-iquote", workDir) // for _cgo_export.h - execRoot, err := bazelExecRoot() + // Trim the path in //line comments emitted by cgo. + trimPath, err := createTrimPath() if err != nil { return "", nil, nil, err } - // Trim the execroot from the //line comments emitted by cgo. - args := goenv.goTool("cgo", "-srcdir", srcDir, "-objdir", workDir, "-trimpath", execRoot) + args := goenv.goTool("cgo", "-srcdir", srcDir, "-objdir", workDir, "-trimpath", trimPath) if ldflagsFile != nil { // The "@" prefix tells cgo to read arguments from the file. args = append(args, "-ldflags", "@"+ldflagsFile.Name()) @@ -421,18 +421,6 @@ func gatherSrcs(dir string, srcs []string) ([]string, error) { return copiedBases, nil } -func bazelExecRoot() (string, error) { - // Bazel executes the builder with a working directory of the form - // .../execroot/. By stripping the last segment, we obtain a - // prefix of all possible source files, even when contained in external - // repositories. - cwd, err := os.Getwd() - if err != nil { - return "", err - } - return filepath.Dir(cwd), nil -} - type cgoError []string func (e cgoError) Error() string { diff --git a/go/tools/builders/compilepkg.go b/go/tools/builders/compilepkg.go index 110a63824b..e2b0be5461 100644 --- a/go/tools/builders/compilepkg.go +++ b/go/tools/builders/compilepkg.go @@ -353,14 +353,35 @@ func compileArchive( return err } } - gcFlags = append(gcFlags, createTrimPath(gcFlags, srcDir)) + gcFlags = append(gcFlags, "-trimpath="+srcDir) } else { if cgoExportHPath != "" { if err := os.WriteFile(cgoExportHPath, nil, 0o666); err != nil { return err } } - gcFlags = append(gcFlags, createTrimPath(gcFlags, ".")) + trimPath, err := createTrimPath() + if err != nil { + return err + } + // Preserve an existing -trimpath argument, applying abs() to each prefix. + for i, flag := range gcFlags { + if strings.HasPrefix(flag, "-trimpath=") { + gcFlags = append(gcFlags[:i], gcFlags[i+1:]...) + rewrites := strings.Split(flag[len("-trimpath="):], ";") + for j, rewrite := range rewrites { + prefix, replace := rewrite, "" + if p := strings.LastIndex(rewrite, "=>"); p >= 0 { + prefix, replace = rewrite[:p], rewrite[p:] + } + rewrites[j] = abs(prefix) + replace + } + rewrites = append(rewrites, trimPath) + trimPath = strings.Join(rewrites, ";") + break + } + } + gcFlags = append(gcFlags, "-trimpath="+trimPath) } importcfgPath, err := checkImportsAndBuildCfg(goenv, importPath, srcs, deps, packageListPath, recompileInternalDeps, compilingWithCgo, coverMode, workDir) @@ -531,7 +552,7 @@ func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPa args = append(args, "-linkobj", outLinkobjPath) args = append(args, "--") args = append(args, srcs...) - absArgs(args, []string{"-I", "-o", "-trimpath", "-importcfg"}) + absArgs(args, []string{"-I", "-o", "-importcfg"}) return goenv.runCommand(args) } @@ -542,14 +563,16 @@ func appendToArchive(goenv *env, pack, outPath string, objFiles []string) error return goenv.runCommand(args) } -func createTrimPath(gcFlags []string, path string) string { - for _, flag := range gcFlags { - if strings.HasPrefix(flag, "-trimpath=") { - return flag + ":" + path - } +func createTrimPath() (string, error) { + trimPath, err := os.Getwd() + if err != nil { + return "", err } - - return "-trimpath=" + path + // Create a trim path to make paths relative to the working directory. + // First, attempt to trim the working directory, and if this fails, replace + // the parent of the working directory with "..". + trimPath = fmt.Sprintf("%s;%s=>..", trimPath, filepath.Dir(trimPath)) + return trimPath, nil } func sanitizePathForIdentifier(path string) string { diff --git a/tests/core/go_library/BUILD.bazel b/tests/core/go_library/BUILD.bazel index a3d60128d6..08c0881512 100644 --- a/tests/core/go_library/BUILD.bazel +++ b/tests/core/go_library/BUILD.bazel @@ -208,3 +208,8 @@ go_library( "//tests/core/go_test:__pkg__", ], ) + +go_bazel_test( + name = "trimpath_test", + srcs = ["trimpath_test.go"], +) diff --git a/tests/core/go_library/README.rst b/tests/core/go_library/README.rst index 4410902fe8..ba5af96eaa 100644 --- a/tests/core/go_library/README.rst +++ b/tests/core/go_library/README.rst @@ -7,6 +7,7 @@ Basic go_library functionality .. _#1772: https://github.com/bazelbuild/rules_go/issues/1772 .. _#2058: https://github.com/bazelbuild/rules_go/issues/2058 .. _#3558: https://github.com/bazelbuild/rules_go/issues/3558 +.. _#4434: https://github.com/bazel-contrib/rules_go/issues/4434 empty ----- @@ -54,4 +55,9 @@ no_srcs_test ------------ Verifies that `go_library`_ targets without Go source files build concurrently, -even unsandboxed, and reproducibly. Verifies `#3558`_. \ No newline at end of file +even unsandboxed, and reproducibly. Verifies `#3558`_. + +trimpath_test +------------- + +Verifies that trimpath has the expected effect on paths. Verifies `#4434`_. diff --git a/tests/core/go_library/trimpath_test.go b/tests/core/go_library/trimpath_test.go new file mode 100644 index 0000000000..a3311eb2dd --- /dev/null +++ b/tests/core/go_library/trimpath_test.go @@ -0,0 +1,163 @@ +// Copyright 2025 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package trimpath_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "maincgo", + srcs = ["maincgo.go"], + cgo = True, + importpath = "example.com/main_repo/maincgo", + visibility = ["//visibility:private"], +) + +go_library( + name = "main_lib", + srcs = ["main.go"], + deps = [":maincgo", "@other_repo//:other", "@other_repo//:othercgo"], + importpath = "example.com/main_repo/main", + visibility = ["//visibility:private"], +) + +go_binary( + name = "main", + embed = [":main_lib"], + visibility = ["//visibility:public"], +) +-- main.go -- +package main + +import "runtime" +import "fmt" +import "example.com/main_repo/maincgo" +import "example.com/other_repo/other" +import "example.com/other_repo/othercgo" + +func File() string { + _, file, _, _ := runtime.Caller(0) + return file +} + +func main() { + fmt.Println(File()) + fmt.Println(maincgo.File()) + fmt.Println(other.File()) + fmt.Println(othercgo.File()) +} +-- maincgo.go -- +package maincgo + +import "C" +import "runtime" + +func File() string { + _, file, _, _ := runtime.Caller(0) + return file +} +-- other_repo/WORKSPACE -- +-- other_repo/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "other", + srcs = ["other.go"], + importpath = "example.com/other_repo/other", + visibility = ["//visibility:public"], +) + +go_library( + name = "othercgo", + srcs = ["othercgo.go"], + cgo = True, + importpath = "example.com/other_repo/othercgo", + visibility = ["//visibility:public"], +) +-- other_repo/other.go -- +package other + +import "runtime" + +func File() string { + _, file, _, _ := runtime.Caller(0) + return file +} +-- other_repo/othercgo.go -- +package othercgo + +import "C" +import "runtime" + +func File() string { + _, file, _, _ := runtime.Caller(0) + return file +} +`, + WorkspaceSuffix: ` +local_repository( + name = "other_repo", + path = "other_repo", +) +`, + }) +} + +// These are the expected paths after applying trimpath. +var expectedDefault = ` +main.go +maincgo.go +external/other_repo/other.go +external/other_repo/othercgo.go +` + +var expectedSibling = ` +main.go +maincgo.go +../other_repo/other.go +../other_repo/othercgo.go +` + +func TestTrimpath(t *testing.T) { + t.Run("default", func(t *testing.T) { + out, err := bazel_testing.BazelOutput("run", "//:main") + if err != nil { + t.Fatal(err) + } + outStr := "\n" + string(out) + if outStr != expectedDefault { + t.Fatal("actual", outStr, "vs expected", expectedDefault) + } + }) + t.Run("experimental_sibling_repository_layout", func(t *testing.T) { + out, err := bazel_testing.BazelOutput("run", "--experimental_sibling_repository_layout", "//:main") + if err != nil { + t.Fatal(err) + } + outStr := "\n" + string(out) + if outStr != expectedSibling { + t.Fatal("actual", outStr, "vs expected", expectedSibling) + } + }) +} diff --git a/tests/core/go_plugin_with_proto_library/BUILD.bazel b/tests/core/go_plugin_with_proto_library/BUILD.bazel index 468d8b359d..25a31b8b1f 100644 --- a/tests/core/go_plugin_with_proto_library/BUILD.bazel +++ b/tests/core/go_plugin_with_proto_library/BUILD.bazel @@ -25,7 +25,7 @@ proto_library( go_proto_library( name = "validate", - gc_goopts = ["-trimpath=$(BINDIR)=>."], + gc_goopts = ["-trimpath=$(BINDIR)"], importpath = "go_plugin_with_proto_library/validate", proto = ":validate_proto", )