| // 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/functions.h" |
| |
| #include "tools/gn/parse_tree.h" |
| #include "tools/gn/scope.h" |
| #include "tools/gn/template.h" |
| #include "tools/gn/value.h" |
| |
| namespace functions { |
| |
| const char kTemplate[] = "template"; |
| const char kTemplate_HelpShort[] = "template: Define a template rule."; |
| const char kTemplate_Help[] = |
| R"(template: Define a template rule. |
| |
| A template defines a custom name that acts like a function. It provides a way |
| to add to the built-in target types. |
| |
| The template() function is used to declare a template. To invoke the |
| template, just use the name of the template like any other target type. |
| |
| Often you will want to declare your template in a special file that other |
| files will import (see "gn help import") so your template rule can be shared |
| across build files. |
| |
| Variables and templates: |
| |
| When you call template() it creates a closure around all variables currently |
| in scope with the code in the template block. When the template is invoked, |
| the closure will be executed. |
| |
| When the template is invoked, the code in the caller is executed and passed |
| to the template code as an implicit "invoker" variable. The template uses |
| this to read state out of the invoking code. |
| |
| One thing explicitly excluded from the closure is the "current directory" |
| against which relative file names are resolved. The current directory will be |
| that of the invoking code, since typically that code specifies the file |
| names. This means all files internal to the template should use absolute |
| names. |
| |
| A template will typically forward some or all variables from the invoking |
| scope to a target that it defines. Often, such variables might be optional. |
| Use the pattern: |
| |
| if (defined(invoker.deps)) { |
| deps = invoker.deps |
| } |
| |
| The function forward_variables_from() provides a shortcut to forward one or |
| more or possibly all variables in this manner: |
| |
| forward_variables_from(invoker, ["deps", "public_deps"]) |
| |
| Target naming |
| |
| Your template should almost always define a built-in target with the name the |
| template invoker specified. For example, if you have an IDL template and |
| somebody does: |
| idl("foo") {... |
| you will normally want this to expand to something defining a source_set or |
| static_library named "foo" (among other things you may need). This way, when |
| another target specifies a dependency on "foo", the static_library or |
| source_set will be linked. |
| |
| It is also important that any other targets your template expands to have |
| unique names, or you will get collisions. |
| |
| Access the invoking name in your template via the implicit "target_name" |
| variable. This should also be the basis for how other targets that a template |
| expands to ensure uniqueness. |
| |
| A typical example would be a template that defines an action to generate some |
| source files, and a source_set to compile that source. Your template would |
| name the source_set "target_name" because that's what you want external |
| targets to depend on to link your code. And you would name the action |
| something like "${target_name}_action" to make it unique. The source set |
| would have a dependency on the action to make it run. |
| |
| Overriding builtin targets |
| |
| You can use template to redefine a built-in target in which case your template |
| takes a precedence over the built-in one. All uses of the target from within |
| the template definition will refer to the built-in target which makes it |
| possible to extend the behavior of the built-in target: |
| |
| template("shared_library") { |
| shared_library(shlib) { |
| forward_variables_from(invoker, "*") |
| ... |
| } |
| } |
| |
| Example of defining a template |
| |
| template("my_idl") { |
| # Be nice and help callers debug problems by checking that the variables |
| # the template requires are defined. This gives a nice message rather than |
| # giving the user an error about an undefined variable in the file defining |
| # the template |
| # |
| # You can also use defined() to give default values to variables |
| # unspecified by the invoker. |
| assert(defined(invoker.sources), |
| "Need sources in $target_name listing the idl files.") |
| |
| # Name of the intermediate target that does the code gen. This must |
| # incorporate the target name so it's unique across template |
| # instantiations. |
| code_gen_target_name = target_name + "_code_gen" |
| |
| # Intermediate target to convert IDL to C source. Note that the name is |
| # based on the name the invoker of the template specified. This way, each |
| # time the template is invoked we get a unique intermediate action name |
| # (since all target names are in the global scope). |
| action_foreach(code_gen_target_name) { |
| # Access the scope defined by the invoker via the implicit "invoker" |
| # variable. |
| sources = invoker.sources |
| |
| # Note that we need an absolute path for our script file name. The |
| # current directory when executing this code will be that of the invoker |
| # (this is why we can use the "sources" directly above without having to |
| # rebase all of the paths). But if we need to reference a script relative |
| # to the template file, we'll need to use an absolute path instead. |
| script = "//tools/idl/idl_code_generator.py" |
| |
| # Tell GN how to expand output names given the sources. |
| # See "gn help source_expansion" for more. |
| outputs = [ "$target_gen_dir/{{source_name_part}}.cc", |
| "$target_gen_dir/{{source_name_part}}.h" ] |
| } |
| |
| # Name the source set the same as the template invocation so instancing |
| # this template produces something that other targets can link to in their |
| # deps. |
| source_set(target_name) { |
| # Generates the list of sources, we get these from the action_foreach |
| # above. |
| sources = get_target_outputs(":$code_gen_target_name") |
| |
| # This target depends on the files produced by the above code gen target. |
| deps = [ ":$code_gen_target_name" ] |
| } |
| } |
| |
| Example of invoking the resulting template |
| |
| # This calls the template code above, defining target_name to be |
| # "foo_idl_files" and "invoker" to be the set of stuff defined in the curly |
| # brackets. |
| my_idl("foo_idl_files") { |
| # Goes into the template as "invoker.sources". |
| sources = [ "foo.idl", "bar.idl" ] |
| } |
| |
| # Here is a target that depends on our template. |
| executable("my_exe") { |
| # Depend on the name we gave the template call above. Internally, this will |
| # produce a dependency from executable to the source_set inside the |
| # template (since it has this name), which will in turn depend on the code |
| # gen action. |
| deps = [ ":foo_idl_files" ] |
| } |
| )"; |
| |
| Value RunTemplate(Scope* scope, |
| const FunctionCallNode* function, |
| const std::vector<Value>& args, |
| BlockNode* block, |
| Err* err) { |
| // Of course you can have configs and targets in a template. But here, we're |
| // not actually executing the block, only declaring it. Marking the template |
| // declaration as non-nestable means that you can't put it inside a target, |
| // for example. |
| NonNestableBlock non_nestable(scope, function, "template"); |
| if (!non_nestable.Enter(err)) |
| return Value(); |
| |
| // TODO(brettw) determine if the function is built-in and throw an error if |
| // it is. |
| if (args.size() != 1) { |
| *err = |
| Err(function->function(), "Need exactly one string arg to template."); |
| return Value(); |
| } |
| if (!args[0].VerifyTypeIs(Value::STRING, err)) |
| return Value(); |
| std::string template_name = args[0].string_value(); |
| |
| const Template* existing_template = scope->GetTemplate(template_name); |
| if (existing_template) { |
| *err = Err(function, "Duplicate template definition.", |
| "A template with this name was already defined."); |
| err->AppendSubErr( |
| Err(existing_template->GetDefinitionRange(), "Previous definition.")); |
| return Value(); |
| } |
| |
| scope->AddTemplate(template_name, new Template(scope, function)); |
| |
| // The template object above created a closure around the variables in the |
| // current scope. The template code will execute in that context when it's |
| // invoked. But this means that any variables defined above that are used |
| // by the template won't get marked used just by defining the template. The |
| // result can be spurious unused variable errors. |
| // |
| // The "right" thing to do would be to walk the syntax tree inside the |
| // template, find all identifier references, and mark those variables used. |
| // This is annoying and error-prone to implement and takes extra time to run |
| // for this narrow use case. |
| // |
| // Templates are most often defined in .gni files which don't get |
| // used-variable checking anyway, and this case is annoying enough that the |
| // incremental value of unused variable checking isn't worth the |
| // alternatives. So all values in scope before this template definition are |
| // exempted from unused variable checking. |
| scope->MarkAllUsed(); |
| |
| return Value(); |
| } |
| |
| } // namespace functions |