Skip to content

Commit 929ae31

Browse files
committed
Translate paths in compilepkg output.
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
1 parent 1c41dc5 commit 929ae31

File tree

3 files changed

+67
-15
lines changed

3 files changed

+67
-15
lines changed

go/tools/builders/cgo2.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333
)
3434

3535
// cgo2 processes a set of mixed source files with cgo.
36-
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, cgoGoSrcsPath string) (srcDir string, allGoSrcs, cObjs []string, err error) {
36+
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, cgoGoSrcsPath string) (allGoSrcsDir string, allGoSrcs []pathPair, cObjs []string, err error) {
3737
// Report an error if the C/C++ toolchain wasn't configured.
3838
if cc == "" {
3939
err := cgoError(cgoSrcs[:])
@@ -132,7 +132,7 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
132132

133133
// If cgo sources are in different directories, gather them into a temporary
134134
// directory so we can use -srcdir.
135-
srcDir = filepath.Dir(cgoSrcs[0])
135+
srcDir := filepath.Dir(cgoSrcs[0])
136136
srcsInSingleDir := true
137137
for _, src := range cgoSrcs[1:] {
138138
if filepath.Dir(src) != srcDir {
@@ -199,13 +199,16 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
199199
// Note: The tools/gopackagesdriver will break if the naming convention
200200
// changes for genGoSrcs files. Changes here should be reflected
201201
// there.
202-
genGoSrcs := make([]string, 1+len(cgoSrcs))
203-
genGoSrcs[0] = filepath.Join(workDir, "_cgo_gotypes.go")
202+
// genGoSrcs contains the go source files generated by the cgo compiler plus 2 additional files, one at each end:
203+
// genGoSrcs[0] is the gotypes file, while genGoSrcs[-1] is the imports file.
204+
genGoSrcs := make([]pathPair, 2+len(cgoSrcs))
205+
genGoSrcs[0].workingPath = filepath.Join(workDir, "_cgo_gotypes.go")
204206
genCSrcs := make([]string, 1+len(cgoSrcs))
205207
genCSrcs[0] = filepath.Join(workDir, "_cgo_export.c")
206208
for i, src := range cgoSrcs {
207209
stem := strings.TrimSuffix(filepath.Base(src), ".go")
208-
genGoSrcs[i+1] = filepath.Join(workDir, stem+".cgo1.go")
210+
genGoSrcs[i+1].inputPath = src
211+
genGoSrcs[i+1].workingPath = filepath.Join(workDir, stem+".cgo1.go")
209212
genCSrcs[i+1] = filepath.Join(workDir, stem+".cgo2.c")
210213
}
211214
cgoMainC := filepath.Join(workDir, "_cgo_main.c")
@@ -279,10 +282,10 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
279282
if err := goenv.runCommand(args); err != nil {
280283
return "", nil, nil, err
281284
}
282-
genGoSrcs = append(genGoSrcs, cgoImportsGo)
285+
genGoSrcs[len(genGoSrcs)-1].workingPath = cgoImportsGo
283286
if cgoGoSrcsPath != "" {
284287
for _, src := range genGoSrcs {
285-
if err := copyFile(src, filepath.Join(cgoGoSrcsPath, filepath.Base(src))); err != nil {
288+
if err := copyFile(src.workingPath, filepath.Join(cgoGoSrcsPath, filepath.Base(src.workingPath))); err != nil {
286289
return "", nil, nil, err
287290
}
288291
}
@@ -295,9 +298,10 @@ func cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSr
295298
return "", nil, nil, err
296299
}
297300

298-
allGoSrcs = make([]string, len(goSrcs)+len(genGoSrcs))
301+
allGoSrcs = make([]pathPair, len(goSrcs)+len(genGoSrcs))
299302
for i := range goSrcs {
300-
allGoSrcs[i] = filepath.Join(workDir, goBases[i])
303+
allGoSrcs[i].inputPath = goSrcs[i]
304+
allGoSrcs[i].workingPath = filepath.Join(workDir, goBases[i])
301305
}
302306
copy(allGoSrcs[len(goSrcs):], genGoSrcs)
303307
return workDir, allGoSrcs, cObjs, nil

go/tools/builders/compilepkg.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,15 +329,20 @@ func compileArchive(
329329
// If we have cgo, generate separate C and go files, and compile the
330330
// C files.
331331
var objFiles []string
332+
var goSrcsMapping []pathPair
332333
if compilingWithCgo {
333334
var srcDir string
334335
if coverMode != "" && cgoGoSrcsForNogoPath != "" {
335336
// If the package uses Cgo, compile .s and .S files with cgo2, not the Go assembler.
336337
// Otherwise: the .s/.S files will be compiled with the Go assembler later
337-
srcDir, goSrcs, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath, "")
338+
srcDir, goSrcsMapping, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath, "")
338339
if err != nil {
339340
return err
340341
}
342+
goSrcs = make([]string, len(goSrcsMapping))
343+
for i, v := range goSrcsMapping {
344+
goSrcs[i] = v.workingPath
345+
}
341346
// Also run cgo on original source files, not coverage instrumented, if using nogo.
342347
// The compilation outputs are only used to run cgo, but the generated sources are
343348
// passed to the separate nogo action via cgoGoSrcsForNogoPath.
@@ -348,10 +353,14 @@ func compileArchive(
348353
} else {
349354
// If the package uses Cgo, compile .s and .S files with cgo2, not the Go assembler.
350355
// Otherwise: the .s/.S files will be compiled with the Go assembler later
351-
srcDir, goSrcs, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath, cgoGoSrcsForNogoPath)
356+
srcDir, goSrcsMapping, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath, cgoGoSrcsForNogoPath)
352357
if err != nil {
353358
return err
354359
}
360+
goSrcs = make([]string, len(goSrcsMapping))
361+
for i, v := range goSrcsMapping {
362+
goSrcs[i] = v.workingPath
363+
}
355364
}
356365
gcFlags = append(gcFlags, createTrimPath(gcFlags, srcDir))
357366
} else {
@@ -421,7 +430,7 @@ func compileArchive(
421430
}
422431

423432
// Compile the filtered .go files.
424-
if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, pgoprofile, outLinkObj, outInterfacePath, coverageCfg); err != nil {
433+
if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, goSrcsMapping, pgoprofile, outLinkObj, outInterfacePath, coverageCfg); err != nil {
425434
return err
426435
}
427436

@@ -508,7 +517,7 @@ func checkImportsAndBuildCfg(goenv *env, importPath string, srcs archiveSrcs, de
508517
return importcfgPath, nil
509518
}
510519

511-
func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath string, gcFlags []string, pgoprofile, outLinkobjPath, outInterfacePath, coverageCfg string) error {
520+
func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath string, gcFlags []string, paths []pathPair, pgoprofile, outLinkobjPath, outInterfacePath, coverageCfg string) error {
512521
args := goenv.goTool("compile")
513522
args = append(args, "-p", packagePath, "-importcfg", importcfgPath, "-pack")
514523
if embedcfgPath != "" {
@@ -532,7 +541,7 @@ func compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPa
532541
args = append(args, "--")
533542
args = append(args, srcs...)
534543
absArgs(args, []string{"-I", "-o", "-trimpath", "-importcfg"})
535-
return goenv.runCommand(args)
544+
return goenv.runCommandAndReplacePaths(args, paths)
536545
}
537546

538547
func appendToArchive(goenv *env, pack, outPath string, objFiles []string) error {

go/tools/builders/env.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ type env struct {
6868
shouldPreserveWorkDir bool
6969
}
7070

71+
// pathPair maps the input path given to the builder to the
72+
// working path, usually in a temporary directory, given to
73+
// tools like cgo2.
74+
type pathPair struct {
75+
// inputPath is the path given to the builder.
76+
// Files generated by the builder will have an inputPath of "".
77+
inputPath string
78+
// workingPath is the path given to the tool itself.
79+
workingPath string
80+
}
81+
7182
// envFlags registers flags common to multiple builders and returns an env
7283
// configured with those flags.
7384
func envFlags(flags *flag.FlagSet) *env {
@@ -141,7 +152,8 @@ func (e *env) goCmd(cmd string, args ...string) []string {
141152
}
142153

143154
// runCommand executes a subprocess that inherits stdout, stderr, and the
144-
// environment from this process.
155+
// environment from this process. Paths under the working directory will
156+
// be relativized in the output.
145157
func (e *env) runCommand(args []string) error {
146158
cmd := exec.Command(args[0], args[1:]...)
147159
// Redirecting stdout to stderr. This mirrors behavior in the go command:
@@ -154,6 +166,22 @@ func (e *env) runCommand(args []string) error {
154166
return err
155167
}
156168

169+
// runCommandAndReplacePaths executes a subprocess that inherits stdout,
170+
// stderr, and the environment from this process. The stderr stream
171+
// will be filtered to replace the given pairs of paths, and then paths
172+
// under the working directory will be relativized in the output.
173+
func (e *env) runCommandAndReplacePaths(args []string, paths []pathPair) error {
174+
cmd := exec.Command(args[0], args[1:]...)
175+
// Redirecting stdout to stderr. This mirrors behavior in the go command:
176+
// https://go.googlesource.com/go/+/refs/tags/go1.15.2/src/cmd/go/internal/work/exec.go#1958
177+
buf := &bytes.Buffer{}
178+
cmd.Stdout = buf
179+
cmd.Stderr = buf
180+
err := runAndLogCommand(cmd, e.verbose)
181+
os.Stderr.Write(relativizePaths(replacePaths(paths, buf.Bytes())))
182+
return err
183+
}
184+
157185
// runCommandToFile executes a subprocess and writes stdout/stderr to the given
158186
// writers.
159187
func (e *env) runCommandToFile(out, err io.Writer, args []string) error {
@@ -480,6 +508,17 @@ func relativizePaths(output []byte) []byte {
480508
return bytes.ReplaceAll(output, dirBytes, []byte{'.'})
481509
}
482510

511+
// replacePaths converts any instances in data of the destPath of any
512+
// element of the paths to the corresponding sourcePath.
513+
func replacePaths(paths []pathPair, data []byte) []byte {
514+
for _, pair := range paths {
515+
if pair.inputPath != "" && pair.workingPath != pair.inputPath {
516+
data = bytes.ReplaceAll(data, []byte(pair.workingPath), []byte(pair.inputPath))
517+
}
518+
}
519+
return data
520+
}
521+
483522
// formatCommand formats cmd as a string that can be pasted into a shell.
484523
// Spaces in environment variables and arguments are escaped as needed.
485524
func formatCommand(cmd *exec.Cmd) string {

0 commit comments

Comments
 (0)