// 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/args.h"

#include "build/build_config.h"
#include "tools/gn/variables.h"

#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

const char kBuildArgs_Help[] =
    "Build Arguments Overview\n"
    "\n"
    "  Build arguments are variables passed in from outside of the build\n"
    "  that build files can query to determine how the build works.\n"
    "\n"
    "How build arguments are set\n"
    "\n"
    "  First, system default arguments are set based on the current system.\n"
    "  The built-in arguments are:\n"
    "   - cpu_arch (by default this is the same as \"default_cpu_arch\")\n"
    "   - default_cpu_arch\n"
    "   - default_os\n"
    "   - os (by default this is the same as \"default_os\")\n"
    "\n"
    "  Second, arguments specified on the command-line via \"--args\" are\n"
    "  applied. These can override the system default ones, and add new ones.\n"
    "  These are whitespace-separated. For example:\n"
    "\n"
    "    gn --args=\"enable_doom_melon=false\" os=\\\"beos\\\"\n"
    "\n"
    "  Third, toolchain overrides are applied. These are specified in the\n"
    "  toolchain_args section of a toolchain definition. The use-case for\n"
    "  this is that a toolchain may be building code for a different\n"
    "  platform, and that it may want to always specify Posix, for example.\n"
    "  See \"gn help toolchain_args\" for more.\n"
    "\n"
    "  It is an error to specify an override for a build argument that never\n"
    "  appears in a \"declare_args\" call.\n"
    "\n"
    "How build arguments are used\n"
    "\n"
    "  If you want to use an argument, you use declare_args() and specify\n"
    "  default values. These default values will apply if none of the steps\n"
    "  listed in the \"How build arguments are set\" section above apply to\n"
    "  the given argument, but the defaults will not override any of these.\n"
    "\n"
    "  Often, the root build config file will declare global arguments that\n"
    "  will be passed to all buildfiles. Individual build files can also\n"
    "  specify arguments that apply only to those files. It is also usedful\n"
    "  to specify build args in an \"import\"-ed file if you want such\n"
    "  arguments to apply to multiple buildfiles.\n";

Args::Args() {
}

Args::Args(const Args& other)
    : overrides_(other.overrides_),
      all_overrides_(other.all_overrides_),
      declared_arguments_(other.declared_arguments_) {
}

Args::~Args() {
}

void Args::AddArgOverride(const char* name, const Value& value) {
  overrides_[base::StringPiece(name)] = value;
  all_overrides_[base::StringPiece(name)] = value;
}

void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
  for (Scope::KeyValueMap::const_iterator i = overrides.begin();
       i != overrides.end(); ++i) {
    overrides_[i->first] = i->second;
    all_overrides_[i->first] = i->second;
  }
}

void Args::SetupRootScope(Scope* dest,
                          const Scope::KeyValueMap& toolchain_overrides) const {
  SetSystemVars(dest);
  ApplyOverrides(overrides_, dest);
  ApplyOverrides(toolchain_overrides, dest);
  SaveOverrideRecord(toolchain_overrides);
}

bool Args::DeclareArgs(const Scope::KeyValueMap& args,
                       Scope* scope_to_set,
                       Err* err) const {
  base::AutoLock lock(lock_);

  for (Scope::KeyValueMap::const_iterator i = args.begin();
       i != args.end(); ++i) {
    // Verify that the value hasn't already been declared. We want each value
    // to be declared only once.
    //
    // The tricky part is that a buildfile can be interpreted multiple times
    // when used from different toolchains, so we can't just check that we've
    // seen it before. Instead, we check that the location matches. We
    // additionally check that the value matches to prevent people from
    // declaring defaults based on other parameters that may change. The
    // rationale is that you should have exactly one default value for each
    // argument that we can display in the help.
    Scope::KeyValueMap::iterator previously_declared =
        declared_arguments_.find(i->first);
    if (previously_declared != declared_arguments_.end()) {
      if (previously_declared->second.origin() != i->second.origin()) {
        // Declaration location mismatch.
        *err = Err(i->second.origin(), "Duplicate build arg declaration.",
            "Here you're declaring an argument that was already declared "
            "elsewhere.\nYou can only declare each argument once in the entire "
            "build so there is one\ncanonical place for documentation and the "
            "default value. Either move this\nargument to the build config "
            "file (for visibility everywhere) or to a .gni file\nthat you "
            "\"import\" from the files where you need it (preferred).");
        err->AppendSubErr(Err(previously_declared->second.origin(),
                              "Previous declaration.",
                              "See also \"gn help buildargs\" for more on how "
                              "build args work."));
        return false;
      } else if (previously_declared->second != i->second) {
        // Default value mismatch.
        *err = Err(i->second.origin(),
            "Non-constant default value for build arg.",
            "Each build arg should have one default value so we report it "
            "nicely in the\n\"gn args\" command. Please make this value "
            "constant.");
        return false;
      }
    } else {
      declared_arguments_.insert(*i);
    }

    // Only set on the current scope to the new value if it hasn't been already
    // set. Mark the variable used so the build script can override it in
    // certain cases without getting unused value errors.
    if (!scope_to_set->GetValue(i->first)) {
      scope_to_set->SetValue(i->first, i->second, i->second.origin());
      scope_to_set->MarkUsed(i->first);
    }
  }

  return true;
}

bool Args::VerifyAllOverridesUsed(Err* err) const {
  base::AutoLock lock(lock_);

  for (Scope::KeyValueMap::const_iterator i = all_overrides_.begin();
       i != all_overrides_.end(); ++i) {
    if (declared_arguments_.find(i->first) == declared_arguments_.end()) {
      *err = Err(i->second.origin(), "Build arg has no effect.",
          "The value \"" + i->first.as_string() + "\" was set a build "
          "argument\nbut never appeared in a declare_args() block in any "
          "buildfile.");
      return false;
    }
  }
  return true;
}

void Args::SetSystemVars(Scope* dest) const {
  // Host OS.
  const char* os = NULL;
#if defined(OS_WIN)
  os = "win";
#elif defined(OS_MACOSX)
  os = "mac";
#elif defined(OS_LINUX)
  os = "linux";
#else
  #error Unknown OS type.
#endif
  Value os_val(NULL, std::string(os));
  dest->SetValue(variables::kBuildOs, os_val, NULL);
  dest->SetValue(variables::kOs, os_val, NULL);

  // Host architecture.
  static const char kIa32[] = "ia32";
  static const char kIa64[] = "ia64";
  const char* arch = NULL;
#if defined(OS_WIN)
  // ...on Windows, set the CPU architecture based on the underlying OS, not
  // whatever the current bit-tedness of the GN binary is.
  const base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
  switch (os_info->architecture()) {
    case base::win::OSInfo::X86_ARCHITECTURE:
      arch = kIa32;
      break;
    case base::win::OSInfo::X64_ARCHITECTURE:
      arch = kIa64;
      break;
    default:
      CHECK(false) << "Windows architecture not handled.";
      break;
  }
#else
  // ...on all other platforms, just use the bit-tedness of the current
  // process.
  #if defined(ARCH_CPU_X86_64)
    arch = kIa64;
  #elif defined(ARCH_CPU_X86)
    arch = kIa32;
  #elif defined(ARCH_CPU_ARMEL)
    static const char kArm[] = "arm";
    arch = kArm;
  #else
    #error Unknown architecture.
  #endif
#endif
  // Avoid unused var warning.
  (void)kIa32;
  (void)kIa64;

  Value arch_val(NULL, std::string(arch));
  dest->SetValue(variables::kBuildCpuArch, arch_val, NULL);
  dest->SetValue(variables::kCpuArch, arch_val, NULL);

  // Save the OS and architecture as build arguments that are implicitly
  // declared. This is so they can be overridden in a toolchain build args
  // override, and so that they will appear in the "gn args" output.
  //
  // Do not declare the build* variants since these shouldn't be changed.
  //
  // Mark these variables used so the build config file can override them
  // without geting a warning about overwriting an unused variable.
  declared_arguments_[variables::kOs] = os_val;
  declared_arguments_[variables::kCpuArch] = arch_val;
  dest->MarkUsed(variables::kCpuArch);
  dest->MarkUsed(variables::kOs);
}


void Args::ApplyOverrides(const Scope::KeyValueMap& values,
                          Scope* scope) const {
  for (Scope::KeyValueMap::const_iterator i = values.begin();
       i != values.end(); ++i)
    scope->SetValue(i->first, i->second, i->second.origin());
}

void Args::SaveOverrideRecord(const Scope::KeyValueMap& values) const {
  base::AutoLock lock(lock_);
  for (Scope::KeyValueMap::const_iterator i = values.begin();
       i != values.end(); ++i)
    all_overrides_[i->first] = i->second;
}
