[desc] Silence print() statements when outputing json

Instead of outputting any print() statements encountered during the
running of the Setup, ignore them with a no-op print callback which
is swapped back for the original at the end of the command.

Change-Id: Ic87dcb444110464fa9d56c6ca02f8e1c7a78724a
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/15440
Reviewed-by: David Turner <digit@google.com>
Commit-Queue: Aaron Wood <aaronwood@google.com>
diff --git a/src/gn/build_settings.cc b/src/gn/build_settings.cc
index b364ea2..6a26495 100644
--- a/src/gn/build_settings.cc
+++ b/src/gn/build_settings.cc
@@ -78,3 +78,10 @@
   if (item_defined_callback_)
     item_defined_callback_(std::move(item));
 }
+
+const BuildSettings::PrintCallback BuildSettings::swap_print_callback(
+    const BuildSettings::PrintCallback callback) {
+  auto temp = std::move(print_callback_);
+  print_callback_ = callback;
+  return temp;
+}
\ No newline at end of file
diff --git a/src/gn/build_settings.h b/src/gn/build_settings.h
index 82f11db..dd3bd9c 100644
--- a/src/gn/build_settings.h
+++ b/src/gn/build_settings.h
@@ -129,7 +129,8 @@
   // callback is is_null() (the default) the output will be printed to the
   // console.
   const PrintCallback& print_callback() const { return print_callback_; }
-  void set_print_callback(const PrintCallback& cb) { print_callback_ = cb; }
+  void set_print_callback(const PrintCallback cb) { print_callback_ = cb; }
+  const PrintCallback swap_print_callback(const PrintCallback cb);
 
   // A list of files that can call exec_script(). If the returned pointer is
   // null, exec_script may be called from anywhere.
diff --git a/src/gn/command_desc.cc b/src/gn/command_desc.cc
index 95652f1..b50d4e9 100644
--- a/src/gn/command_desc.cc
+++ b/src/gn/command_desc.cc
@@ -623,6 +623,25 @@
       each one was set from.
 )";
 
+class PrintCallbackHolder {
+ public:
+  PrintCallbackHolder() {}
+  ~PrintCallbackHolder() {
+    if (_settings.has_value()) {
+      _settings.value()->swap_print_callback(_callback.value());
+    }
+  }
+  void SwapCallbacks(BuildSettings* settings,
+                     BuildSettings::PrintCallback new_callback) {
+    _settings = settings;
+    _callback = settings->swap_print_callback(new_callback);
+  }
+
+ private:
+  std::optional<BuildSettings*> _settings;
+  std::optional<BuildSettings::PrintCallback> _callback;
+};
+
 int RunDesc(const std::vector<std::string>& args) {
   if (args.size() != 2 && args.size() != 3) {
     Err(Location(), "Unknown command format. See \"gn help desc\"",
@@ -634,6 +653,16 @@
 
   // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
+
+  bool json = cmdline->GetSwitchValueString("format") == "json";
+  PrintCallbackHolder print_callback_holder;
+  if (json) {
+    // Silence all output while running desc if outputting to json.
+    BuildSettings* settings = &setup->build_settings();
+    print_callback_holder.SwapCallbacks(settings,
+                                        [](const std::string& str) {});
+  }
+
   if (!setup->DoSetup(args[0], false))
     return 1;
   if (!setup->Run())
@@ -657,8 +686,6 @@
   if (args.size() == 3)
     what_to_print = args[2];
 
-  bool json = cmdline->GetSwitchValueString("format") == "json";
-
   if (target_matches.empty() && config_matches.empty()) {
     OutputString(
         "The input " + args[1] + " matches no targets, configs or files.\n",