Solve potential corpus name conflict

Solve potential corpus name conflict

Fuzzing_corpus copies all corpus files into the same output directory,
if the name of any two corpus files are the same, then the process can't
be completed.

The make_corpus_dir.py will change the name a/b/c.txt to a-b-c.txt to
avoid the name conflict.

Signed-off-by: tengpeng <tengpeng.li2020@gmail.com>

	modified:   fuzzing/common.bzl
	modified:   fuzzing/tools/BUILD
	new file:   fuzzing/tools/make_corpus_dir.py
diff --git a/fuzzing/common.bzl b/fuzzing/common.bzl
index c1604a9..89e6555 100644
--- a/fuzzing/common.bzl
+++ b/fuzzing/common.bzl
@@ -84,16 +84,14 @@
 def _fuzzing_corpus_impl(ctx):
     corpus_dir = ctx.actions.declare_directory(ctx.attr.name)
     cp_args = ctx.actions.args()
-    cp_args.add_all(ctx.files.srcs)
+    cp_args.add_joined("--corpus_list", ctx.files.srcs, join_with = ",")
+    cp_args.add("--output_dir=" + corpus_dir.path)
 
-    # Add destination to the arguments
-    cp_args.add(corpus_dir.path)
-
-    ctx.actions.run_shell(
+    ctx.actions.run(
         inputs = ctx.files.srcs,
         outputs = [corpus_dir],
         arguments = [cp_args],
-        command = "mkdir " + corpus_dir.path + "; cp -r $@",
+        executable = ctx.executable._corpus_tool,
     )
 
     return [DefaultInfo(
@@ -108,6 +106,12 @@
 specified in the srcs attribute.
 """,
     attrs = {
+        "_corpus_tool": attr.label(
+            default = Label("//fuzzing/tools:make_corpus_dir"),
+            doc = "The tool script to copy and rename the corpus.",
+            executable = True,
+            cfg = "host",
+        ),
         "srcs": attr.label_list(
             doc = "The corpus files for the fuzzing test.",
             allow_files = True,
diff --git a/fuzzing/tools/BUILD b/fuzzing/tools/BUILD
index c559a8f..bf81d24 100644
--- a/fuzzing/tools/BUILD
+++ b/fuzzing/tools/BUILD
@@ -27,6 +27,12 @@
 )
 
 py_binary(
+    name = "make_corpus_dir",
+    srcs = ["make_corpus_dir.py"],
+    visibility = ["//visibility:public"],
+)
+
+py_binary(
     name = "validate_dict",
     srcs = ["validate_dict.py"],
     visibility = ["//visibility:public"],
diff --git a/fuzzing/tools/make_corpus_dir.py b/fuzzing/tools/make_corpus_dir.py
new file mode 100644
index 0000000..fd8a65f
--- /dev/null
+++ b/fuzzing/tools/make_corpus_dir.py
@@ -0,0 +1,65 @@
+#
+# Copyright 2020 Google LLC
+#
+# 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.
+
+# Lint as: python3
+"""
+Copies and renames a set of corpus files into a given directory.
+"""
+
+from absl import app
+from absl import flags
+from sys import stderr
+import glob
+import os
+import shutil
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_list("corpus_list", [],
+                  "Each element in the list stands for a corpus file")
+
+flags.DEFINE_string("output_dir", None, "The path of the output directory")
+
+flags.mark_flag_as_required("output_dir")
+
+
+def main(argv):
+    if not os.path.exists(FLAGS.output_dir):
+        os.makedirs(FLAGS.output_dir)
+
+    expanded_file_list = []
+    for corpus in FLAGS.corpus_list:
+        if not os.path.exists(corpus):
+            print("ERROR: file " + corpus + " doesn't exist.", file=stderr)
+            return -1
+        if os.path.isdir(corpus):
+            # The first element in glob("dir/**") is "dir/", which needs to be excluded
+            expanded_file_list += glob.glob(os.path.join(corpus, "**"), recursive=True)[1:]
+        else:
+            expanded_file_list.append(corpus)
+    
+    for corpus in expanded_file_list:
+        dest = os.path.join(FLAGS.output_dir, corpus.replace("/", "-"))
+        # Whatever the separator we choose, there is an chance that
+        # the dest name conflicts with another file
+        if os.path.exists(dest):
+            print("ERROR: file " + dest + " existed.", file=stderr)
+            return -1
+        shutil.copy(corpus, dest)
+    return 0
+
+
+if __name__ == '__main__':
+    app.run(main)