// Copyright (c) 2013 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 "tools/gn/target.h"

#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/substitution_writer.h"

namespace {

typedef std::set<const Config*> ConfigSet;

// Merges the dependent configs from the given target to the given config list.
void MergeDirectDependentConfigsFrom(const Target* from_target,
                                     UniqueVector<LabelConfigPair>* dest) {
  const UniqueVector<LabelConfigPair>& direct =
      from_target->direct_dependent_configs();
  for (size_t i = 0; i < direct.size(); i++)
    dest->push_back(direct[i]);
}

// Like MergeDirectDependentConfigsFrom above except does the "all dependent"
// ones. This additionally adds all configs to the all_dependent_configs_ of
// the dest target given in *all_dest.
void MergeAllDependentConfigsFrom(const Target* from_target,
                                  UniqueVector<LabelConfigPair>* dest,
                                  UniqueVector<LabelConfigPair>* all_dest) {
  const UniqueVector<LabelConfigPair>& all =
      from_target->all_dependent_configs();
  for (size_t i = 0; i < all.size(); i++) {
    all_dest->push_back(all[i]);
    dest->push_back(all[i]);
  }
}

Err MakeTestOnlyError(const Target* from, const Target* to) {
  return Err(from->defined_from(), "Test-only dependency not allowed.",
      from->label().GetUserVisibleName(false) + "\n"
      "which is NOT marked testonly can't depend on\n" +
      to->label().GetUserVisibleName(false) + "\n"
      "which is marked testonly. Only targets with \"testonly = true\"\n"
      "can depend on other test-only targets.\n"
      "\n"
      "Either mark it test-only or don't do this dependency.");
}

// Inserts the given groups dependencies, starting at the given index of the
// given vector. Returns the number of items inserted.
size_t InsertGroupDeps(LabelTargetVector* vector,
                       size_t insert_at,
                       const Target* group) {
  const LabelTargetVector& deps = group->deps();
  vector->insert(vector->begin() + insert_at, deps.begin(), deps.end());

  // Clear the origin of each of the insertions. This marks these dependencies
  // as internally generated.
  for (size_t i = insert_at; i < deps.size() + insert_at; i++)
    (*vector)[i].origin = NULL;

  return deps.size();
}

}  // namespace

Target::Target(const Settings* settings, const Label& label)
    : Item(settings, label),
      output_type_(UNKNOWN),
      all_headers_public_(true),
      check_includes_(true),
      testonly_(false),
      hard_dep_(false),
      toolchain_(NULL) {
}

Target::~Target() {
}

// static
const char* Target::GetStringForOutputType(OutputType type) {
  switch (type) {
    case UNKNOWN:
      return "Unknown";
    case GROUP:
      return "Group";
    case EXECUTABLE:
      return "Executable";
    case SHARED_LIBRARY:
      return "Shared library";
    case STATIC_LIBRARY:
      return "Static library";
    case SOURCE_SET:
      return "Source set";
    case COPY_FILES:
      return "Copy";
    case ACTION:
      return "Action";
    case ACTION_FOREACH:
      return "ActionForEach";
    default:
      return "";
  }
}

Target* Target::AsTarget() {
  return this;
}

const Target* Target::AsTarget() const {
  return this;
}

bool Target::OnResolved(Err* err) {
  DCHECK(output_type_ != UNKNOWN);
  DCHECK(toolchain_) << "Toolchain should have been set before resolving.";

  ExpandGroups();

  // Copy our own dependent configs to the list of configs applying to us.
  configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
  configs_.Append(direct_dependent_configs_.begin(),
                  direct_dependent_configs_.end());

  // Copy our own libs and lib_dirs to the final set. This will be from our
  // target and all of our configs. We do this specially since these must be
  // inherited through the dependency tree (other flags don't work this way).
  for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
    const ConfigValues& cur = iter.cur();
    all_lib_dirs_.append(cur.lib_dirs().begin(), cur.lib_dirs().end());
    all_libs_.append(cur.libs().begin(), cur.libs().end());
  }

  if (output_type_ != GROUP) {
    // Don't pull target info like libraries and configs from dependencies into
    // a group target. When A depends on a group G, the G's dependents will
    // be treated as direct dependencies of A, so this is unnecessary and will
    // actually result in duplicated settings (since settings will also be
    // pulled from G to A in case G has configs directly on it).
    PullDependentTargetInfo();
  }
  PullForwardedDependentConfigs();
  PullRecursiveHardDeps();

  FillOutputFiles();

  if (!CheckVisibility(err))
    return false;
  if (!CheckTestonly(err))
    return false;

  return true;
}

bool Target::IsLinkable() const {
  return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY;
}

std::string Target::GetComputedOutputName(bool include_prefix) const {
  DCHECK(toolchain_)
      << "Toolchain must be specified before getting the computed output name.";

  const std::string& name = output_name_.empty() ? label().name()
                                                 : output_name_;

  std::string result;
  if (include_prefix) {
    const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
    const std::string& prefix = tool->output_prefix();
    // Only add the prefix if the name doesn't already have it.
    if (!StartsWithASCII(name, prefix, true))
      result = prefix;
  }

  result.append(name);
  return result;
}

bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
  DCHECK(!toolchain_);
  DCHECK_NE(UNKNOWN, output_type_);
  toolchain_ = toolchain;

  const Tool* tool = toolchain->GetToolForTargetFinalOutput(this);
  if (tool)
    return true;

  // Tool not specified for this target type.
  if (err) {
    *err = Err(defined_from(), "This target uses an undefined tool.",
        base::StringPrintf(
            "The target %s\n"
            "of type \"%s\"\n"
            "uses toolchain %s\n"
            "which doesn't have the tool \"%s\" defined.\n\n"
            "Alas, I can not continue.",
            label().GetUserVisibleName(false).c_str(),
            GetStringForOutputType(output_type_),
            label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
            Toolchain::ToolTypeToName(
                toolchain->GetToolTypeForTargetFinalOutput(this)).c_str()));
  }
  return false;
}

void Target::ExpandGroups() {
  // Convert any groups we depend on to just direct dependencies on that
  // group's deps. We insert the new deps immediately after the group so that
  // the ordering is preserved. We need to keep the original group so that any
  // flags, etc. that it specifies itself are applied to us.
  // TODO(brettw) bug 403488 this should also handle datadeps.
  for (size_t i = 0; i < deps_.size(); i++) {
    const Target* dep = deps_[i].ptr;
    if (dep->output_type_ == GROUP)
      i += InsertGroupDeps(&deps_, i + 1, dep);
  }
}

void Target::PullDependentTargetInfo() {
  // Gather info from our dependents we need.
  for (size_t dep_i = 0; dep_i < deps_.size(); dep_i++) {
    const Target* dep = deps_[dep_i].ptr;
    MergeAllDependentConfigsFrom(dep, &configs_, &all_dependent_configs_);
    MergeDirectDependentConfigsFrom(dep, &configs_);

    // Direct dependent libraries.
    if (dep->output_type() == STATIC_LIBRARY ||
        dep->output_type() == SHARED_LIBRARY ||
        dep->output_type() == SOURCE_SET)
      inherited_libraries_.push_back(dep);

    // Inherited libraries and flags are inherited across static library
    // boundaries.
    if (dep->output_type() != SHARED_LIBRARY &&
        dep->output_type() != EXECUTABLE) {
      inherited_libraries_.Append(dep->inherited_libraries().begin(),
                                  dep->inherited_libraries().end());

      // Inherited library settings.
      all_lib_dirs_.append(dep->all_lib_dirs());
      all_libs_.append(dep->all_libs());
    }
  }
}

void Target::PullForwardedDependentConfigs() {
  // Groups implicitly forward all if its dependency's configs.
  if (output_type() == GROUP) {
    for (size_t i = 0; i < deps_.size(); i++)
      forward_dependent_configs_.push_back(deps_[i]);
  }

  // Forward direct dependent configs if requested.
  for (size_t dep = 0; dep < forward_dependent_configs_.size(); dep++) {
    const Target* from_target = forward_dependent_configs_[dep].ptr;

    // The forward_dependent_configs_ must be in the deps already, so we
    // don't need to bother copying to our configs, only forwarding.
    DCHECK(std::find_if(deps_.begin(), deps_.end(),
                        LabelPtrPtrEquals<Target>(from_target)) !=
           deps_.end());
    direct_dependent_configs_.Append(
        from_target->direct_dependent_configs().begin(),
        from_target->direct_dependent_configs().end());
  }
}

void Target::PullRecursiveHardDeps() {
  for (size_t dep_i = 0; dep_i < deps_.size(); dep_i++) {
    const Target* dep = deps_[dep_i].ptr;
    if (dep->hard_dep())
      recursive_hard_deps_.insert(dep);

    // Android STL doesn't like insert(begin, end) so do it manually.
    // TODO(brettw) this can be changed to insert(dep->begin(), dep->end()) when
    // Android uses a better STL.
    for (std::set<const Target*>::const_iterator cur =
             dep->recursive_hard_deps().begin();
         cur != dep->recursive_hard_deps().end(); ++cur)
      recursive_hard_deps_.insert(*cur);
  }
}

void Target::FillOutputFiles() {
  const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
  switch (output_type_) {
    case GROUP:
    case SOURCE_SET:
    case COPY_FILES:
    case ACTION:
    case ACTION_FOREACH: {
      // These don't get linked to and use stamps which should be the first
      // entry in the outputs. These stamps are named
      // "<target_out_dir>/<targetname>.stamp".
      dependency_output_file_ = GetTargetOutputDirAsOutputFile(this);
      dependency_output_file_.value().append(GetComputedOutputName(true));
      dependency_output_file_.value().append(".stamp");
      break;
    }
    case EXECUTABLE:
      // Executables don't get linked to, but the first output is used for
      // dependency management.
      CHECK_GE(tool->outputs().list().size(), 1u);
      dependency_output_file_ =
          SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
              this, tool, tool->outputs().list()[0]);
      break;
    case STATIC_LIBRARY:
      // Static libraries both have dependencies and linking going off of the
      // first output.
      CHECK(tool->outputs().list().size() >= 1);
      link_output_file_ = dependency_output_file_ =
          SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
              this, tool, tool->outputs().list()[0]);
      break;
    case SHARED_LIBRARY:
      CHECK(tool->outputs().list().size() >= 1);
      if (tool->link_output().empty() && tool->depend_output().empty()) {
        // Default behavior, use the first output file for both.
        link_output_file_ = dependency_output_file_ =
            SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
                this, tool, tool->outputs().list()[0]);
      } else {
        // Use the tool-specified ones.
        if (!tool->link_output().empty()) {
          link_output_file_ =
              SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
                  this, tool, tool->link_output());
        }
        if (!tool->depend_output().empty()) {
          dependency_output_file_ =
              SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
                  this, tool, tool->link_output());
        }
      }
      break;
    case UNKNOWN:
    default:
      NOTREACHED();
  }
}

bool Target::CheckVisibility(Err* err) const {
  // Only check visibility when the origin of the dependency is non-null. These
  // are dependencies added by the GN files. Internally added dependencies
  // (expanded groups) will have a null origin. We don't want to check
  // visibility for these, since the point of a group would often be to
  // forward visibility.
  for (size_t i = 0; i < deps_.size(); i++) {
    if (deps_[i].origin &&
        !Visibility::CheckItemVisibility(this, deps_[i].ptr, err))
      return false;
  }

  for (size_t i = 0; i < datadeps_.size(); i++) {
    if (deps_[i].origin &&
        !Visibility::CheckItemVisibility(this, datadeps_[i].ptr, err))
      return false;
  }

  return true;
}

bool Target::CheckTestonly(Err* err) const {
  // If there current target is marked testonly, it can include both testonly
  // and non-testonly targets, so there's nothing to check.
  if (testonly())
    return true;

  // Verify no deps have "testonly" set.
  for (size_t i = 0; i < deps_.size(); i++) {
    if (deps_[i].ptr->testonly()) {
      *err = MakeTestOnlyError(this, deps_[i].ptr);
      return false;
    }
  }

  for (size_t i = 0; i < datadeps_.size(); i++) {
    if (datadeps_[i].ptr->testonly()) {
      *err = MakeTestOnlyError(this, datadeps_[i].ptr);
      return false;
    }
  }

  return true;
}