// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <optional>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "gn/commands.h"
#include "gn/err.h"
#include "gn/ninja_tools.h"
#include "gn/setup.h"
#include "gn/source_dir.h"
#include "gn/switches.h"
#include "gn/version.h"

namespace commands {

namespace {

bool CleanStaleOneDir(const base::FilePath& ninja_executable,
                      const std::string& dir) {
  // Deliberately leaked to avoid expensive process teardown.
  Setup* setup = new Setup;
  if (!setup->DoSetup(dir, false))
    return false;

  base::FilePath build_dir(setup->build_settings().GetFullPath(
      SourceDir(setup->build_settings().build_dir().value())));

  // The order of operations for these tools is:
  // 1. cleandead - This eliminates old files from the build directory.
  // 2. recompact - This prunes old entries from the ninja log and deps files.
  //
  // This order is ideal because the files removed by cleandead will no longer
  // be found during the recompact, so ninja can prune their entries.
  Err err;
  if (!InvokeNinjaCleanDeadTool(ninja_executable, build_dir, &err)) {
    err.PrintToStdout();
    return false;
  }

  if (!InvokeNinjaRecompactTool(ninja_executable, build_dir, &err)) {
    err.PrintToStdout();
    return false;
  }
  return true;
}

}  // namespace

const char kCleanStale[] = "clean_stale";
const char kCleanStale_HelpShort[] =
    "clean_stale: Cleans the stale output files from the output directory.";
const char kCleanStale_Help[] =
    R"(gn clean_stale [--ninja-executable=...] <out_dir>...

  Removes the no longer needed output files from the build directory and prunes
  their records from the ninja build log and dependency database. These are
  output files that were generated from previous builds, but the current build
  graph no longer references them.

  This command requires a ninja executable of at least version 1.10.0. The
  executable must be provided by the --ninja-executable switch.

Options

  --ninja-executable=<string>
      Can be used to specify the ninja executable to use.
)";

int RunCleanStale(const std::vector<std::string>& args) {
  if (args.empty()) {
    Err(Location(), "Missing argument.",
        "Usage: \"gn clean_stale <out_dir>...\"")
        .PrintToStdout();
    return 1;
  }

  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
  base::FilePath ninja_executable =
      cmdline->GetSwitchValuePath(switches::kNinjaExecutable);
  if (ninja_executable.empty()) {
    Err(Location(), "No --ninja-executable provided.",
        "--clean-stale requires a ninja executable to run. You can "
        "provide one on the command line via --ninja-executable.")
        .PrintToStdout();
    return 1;
  }

  for (const std::string& dir : args) {
    if (!CleanStaleOneDir(ninja_executable, dir))
      return 1;
  }

  return 0;
}

}  // namespace commands
