普通文本  |  156行  |  4.51 KB

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

#include "tools/gn/build_settings.h"
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/scope.h"
#include "tools/gn/value.h"
#include "tools/gn/value_extractors.h"
#include "tools/gn/variables.h"

namespace {

// Returns true if the list of files looks like it might have a {{ }} pattern
// in it. Used for error checking.
bool FileListHasPattern(const Target::FileList& files) {
  for (size_t i = 0; i < files.size(); i++) {
    if (files[i].value().find("{{") != std::string::npos &&
        files[i].value().find("}}") != std::string::npos)
      return true;
  }
  return false;
}

}  // namespace

ActionTargetGenerator::ActionTargetGenerator(
    Target* target,
    Scope* scope,
    const FunctionCallNode* function_call,
    Target::OutputType type,
    Err* err)
    : TargetGenerator(target, scope, function_call, err),
      output_type_(type) {
}

ActionTargetGenerator::~ActionTargetGenerator() {
}

void ActionTargetGenerator::DoRun() {
  target_->set_output_type(output_type_);

  FillSources();
  if (err_->has_error())
    return;
  if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) {
    // Foreach rules must always have some sources to have an effect.
    *err_ = Err(function_call_, "action_foreach target has no sources.",
        "If you don't specify any sources, there is nothing to run your\n"
        "script over.");
    return;
  }

  FillInputs();
  if (err_->has_error())
    return;

  FillScript();
  if (err_->has_error())
    return;

  FillScriptArgs();
  if (err_->has_error())
    return;

  FillOutputs();
  if (err_->has_error())
    return;

  FillDepfile();
  if (err_->has_error())
    return;

  CheckOutputs();
  if (err_->has_error())
    return;

  // Action outputs don't depend on the current toolchain so we can skip adding
  // that dependency.
}

void ActionTargetGenerator::FillScript() {
  // If this gets called, the target type requires a script, so error out
  // if it doesn't have one.
  const Value* value = scope_->GetValue(variables::kScript, true);
  if (!value) {
    *err_ = Err(function_call_, "This target type requires a \"script\".");
    return;
  }
  if (!value->VerifyTypeIs(Value::STRING, err_))
    return;

  SourceFile script_file =
      scope_->GetSourceDir().ResolveRelativeFile(value->string_value());
  if (script_file.value().empty()) {
    *err_ = Err(*value, "script name is empty");
    return;
  }
  target_->action_values().set_script(script_file);
}

void ActionTargetGenerator::FillScriptArgs() {
  const Value* value = scope_->GetValue(variables::kArgs, true);
  if (!value)
    return;

  std::vector<std::string> args;
  if (!ExtractListOfStringValues(*value, &args, err_))
    return;
  target_->action_values().swap_in_args(&args);
}

void ActionTargetGenerator::FillDepfile() {
  const Value* value = scope_->GetValue(variables::kDepfile, true);
  if (!value)
    return;
  target_->action_values().set_depfile(
      scope_->settings()->build_settings()->build_dir().ResolveRelativeFile(
          value->string_value()));
}

void ActionTargetGenerator::CheckOutputs() {
  const Target::FileList& outputs = target_->action_values().outputs();
  if (outputs.empty()) {
    *err_ = Err(function_call_, "Action has no outputs.",
        "If you have no outputs, the build system can not tell when your\n"
        "script needs to be run.");
    return;
  }

  if (output_type_ == Target::ACTION) {
    // Make sure the outputs for an action have no patterns in them.
    if (FileListHasPattern(outputs)) {
      *err_ = Err(function_call_, "Action has patterns in the output.",
          "An action target should have the outputs completely specified. If\n"
          "you want to provide a mapping from source to output, use an\n"
          "\"action_foreach\" target.");
      return;
    }
  } else if (output_type_ == Target::ACTION_FOREACH) {
    // A foreach target should always have a pattern in the outputs.
    if (!FileListHasPattern(outputs)) {
      *err_ = Err(function_call_,
          "action_foreach should have a pattern in the output.",
          "An action_foreach target should have a source expansion pattern in\n"
          "it to map source file to unique output file name. Otherwise, the\n"
          "build system can't determine when your script needs to be run.");
      return;
    }
  }
}