Copy kernel module source including subfolders

Previously the build used the GN copy() target to copy the source
of a to-be-built kernel module into the output directory. However,
the target does not support preserving subfolder structure. Replace
it with a Python script.

A new 'source_dir_copy' template is added, which creates a dependency
on the files in the source directory using the 'source_dir' target.
Then it copies the files into 'target_out_dir'. When the source
directory changes, the copying Python script is invoked again, updating
the copy in the out folder.

Change-Id: I23f2807fbb77bcf6d933e9541f5dbf1f2429b410
diff --git a/build/linux/copy_dirs.py b/build/linux/copy_dirs.py
new file mode 100644
index 0000000..e44128e
--- /dev/null
+++ b/build/linux/copy_dirs.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+#
+# 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.
+
+#!/usr/bin/env python
+"""Copies all files inside one folder to another, preserving subfolders."""
+
+import argparse
+import os
+import shutil
+import sys
+
+def main():
+	parser = argparse.ArgumentParser()
+	parser.add_argument("source_folder",
+	                    help="directory to be copied from")
+	parser.add_argument("destination_folder",
+	                    help="directory to be copied into")
+	parser.add_argument("stamp_file",
+	                    help="stamp file to be touched")
+	args = parser.parse_args()
+
+	# Walk the subfolders of the source directory and copy individual files.
+	# Not using shutil.copytree() because it never overwrites files.
+	for root, _, files in os.walk(args.source_folder):
+		for f in files:
+			abs_src_path = os.path.join(root, f)
+			rel_path = os.path.relpath(abs_src_path, args.source_folder)
+			abs_dst_path = os.path.join(args.destination_folder, rel_path)
+			abs_dst_folder = os.path.dirname(abs_dst_path)
+			if not os.path.isdir(abs_dst_folder):
+				os.makedirs(abs_dst_folder)
+			shutil.copyfile(abs_src_path, abs_dst_path)
+
+	# Touch `stamp_file`.
+	with open(args.stamp_file, "w"):
+		pass
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/build/linux/linux.gni b/build/linux/linux.gni
index 6ac0b8a..de0f8a3 100644
--- a/build/linux/linux.gni
+++ b/build/linux/linux.gni
@@ -28,6 +28,29 @@
   }
 }
 
+template("source_dir_copy") {
+  source_dir_target = "${target_name}__source_dir"
+
+  source_dir(source_dir_target) {
+    path = invoker.path
+  }
+
+  action("${target_name}") {
+    script = "//build/linux/copy_dirs.py"
+    outputs = [
+      "$target_out_dir/${target_name}.script.stamp",
+    ]
+    args = [
+      rebase_path(invoker.path),
+      rebase_path(target_out_dir),
+      rebase_path(outputs[0]),
+    ]
+    deps = [
+      ":${source_dir_target}",
+    ]
+  }
+}
+
 template("linux_kernel") {
   source_target = "${target_name}__source"
   defconfig_target = "${target_name}__defconfig"
@@ -109,15 +132,11 @@
   # 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}}",
-    ]
+
+  source_target = "${target_name}__source"
+
+  source_dir_copy(source_target) {
+    path = invoker.module_dir
   }
 
   action(target_name) {
@@ -127,7 +146,7 @@
       "--directory",
       rebase_path(target_out_dir),
       "HAFNIUM_PATH=" + rebase_path("//"),
-      "KERNEL_PATH=" + rebase_path(invoker.kernel_src_dir),
+      "KERNEL_PATH=" + rebase_path(invoker.kernel_dir),
       "O=" +
           rebase_path(get_label_info(invoker.kernel_target, "target_out_dir")),
       "CC=" + rebase_path("//prebuilts/linux-x64/clang/bin/clang"),
@@ -138,7 +157,7 @@
       "${target_out_dir}/${invoker.module_name}.ko",
     ]
     deps = [
-      ":${target_name}__copy_source",
+      ":${source_target}",
       "${invoker.kernel_target}__defconfig",
     ]
   }
diff --git a/driver/BUILD.gn b/driver/BUILD.gn
index 0fae976..2cb214a 100644
--- a/driver/BUILD.gn
+++ b/driver/BUILD.gn
@@ -16,11 +16,7 @@
 
 linux_kernel_module("linux") {
   module_name = "hafnium"
+  module_dir = "./linux"
   kernel_target = "//third_party:linux"
-  kernel_src_dir = "//third_party/linux"
-  sources = [
-    "linux/Makefile",
-    "linux/hf_call.S",
-    "linux/main.c",
-  ]
+  kernel_dir = "//third_party/linux"
 }