Allow to pass in the use_ccache via go build.

Removes usage of go build tags in favor of passing in
configuration via -ldflags -X ...

BUG=chromium:773875
TEST=unit test

Change-Id: I4e8a58e1679b2858e9d4620d6b9c7a35ad08a6ee
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1670987
Reviewed-by: George Burgess <gbiv@chromium.org>
Tested-by: Tobias Bosch <tbosch@google.com>
diff --git a/compiler_wrapper/build b/compiler_wrapper/build
new file mode 100755
index 0000000..9365b73
--- /dev/null
+++ b/compiler_wrapper/build
@@ -0,0 +1,35 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Build script that wraps go build and sets the correct linker variables."""
+
+from __future__ import print_function
+
+import argparse
+import os
+import os.path
+import subprocess
+import sys
+
+def parse_args():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--config", required=True,
+                      choices=['cros.hardened', 'cros.nonhardened'], type=str)
+  parser.add_argument("--use_ccache", required=True,
+                      choices=['true', 'false'], type=str)
+  return parser.parse_known_args()
+
+def calc_go_args(parsed_args, rest_args):
+  src_dir = os.path.dirname(sys.argv[0])
+  ldFlags = ['-X', 'main.ConfigName=' + parsed_args.config,
+             '-X', 'main.UseCCache='+parsed_args.use_ccache]
+  return (['go', 'build', '-ldflags', ' '.join(ldFlags)]
+          + rest_args + [src_dir])
+
+def main():
+  sys.exit(subprocess.call(calc_go_args(*parse_args())))
+
+if __name__ == '__main__':
+  main()
diff --git a/compiler_wrapper/build_cros_hardened_wrapper.sh b/compiler_wrapper/build_cros_hardened_wrapper.sh
deleted file mode 100755
index 82d9e14..0000000
--- a/compiler_wrapper/build_cros_hardened_wrapper.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash -eu
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-go build -tags "cros hardened" "$@"
diff --git a/compiler_wrapper/build_cros_nonhardened_wrapper.sh b/compiler_wrapper/build_cros_nonhardened_wrapper.sh
deleted file mode 100755
index 94e591f..0000000
--- a/compiler_wrapper/build_cros_nonhardened_wrapper.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash -eu
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-go build -tags "cros nonhardened" "$@"
diff --git a/compiler_wrapper/ccache_flag.go b/compiler_wrapper/ccache_flag.go
index 13ee234..95dd296 100644
--- a/compiler_wrapper/ccache_flag.go
+++ b/compiler_wrapper/ccache_flag.go
@@ -15,7 +15,7 @@
 		return arg.Value
 	})
 
-	if useCCache {
+	if builder.cfg.useCCache && useCCache {
 		// We need to get ccache to make relative paths from within the
 		// sysroot.  This lets us share cached files across boards (if
 		// all other things are equal of course like CFLAGS) as well as
diff --git a/compiler_wrapper/ccache_flag_test.go b/compiler_wrapper/ccache_flag_test.go
index 15fc317..b46ab38 100644
--- a/compiler_wrapper/ccache_flag_test.go
+++ b/compiler_wrapper/ccache_flag_test.go
@@ -4,24 +4,34 @@
 	"testing"
 )
 
-func TestCallCCache(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
-		wrapperCmd := ctx.newCommand(gccX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+func TestCallCCacheGivenConfig(t *testing.T) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
 			t.Error(err)
 		}
-		if err := verifyArgOrder(cmd, wrapperCmd.path+".real", mainCc); err != nil {
+		if err := verifyArgOrder(cmd, gccX86_64+".real", mainCc); err != nil {
 			t.Error(err)
 		}
 	})
 }
 
-func TestNotCallCCacheIfNoCCacheArgGiven(t *testing.T) {
+func TestNotCallCCacheGivenConfig(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
-		wrapperCmd := ctx.newCommand(gccX86_64, "-noccache", mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
-		if err := verifyPath(cmd, wrapperCmd.path+".real"); err != nil {
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
+		if err := verifyPath(cmd, gccX86_64+".real"); err != nil {
+			t.Error(err)
+		}
+	})
+}
+
+func TestNotCallCCacheGivenConfigAndNoCCacheArg(t *testing.T) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, "-noccache", mainCc)))
+		if err := verifyPath(cmd, gccX86_64+".real"); err != nil {
 			t.Error(err)
 		}
 		if err := verifyArgCount(cmd, 0, "-noccache"); err != nil {
@@ -31,7 +41,7 @@
 }
 
 func TestSetCacheDir(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyEnvUpdate(cmd, "CCACHE_DIR=/var/cache/distfiles/ccache"); err != nil {
@@ -41,7 +51,7 @@
 }
 
 func TestSetCacheBaseDirToSysroot(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyEnvUpdate(cmd,
@@ -52,7 +62,7 @@
 }
 
 func TestSetCacheUmask(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyEnvUpdate(cmd, "CCACHE_UMASK=002"); err != nil {
@@ -62,7 +72,7 @@
 }
 
 func TestUpdateSandboxRewrite(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyNoEnvUpdate(cmd, "SANDBOX_WRITE"); err != nil {
@@ -80,7 +90,7 @@
 }
 
 func TestClearCacheDisable(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyNoEnvUpdate(cmd, "SANDBOX_WRITE"); err != nil {
@@ -97,7 +107,7 @@
 }
 
 func TestAddCCacheCpp2FlagForClang(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(clangX86_64, mainCc)))
 		if err := verifyEnvUpdate(cmd, "CCACHE_CPP2=yes"); err != nil {
@@ -107,7 +117,7 @@
 }
 
 func TestOmitCCacheCpp2FlagForGcc(t *testing.T) {
-	withTestContext(t, func(ctx *testContext) {
+	withCCacheEnabledTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyNoEnvUpdate(cmd, "CCACHE_CPP2"); err != nil {
@@ -115,3 +125,10 @@
 		}
 	})
 }
+
+func withCCacheEnabledTestContext(t *testing.T, work func(ctx *testContext)) {
+	withTestContext(t, func(ctx *testContext) {
+		ctx.cfg.useCCache = true
+		work(ctx)
+	})
+}
diff --git a/compiler_wrapper/command.go b/compiler_wrapper/command.go
index c73acb8..618a132 100644
--- a/compiler_wrapper/command.go
+++ b/compiler_wrapper/command.go
@@ -60,7 +60,7 @@
 	return nil
 }
 
-func newCommandBuilder(env env, cmd *command) (*commandBuilder, error) {
+func newCommandBuilder(env env, cfg *config, cmd *command) (*commandBuilder, error) {
 	basename := filepath.Base(cmd.path)
 	nameParts := strings.Split(basename, "-")
 	if len(nameParts) != 5 {
@@ -83,6 +83,7 @@
 		path: cmd.path,
 		args: createBuilderArgs( /*fromUser=*/ true, cmd.args),
 		env:  env,
+		cfg:  cfg,
 		target: builderTarget{
 			target:       strings.Join(nameParts[:4], "-"),
 			arch:         nameParts[0],
@@ -101,6 +102,7 @@
 	args       []builderArg
 	envUpdates []string
 	env        env
+	cfg        *config
 }
 
 type builderArg struct {
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 72e1749..6034fb0 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -15,7 +15,7 @@
 	if err := checkUnsupportedFlags(wrapperCmd); err != nil {
 		return nil, err
 	}
-	builder, err := newCommandBuilder(env, wrapperCmd)
+	builder, err := newCommandBuilder(env, cfg, wrapperCmd)
 	if err != nil {
 		return nil, err
 	}
diff --git a/compiler_wrapper/config.go b/compiler_wrapper/config.go
index 909edf0..1e78fbe 100644
--- a/compiler_wrapper/config.go
+++ b/compiler_wrapper/config.go
@@ -1,6 +1,13 @@
 package main
 
+import (
+	"fmt"
+	"strconv"
+)
+
 type config struct {
+	// Whether to use ccache.
+	useCCache bool
 	// Flags to add to gcc and clang.
 	commonFlags []string
 	// Flags to add to gcc only.
@@ -14,57 +21,98 @@
 	overrideOldWrapperConfig bool
 }
 
+// UseCCache can be set via a linker flag.
+// Value will be passed to strconv.ParseBool.
+// E.g. go build -ldflags '-X config.UseCCache=true'.
+var UseCCache = "unknown"
+
+// ConfigName can be set via a linker flag.
+// Value has to be one of:
+// - "cros.hardened"
+// - "cros.nonhardened"
+var ConfigName = "unknown"
+
+// Returns the configuration matching the UseCCache and ConfigName.
+func getRealConfig() (*config, error) {
+	useCCache, err := strconv.ParseBool(UseCCache)
+	if err != nil {
+		return nil, fmt.Errorf("Parse error for UseCCache: %s", err)
+	}
+	config, err := getConfig(useCCache, ConfigName)
+	if err != nil {
+		return nil, err
+	}
+	return config, nil
+}
+
+func getConfig(useCCache bool, configName string) (*config, error) {
+	switch configName {
+	case "cros.hardened":
+		return getCrosHardenedConfig(useCCache), nil
+	case "cros.nonhardened":
+		return getCrosNonHardenedConfig(useCCache), nil
+	default:
+		return nil, fmt.Errorf("Unknown config name: %s", configName)
+	}
+}
+
 // Full hardening.
-// Temporarily disable function splitting because of chromium:434751.
-var crosHardenedConfig = config{
-	rootRelPath:    "../../../../..",
-	oldWrapperPath: "./sysroot_wrapper.hardened.old",
-	commonFlags: []string{
-		"-fPIE",
-		"-D_FORTIFY_SOURCE=2",
-		"-fstack-protector-strong",
-		"-pie",
-		"-fno-omit-frame-pointer",
-	},
-	gccFlags: []string{
-		"-Wno-unused-local-typedefs",
-		"-Wno-maybe-uninitialized",
-		"-fno-reorder-blocks-and-partition",
-	},
-	// Temporarily disable tautological-*-compare chromium:778316.
-	// Temporarily add no-unknown-warning-option to deal with old clang versions.
-	// Temporarily disable Wsection since kernel gets a bunch of these. chromium:778867
-	// Disable "-faddrsig" since it produces object files that strip doesn't understand, chromium:915742.
-	clangFlags: []string{
-		"-Wno-tautological-unsigned-enum-zero-compare",
-		"-Qunused-arguments",
-		"-grecord-gcc-switches",
-		"-Wno-section",
-		"-Wno-unknown-warning-option",
-		"-fno-addrsig",
-		"-Wno-tautological-constant-compare",
-	},
+func getCrosHardenedConfig(useCCache bool) *config {
+	// Temporarily disable function splitting because of chromium:434751.
+	return &config{
+		useCCache:      useCCache,
+		rootRelPath:    "../../../../..",
+		oldWrapperPath: "./sysroot_wrapper.hardened.old",
+		commonFlags: []string{
+			"-fPIE",
+			"-D_FORTIFY_SOURCE=2",
+			"-fstack-protector-strong",
+			"-pie",
+			"-fno-omit-frame-pointer",
+		},
+		gccFlags: []string{
+			"-Wno-unused-local-typedefs",
+			"-Wno-maybe-uninitialized",
+			"-fno-reorder-blocks-and-partition",
+		},
+		// Temporarily disable tautological-*-compare chromium:778316.
+		// Temporarily add no-unknown-warning-option to deal with old clang versions.
+		// Temporarily disable Wsection since kernel gets a bunch of these. chromium:778867
+		// Disable "-faddrsig" since it produces object files that strip doesn't understand, chromium:915742.
+		clangFlags: []string{
+			"-Wno-tautological-unsigned-enum-zero-compare",
+			"-Qunused-arguments",
+			"-grecord-gcc-switches",
+			"-Wno-section",
+			"-Wno-unknown-warning-option",
+			"-fno-addrsig",
+			"-Wno-tautological-constant-compare",
+		},
+	}
 }
 
 // Flags to be added to non-hardened toolchain.
-var crosNonHardenedConfig = config{
-	rootRelPath:    "../../../../..",
-	oldWrapperPath: "./sysroot_wrapper.old",
-	commonFlags:    []string{},
-	gccFlags: []string{
-		"-Wno-unused-local-typedefs",
-		"-Wno-maybe-uninitialized",
-		"-Wtrampolines",
-		"-Wno-deprecated-declarations",
-	},
-	// Temporarily disable tautological-*-compare chromium:778316.
-	// Temporarily add no-unknown-warning-option to deal with old clang versions.
-	// Temporarily disable Wsection since kernel gets a bunch of these. chromium:778867
-	clangFlags: []string{
-		"-Wno-unknown-warning-option",
-		"-Qunused-arguments",
-		"-Wno-section",
-		"-Wno-tautological-unsigned-enum-zero-compare",
-		"-Wno-tautological-constant-compare",
-	},
+func getCrosNonHardenedConfig(useCCache bool) *config {
+	return &config{
+		useCCache:      useCCache,
+		rootRelPath:    "../../../../..",
+		oldWrapperPath: "./sysroot_wrapper.old",
+		commonFlags:    []string{},
+		gccFlags: []string{
+			"-Wno-unused-local-typedefs",
+			"-Wno-maybe-uninitialized",
+			"-Wtrampolines",
+			"-Wno-deprecated-declarations",
+		},
+		// Temporarily disable tautological-*-compare chromium:778316.
+		// Temporarily add no-unknown-warning-option to deal with old clang versions.
+		// Temporarily disable Wsection since kernel gets a bunch of these. chromium:778867
+		clangFlags: []string{
+			"-Wno-unknown-warning-option",
+			"-Qunused-arguments",
+			"-Wno-section",
+			"-Wno-tautological-unsigned-enum-zero-compare",
+			"-Wno-tautological-constant-compare",
+		},
+	}
 }
diff --git a/compiler_wrapper/config_test.go b/compiler_wrapper/config_test.go
index 9a75acb..0dcd58d 100644
--- a/compiler_wrapper/config_test.go
+++ b/compiler_wrapper/config_test.go
@@ -8,12 +8,12 @@
 func TestFullHardeningConfigAndGcc(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
 		initFullHardeningConfig(ctx)
-		wrapperCmd := ctx.newCommand(gccX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
 			t.Error(err)
 		}
-		if err := verifyArgOrder(cmd, wrapperCmd.path+".real", "--sysroot=/usr/x86_64-cros-linux-gnu", "-Wno-unused-local-typedefs",
+		if err := verifyArgOrder(cmd, gccX86_64+".real", "--sysroot=/usr/x86_64-cros-linux-gnu", "-Wno-unused-local-typedefs",
 			"-Wno-maybe-uninitialized", "-fno-reorder-blocks-and-partition", "-fPIE", "-D_FORTIFY_SOURCE=2", "-fstack-protector-strong",
 			"-pie", "-fno-omit-frame-pointer", "main.cc", "-mno-movbe"); err != nil {
 			t.Error(err)
@@ -24,8 +24,8 @@
 func TestFullHardeningConfigAndClang(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
 		initFullHardeningConfig(ctx)
-		wrapperCmd := ctx.newCommand(clangX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(clangX86_64, mainCc)))
 		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
 			t.Error(err)
 		}
@@ -46,12 +46,12 @@
 func TestNonHardeningConfigAndGcc(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
 		initNonHardeningConfig(ctx)
-		wrapperCmd := ctx.newCommand(gccX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
 			t.Error(err)
 		}
-		if err := verifyArgOrder(cmd, wrapperCmd.path+".real", "--sysroot=/usr/x86_64-cros-linux-gnu",
+		if err := verifyArgOrder(cmd, gccX86_64+".real", "--sysroot=/usr/x86_64-cros-linux-gnu",
 			"-Wno-unused-local-typedefs", "-Wno-maybe-uninitialized", "-Wtrampolines",
 			"-Wno-deprecated-declarations", "main.cc", "-mno-movbe"); err != nil {
 			t.Error(err)
@@ -62,8 +62,8 @@
 func TestNonHardeningConfigAndClang(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
 		initNonHardeningConfig(ctx)
-		wrapperCmd := ctx.newCommand(clangX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(clangX86_64, mainCc)))
 		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
 			t.Error(err)
 		}
@@ -80,12 +80,89 @@
 	})
 }
 
+func TestRealConfigWithUseCCacheFlag(t *testing.T) {
+	resetGlobals()
+	defer resetGlobals()
+	ConfigName = "cros.hardened"
+
+	UseCCache = "false"
+	cfg, err := getRealConfig()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if cfg.useCCache {
+		t.Fatal("UseCCache: Expected false got true")
+	}
+
+	UseCCache = "true"
+	cfg, err = getRealConfig()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !cfg.useCCache {
+		t.Fatal("UseCCache: Expected true got false")
+	}
+
+	UseCCache = "invalid"
+	_, err = getRealConfig()
+	if err == nil {
+		t.Fatalf("UseCCache: Expected an error, got none")
+	}
+}
+
+func TestRealConfigWithConfigNameFlag(t *testing.T) {
+	resetGlobals()
+	defer resetGlobals()
+	UseCCache = "false"
+
+	ConfigName = "cros.hardened"
+	cfg, err := getRealConfig()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !isHardened(cfg) {
+		t.Fatal("ConfigName: Expected hardened config got non hardened")
+	}
+
+	ConfigName = "cros.nonhardened"
+	cfg, err = getRealConfig()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if isHardened(cfg) {
+		t.Fatal("ConfigName: Expected non hardened config got hardened")
+	}
+
+	ConfigName = "invalid"
+	_, err = getRealConfig()
+	if err == nil {
+		t.Fatalf("ConfigName: Expected an error, got none")
+	}
+}
+
+func isHardened(cfg *config) bool {
+	for _, arg := range cfg.commonFlags {
+		if arg == "-pie" {
+			return true
+		}
+	}
+	return false
+}
+
 func initFullHardeningConfig(ctx *testContext) {
-	*ctx.cfg = crosHardenedConfig
+	useCCache := true
+	*ctx.cfg = *getCrosHardenedConfig(useCCache)
 	ctx.setOldWrapperPath(oldHardenedWrapperPathForTest)
 }
 
 func initNonHardeningConfig(ctx *testContext) {
-	*ctx.cfg = crosNonHardenedConfig
+	useCCache := true
+	*ctx.cfg = *getCrosNonHardenedConfig(useCCache)
 	ctx.setOldWrapperPath(oldNonHardenedWrapperPathForTest)
 }
+
+func resetGlobals() {
+	// Set all global variables to a defined state.
+	ConfigName = "unknown"
+	UseCCache = "unknown"
+}
diff --git a/compiler_wrapper/cros_hardened_config.go b/compiler_wrapper/cros_hardened_config.go
deleted file mode 100644
index 0ab8ab4..0000000
--- a/compiler_wrapper/cros_hardened_config.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// +build cros,hardened
-
-package main
-
-func getRealConfig() *config {
-	return &crosHardenedConfig
-}
diff --git a/compiler_wrapper/cros_nonhardened_config.go b/compiler_wrapper/cros_nonhardened_config.go
deleted file mode 100644
index 9106a4d..0000000
--- a/compiler_wrapper/cros_nonhardened_config.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// +build cros,nonhardened
-
-package main
-
-func getRealConfig() *config {
-	return &crosNonHardenedConfig
-}
diff --git a/compiler_wrapper/gcc_flags_test.go b/compiler_wrapper/gcc_flags_test.go
index 12119f0..9e34216 100644
--- a/compiler_wrapper/gcc_flags_test.go
+++ b/compiler_wrapper/gcc_flags_test.go
@@ -6,9 +6,9 @@
 
 func TestCallRealGcc(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
-		wrapperCmd := ctx.newCommand(gccX86_64, "-noccache", mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
-		if err := verifyPath(cmd, wrapperCmd.path+".real"); err != nil {
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
+		if err := verifyPath(cmd, gccX86_64+".real"); err != nil {
 			t.Error(err)
 		}
 	})
@@ -16,8 +16,8 @@
 
 func TestCallRealGPlusPlus(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
-		wrapperCmd := ctx.newCommand("./x86_64-cros-linux-gnu-g++", "-noccache", mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand("./x86_64-cros-linux-gnu-g++", mainCc)))
 		if err := verifyPath(cmd, "\\./x86_64-cros-linux-gnu-g\\+\\+\\.real"); err != nil {
 			t.Error(err)
 		}
diff --git a/compiler_wrapper/gomacc_flag_test.go b/compiler_wrapper/gomacc_flag_test.go
index 1372df2..5e8d66c 100644
--- a/compiler_wrapper/gomacc_flag_test.go
+++ b/compiler_wrapper/gomacc_flag_test.go
@@ -11,12 +11,12 @@
 		// Create a file so the gomacc path is valid.
 		ctx.writeFile(gomaPath, "")
 		ctx.env = []string{"GOMACC_PATH=" + gomaPath}
-		wrapperCmd := ctx.newCommand(gccX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyPath(cmd, gomaPath); err != nil {
 			t.Error(err)
 		}
-		if err := verifyArgOrder(cmd, wrapperCmd.path+".real", mainCc); err != nil {
+		if err := verifyArgOrder(cmd, gccX86_64+".real", mainCc); err != nil {
 			t.Error(err)
 		}
 	})
@@ -29,7 +29,7 @@
 		ctx.env = []string{"GOMACC_PATH=" + gomaPath}
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
-		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
+		if err := verifyPath(cmd, gccX86_64+".real"); err != nil {
 			t.Error(err)
 		}
 	})
@@ -39,7 +39,7 @@
 	withTestContext(t, func(ctx *testContext) {
 		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
 			ctx.newCommand(gccX86_64, mainCc)))
-		if err := verifyPath(cmd, "/usr/bin/ccache"); err != nil {
+		if err := verifyPath(cmd, gccX86_64+".real"); err != nil {
 			t.Error(err)
 		}
 	})
diff --git a/compiler_wrapper/main.go b/compiler_wrapper/main.go
index 5323a30..9e13132 100644
--- a/compiler_wrapper/main.go
+++ b/compiler_wrapper/main.go
@@ -1,13 +1,11 @@
-// +build cros
-
-// This binary uses the following build tags:
-// - cros: Whether the wrapper should be built for ChromeOS
-// - nonhardened: For a non-hardened set of compiler flags
-// - hardened: For a hardened set of compiler flags
+// This binary requires the following linker variables:
+// - main.UseCCache: Whether to use ccache.
+// - main.ConfigName: Name of the configuration to use.
+//   See config.go for the supported values.
 //
-// There is a bash script for every meaningful combination.
-// E.g. ./build_cros_hardened_wrapper.sh will build the ChromeOS
-// hardened compiler wrapper.
+// The script ./build simplifies the call to `go build`.
+// E.g. ./build --use_ccache=true --config=cros.hardened will build a
+// binary that uses the ccache for ChromeOS with hardened flags.
 //
 // Test arguments:
 // - crosroot: Specifies the ChromeOS toolchain root directory (aka chroot).
@@ -33,7 +31,10 @@
 	if err != nil {
 		log.Fatal(err)
 	}
-	cfg := getRealConfig()
+	cfg, err := getRealConfig()
+	if err != nil {
+		log.Fatal(err)
+	}
 	if shouldForwardToOldWrapper(env, wrapperCmd) {
 		err := forwardToOldWrapper(env, cfg, wrapperCmd)
 		if err != nil {
diff --git a/compiler_wrapper/oldwrapper.go b/compiler_wrapper/oldwrapper.go
index bc9d5ea..4607773 100644
--- a/compiler_wrapper/oldwrapper.go
+++ b/compiler_wrapper/oldwrapper.go
@@ -8,6 +8,7 @@
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"text/template"
 )
@@ -101,19 +102,31 @@
 		}
 		absOldWrapperPath = filepath.Join(absWrapperDir, cfg.oldWrapperPath)
 	}
+	oldWrapperContentBytes, err := ioutil.ReadFile(absOldWrapperPath)
+	if err != nil {
+		return err
+	}
+	oldWrapperContent := string(oldWrapperContentBytes)
+	// Disable the original call to main()
+	oldWrapperContent = strings.ReplaceAll(oldWrapperContent, "__name__", "'none'")
+	// Inject the value of cfg.useCCache
+	if !cfg.useCCache {
+		oldWrapperContent = regexp.MustCompile(`True\s+#\s+@CCACHE_DEFAULT@`).ReplaceAllString(oldWrapperContent, "False #")
+	}
+
 	// Note: Fieldnames need to be upper case so that they can be read via reflection.
 	mockData := struct {
-		CmdPath         string
-		WrapperPath     string
-		RootRelPath     string
-		MockForks       bool
-		OverwriteConfig bool
-		CommonFlags     []string
-		GccFlags        []string
-		ClangFlags      []string
+		CmdPath           string
+		OldWrapperContent string
+		RootRelPath       string
+		MockForks         bool
+		OverwriteConfig   bool
+		CommonFlags       []string
+		GccFlags          []string
+		ClangFlags        []string
 	}{
 		wrapperCmd.path,
-		absOldWrapperPath,
+		oldWrapperContent,
 		cfg.rootRelPath,
 		mockForks,
 		cfg.overrideOldWrapperConfig,
@@ -122,11 +135,7 @@
 		cfg.clangFlags,
 	}
 
-	const mockTemplate = `from __future__ import print_function
-import imp
-import os
-import sys
-
+	const mockTemplate = `{{.OldWrapperContent}}
 {{if .MockForks}}
 init_env = os.environ.copy()
 
@@ -144,20 +153,15 @@
 
 sys.argv[0] = '{{.CmdPath}}'
 
-# Equivalent to "import wrapper", but does not need a ".py" suffix.
-# Also avoids writing '.pyc' files
-sys.dont_write_bytecode = True
-wrapper = imp.load_source('wrapper', '{{.WrapperPath}}')
-
-wrapper.ROOT_REL_PATH = '{{.RootRelPath}}'
+ROOT_REL_PATH = '{{.RootRelPath}}'
 
 {{if .OverwriteConfig}}
-wrapper.FLAGS_TO_ADD=set([{{range .CommonFlags}}'{{.}}',{{end}}])
-wrapper.GCC_FLAGS_TO_ADD=set([{{range .GccFlags}}'{{.}}',{{end}}])
-wrapper.CLANG_FLAGS_TO_ADD=set([{{range .ClangFlags}}'{{.}}',{{end}}])
+FLAGS_TO_ADD=set([{{range .CommonFlags}}'{{.}}',{{end}}])
+GCC_FLAGS_TO_ADD=set([{{range .GccFlags}}'{{.}}',{{end}}])
+CLANG_FLAGS_TO_ADD=set([{{range .ClangFlags}}'{{.}}',{{end}}])
 {{end}}
 
-wrapper.main()
+sys.exit(main())
 `
 
 	tmpl, err := template.New("mock").Parse(mockTemplate)
diff --git a/compiler_wrapper/sysroot_flag_test.go b/compiler_wrapper/sysroot_flag_test.go
index dd72629..93d995d 100644
--- a/compiler_wrapper/sysroot_flag_test.go
+++ b/compiler_wrapper/sysroot_flag_test.go
@@ -37,8 +37,8 @@
 func TestSetSysrootRelativeToWrapperPath(t *testing.T) {
 	withTestContext(t, func(ctx *testContext) {
 		ctx.cfg.rootRelPath = "somepath"
-		wrapperCmd := ctx.newCommand(gccX86_64, mainCc)
-		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg, wrapperCmd))
+		cmd := ctx.must(calcCompilerCommandAndCompareToOld(ctx, ctx.cfg,
+			ctx.newCommand(gccX86_64, mainCc)))
 		if err := verifyArgOrder(cmd,
 			"--sysroot="+ctx.tempDir+"/somepath/usr/x86_64-cros-linux-gnu", mainCc); err != nil {
 			t.Error(err)