// 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 "tools/gn/config_values_extractors.h"
#include "tools/gn/scheduler.h"

namespace {

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

// Merges the dependent configs from the given target to the given config list.
// The unique_configs list is used for de-duping so values already added will
// not be added again.
void MergeDirectDependentConfigsFrom(const Target* from_target,
                                     ConfigSet* unique_configs,
                                     LabelConfigVector* dest) {
  const LabelConfigVector& direct = from_target->direct_dependent_configs();
  for (size_t i = 0; i < direct.size(); i++) {
    if (unique_configs->find(direct[i].ptr) == unique_configs->end()) {
      unique_configs->insert(direct[i].ptr);
      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,
                                  ConfigSet* unique_configs,
                                  LabelConfigVector* dest,
                                  LabelConfigVector* all_dest) {
  const LabelConfigVector& all = from_target->all_dependent_configs();
  for (size_t i = 0; i < all.size(); i++) {
    // Always add it to all_dependent_configs_ since it might not be in that
    // list even if we've seen it applied to this target before. This may
    // introduce some duplicates in all_dependent_configs_, but those will
    // we removed when they're actually applied to a target.
    all_dest->push_back(all[i]);
    if (unique_configs->find(all[i].ptr) == unique_configs->end()) {
      // One we haven't seen yet, also apply it to ourselves.
      dest->push_back(all[i]);
      unique_configs->insert(all[i].ptr);
    }
  }
}

}  // namespace

Target::Target(const Settings* settings, const Label& label)
    : Item(settings, label),
      output_type_(UNKNOWN),
      hard_dep_(false),
      external_(false) {
}

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 COPY_FILES:
      return "Copy";
    case CUSTOM:
      return "Custom";
    default:
      return "";
  }
}

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

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

void Target::OnResolved() {
  DCHECK(output_type_ != UNKNOWN);

  // 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.
  size_t original_deps_size = deps_.size();
  for (size_t i = 0; i < original_deps_size; i++) {
    const Target* dep = deps_[i].ptr;
    if (dep->output_type_ == GROUP) {
      deps_.insert(deps_.begin() + i + 1, dep->deps_.begin(), dep->deps_.end());
      i += dep->deps_.size();
    }
  }

  // Only add each config once. First remember the target's configs.
  ConfigSet unique_configs;
  for (size_t i = 0; i < configs_.size(); i++)
    unique_configs.insert(configs_[i].ptr);

  // Copy our own dependent configs to the list of configs applying to us.
  for (size_t i = 0; i < all_dependent_configs_.size(); i++) {
    if (unique_configs.find(all_dependent_configs_[i].ptr) ==
        unique_configs.end()) {
      unique_configs.insert(all_dependent_configs_[i].ptr);
      configs_.push_back(all_dependent_configs_[i]);
    }
  }
  for (size_t i = 0; i < direct_dependent_configs_.size(); i++) {
    if (unique_configs.find(direct_dependent_configs_[i].ptr) ==
        unique_configs.end()) {
      unique_configs.insert(direct_dependent_configs_[i].ptr);
      configs_.push_back(direct_dependent_configs_[i]);
    }
  }

  // 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(&unique_configs);
  }
}

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

void Target::PullDependentTargetInfo(std::set<const Config*>* unique_configs) {
  // 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, unique_configs, &configs_,
                                 &all_dependent_configs_);
    MergeDirectDependentConfigsFrom(dep, unique_configs, &configs_);

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

    // Inherited libraries and flags are inherited across static library
    // boundaries.
    if (dep->output_type() != SHARED_LIBRARY &&
        dep->output_type() != EXECUTABLE) {
      const std::set<const Target*> inherited = dep->inherited_libraries();
      for (std::set<const Target*>::const_iterator i = inherited.begin();
           i != inherited.end(); ++i)
        inherited_libraries_.insert(*i);

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

  // 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_.insert(
        direct_dependent_configs_.end(),
        from_target->direct_dependent_configs().begin(),
        from_target->direct_dependent_configs().end());
  }
}