AAPT2: Accept a file with arguments when argument list is too long

Bug:22775504
Change-Id: Ife73d4e4611016c9ee7b56264bc6a765c54beba3
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8c8bffa..4997120 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -1459,6 +1459,21 @@
         return 1;
     }
 
+    // Expand all argument-files passed into the command line. These start with '@'.
+    std::vector<std::string> argList;
+    for (const std::string& arg : flags.getArgs()) {
+        if (util::stringStartsWith<char>(arg, "@")) {
+            const std::string path = arg.substr(1, arg.size() - 1);
+            std::string error;
+            if (!file::appendArgsFromFile(path, &argList, &error)) {
+                context.getDiagnostics()->error(DiagMessage(path) << error);
+                return 1;
+            }
+        } else {
+            argList.push_back(arg);
+        }
+    }
+
     if (verbose) {
         context.setVerbose(verbose);
     }
@@ -1568,7 +1583,7 @@
     }
 
     LinkCommand cmd(&context, options);
-    return cmd.run(flags.getArgs());
+    return cmd.run(argList);
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index bb093ab..f5e49f1 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -18,6 +18,7 @@
 #include "util/Util.h"
 
 #include <algorithm>
+#include <android-base/file.h>
 #include <cerrno>
 #include <cstdio>
 #include <dirent.h>
@@ -190,6 +191,23 @@
     return std::move(fileMap);
 }
 
+bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+                        std::string* outError) {
+    std::string contents;
+    if (!android::base::ReadFileToString(path.toString(), &contents)) {
+        if (outError) *outError = "failed to read argument-list file";
+        return false;
+    }
+
+    for (StringPiece line : util::tokenize<char>(contents, ' ')) {
+        line = util::trimWhitespace(line);
+        if (!line.empty()) {
+            outArgList->push_back(line.toString());
+        }
+    }
+    return true;
+}
+
 bool FileFilter::setPattern(const StringPiece& pattern) {
     mPatternTokens = util::splitAndLowercase(pattern, ':');
     return true;
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index c2e6115..4d8a1fe 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -95,6 +95,12 @@
  */
 Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError);
 
+/**
+ * Reads the file at path and appends each line to the outArgList vector.
+ */
+bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+                        std::string* outError);
+
 /*
  * Filter that determines which resource files/directories are
  * processed by AAPT. Takes a pattern string supplied by the user.