// Copyright 2014 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 "gn/err.h"
#include "gn/filesystem_utils.h"
#include "gn/functions.h"
#include "gn/label.h"
#include "gn/parse_tree.h"
#include "gn/value.h"

namespace functions {

const char kGetLabelInfo[] = "get_label_info";
const char kGetLabelInfo_HelpShort[] =
    "get_label_info: Get an attribute from a target's label.";
const char kGetLabelInfo_Help[] =
    R"*(get_label_info: Get an attribute from a target's label.

  get_label_info(target_label, what)

  Given the label of a target, returns some attribute of that target. The
  target need not have been previously defined in the same file, since none of
  the attributes depend on the actual target definition, only the label itself.

  See also "gn help get_target_outputs".

Possible values for the "what" parameter

  "name"
      The short name of the target. This will match the value of the
      "target_name" variable inside that target's declaration. For the label
      "//foo/bar:baz" this will return "baz".

  "dir"
      The directory containing the target's definition, with no slash at the
      end. For the label "//foo/bar:baz" this will return "//foo/bar".

  "target_gen_dir"
      The generated file directory for the target. This will match the value of
      the "target_gen_dir" variable when inside that target's declaration.

  "root_gen_dir"
      The root of the generated file tree for the target. This will match the
      value of the "root_gen_dir" variable when inside that target's
      declaration.

  "target_out_dir
      The output directory for the target. This will match the value of the
      "target_out_dir" variable when inside that target's declaration.

  "root_out_dir"
      The root of the output file tree for the target. This will match the
      value of the "root_out_dir" variable when inside that target's
      declaration.

  "label_no_toolchain"
      The fully qualified version of this label, not including the toolchain.
      For the input ":bar" it might return "//foo:bar".

  "label_with_toolchain"
      The fully qualified version of this label, including the toolchain. For
      the input ":bar" it might return "//foo:bar(//toolchain:x64)".

  "toolchain"
      The label of the toolchain. This will match the value of the
      "current_toolchain" variable when inside that target's declaration.

Examples

  get_label_info(":foo", "name")
  # Returns string "foo".

  get_label_info("//foo/bar:baz", "target_gen_dir")
  # Returns string "//out/Debug/gen/foo/bar".
)*";

Value RunGetLabelInfo(Scope* scope,
                      const FunctionCallNode* function,
                      const std::vector<Value>& args,
                      Err* err) {
  if (args.size() != 2) {
    *err = Err(function, "Expected two arguments.");
    return Value();
  }

  // Resolve the requested label.
  Label label =
      Label::Resolve(scope->GetSourceDir(),
                     scope->settings()->build_settings()->root_path_utf8(),
                     ToolchainLabelForScope(scope), args[0], err);
  if (label.is_null())
    return Value();

  // Extract the "what" parameter.
  if (!args[1].VerifyTypeIs(Value::STRING, err))
    return Value();
  const std::string& what = args[1].string_value();

  Value result(function, Value::STRING);
  if (what == "name") {
    result.string_value() = label.name();

  } else if (what == "dir") {
    result.string_value() = DirectoryWithNoLastSlash(label.dir());

  } else if (what == "target_gen_dir") {
    result.string_value() = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
        BuildDirContext(scope, label.GetToolchainLabel()), label.dir(),
        BuildDirType::GEN));

  } else if (what == "root_gen_dir") {
    result.string_value() = DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
        BuildDirContext(scope, label.GetToolchainLabel()), BuildDirType::GEN));

  } else if (what == "target_out_dir") {
    result.string_value() = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
        BuildDirContext(scope, label.GetToolchainLabel()), label.dir(),
        BuildDirType::OBJ));

  } else if (what == "root_out_dir") {
    result.string_value() = DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
        BuildDirContext(scope, label.GetToolchainLabel()),
        BuildDirType::TOOLCHAIN_ROOT));

  } else if (what == "toolchain") {
    result.string_value() = label.GetToolchainLabel().GetUserVisibleName(false);

  } else if (what == "label_no_toolchain") {
    result.string_value() =
        label.GetWithNoToolchain().GetUserVisibleName(false);

  } else if (what == "label_with_toolchain") {
    result.string_value() = label.GetUserVisibleName(true);

  } else {
    *err = Err(args[1], "Unknown value for \"what\" parameter.");
    return Value();
  }

  return result;
}

}  // namespace functions
