text: // 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 <stddef.h>
  
  #include <algorithm>
  
  #include "base/command_line.h"
  #include "base/strings/stringprintf.h"
  #include "gn/commands.h"
  #include "gn/setup.h"
  #include "gn/standard_out.h"
  
  namespace commands {
  
  const char kOutputs[] = "outputs";
  const char kOutputs_HelpShort[] = "outputs: Which files a source/target make.";
  const char kOutputs_Help[] =
      R"(gn outputs <out_dir> <list of target or file names...> *
  
    Lists the output files corresponding to the given target(s) or file name(s).
    There can be multiple outputs because there can be more than one output
    generated by a build step, and there can be more than one toolchain matched.
    You can also list multiple inputs which will generate a union of all the
    outputs from those inputs.
  
     - The input target/file names are relative to the current directory.
  
     - The output file names are relative to the root build directory.
  
     This command is useful for finding a ninja command that will build only a
     portion of the build.
  
  Target outputs
  
    If the parameter is a target name that includes a toolchain, it will match
    only that target in that toolchain. If no toolchain is specified, it will
    match all targets with that name in any toolchain.
  
    The result will be the outputs specified by that target which could be a
    library, executable, output of an action, a stamp file, etc.
  
  File outputs
  
    If the parameter is a file name it will compute the output for that compile
    step for all targets in all toolchains that contain that file as a source
    file.
  
    If the source is not compiled (e.g. a header or text file), the command will
    produce no output.
  
    If the source is listed as an "input" to a binary target or action will
    resolve to that target's outputs.
  
  Example
  
    gn outputs out/debug some/directory:some_target
        Find the outputs of a given target.
  
    gn outputs out/debug src/project/my_file.cc | xargs ninja -C out/debug
        Compiles just the given source file in all toolchains it's referenced in.
  
    git diff --name-only | xargs gn outputs out/x64 | xargs ninja -C out/x64
        Compiles all files changed in git.
  )";
  
  int RunOutputs(const std::vector<std::string>& args) {
    if (args.size() < 2) {
      Err(Location(),
          "Expected a build dir and one or more input files or targets.\n"
          "Usage: \"gn outputs <out_dir> <target-or-file>*\"")
          .PrintToStdout();
      return 1;
    }
  
    // Deliberately leaked to avoid expensive process teardown.
    Setup* setup = new Setup;
    if (!setup->DoSetup(args[0], false))
      return 1;
    if (!setup->Run())
      return 1;
  
    std::vector<std::string> inputs(args.begin() + 1, args.end());
  
    UniqueVector<const Target*> target_matches;
    UniqueVector<const Config*> config_matches;
    UniqueVector<const Toolchain*> toolchain_matches;
    UniqueVector<SourceFile> file_matches;
    if (!ResolveFromCommandLineInput(setup, inputs, false, &target_matches,
                                     &config_matches, &toolchain_matches,
                                     &file_matches))
      return 1;
  
    // We only care about targets and files.
    if (target_matches.empty() && file_matches.empty()) {
      Err(Location(), "The input matched no targets or files.").PrintToStdout();
      return 1;
    }
  
    // Resulting outputs.
    std::vector<OutputFile> outputs;
  
    // Files. This must go first because it may add to the "targets" list.
    std::vector<const Target*> all_targets =
        setup->builder().GetAllResolvedTargets();
    for (const SourceFile& file : file_matches) {
      std::vector<TargetContainingFile> targets;
      GetTargetsContainingFile(setup, all_targets, file, false, &targets);
      if (targets.empty()) {
        Err(Location(), base::StringPrintf("No targets reference the file '%s'.",
                                           file.value().c_str()))
            .PrintToStdout();
        return 1;
      }
  
      // There can be more than one target that references this file, evaluate the
      // output name in all of them.
      for (const TargetContainingFile& pair : targets) {
        if (pair.second == HowTargetContainsFile::kInputs) {
          // Inputs maps to the target itself. This will be evaluated below.
          target_matches.push_back(pair.first);
        } else if (pair.second == HowTargetContainsFile::kSources) {
          // Source file, check it.
          const char* computed_tool = nullptr;
          std::vector<OutputFile> file_outputs;
          pair.first->GetOutputFilesForSource(file, &computed_tool,
                                              &file_outputs);
          outputs.insert(outputs.end(), file_outputs.begin(), file_outputs.end());
        }
      }
    }
  
    // Targets.
    for (const Target* target : target_matches) {
      std::vector<SourceFile> output_files;
      Err err;
      if (!target->GetOutputsAsSourceFiles(LocationRange(), true, &output_files,
                                           &err)) {
        err.PrintToStdout();
        return 1;
      }
  
      // Convert to OutputFiles.
      for (const SourceFile& file : output_files)
        outputs.emplace_back(&setup->build_settings(), file);
    }
  
    // Print.
    for (const OutputFile& output_file : outputs)
      printf("%s\n", output_file.value().c_str());
    return 0;
  }
  
  }  // namespace commands
  