// 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.
#ifndef TOOLS_GN_FILE_TEMPLATE_H_
#define TOOLS_GN_FILE_TEMPLATE_H_
#include <iosfwd>
#include "base/basictypes.h"
#include "base/containers/stack_container.h"
#include "tools/gn/err.h"
#include "tools/gn/value.h"
struct EscapeOptions;
class ParseNode;
class Target;
extern const char kSourceExpansion_Help[];
// A FileTemplate object implements source expansion for a given "template"
// (either outputs or args, depending on the target type).
//
// There are two ways you can use this. You can make a template and then
// apply a source to it to get a list of outputs manually. Or you can do the
// actual substitution in Ninja, writing the arguments in a rule and using
// variables in build statements to invoke the rule with the right
// substitutions.
class FileTemplate {
public:
struct Subrange {
enum Type {
LITERAL = 0,
// {{source}} -> expands to be the source file name relative to the build
// root dir.
SOURCE,
// {{source_name_part}} -> file name without extension or directory.
// Maps "foo/bar.txt" to "bar".
NAME_PART,
// {{source_file_part}} -> file name including extension but no directory.
// Maps "foo/bar.txt" to "bar.txt".
FILE_PART,
NUM_TYPES // Must be last
};
Subrange(Type t, const std::string& l = std::string())
: type(t),
literal(l) {
}
Type type;
// When type_ == LITERAL, this specifies the literal.
std::string literal;
};
// Constructs a template from the given value. On error, the err will be
// set. In this case you should not use this object.
FileTemplate(const Value& t, Err* err);
FileTemplate(const std::vector<std::string>& t);
~FileTemplate();
// Returns an output template representing the given target's script
// outputs.
static FileTemplate GetForTargetOutputs(const Target* target);
// Returns true if the given substitution type is used by this template.
bool IsTypeUsed(Subrange::Type type) const;
// Returns true if there are any substitutions.
bool has_substitutions() const { return has_substitutions_; }
// Applies this template to the given list of sources, appending all
// results to the given dest list. The sources must be a list for the
// one that takes a value as an input, otherwise the given error will be set.
void Apply(const Value& sources,
const ParseNode* origin,
std::vector<Value>* dest,
Err* err) const;
void ApplyString(const std::string& input,
std::vector<std::string>* output) const;
// Writes a string representing the template with Ninja variables for the
// substitutions, and the literals escaped for Ninja consumption.
//
// For example, if the input is "foo{{source_name_part}}bar" this will write
// foo${source_name_part}bar. If there are multiple templates (we were
// constucted with a list of more than one item) then the values will be
// separated by spaces.
//
// If this template is nonempty, we will first print out a space to separate
// it from the previous command.
//
// The names used for the Ninja variables will be the same ones used by
// WriteNinjaVariablesForSubstitution. You would use this to define the Ninja
// rule, and then define the variables to substitute for each file using
// WriteNinjaVariablesForSubstitution.
void WriteWithNinjaExpansions(std::ostream& out) const;
// Writes to the given stream the variable declarations for extracting the
// required parts of the given source file string. The results will be
// indented two spaces.
//
// This is used to set up a build statement to invoke a rule where the rule
// contains a representation of this file template to be expanded by Ninja
// (see GetWithNinjaExpansions).
void WriteNinjaVariablesForSubstitution(
std::ostream& out,
const std::string& source,
const EscapeOptions& escape_options) const;
// Returns the Ninja variable name used by the above Ninja functions to
// substitute for the given type.
static const char* GetNinjaVariableNameForType(Subrange::Type type);
// Extracts the given type of substitution from the given source. The source
// should be the file name relative to the output directory.
static std::string GetSubstitution(const std::string& source,
Subrange::Type type);
// Known template types, these include the "{{ }}"
static const char kSource[];
static const char kSourceNamePart[];
static const char kSourceFilePart[];
private:
typedef base::StackVector<Subrange, 8> Template;
typedef base::StackVector<Template, 8> TemplateVector;
void ParseInput(const Value& value, Err* err);
// Parses a template string and adds it to the templates_ list.
void ParseOneTemplateString(const std::string& str);
TemplateVector templates_;
// The corresponding value is set to true if the given subrange type is
// required. This allows us to precompute these types whem applying them
// to a given source file.
bool types_required_[Subrange::NUM_TYPES];
// Set when any of the types_required_ is true. Otherwise, everythins is a
// literal (a common case so we can optimize some code paths).
bool has_substitutions_;
};
#endif // TOOLS_GN_FILE_TEMPLATE_H_