Makefile: Rework toolchain logic to enable multi-toolchains

We want to allow building Linux, and potentially other projects, with
a wide range of toolchains to catch compiler problems during
development. However, not all projects can be built with arbitrary
toolchains. Eg. kvm-unit-tests and u-boot don't build with gcc-5.1 but
we want to support it because that is the oldest toolchain supported by
Linux.

Extract the toolchain logic into toolchain.mk and wrap it with a
`define_toolchain` function. The function takes a toolchain name and
a prefix, and defines variables with the given prefix that contain paths
and information about the given toolchain. This way we can define a
different toolchains for Linux, KUT, u-boot, etc.

Migrate existing projects to this setup.

Bug: 193407077
Test: make test
Change-Id: I625ba13400be3c7b9ca3204cb7615fa2c000944a
diff --git a/Makefile b/Makefile
index 8979764..88096b0 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,8 @@
 LINUX_DIST := $(DIST_DIR)/linux
 SCRIPTS_DIR := $(ROOT_DIR)/build
 
+include $(SCRIPTS_DIR)/toolchain.mk
+
 ##
 ## Common options
 ##
@@ -65,54 +67,7 @@
 # GCC will always use the latest version for building everything else other
 # than the kernel.
 TOOLCHAIN ?= clang
-
-# Find the toolchain path for the current OS.
-UNNAME_S := $(shell uname -s | tr '[:upper:]' '[:lower:]')
-TOOLCHAIN_CLANG := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/clang/clang-r416183b/
-TOOLCHAIN_GCC_49 := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/gcc/aarch64-linux-gnu-4.9/
-TOOLCHAIN_GCC_51 := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/gcc/aarch64-linux-gnu-5.1/
-TOOLCHAIN_GCC_92 := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/gcc/aarch64-linux-gnu-9.2/
-
-# Set the toolchain build binary paths and prefixes.
-TARGET = aarch64-linux-gnu
-TARGET_GCC_92 = aarch64-none-linux-gnu
-
-ifeq ($(TOOLCHAIN), gcc-5.1)
-TOOLCHAIN_BIN := $(TOOLCHAIN_GCC_51)/bin
-else ifeq ($(TOOLCHAIN), gcc-9.2)
-TOOLCHAIN_BIN := $(TOOLCHAIN_GCC_92)/bin
-TARGET = $(TARGET_GCC_92)
-else ifeq ($(TOOLCHAIN), clang)
-TOOLCHAIN_BIN := $(TOOLCHAIN_CLANG)/bin
-CC := $(TOOLCHAIN_BIN)/clang
-LD := $(TOOLCHAIN_BIN)/ld.lld
-OBJCOPY := $(TOOLCHAIN_BIN)/llvm-objcopy
-OBJDUMP := $(TOOLCHAIN_BIN)/llvm-objdump
-# The GCC toolchain is needed when building kvm-unit-tests and Linux with Clang.
-GCC_TOOLCHAIN_DIR = $(TOOLCHAIN_GCC_49)
-GCC_TOOLCHAIN_BIN = $(GCC_TOOLCHAIN_DIR)/bin/
-LINUX_EXTRA = LLVM=1 LLVM_IAS=1 GCC_TOOLCHAIN_DIR="$(GCC_TOOLCHAIN_BIN)"
-# Some targets require additional flags to build with Clang.
-COMMON_CFLAGS := -Qunused-arguments --target=$(TARGET) -fno-integrated-as -Wno-asm-operand-widths -fpic --gcc-toolchain=$(GCC_TOOLCHAIN_DIR) --sysroot=$(GCC_TOOLCHAIN_DIR)/$(TARGET)/libc --prefix=$(GCC_TOOLCHAIN_BIN)/$(TARGET)-
-else
-$(error Unrecognized toolchain: TOOLCHAIN=$(TOOLCHAIN))
-endif
-
-LINUX_TOOLCHAIN_BIN := $(TOOLCHAIN_BIN)
-LINUX_TARGET := $(TARGET)
-
-# Common to all GCC versions:
-# Use the latest GCC toolchain to build everything other than the kernel
-# because kvm-unit-tests as well as u-boot require recent versions.
-ifneq ($(TOOLCHAIN), clang)
-TARGET = $(TARGET_GCC_92)
-TOOLCHAIN_BIN := $(TOOLCHAIN_GCC_92)/bin
-GCC_TOOLCHAIN_BIN := $(TOOLCHAIN_BIN)
-CC := $(TOOLCHAIN_BIN)/$(TARGET)-gcc
-LD := $(TOOLCHAIN_BIN)/$(TARGET)-ld
-OBJCOPY := $(TOOLCHAIN_BIN)/$(TARGET)-objcopy
-OBJDUMP := $(TOOLCHAIN_BIN)/$(TARGET)-objdump
-endif
+$(eval $(call define_toolchain,$(TOOLCHAIN)))
 
 ##
 ## Common targets
@@ -201,6 +156,16 @@
 KUT_DIST := $(DIST_DIR)/kvm-unit-tests
 KUT_STAMP := $(KUT_OUT)/kvm-unit-tests.stamp
 
+ifneq ($(TOOLCHAIN_IS_LEGACY),1)
+KUT_TOOLCHAIN ?= $(TOOLCHAIN)
+else
+KUT_TOOLCHAIN ?= gcc-9.2
+endif
+$(eval $(call define_toolchain,$(KUT_TOOLCHAIN),KUT_))
+
+ifeq ($(KUT_TOOLCHAIN_IS_LLVM),1)
+KUT_CFLAGS += --target=$(KUT_TARGET) -fpic
+endif
 
 .PHONY: kvm-unit-tests
 kvm-unit-tests $(KUT_STAMP):
@@ -209,9 +174,9 @@
 		$(KUT_SRC)/configure \
 			--erratatxt=$(KUT_SRC)/errata.txt \
 			--prefix=$(KUT_OUT) --arch=$(ARCH) \
-			--cc=$(CC) --ld=$(LD) \
-			--objcopy=$(OBJCOPY) --objdump=$(OBJDUMP)
-	COMMON_CFLAGS="$(COMMON_CFLAGS)" \
+			--cc=$(KUT_CC) --ld=$(KUT_LD) \
+			--objcopy=$(KUT_OBJCOPY) --objdump=$(KUT_OBJDUMP)
+	COMMON_CFLAGS="$(KUT_CFLAGS)" \
 		$(MAKE) -C $(KUT_OUT) standalone
 	$(call copy_to_dist, $(KUT_OUT)/tests, $(KUT_DIST))
 	touch $(KUT_STAMP)
@@ -229,20 +194,30 @@
 LINUX_VERBOSE ?= $(VERBOSE)
 LINUX_SRC ?= $(ROOT_DIR)/linux
 LINUX_VMLINUX = $(LINUX_OUT)/vmlinux
+LINUX_CONFIG := $(LINUX_OUT)/.config
 LINUX_DIST_FILES = $(LINUX_CONFIG) $(LINUX_VMLINUX) $(KERNEL_IMAGE)
 
+LINUX_TOOLCHAIN ?= $(TOOLCHAIN)
+$(eval $(call define_toolchain,$(LINUX_TOOLCHAIN),LINUX_))
+
+ifeq ($(LINUX_TOOLCHAIN_IS_LLVM),1)
+LINUX_EXTRA += LLVM=1 LLVM_IAS=1
+endif
+
 LINUX_MAKE := \
 	PATH=$(LINUX_TOOLCHAIN_BIN):$(PATH) \
 	ARCH=$(ARCH) \
 	CROSS_COMPILE="$(LINUX_TARGET)-" \
 	$(MAKE) \
 	$(LINUX_EXTRA) \
+	CC=$(LINUX_CC) \
+	LD=$(LINUX_LD) \
+	OBJCOPY=$(LINUX_OBJCOPY) \
+	OBJDUMP=$(LINUX_OBJDUMP) \
 	-C $(LINUX_SRC) \
 	V=$(LINUX_VERBOSE) \
 	O=$(LINUX_OUT)
 
-LINUX_CONFIG := $(LINUX_OUT)/.config
-
 .PHONY: linux_defconfig
 linux_defconfig $(LINUX_CONFIG):
 	+ $(LINUX_MAKE) $(LINUX_DEFCONFIG)
diff --git a/toolchain.mk b/toolchain.mk
new file mode 100644
index 0000000..355648c
--- /dev/null
+++ b/toolchain.mk
@@ -0,0 +1,57 @@
+# Copyright 2021 The Android KVM Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Find the toolchain path for the current OS.
+UNNAME_S := $(shell uname -s | tr '[:upper:]' '[:lower:]')
+TOOLCHAIN_arm64_clang := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/clang/clang-r416183b
+TOOLCHAIN_arm64_gcc-5.1 := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/gcc/aarch64-linux-gnu-5.1
+TOOLCHAIN_arm64_gcc-9.2 := $(ROOT_DIR)/toolchains/$(UNNAME_S)-x86/gcc/aarch64-linux-gnu-9.2
+
+# Set the toolchain build binary paths and prefixes.
+TARGET_arm64_clang := aarch64-linux-gnu
+TARGET_arm64_gcc-5.1 := aarch64-linux-gnu
+TARGET_arm64_gcc-9.2 := aarch64-none-linux-gnu
+
+#
+# $(1): toolchain name
+# $(2): toolchain variables' prefix
+#
+define define_toolchain
+    $(2)TOOLCHAIN_IS_LEGACY := 0
+    $(2)TOOLCHAIN_IS_LLVM := 0
+
+    ifeq ($(1), gcc-5.1)
+        $(2)TOOLCHAIN_IS_LEGACY := 1
+    else ifeq ($(1), gcc-9.2)
+    else ifeq ($(1), clang)
+        $(2)TOOLCHAIN_IS_LLVM := 1
+    else
+        $$(error Unrecognized toolchain: $(1))
+    endif
+
+    $(2)TOOLCHAIN_BIN :=    $(TOOLCHAIN_$(ARCH)_$(1))/bin
+    $(2)TARGET :=           $(TARGET_$(ARCH)_$(1))
+
+    ifeq ($$($(2)TOOLCHAIN_IS_LLVM),1)
+        $(2)CC :=           $$($(2)TOOLCHAIN_BIN)/clang
+        $(2)LD :=           $$($(2)TOOLCHAIN_BIN)/ld.lld
+        $(2)OBJCOPY :=      $$($(2)TOOLCHAIN_BIN)/llvm-objcopy
+        $(2)OBJDUMP :=      $$($(2)TOOLCHAIN_BIN)/llvm-objdump
+    else
+        $(2)CC :=           $$($(2)TOOLCHAIN_BIN)/$$($(2)TARGET)-gcc
+        $(2)LD :=           $$($(2)TOOLCHAIN_BIN)/$$($(2)TARGET)-ld
+        $(2)OBJCOPY :=      $$($(2)TOOLCHAIN_BIN)/$$($(2)TARGET)-objcopy
+        $(2)OBJDUMP :=      $$($(2)TOOLCHAIN_BIN)/$$($(2)TARGET)-objdump
+    endif
+endef