Skip to content

Commit

Permalink
Translate paths in compilepkg output.
Browse files Browse the repository at this point in the history
When cgo2 is involved in a build, the go srcs are linked/copied
into a temporary directory to keep everything in the same
directory. Those paths are unrecognizable to IDEs, being absolute
paths outside the project directory.

This adds a translation step to replace paths in the compiler output,
translating them back to the input paths that were given to the tool
in the first place.

This doesn't affect -trimpath, which affects runtime error messages
but not compilation-time error messages; for the same reason, this
change can't use that flag to do its dirty work.

Fixes cockroachdb/cockroach#76377.

Release note: None
  • Loading branch information
mari-crl committed Apr 7, 2022
1 parent f96cc04 commit 2a778e9
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 13 deletions.
20 changes: 12 additions & 8 deletions go/tools/builders/cgo2.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

// cgo2 processes a set of mixed source files with cgo.
func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, packagePath, packageName string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags []string, cgoExportHPath string) (srcDir string, allGoSrcs, cObjs []string, err error) {
func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, packagePath, packageName string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags []string, cgoExportHPath string) (allGoSrcsDir string, allGoSrcs []pathPair, cObjs []string, err error) {
// Report an error if the C/C++ toolchain wasn't configured.
if cc == "" {
err := cgoError(cgoSrcs[:])
Expand Down Expand Up @@ -96,7 +96,7 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr

// If cgo sources are in different directories, gather them into a temporary
// directory so we can use -srcdir.
srcDir = filepath.Dir(cgoSrcs[0])
srcDir := filepath.Dir(cgoSrcs[0])
srcsInSingleDir := true
for _, src := range cgoSrcs[1:] {
if filepath.Dir(src) != srcDir {
Expand Down Expand Up @@ -151,13 +151,16 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
return "", nil, nil, err
}
}
genGoSrcs := make([]string, 1+len(cgoSrcs))
genGoSrcs[0] = filepath.Join(workDir, "_cgo_gotypes.go")
// genGoSrcs contains the go source files generated by the cgo compiler plus 2 additional files, one at each end:
// genGoSrcs[0] is the gotypes file, while genGoSrcs[-1] is the imports file.
genGoSrcs := make([]pathPair, 2+len(cgoSrcs))
genGoSrcs[0].workingPath = filepath.Join(workDir, "_cgo_gotypes.go")
genCSrcs := make([]string, 1+len(cgoSrcs))
genCSrcs[0] = filepath.Join(workDir, "_cgo_export.c")
for i, src := range cgoSrcs {
stem := strings.TrimSuffix(filepath.Base(src), ".go")
genGoSrcs[i+1] = filepath.Join(workDir, stem+".cgo1.go")
genGoSrcs[i+1].inputPath = src
genGoSrcs[i+1].workingPath = filepath.Join(workDir, stem+".cgo1.go")
genCSrcs[i+1] = filepath.Join(workDir, stem+".cgo2.c")
}
cgoMainC := filepath.Join(workDir, "_cgo_main.c")
Expand Down Expand Up @@ -200,7 +203,7 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
if err := goenv.runCommand(args); err != nil {
return "", nil, nil, err
}
genGoSrcs = append(genGoSrcs, cgoImportsGo)
genGoSrcs[len(genGoSrcs)-1].workingPath = cgoImportsGo

// Copy regular Go source files into the work directory so that we can
// use -trimpath=workDir.
Expand All @@ -209,9 +212,10 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
return "", nil, nil, err
}

allGoSrcs = make([]string, len(goSrcs)+len(genGoSrcs))
allGoSrcs = make([]pathPair, len(goSrcs)+len(genGoSrcs))
for i := range goSrcs {
allGoSrcs[i] = filepath.Join(workDir, goBases[i])
allGoSrcs[i].inputPath = goSrcs[i]
allGoSrcs[i].workingPath = filepath.Join(workDir, goBases[i])
}
copy(allGoSrcs[len(goSrcs):], genGoSrcs)
return workDir, allGoSrcs, cObjs, nil
Expand Down
13 changes: 9 additions & 4 deletions go/tools/builders/compilepkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,20 @@ func compileArchive(
// If we have cgo, generate separate C and go files, and compile the
// C files.
var objFiles []string
var goSrcsMapping []pathPair
if cgoEnabled && haveCgo {
// TODO(#2006): Compile .s and .S files with cgo2, not the Go assembler.
// If cgo is not enabled or we don't have other cgo sources, don't
// compile .S files.
var srcDir string
srcDir, goSrcs, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, nil, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath)
srcDir, goSrcsMapping, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, nil, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath)
if err != nil {
return err
}
goSrcs = make([]string, len(goSrcsMapping))
for i, v := range goSrcsMapping {
goSrcs[i] = v.workingPath
}

gcFlags = append(gcFlags, fmt.Sprintf("-trimpath=%s=>%s", abs(srcDir), packagePath))
} else {
Expand Down Expand Up @@ -395,7 +400,7 @@ func compileArchive(
}

// Compile the filtered .go files.
if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, outPath); err != nil {
if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, goSrcsMapping, outPath); err != nil {
return err
}

Expand Down Expand Up @@ -465,7 +470,7 @@ func compileArchive(
return appendFiles(goenv, outXPath, []string{pkgDefPath})
}

func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath string, gcFlags []string, outPath string) error {
func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath string, gcFlags []string, paths []pathPair, outPath string) error {
args := goenv.goTool("compile")
args = append(args, "-p", packagePath, "-importcfg", importcfgPath, "-pack")
if embedcfgPath != "" {
Expand All @@ -482,7 +487,7 @@ func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPa
args = append(args, "--")
args = append(args, srcs...)
absArgs(args, []string{"-I", "-o", "-importcfg"})
return goenv.runCommand(args)
return goenv.runCommandAndReplacePaths(args, paths)
}

func runNogo(ctx context.Context, workDir string, nogoPath string, srcs []string, deps []archive, packagePath, importcfgPath, outFactsPath string) error {
Expand Down
41 changes: 40 additions & 1 deletion go/tools/builders/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ type env struct {
shouldPreserveWorkDir bool
}

// pathPair maps the input path given to the builder to the
// working path, usually in a temporary directory, given to
// tools like cgo2.
type pathPair struct {
// inputPath is the path given to the builder.
// Files generated by the builder will have an inputPath of "".
inputPath string
// workingPath is the path given to the tool itself.
workingPath string
}

// envFlags registers flags common to multiple builders and returns an env
// configured with those flags.
func envFlags(flags *flag.FlagSet) *env {
Expand Down Expand Up @@ -128,7 +139,8 @@ func (e *env) goCmd(cmd string, args ...string) []string {
}

// runCommand executes a subprocess that inherits stdout, stderr, and the
// environment from this process.
// environment from this process. Paths under the working directory will
// be relativized in the output.
func (e *env) runCommand(args []string) error {
cmd := exec.Command(args[0], args[1:]...)
// Redirecting stdout to stderr. This mirrors behavior in the go command:
Expand All @@ -141,6 +153,22 @@ func (e *env) runCommand(args []string) error {
return err
}

// runCommandAndReplacePaths executes a subprocess that inherits stdout,
// stderr, and the environment from this process. The stderr stream
// will be filtered to replace the given pairs of paths, and then paths
// under the working directory will be relativized in the output.
func (e *env) runCommandAndReplacePaths(args []string, paths []pathPair) error {
cmd := exec.Command(args[0], args[1:]...)
// Redirecting stdout to stderr. This mirrors behavior in the go command:
// https://go.googlesource.com/go/+/refs/tags/go1.15.2/src/cmd/go/internal/work/exec.go#1958
buf := &bytes.Buffer{}
cmd.Stdout = buf
cmd.Stderr = buf
err := runAndLogCommand(cmd, e.verbose)
os.Stderr.Write(relativizePaths(replacePaths(paths, buf.Bytes())))
return err
}

// runCommandToFile executes a subprocess and writes the output to the given
// writer.
func (e *env) runCommandToFile(w io.Writer, args []string) error {
Expand Down Expand Up @@ -356,6 +384,17 @@ func relativizePaths(output []byte) []byte {
return bytes.ReplaceAll(output, dirBytes, []byte{'.'})
}

// replacePaths converts any instances in data of the destPath of any
// element of the paths to the corresponding sourcePath.
func replacePaths(paths []pathPair, data []byte) []byte {
for _, pair := range paths {
if pair.inputPath != "" && pair.workingPath != pair.inputPath {
data = bytes.ReplaceAll(data, []byte(pair.workingPath), []byte(pair.inputPath))
}
}
return data
}

// formatCommand formats cmd as a string that can be pasted into a shell.
// Spaces in environment variables and arguments are escaped as needed.
func formatCommand(cmd *exec.Cmd) string {
Expand Down

0 comments on commit 2a778e9

Please sign in to comment.