Support -clang-syntax flag in the compiler wrapper.

BUG=chromium:773875
TEST=unit test

Change-Id: Iea4a3c395c09b2a97d8a9975cf642f985f2dbec7
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1691113
Reviewed-by: George Burgess <gbiv@chromium.org>
Tested-by: Tobias Bosch <tbosch@google.com>
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 4ef3039..e5b7234 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -18,7 +18,7 @@
 	} else if cfg.oldWrapperPath != "" {
 		exitCode, compilerErr = callCompilerWithRunAndCompareToOldWrapper(env, cfg, inputCmd)
 	} else {
-		compilerErr = callCompilerWithExec(env, cfg, inputCmd)
+		exitCode, compilerErr = callCompilerWithExec(env, cfg, inputCmd)
 	}
 	if compilerErr != nil {
 		printCompilerError(env.stderr(), compilerErr)
@@ -31,8 +31,8 @@
 	recordingEnv := &commandRecordingEnv{
 		env: env,
 	}
-	compilerCmd, err := calcCompilerCommand(recordingEnv, cfg, inputCmd)
-	if err != nil {
+	compilerCmd, exitCode, err := calcCompilerCommand(recordingEnv, cfg, inputCmd)
+	if err != nil || exitCode != 0 {
 		return exitCode, err
 	}
 	exitCode = 0
@@ -44,7 +44,7 @@
 		}
 		var ok bool
 		if exitCode, ok = getExitCode(err); !ok {
-			return exitCode, wrapErrorwithSourceLocf(err, "failed to execute %s %s", compilerCmd.path, compilerCmd.args)
+			return exitCode, wrapErrorwithSourceLocf(err, "failed to execute %#v", compilerCmd)
 		}
 	}
 	if err := compareToOldWrapper(env, cfg, inputCmd, recordingEnv.cmdResults); err != nil {
@@ -53,61 +53,87 @@
 	return exitCode, nil
 }
 
-func callCompilerWithExec(env env, cfg *config, inputCmd *command) error {
-	compilerCmd, err := calcCompilerCommand(env, cfg, inputCmd)
-	if err != nil {
-		return err
+func callCompilerWithExec(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
+	compilerCmd, exitCode, err := calcCompilerCommand(env, cfg, inputCmd)
+	if err != nil || exitCode != 0 {
+		return exitCode, err
 	}
 	if err := env.exec(compilerCmd); err != nil {
 		// Note: No need to check for exit code error as exec will
 		// stop this control flow once the command started executing.
 		if userErr, ok := getCCacheError(compilerCmd, err); ok {
-			return userErr
+			return exitCode, userErr
 		}
-		return wrapErrorwithSourceLocf(err, "failed to execute %s %s", compilerCmd.path, compilerCmd.args)
+		return exitCode, wrapErrorwithSourceLocf(err, "failed to execute %#v", compilerCmd)
 	}
-	return nil
+	return exitCode, nil
 }
 
-func calcCompilerCommand(env env, cfg *config, inputCmd *command) (*command, error) {
+func calcCompilerCommand(env env, cfg *config, inputCmd *command) (compilerCmd *command, exitCode int, err error) {
 	if err := checkUnsupportedFlags(inputCmd); err != nil {
-		return nil, err
+		return nil, exitCode, err
 	}
-	absWrapperDir, err := getAbsWrapperDir(env, inputCmd.path)
+	mainBuilder, err := newCommandBuilder(env, cfg, inputCmd)
 	if err != nil {
-		return nil, err
+		return nil, exitCode, err
 	}
-	rootPath := filepath.Join(absWrapperDir, cfg.rootRelPath)
-	builder, err := newCommandBuilder(env, cfg, inputCmd)
-	if err != nil {
-		return nil, err
-	}
-	useClang := builder.target.compilerType == clangType
-	sysroot := processSysrootFlag(rootPath, builder)
-	if useClang {
-		builder.addPreUserArgs(cfg.clangFlags...)
+	clangSyntax := processClangSyntaxFlag(mainBuilder)
+	if mainBuilder.target.compilerType == clangType {
+		compilerCmd, err = calcClangCommand(mainBuilder)
+		if err != nil {
+			return nil, exitCode, err
+		}
 	} else {
-		builder.addPreUserArgs(cfg.gccFlags...)
+		if clangSyntax {
+			clangCmd, err := calcClangCommand(mainBuilder.clone())
+			if err != nil {
+				return nil, 0, err
+			}
+			exitCode, err = checkClangSyntax(env, clangCmd)
+			if err != nil || exitCode != 0 {
+				return nil, exitCode, err
+			}
+		}
+		compilerCmd = calcGccCommand(mainBuilder)
 	}
-	builder.addPreUserArgs(cfg.commonFlags...)
+
+	return compilerCmd, exitCode, nil
+}
+
+func calcClangCommand(builder *commandBuilder) (*command, error) {
+	sysroot := processSysrootFlag(builder)
+	builder.addPreUserArgs(builder.cfg.clangFlags...)
+	calcCommonPreUserArgs(builder)
+	if err := processClangFlags(builder); err != nil {
+		return nil, err
+	}
+	processGomaCCacheFlags(sysroot, builder)
+	return builder.build(), nil
+}
+
+func calcGccCommand(builder *commandBuilder) *command {
+	sysroot := processSysrootFlag(builder)
+	builder.addPreUserArgs(builder.cfg.gccFlags...)
+	calcCommonPreUserArgs(builder)
+	processGccFlags(builder)
+	processGomaCCacheFlags(sysroot, builder)
+	return builder.build()
+}
+
+func calcCommonPreUserArgs(builder *commandBuilder) {
+	builder.addPreUserArgs(builder.cfg.commonFlags...)
 	processPieFlags(builder)
 	processStackProtectorFlags(builder)
 	processThumbCodeFlags(builder)
 	processX86Flags(builder)
 	processSanitizerFlags(builder)
-	if useClang {
-		if err := processClangFlags(rootPath, builder); err != nil {
-			return nil, err
-		}
-	} else {
-		processGccFlags(builder)
-	}
+}
+
+func processGomaCCacheFlags(sysroot string, builder *commandBuilder) {
 	gomaccUsed := processGomaCccFlags(builder)
 	if !gomaccUsed {
 		processCCacheFlag(sysroot, builder)
 	}
-
-	return builder.build(), nil
 }
 
 func getAbsWrapperDir(env env, wrapperPath string) (string, error) {