blob: 35e0db464c1a643994cef442db6a250ccc993e41 [file] [log] [blame]
// Copyright 2018 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/generated_file_target_generator.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/target.h"
#include "tools/gn/variables.h"
GeneratedFileTargetGenerator::GeneratedFileTargetGenerator(
Target* target,
Scope* scope,
const FunctionCallNode* function_call,
Target::OutputType type,
Err* err)
: TargetGenerator(target, scope, function_call, err), output_type_(type) {}
GeneratedFileTargetGenerator::~GeneratedFileTargetGenerator() = default;
void GeneratedFileTargetGenerator::DoRun() {
target_->set_output_type(output_type_);
if (!FillOutputs(false))
return;
if (target_->action_values().outputs().list().size() != 1) {
*err_ = Err(
function_call_, "generated_file target must have exactly one output.",
"You must specify exactly one value in the \"outputs\" array for the "
"destination of the write\n(see \"gn help generated_file\").");
return;
}
if (!FillContents())
return;
if (!FillDataKeys())
return;
// One of data and data_keys should be defined.
if (!contents_defined_ && !data_keys_defined_) {
*err_ = Err(
function_call_, "Either contents or data_keys should be set.",
"The generated_file target requires either the \"contents\" variable "
"or the \"data_keys\" variable be set. See \"gn help "
"generated_file\".");
return;
}
if (!FillRebase())
return;
if (!FillWalkKeys())
return;
if (!FillOutputConversion())
return;
}
bool GeneratedFileTargetGenerator::FillContents() {
const Value* value = scope_->GetValue(variables::kWriteValueContents, true);
if (!value)
return true;
target_->set_contents(*value);
contents_defined_ = true;
return true;
}
bool GeneratedFileTargetGenerator::IsMetadataCollectionTarget(
const std::string_view& variable,
const ParseNode* origin) {
if (contents_defined_) {
*err_ =
Err(origin, std::string(variable) + " won't be used.",
"\"contents\" is defined on this target, and so setting " +
std::string(variable) +
" will have no effect as no metdata collection will occur.");
return false;
}
return true;
}
bool GeneratedFileTargetGenerator::FillOutputConversion() {
const Value* value =
scope_->GetValue(variables::kWriteOutputConversion, true);
if (!value) {
target_->set_output_conversion(Value(function_call_, ""));
return true;
}
if (!value->VerifyTypeIs(Value::STRING, err_))
return false;
// Otherwise, the value itself will be checked when the conversion is done.
target_->set_output_conversion(*value);
return true;
}
bool GeneratedFileTargetGenerator::FillRebase() {
const Value* value = scope_->GetValue(variables::kRebase, true);
if (!value)
return true;
if (!IsMetadataCollectionTarget(variables::kRebase, value->origin()))
return false;
if (!value->VerifyTypeIs(Value::STRING, err_))
return false;
if (value->string_value().empty())
return true; // Treat empty string as the default and do nothing.
const BuildSettings* build_settings = scope_->settings()->build_settings();
SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
*value, err_, build_settings->root_path_utf8());
if (err_->has_error())
return false;
target_->set_rebase(dir);
return true;
}
bool GeneratedFileTargetGenerator::FillDataKeys() {
const Value* value = scope_->GetValue(variables::kDataKeys, true);
if (!value)
return true;
if (!IsMetadataCollectionTarget(variables::kDataKeys, value->origin()))
return false;
if (!value->VerifyTypeIs(Value::LIST, err_))
return false;
for (const Value& v : value->list_value()) {
// Keys must be strings.
if (!v.VerifyTypeIs(Value::STRING, err_))
return false;
target_->data_keys().push_back(v.string_value());
}
data_keys_defined_ = true;
return true;
}
bool GeneratedFileTargetGenerator::FillWalkKeys() {
const Value* value = scope_->GetValue(variables::kWalkKeys, true);
// If we define this and contents, that's an error.
if (value &&
!IsMetadataCollectionTarget(variables::kWalkKeys, value->origin()))
return false;
// If we don't define it, we want the default value which is a list
// containing the empty string.
if (!value) {
target_->walk_keys().push_back("");
return true;
}
// Otherwise, pull and validate the specified value.
if (!value->VerifyTypeIs(Value::LIST, err_))
return false;
for (const Value& v : value->list_value()) {
// Keys must be strings.
if (!v.VerifyTypeIs(Value::STRING, err_))
return false;
target_->walk_keys().push_back(v.string_value());
}
return true;
}