cmd/go: pass in overlaid file paths to C compiler

This change moves the code in work.(*Builder).cgo that, when there is
an overlay, copies non-Go files to objdir into work.(*Builder).Build,
and creates an overlay structure mapping from the nominal file paths
into the copies in objdir. That's propagated through to
work.(*Builder).ccompile, which will use it to pass in the path to the
overlaid contents in objdir when calling the compiler.

This allows for overlays of C/C++/Fortran files.

For #39958

Change-Id: I9a2e3d3ba6afdf7ce19be1dbf4eee34805cdc05f
Reviewed-on: https://go-review.googlesource.com/c/go/+/266376
Trust: Michael Matloob <matloob@golang.org>
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go
index 825e763..f461c57 100644
--- a/src/cmd/go/internal/work/action.go
+++ b/src/cmd/go/internal/work/action.go
@@ -93,11 +93,12 @@
 	output    []byte     // output redirect buffer (nil means use b.Print)
 
 	// Execution state.
-	pending   int         // number of deps yet to complete
-	priority  int         // relative execution priority
-	Failed    bool        // whether the action failed
-	json      *actionJSON // action graph information
-	traceSpan *trace.Span
+	pending      int               // number of deps yet to complete
+	priority     int               // relative execution priority
+	Failed       bool              // whether the action failed
+	json         *actionJSON       // action graph information
+	nonGoOverlay map[string]string // map from non-.go source files to copied files in objdir. Nil if no overlay is used.
+	traceSpan    *trace.Span
 }
 
 // BuildActionID returns the action ID section of a's build ID.
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index eb76ad4..2c40a4b 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -538,6 +538,34 @@
 		}
 	}
 
+	// Compute overlays for .c/.cc/.h/etc. and if there are any overlays
+	// put correct contents of all those files in the objdir, to ensure
+	// the correct headers are included. nonGoOverlay is the overlay that
+	// points from nongo files to the copied files in objdir.
+	nonGoFileLists := [][]string{a.Package.CFiles, a.Package.SFiles, a.Package.CXXFiles, a.Package.HFiles, a.Package.FFiles}
+OverlayLoop:
+	for _, fs := range nonGoFileLists {
+		for _, f := range fs {
+			if _, ok := fsys.OverlayPath(mkAbs(p.Dir, f)); ok {
+				a.nonGoOverlay = make(map[string]string)
+				break OverlayLoop
+			}
+		}
+	}
+	if a.nonGoOverlay != nil {
+		for _, fs := range nonGoFileLists {
+			for i := range fs {
+				from := mkAbs(p.Dir, fs[i])
+				opath, _ := fsys.OverlayPath(from)
+				dst := objdir + filepath.Base(fs[i])
+				if err := b.copyFile(dst, opath, 0666, false); err != nil {
+					return err
+				}
+				a.nonGoOverlay[from] = dst
+			}
+		}
+	}
+
 	// Run SWIG on each .swig and .swigcxx file.
 	// Each run will generate two files, a .go file and a .c or .cxx file.
 	// The .go file will use import "C" and is to be processed by cgo.
@@ -2269,7 +2297,11 @@
 		}
 	}
 
-	output, err := b.runOut(a, filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file))
+	overlayPath := file
+	if p, ok := a.nonGoOverlay[overlayPath]; ok {
+		overlayPath = p
+	}
+	output, err := b.runOut(a, filepath.Dir(overlayPath), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(overlayPath))
 	if len(output) > 0 {
 		// On FreeBSD 11, when we pass -g to clang 3.8 it
 		// invokes its internal assembler with -dwarf-version=2.
@@ -2655,8 +2687,6 @@
 		cfiles = append(cfiles, f+".cgo2.c")
 	}
 
-	hfiles := append([]string{}, p.HFiles...)
-
 	// TODO: make cgo not depend on $GOARCH?
 
 	cgoflags := []string{}
@@ -2703,35 +2733,6 @@
 
 	execdir := p.Dir
 
-	// If any of the Cgo, C, or H files are overlaid, copy them all to
-	// objdir to ensure that they refer to the right header files.
-	// TODO(#39958): Ideally, we'd always do this, but this could
-	// subtly break some cgo files that include .h files across directory
-	// boundaries, even though they shouldn't.
-	hasOverlay := false
-	cgoFileLists := [][]string{gccfiles, gxxfiles, mfiles, ffiles, hfiles}
-OverlayLoop:
-	for _, fs := range cgoFileLists {
-		for _, f := range fs {
-			if _, ok := fsys.OverlayPath(mkAbs(p.Dir, f)); ok {
-				hasOverlay = true
-				break OverlayLoop
-			}
-		}
-	}
-	if hasOverlay {
-		execdir = objdir
-		for _, fs := range cgoFileLists {
-			for i := range fs {
-				opath, _ := fsys.OverlayPath(mkAbs(p.Dir, fs[i]))
-				fs[i] = objdir + filepath.Base(fs[i])
-				if err := b.copyFile(fs[i], opath, 0666, false); err != nil {
-					return nil, nil, err
-				}
-			}
-		}
-	}
-
 	// Rewrite overlaid paths in cgo files.
 	// cgo adds //line and #line pragmas in generated files with these paths.
 	var trimpath []string
diff --git a/src/cmd/go/testdata/script/build_overlay.txt b/src/cmd/go/testdata/script/build_overlay.txt
index f9487da..58c0de9 100644
--- a/src/cmd/go/testdata/script/build_overlay.txt
+++ b/src/cmd/go/testdata/script/build_overlay.txt
@@ -106,6 +106,7 @@
 		"printpath/main.go": "overlay/printpath.go",
 		"printpath/other.go": "overlay2/printpath2.go",
 		"cgo_hello_replace/cgo_header.h": "overlay/cgo_head.h",
+		"cgo_hello_replace/hello.c": "overlay/hello.c",
 		"cgo_hello_quote/cgo_hello.go": "overlay/cgo_hello_quote.go",
 		"cgo_hello_quote/cgo_header.h": "overlay/cgo_head.h",
 		"cgo_hello_angle/cgo_hello.go": "overlay/cgo_hello_angle.go",
@@ -125,10 +126,10 @@
  // Test that this header is replaced with one that has the proper declaration.
 void say_goodbye();
 
--- m/cgo_hello_replace/goodbye.c --
+-- m/cgo_hello_replace/hello.c --
 #include <stdio.h>
 
-void say_hello() { puts("hello cgo\n"); fflush(stdout); }
+void say_goodbye() { puts("goodbye cgo\n"); fflush(stdout); }
 
 -- m/overlay/f.go --
 package main
@@ -204,6 +205,14 @@
 }
 -- m/overlay/cgo_head.h --
 void say_hello();
+-- m/overlay/hello.c --
+#include <stdio.h>
+
+void say_hello() { puts("hello cgo\n"); fflush(stdout); }
+-- m/overlay/asm_file.s --
+TEXT ·foo(SB),0,$0
+	RET
+
 -- m/cgo_hello_quote/hello.c --
 #include <stdio.h>
 
@@ -242,4 +251,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}