summaryrefslogtreecommitdiffstats
path: root/tools/gn/file_template.h
blob: d66b3e3c4b6e961fc4f6d53c0e549de83296610b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// 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_