// 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 "tools/gn/substitution_type.h"

#include <stddef.h>
#include <stdlib.h>

#include "tools/gn/err.h"

const char* kSubstitutionNames[SUBSTITUTION_NUM_TYPES] = {
    "<<literal>>",  // SUBSTITUTION_LITERAL

    "{{source}}",  // SUBSTITUTION_SOURCE
    "{{output}}",  // SUBSTITUTION_OUTPUT

    "{{source_name_part}}",          // SUBSTITUTION_NAME_PART
    "{{source_file_part}}",          // SUBSTITUTION_FILE_PART
    "{{source_dir}}",                // SUBSTITUTION_SOURCE_DIR
    "{{source_root_relative_dir}}",  // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR
    "{{source_gen_dir}}",            // SUBSTITUTION_SOURCE_GEN_DIR
    "{{source_out_dir}}",            // SUBSTITUTION_SOURCE_OUT_DIR
    "{{source_target_relative}}",    // SUBSTITUTION_SOURCE_TARGET_RELATIVE

    "{{label}}",               // SUBSTITUTION_LABEL
    "{{label_name}}",          // SUBSTITUTION_LABEL_NAME
    "{{root_gen_dir}}",        // SUBSTITUTION_ROOT_GEN_DIR
    "{{root_out_dir}}",        // SUBSTITUTION_ROOT_OUT_DIR
    "{{target_gen_dir}}",      // SUBSTITUTION_TARGET_GEN_DIR
    "{{target_out_dir}}",      // SUBSTITUTION_TARGET_OUT_DIR
    "{{target_output_name}}",  // SUBSTITUTION_TARGET_OUTPUT_NAME

    "{{asmflags}}",      // SUBSTITUTION_ASMFLAGS
    "{{cflags}}",        // SUBSTITUTION_CFLAGS
    "{{cflags_c}}",      // SUBSTITUTION_CFLAGS_C
    "{{cflags_cc}}",     // SUBSTITUTION_CFLAGS_CC
    "{{cflags_objc}}",   // SUBSTITUTION_CFLAGS_OBJC
    "{{cflags_objcc}}",  // SUBSTITUTION_CFLAGS_OBJCC
    "{{defines}}",       // SUBSTITUTION_DEFINES
    "{{include_dirs}}",  // SUBSTITUTION_INCLUDE_DIRS

    "{{inputs}}",            // SUBSTITUTION_LINKER_INPUTS
    "{{inputs_newline}}",    // SUBSTITUTION_LINKER_INPUTS_NEWLINE
    "{{ldflags}}",           // SUBSTITUTION_LDFLAGS
    "{{libs}}",              // SUBSTITUTION_LIBS
    "{{output_dir}}",        // SUBSTITUTION_OUTPUT_DIR
    "{{output_extension}}",  // SUBSTITUTION_OUTPUT_EXTENSION
    "{{solibs}}",            // SUBSTITUTION_SOLIBS

    "{{arflags}}",  // SUBSTITUTION_ARFLAGS

    "{{bundle_root_dir}}",            // SUBSTITUTION_BUNDLE_ROOT_DIR
    "{{bundle_contents_dir}}",        // SUBSTITUTION_BUNDLE_CONTENTS_DIR
    "{{bundle_resources_dir}}",       // SUBSTITUTION_BUNDLE_RESOURCES_DIR
    "{{bundle_executable_dir}}",      // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
    "{{bundle_plugins_dir}}",         // SUBSTITUTION_BUNDLE_PLUGINS_DIR
    "{{bundle_product_type}}",        // SUBSTITUTION_BUNDLE_PRODUCT_TYPE
    "{{bundle_partial_info_plist}}",  // SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST,

    "{{response_file_name}}",  // SUBSTITUTION_RSP_FILE_NAME
};

const char* kSubstitutionNinjaNames[SUBSTITUTION_NUM_TYPES] = {
    nullptr,  // SUBSTITUTION_LITERAL

    "in",   // SUBSTITUTION_SOURCE
    "out",  // SUBSTITUTION_OUTPUT

    "source_name_part",          // SUBSTITUTION_NAME_PART
    "source_file_part",          // SUBSTITUTION_FILE_PART
    "source_dir",                // SUBSTITUTION_SOURCE_DIR
    "source_root_relative_dir",  // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR
    "source_gen_dir",            // SUBSTITUTION_SOURCE_GEN_DIR
    "source_out_dir",            // SUBSTITUTION_SOURCE_OUT_DIR
    "source_target_relative",    // SUBSTITUTION_SOURCE_TARGET_RELATIVE

    "label",               // SUBSTITUTION_LABEL
    "label_name",          // SUBSTITUTION_LABEL_NAME
    "root_gen_dir",        // SUBSTITUTION_ROOT_GEN_DIR
    "root_out_dir",        // SUBSTITUTION_ROOT_OUT_DIR
    "target_gen_dir",      // SUBSTITUTION_TARGET_GEN_DIR
    "target_out_dir",      // SUBSTITUTION_TARGET_OUT_DIR
    "target_output_name",  // SUBSTITUTION_TARGET_OUTPUT_NAME

    "asmflags",      // SUBSTITUTION_ASMFLAGS
    "cflags",        // SUBSTITUTION_CFLAGS
    "cflags_c",      // SUBSTITUTION_CFLAGS_C
    "cflags_cc",     // SUBSTITUTION_CFLAGS_CC
    "cflags_objc",   // SUBSTITUTION_CFLAGS_OBJC
    "cflags_objcc",  // SUBSTITUTION_CFLAGS_OBJCC
    "defines",       // SUBSTITUTION_DEFINES
    "include_dirs",  // SUBSTITUTION_INCLUDE_DIRS

    // LINKER_INPUTS expands to the same Ninja var as SUBSTITUTION_SOURCE. These
    // are used in different contexts and are named differently to keep things
    // clear, but they both expand to the "set of input files" for a build rule.
    "in",                // SUBSTITUTION_LINKER_INPUTS
    "in_newline",        // SUBSTITUTION_LINKER_INPUTS_NEWLINE
    "ldflags",           // SUBSTITUTION_LDFLAGS
    "libs",              // SUBSTITUTION_LIBS
    "output_dir",        // SUBSTITUTION_OUTPUT_DIR
    "output_extension",  // SUBSTITUTION_OUTPUT_EXTENSION
    "solibs",            // SUBSTITUTION_SOLIBS

    "arflags",  // SUBSTITUTION_ARFLAGS

    "bundle_root_dir",        // SUBSTITUTION_BUNDLE_ROOT_DIR
    "bundle_contents_dir",    // SUBSTITUTION_BUNDLE_CONTENTS_DIR
    "bundle_resources_dir",   // SUBSTITUTION_BUNDLE_RESOURCES_DIR
    "bundle_executable_dir",  // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
    "bundle_plugins_dir",     // SUBSTITUTION_BUNDLE_PLUGINS_DIR
    "product_type",           // SUBSTITUTION_BUNDLE_PRODUCT_TYPE
    "partial_info_plist",     // SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST

    "rspfile",  // SUBSTITUTION_RSP_FILE_NAME
};

SubstitutionBits::SubstitutionBits() : used() {}

void SubstitutionBits::MergeFrom(const SubstitutionBits& other) {
  for (size_t i = 0; i < SUBSTITUTION_NUM_TYPES; i++)
    used[i] |= other.used[i];
}

void SubstitutionBits::FillVector(std::vector<SubstitutionType>* vect) const {
  for (size_t i = SUBSTITUTION_FIRST_PATTERN; i < SUBSTITUTION_NUM_TYPES; i++) {
    if (used[i])
      vect->push_back(static_cast<SubstitutionType>(i));
  }
}

bool SubstitutionIsInOutputDir(SubstitutionType type) {
  return type == SUBSTITUTION_SOURCE_GEN_DIR ||
         type == SUBSTITUTION_SOURCE_OUT_DIR ||
         type == SUBSTITUTION_ROOT_GEN_DIR ||
         type == SUBSTITUTION_ROOT_OUT_DIR ||
         type == SUBSTITUTION_TARGET_GEN_DIR ||
         type == SUBSTITUTION_TARGET_OUT_DIR;
}

bool SubstitutionIsInBundleDir(SubstitutionType type) {
  return type == SUBSTITUTION_BUNDLE_ROOT_DIR ||
         type == SUBSTITUTION_BUNDLE_CONTENTS_DIR ||
         type == SUBSTITUTION_BUNDLE_RESOURCES_DIR ||
         type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR ||
         type == SUBSTITUTION_BUNDLE_PLUGINS_DIR;
}

bool IsValidBundleDataSubstitution(SubstitutionType type) {
  return type == SUBSTITUTION_LITERAL ||
         type == SUBSTITUTION_SOURCE_NAME_PART ||
         type == SUBSTITUTION_SOURCE_FILE_PART ||
         type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR ||
         type == SUBSTITUTION_BUNDLE_ROOT_DIR ||
         type == SUBSTITUTION_BUNDLE_CONTENTS_DIR ||
         type == SUBSTITUTION_BUNDLE_RESOURCES_DIR ||
         type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR ||
         type == SUBSTITUTION_BUNDLE_PLUGINS_DIR;
}

bool IsValidSourceSubstitution(SubstitutionType type) {
  return type == SUBSTITUTION_LITERAL || type == SUBSTITUTION_SOURCE ||
         type == SUBSTITUTION_SOURCE_NAME_PART ||
         type == SUBSTITUTION_SOURCE_FILE_PART ||
         type == SUBSTITUTION_SOURCE_DIR ||
         type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR ||
         type == SUBSTITUTION_SOURCE_GEN_DIR ||
         type == SUBSTITUTION_SOURCE_OUT_DIR ||
         type == SUBSTITUTION_SOURCE_TARGET_RELATIVE;
}

bool IsValidScriptArgsSubstitution(SubstitutionType type) {
  return IsValidSourceSubstitution(type) || type == SUBSTITUTION_RSP_FILE_NAME;
}

bool IsValidToolSubstitution(SubstitutionType type) {
  return type == SUBSTITUTION_LITERAL || type == SUBSTITUTION_OUTPUT ||
         type == SUBSTITUTION_LABEL || type == SUBSTITUTION_LABEL_NAME ||
         type == SUBSTITUTION_ROOT_GEN_DIR ||
         type == SUBSTITUTION_ROOT_OUT_DIR ||
         type == SUBSTITUTION_TARGET_GEN_DIR ||
         type == SUBSTITUTION_TARGET_OUT_DIR ||
         type == SUBSTITUTION_TARGET_OUTPUT_NAME;
}

bool IsValidCompilerSubstitution(SubstitutionType type) {
  return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
         type == SUBSTITUTION_SOURCE || type == SUBSTITUTION_ASMFLAGS ||
         type == SUBSTITUTION_CFLAGS || type == SUBSTITUTION_CFLAGS_C ||
         type == SUBSTITUTION_CFLAGS_CC || type == SUBSTITUTION_CFLAGS_OBJC ||
         type == SUBSTITUTION_CFLAGS_OBJCC || type == SUBSTITUTION_DEFINES ||
         type == SUBSTITUTION_INCLUDE_DIRS;
}

bool IsValidCompilerOutputsSubstitution(SubstitutionType type) {
  // All tool types except "output" (which would be infinitely recursive).
  return (IsValidToolSubstitution(type) && type != SUBSTITUTION_OUTPUT) ||
         IsValidSourceSubstitution(type);
}

bool IsValidLinkerSubstitution(SubstitutionType type) {
  return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
         type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
         type == SUBSTITUTION_LDFLAGS || type == SUBSTITUTION_LIBS ||
         type == SUBSTITUTION_OUTPUT_DIR ||
         type == SUBSTITUTION_OUTPUT_EXTENSION || type == SUBSTITUTION_SOLIBS;
}

bool IsValidLinkerOutputsSubstitution(SubstitutionType type) {
  // All valid compiler outputs plus the output extension.
  return IsValidCompilerOutputsSubstitution(type) ||
         type == SUBSTITUTION_OUTPUT_DIR ||
         type == SUBSTITUTION_OUTPUT_EXTENSION;
}

bool IsValidALinkSubstitution(SubstitutionType type) {
  return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
         type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
         type == SUBSTITUTION_ARFLAGS || type == SUBSTITUTION_OUTPUT_DIR ||
         type == SUBSTITUTION_OUTPUT_EXTENSION;
}

bool IsValidCopySubstitution(SubstitutionType type) {
  return IsValidToolSubstitution(type) || type == SUBSTITUTION_SOURCE;
}

bool IsValidCompileXCassetsSubstitution(SubstitutionType type) {
  return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
         type == SUBSTITUTION_BUNDLE_PRODUCT_TYPE ||
         type == SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST;
}

bool EnsureValidSubstitutions(const std::vector<SubstitutionType>& types,
                              bool (*is_valid_subst)(SubstitutionType),
                              const ParseNode* origin,
                              Err* err) {
  for (SubstitutionType type : types) {
    if (!is_valid_subst(type)) {
      *err = Err(origin, "Invalid substitution type.",
                 "The substitution " + std::string(kSubstitutionNames[type]) +
                     " isn't valid for something\n"
                     "operating on a source file such as this.");
      return false;
    }
  }
  return true;
}
