compiler_wrapper: automatic sync

This CL automatically brings toolchain-utils' compiler_wrapper/
directory in sync with chromiumos-overlay's. Please see
go/crostc-repo/+/main:sync_compiler_wrapper_within_cros.sh for more info
on this process.

BUG=None
TEST=None

Change-Id: I81b8f6c5c407b414f647f525fffd6fd7bcc446e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5359381
Commit-Queue: George Burgess <[email protected]>
Tested-by: mobiletc-prebuild Role Account <[email protected]>
Auto-Submit: mobiletc-prebuild Role Account <[email protected]>
Reviewed-by: George Burgess <[email protected]>
diff --git a/compiler_wrapper/README.md b/compiler_wrapper/README.md
index 7a05c81..38ff8a6 100644
--- a/compiler_wrapper/README.md
+++ b/compiler_wrapper/README.md
@@ -2,12 +2,9 @@
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 
+### What
 Toolchain utils compiler wrapper sources.
 
-Build the wrapper:
-./build.py --config=<config name> --use_ccache=<bool> \
-  --use_llvm_next=<bool> --output_file=<file>
-
 Please note that there's a regular syncing operation between
 `chromiumos-overlay/sys-devel/llvm/files/compiler_wrapper` and
 `toolchain-utils/compiler_wrapper`. This sync is one way (from
@@ -15,3 +12,33 @@
 toolchain keep up-to-date with our wrapper easily, as they're a downstream
 consumer of it. For this reason, **please be sure to land all actual changes in
 chromeos-overlay**.
+
+### Build + Run Tests
+1. Install the wrapper locally in chroot (builds as well)
+```
+(chroot) ./install_compiler_wrapper.sh
+```
+
+#### Running a manual test
+Test a manual build command with `-print-cmdline`
+```
+(chroot) x86_64-cros-linux-gnu-clang++ -o test_exec -f<some_flag_to_add>='some_value' -print-cmdline test.cc
+```
+-  `test.cc` doesn't actually have to exist.
+-  The command above will output the additional build flags that are added in by the wrapper.
+
+#### Testing your changes
+1. Add tests to your wrapper changes
+1. Run all the tests via:
+```
+go test -vet=all
+```
+
+### Build Only
+This is handy if you just want to test that the build works.
+
+Build the wrapper:
+```
+./build.py --config=<config name> --use_ccache=<bool> \
+  --use_llvm_next=<bool> --output_file=<file>
+  ```
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 22109e3..7f2c8d1 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -168,8 +168,7 @@
 			return 0, newErrorwithSourceLocf("unsupported compiler: %s", mainBuilder.target.compiler)
 		}
 	} else {
-		_, tidyFlags, tidyMode := processClangTidyFlags(mainBuilder)
-		cSrcFile, iwyuFlags, iwyuMode := processIWYUFlags(mainBuilder)
+		cSrcFile, tidyFlags, tidyMode := processClangTidyFlags(mainBuilder)
 		crashArtifactsDir := detectCrashArtifactsDir(env, cfg)
 		if mainBuilder.target.compilerType == clangType {
 			err := prepareClangCommand(crashArtifactsDir, mainBuilder)
@@ -200,19 +199,6 @@
 				}
 			}
 
-			if iwyuMode != iwyuModeNone {
-				if iwyuMode == iwyuModeError {
-					panic("Unknown IWYU mode")
-				}
-
-				allowCCache = false
-				clangCmdWithoutRemoteBuildAndCCache := mainBuilder.build()
-				err := runIWYU(env, clangCmdWithoutRemoteBuildAndCCache, cSrcFile, iwyuFlags)
-				if err != nil {
-					return 0, err
-				}
-			}
-
 			if remoteBuildUsed, err = processRemoteBuildAndCCacheFlags(allowCCache, mainBuilder); err != nil {
 				return 0, err
 			}
@@ -350,9 +336,9 @@
 	}
 }
 
-func hasFlag(flag string, flagList ...string) bool {
-	for _, flagVal := range flagList {
-		if strings.Contains(flagVal, flag) {
+func hasUserArg(argName string, builder *commandBuilder) bool {
+	for _, argValue := range builder.args {
+		if strings.Contains(argValue.value, argName) && argValue.fromUser {
 			return true
 		}
 	}
@@ -367,8 +353,7 @@
 
 	var crashDiagFlagName = "-fcrash-diagnostics-dir"
 	if crashArtifactsDir != "" &&
-		!hasFlag(crashDiagFlagName, builder.cfg.clangFlags...) &&
-		!hasFlag(crashDiagFlagName, builder.cfg.clangPostFlags...) {
+		!hasUserArg(crashDiagFlagName, builder) {
 		builder.addPreUserArgs(crashDiagFlagName + "=" + crashArtifactsDir)
 	}
 
diff --git a/compiler_wrapper/compiler_wrapper_test.go b/compiler_wrapper/compiler_wrapper_test.go
index 2cace6e..79edab6 100644
--- a/compiler_wrapper/compiler_wrapper_test.go
+++ b/compiler_wrapper/compiler_wrapper_test.go
@@ -253,36 +253,6 @@
 	}
 }
 
-// If "crash-diagnostics-dir" flag is already provided, only use that flag and don't add a dupe
-func TestCrashDiagPreFlag(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
-		ctx.cfg.clangFlags = []string{"-fcrash-diagnostics-dir=/build/something/foo"}
-		ctx.env = []string{
-			"CROS_ARTIFACTS_TMP_DIR=/tmp/foo",
-		}
-		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
-			ctx.newCommand(clangX86_64, mainCc)))
-		if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/build/something/foo"); err != nil {
-			t.Error(err)
-		}
-	})
-}
-
-// If "crash-diagnostics-dir" flag is already provided, only use that flag and don't add a dupe
-func TestCrashDiagPostFlag(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
-		ctx.cfg.clangPostFlags = []string{"-fcrash-diagnostics-dir=/build/something/foo"}
-		ctx.env = []string{
-			"CROS_ARTIFACTS_TMP_DIR=/tmp/foo",
-		}
-		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
-			ctx.newCommand(clangX86_64, mainCc)))
-		if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/build/something/foo"); err != nil {
-			t.Error(err)
-		}
-	})
-}
-
 // If "crash-diagnostics-dir" flag is not provided, add one in
 func TestCrashDiagDefault(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
@@ -291,8 +261,31 @@
 		}
 		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
 			ctx.newCommand(clangX86_64, mainCc)))
+
+		// Verify that we added the default flag
 		if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/tmp/foo/toolchain/clang_crash_diagnostics"); err != nil {
 			t.Error(err)
 		}
 	})
 }
+
+// If "crash-diagnostics-dir" flag is already provided by the user, only use that flag and don't add a dupe
+func TestCrashDiagUserFlag(t *testing.T) {
+	withTestContext(t, func(ctx *testContext) {
+		ctx.env = []string{
+			"CROS_ARTIFACTS_TMP_DIR=/tmp/foo",
+		}
+		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
+			ctx.newCommand(clangX86_64, mainCc, "-fcrash-diagnostics-dir=/build/something/foozz")))
+
+		// Verify that user flag is not removed
+		if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/build/something/foozz"); err != nil {
+			t.Error(err)
+		}
+
+		// Verify that we did not add the default flag
+		if err := verifyArgCount(cmd, 0, "-fcrash-diagnostics-dir=/tmp/foo/toolchain/clang_crash_diagnostics"); err != nil {
+			t.Error(err)
+		}
+	})
+}
diff --git a/compiler_wrapper/iwyu_flag.go b/compiler_wrapper/iwyu_flag.go
deleted file mode 100644
index 5788d8c..0000000
--- a/compiler_wrapper/iwyu_flag.go
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package main
-
-import (
-	"bufio"
-	"bytes"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-)
-
-type useIWYUMode int
-
-const (
-	iwyuModeNone useIWYUMode = iota
-	iwyuModeAll
-	iwyuModeError
-)
-
-var srcFileSuffixes = []string{
-	".c",
-	".cc",
-	".cpp",
-	".C",
-	".cxx",
-	".c++",
-}
-
-func findWithIWYUFlag(args []builderArg) (string, []builderArg) {
-	for i := range args {
-		if args[i].value == "--with-iwyu" {
-			args = append(args[:i], args[i+1:]...)
-			return "1", args
-		}
-	}
-	return "", args
-}
-
-func processIWYUFlags(builder *commandBuilder) (cSrcFile string, iwyuFlags []string, mode useIWYUMode) {
-	builder.transformArgs(func(arg builderArg) string {
-		const prefix = "-iwyu-flag="
-		if !strings.HasPrefix(arg.value, prefix) {
-			return arg.value
-		}
-
-		iwyuFlags = append(iwyuFlags, arg.value[len(prefix):])
-		return ""
-	})
-
-	cSrcFile = ""
-	lastArg := ""
-	for _, arg := range builder.args {
-		if lastArg != "-o" {
-			for _, suffix := range srcFileSuffixes {
-				if strings.HasSuffix(arg.value, suffix) {
-					cSrcFile = arg.value
-					break
-				}
-			}
-		}
-		lastArg = arg.value
-	}
-
-	if cSrcFile == "" {
-		return "", iwyuFlags, iwyuModeNone
-	}
-
-	withIWYU, _ := builder.env.getenv("WITH_IWYU")
-	if withIWYU == "" {
-		withIWYU, builder.args = findWithIWYUFlag(builder.args)
-		if withIWYU == "" {
-			return cSrcFile, iwyuFlags, iwyuModeNone
-		}
-	}
-
-	if withIWYU != "1" {
-		return cSrcFile, iwyuFlags, iwyuModeError
-	}
-
-	return cSrcFile, iwyuFlags, iwyuModeAll
-}
-
-func calcIWYUInvocation(env env, clangCmd *command, cSrcFile string, iwyuFlags ...string) (*command, error) {
-	resourceDir, err := getClangResourceDir(env, clangCmd.Path)
-	if err != nil {
-		return nil, err
-	}
-
-	iwyuPath := filepath.Join(filepath.Dir(clangCmd.Path), "include-what-you-use")
-	args := append([]string{}, iwyuFlags...)
-	args = append(args, "-resource-dir="+resourceDir)
-	args = append(args, clangCmd.Args...)
-
-	for i := 0; i < len(args); i++ {
-		for j := 0; j < len(srcFileSuffixes); j++ {
-			if strings.HasSuffix(args[i], srcFileSuffixes[j]) {
-				args = append(args[:i], args[i+1:]...)
-				break
-			}
-		}
-	}
-	args = append(args, cSrcFile)
-
-	return &command{
-		Path:       iwyuPath,
-		Args:       args,
-		EnvUpdates: clangCmd.EnvUpdates,
-	}, nil
-}
-
-func runIWYU(env env, clangCmd *command, cSrcFile string, extraIWYUFlags []string) error {
-	extraIWYUFlags = append(extraIWYUFlags, "-Xiwyu", "--mapping_file=/usr/share/include-what-you-use/libcxx.imp", "-Xiwyu", "--no_fwd_decls")
-	iwyuCmd, err := calcIWYUInvocation(env, clangCmd, cSrcFile, extraIWYUFlags...)
-	if err != nil {
-		return fmt.Errorf("calculating include-what-you-use invocation: %v", err)
-	}
-
-	// Note: We pass nil as stdin as we checked before that the compiler
-	// was invoked with a source file argument.
-	var stderr bytes.Buffer
-	stderrWriter := bufio.NewWriter(&stderr)
-	exitCode, err := wrapSubprocessErrorWithSourceLoc(iwyuCmd,
-		env.run(iwyuCmd, nil, nil, stderrWriter))
-	stderrMessage := stderr.String()
-	fmt.Fprintln(env.stderr(), stderrMessage)
-
-	if err == nil && exitCode != 0 {
-		// Note: We continue on purpose when include-what-you-use fails
-		// to maintain compatibility with the previous wrapper.
-		fmt.Fprintln(env.stderr(), "include-what-you-use failed")
-	}
-
-	iwyuDir := filepath.Join(getCompilerArtifactsDir(env), "linting-output", "iwyu")
-	if err := os.MkdirAll(iwyuDir, 0777); err != nil {
-		return fmt.Errorf("creating fixes directory at %q: %v", iwyuDir, err)
-	}
-
-	f, err := os.CreateTemp(iwyuDir, "*.out")
-	if err != nil {
-		return fmt.Errorf("making output file for iwyu: %v", err)
-	}
-	writer := bufio.NewWriter(f)
-	if _, err := writer.WriteString(stderrMessage); err != nil {
-		return fmt.Errorf("writing output file for iwyu: %v", err)
-	}
-	if err := writer.Flush(); err != nil {
-		return fmt.Errorf("flushing output file buffer for iwyu: %v", err)
-	}
-	if err := f.Close(); err != nil {
-		return fmt.Errorf("finalizing output file for iwyu: %v", err)
-	}
-
-	return err
-}
diff --git a/compiler_wrapper/iwyu_flag_test.go b/compiler_wrapper/iwyu_flag_test.go
deleted file mode 100644
index e2db3b4..0000000
--- a/compiler_wrapper/iwyu_flag_test.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package main
-
-import (
-	"errors"
-	"io"
-	"strings"
-	"testing"
-)
-
-func TestIWYUArgOrder(t *testing.T) {
-	withIWYUTestContext(t, func(ctx *testContext) {
-		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
-			if ctx.cmdCount == 2 {
-				if err := verifyArgOrder(cmd, "-checks=.*", mainCc, "--", "-resource-dir=.*", mainCc, "--some_arg"); err != nil {
-					return err
-				}
-			}
-			return nil
-		}
-		ctx.must(callCompiler(ctx, ctx.cfg,
-			ctx.newCommand(clangX86_64, mainCc, "--some_arg")))
-		if ctx.cmdCount < 2 {
-			t.Error("expected multiple calls.")
-		}
-	})
-}
-
-func TestIgnoreNonZeroExitCodeFromIWYU(t *testing.T) {
-	withIWYUTestContext(t, func(ctx *testContext) {
-		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
-			if ctx.cmdCount == 2 {
-				return newExitCodeError(23)
-			}
-			return nil
-		}
-		ctx.must(callCompiler(ctx, ctx.cfg,
-			ctx.newCommand(clangX86_64, mainCc)))
-		stderr := ctx.stderrString()
-		if err := verifyNonInternalError(stderr, "include-what-you-use failed"); err != nil {
-			t.Error(err)
-		}
-	})
-}
-
-func TestReportGeneralErrorsFromIWYU(t *testing.T) {
-	withIWYUTestContext(t, func(ctx *testContext) {
-		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
-			if ctx.cmdCount > 1 {
-				return errors.New("someerror")
-			}
-			return nil
-		}
-		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg,
-			ctx.newCommand(clangX86_64, mainCc)))
-		if err := verifyInternalError(stderr); err != nil {
-			t.Fatal(err)
-		}
-		if !strings.Contains(stderr, "someerror") {
-			t.Errorf("unexpected error. Got: %s", stderr)
-		}
-	})
-}
-
-func TestUseIWYUBasedOnFileExtension(t *testing.T) {
-	withIWYUTestContext(t, func(ctx *testContext) {
-		testData := []struct {
-			args []string
-			iwyu bool
-		}{
-			{[]string{"main.cc"}, true},
-			{[]string{"main.cc"}, true},
-			{[]string{"main.C"}, true},
-			{[]string{"main.cxx"}, true},
-			{[]string{"main.c++"}, true},
-			{[]string{"main.xy"}, false},
-			{[]string{"-o", "main.cc"}, false},
-			{[]string{}, false},
-		}
-		for _, tt := range testData {
-			ctx.cmdCount = 0
-			ctx.must(callCompiler(ctx, ctx.cfg,
-				ctx.newCommand(clangX86_64, tt.args...)))
-			if ctx.cmdCount == 2 && !tt.iwyu {
-				t.Errorf("expected a call to iwyu but got none for args %s", tt.args)
-			}
-			if ctx.cmdCount == 1 && tt.iwyu {
-				t.Errorf("expected no call to iwyu but got one for args %s", tt.args)
-			}
-		}
-	})
-}
-
-func TestIWYUFiltersIWYUFlags(t *testing.T) {
-	withIWYUTestContext(t, func(ctx *testContext) {
-		addedFlag := "--some_iwyu_flag=flag"
-		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
-			switch ctx.cmdCount {
-			case 1:
-				if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
-					t.Error(err)
-				} else if err := verifyArgCount(cmd, 0, addedFlag); err != nil {
-					t.Error(err)
-				}
-				return nil
-			case 2:
-				if err := verifyPath(cmd, "usr/bin/include-what-you-use"); err != nil {
-					t.Error(err)
-				} else if verifyArgCount(cmd, 1, addedFlag); err != nil {
-					t.Error(err)
-				}
-				return nil
-			default:
-				return nil
-			}
-		}
-		cmd := ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc, "-iwyu-flag="+addedFlag)))
-		if ctx.cmdCount < 2 {
-			t.Errorf("expected multiple calls.")
-		}
-		if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
-			t.Error(err)
-		}
-	})
-}
-
-func withIWYUTestContext(t *testing.T, work func(ctx *testContext)) {
-	withTestContext(t, func(ctx *testContext) {
-		artifactDir := t.TempDir()
-		ctx.env = []string{"WITH_IWYU=1", "CROS_ARTIFACTS_TMP_DIR=" + artifactDir}
-		work(ctx)
-	})
-}