Build Linux and the Linux driver out of tree

Linux and the Hafnium module have been built inside their source tree so
far. The targets have a hardcoded toolchain but they get built multiple
times, once for each toolchain defined in GN. This can cause the build
to fail when two instances attempt to compile the same file at the same
time. Refactor the build scripts to compile the kernel and the module
in the out/ directory of each corresponding toolchain.

This cleans up the build files a bit but also introduces a new problem.
Out-of-tree kernel modules do not support building out of their folder.
This patch avoids that by copying the source files into
`target_out_dir`.

Test: kokoro/ubuntu/build.sh
Change-Id: I6fc10ebd9623f5f82dd086447b80d935c490765e
diff --git a/build/linux/linux.gni b/build/linux/linux.gni
new file mode 100644
index 0000000..0f096eb
--- /dev/null
+++ b/build/linux/linux.gni
@@ -0,0 +1,105 @@
+# Copyright 2019 The Hafnium 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
+#
+#     https://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.
+
+template("linux_kernel") {
+  # TODO: target has no "sources"
+
+  # Args to build/make.py to start the Linux build.
+  shared_args = [
+    "--directory",
+    rebase_path(invoker.kernel_dir),
+
+    # TODO: Build with toolchain cc instead of a hardcoded one.
+    "CC=" + rebase_path("//prebuilts/linux-x64/clang/bin/clang"),
+    "ARCH=arm64",
+    "CROSS_COMPILE=aarch64-linux-gnu-",
+
+    # Build out-of-tree in `target_out_dir`.
+    "O=" + rebase_path(target_out_dir),
+
+    # TODO: Remove/replace.
+    "-j24",
+  ]
+
+  # Subtarget which runs `defconfig` and `modules_prepare`. Used by targets
+  # which do not require the whole kernel to have been built.
+  action("${target_name}__defconfig") {
+    script = "//build/make.py"
+    args = shared_args + [
+             "defconfig",
+             "modules_prepare",
+           ]
+
+    # We never use the output but GN requires each target to have one, and for
+    # its timestamp to change after a recompile. Use the .config file.
+    outputs = [
+      "${target_out_dir}/.config",
+    ]
+  }
+
+  action(target_name) {
+    script = "//build/make.py"
+    output_file = "${target_out_dir}/${target_name}.bin"
+    args = shared_args + [
+             "--copy_out_file",
+             rebase_path("${target_out_dir}/arch/arm64/boot/Image"),
+             rebase_path(output_file),
+           ]
+    outputs = [
+      output_file,
+    ]
+    deps = [
+      ":${target_name}__defconfig",
+    ]
+  }
+}
+
+template("linux_kernel_module") {
+  # Out-of-tree modules cannot be built outside of their directory.
+  # So as to avoid parallel builds under different toolchains clashing,
+  # work around by copying source files to `target_out_dir`.
+  copy("${target_name}__copy_source") {
+    forward_variables_from(invoker,
+                           [
+                             "sources",
+                             "testonly",
+                           ])
+    outputs = [
+      "${target_out_dir}/{{source_file_part}}",
+    ]
+  }
+
+  action(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+    script = "//build/make.py"
+    args = [
+      "--directory",
+      rebase_path(target_out_dir),
+      "HAFNIUM_PATH=" + rebase_path("//"),
+      "KERNEL_PATH=" + rebase_path(invoker.kernel_src_dir),
+      "O=" +
+          rebase_path(get_label_info(invoker.kernel_target, "target_out_dir")),
+      "CC=" + rebase_path("//prebuilts/linux-x64/clang/bin/clang"),
+      "ARCH=arm64",
+      "CROSS_COMPILE=aarch64-linux-gnu-",
+    ]
+    outputs = [
+      "${target_out_dir}/${invoker.module_name}.ko",
+    ]
+    deps = [
+      ":${target_name}__copy_source",
+      "${invoker.kernel_target}__defconfig",
+    ]
+  }
+}
diff --git a/build/make.py b/build/make.py
index d4cc0d8..75f0b7b 100644
--- a/build/make.py
+++ b/build/make.py
@@ -26,8 +26,8 @@
 def Main():
     parser = argparse.ArgumentParser()
     parser.add_argument("--directory", required=True)
-    parser.add_argument("--out_file", required=True)
-    parser.add_argument("--copy_out_file", required=True)
+    parser.add_argument("--copy_out_file", nargs=2,
+                        help="Copy file after building. Takes two params: <src> <dest>")
     args, make_args = parser.parse_known_args()
 
     os.chdir(args.directory)
@@ -36,7 +36,8 @@
     if status != 0:
         return status
 
-    shutil.copyfile(args.out_file, args.copy_out_file)
+    if args.copy_out_file is not None:
+        shutil.copyfile(args.copy_out_file[0], args.copy_out_file[1])
     return 0