Allow analyze reading from stdin and writing to stdout.

Bug: none
Change-Id: I455f633309a6f59a78505d06a1e43b9d612c2adf
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/6280
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/command_analyze.cc b/tools/gn/command_analyze.cc
index d3b24bf..096cf6b 100644
--- a/tools/gn/command_analyze.cc
+++ b/tools/gn/command_analyze.cc
@@ -14,6 +14,8 @@
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/location.h"
 #include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/string_utils.h"
 
 namespace commands {
 
@@ -50,6 +52,8 @@
      This filtering behavior is also known as "pruning" the list of compile
      targets.
 
+  If input_path is -, input is read from stdin.
+
   output_path is a path indicating where the results of the command are to be
   written. The results will be a file containing a JSON object with one or more
   of following fields:
@@ -83,6 +87,8 @@
      a string describing the error. This includes cases where the input file is
      not in the right format, or contains invalid targets.
 
+  If output_path is -, output is written to stdout.
+
   The command returns 1 if it is unable to read the input file or write the
   output file, or if there is something wrong with the build such that gen
   would also fail, and 0 otherwise. In particular, it returns 0 even if the
@@ -100,10 +106,14 @@
   }
 
   std::string input;
-  bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
-  if (!ret) {
-    Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
-    return 1;
+  if (args[1] == "-") {
+    input = ReadStdin();
+  } else {
+    bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
+    if (!ret) {
+      Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
+      return 1;
+    }
   }
 
   // Deliberately leaked to avoid expensive process teardown.
@@ -122,10 +132,14 @@
     return 1;
   }
 
-  WriteFile(UTF8ToFilePath(args[2]), output, &err);
-  if (err.has_error()) {
-    err.PrintToStdout();
-    return 1;
+  if (args[2] == "-") {
+    OutputString(output + "\n");
+  } else {
+    WriteFile(UTF8ToFilePath(args[2]), output, &err);
+    if (err.has_error()) {
+      err.PrintToStdout();
+      return 1;
+    }
   }
 
   return 0;
diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
index 15689ad..72d6eb8 100644
--- a/tools/gn/command_format.cc
+++ b/tools/gn/command_format.cc
@@ -21,6 +21,7 @@
 #include "tools/gn/scheduler.h"
 #include "tools/gn/setup.h"
 #include "tools/gn/source_file.h"
+#include "tools/gn/string_utils.h"
 #include "tools/gn/switches.h"
 #include "tools/gn/tokenizer.h"
 
@@ -1081,23 +1082,6 @@
   *output = pr.String();
 }
 
-std::string ReadStdin() {
-  static const int kBufferSize = 256;
-  char buffer[kBufferSize];
-  std::string result;
-  while (true) {
-    char* input = nullptr;
-    input = fgets(buffer, kBufferSize, stdin);
-    if (input == nullptr && feof(stdin))
-      return result;
-    int length = static_cast<int>(strlen(buffer));
-    if (length == 0)
-      return result;
-    else
-      result += std::string(buffer, length);
-  }
-}
-
 }  // namespace
 
 bool FormatFileToString(Setup* setup,
diff --git a/tools/gn/string_utils.cc b/tools/gn/string_utils.cc
index ab0aea8..72eb4b1 100644
--- a/tools/gn/string_utils.cc
+++ b/tools/gn/string_utils.cc
@@ -344,3 +344,20 @@
   }
   return result;
 }
+
+std::string ReadStdin() {
+  static const int kBufferSize = 256;
+  char buffer[kBufferSize];
+  std::string result;
+  while (true) {
+    char* input = nullptr;
+    input = fgets(buffer, kBufferSize, stdin);
+    if (input == nullptr && feof(stdin))
+      return result;
+    int length = static_cast<int>(strlen(buffer));
+    if (length == 0)
+      return result;
+    else
+      result += std::string(buffer, length);
+  }
+}
diff --git a/tools/gn/string_utils.h b/tools/gn/string_utils.h
index 3d4e056..a98a485 100644
--- a/tools/gn/string_utils.h
+++ b/tools/gn/string_utils.h
@@ -50,4 +50,7 @@
 std::string_view SpellcheckString(const std::string_view& text,
                                   const std::vector<std::string_view>& words);
 
+// Reads stdin until end-of-data and returns what it read.
+std::string ReadStdin();
+
 #endif  // TOOLS_GN_STRING_UTILS_H_