Home | History | Annotate | Download | only in gn
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "tools/gn/template.h"
      6 
      7 #include "tools/gn/err.h"
      8 #include "tools/gn/functions.h"
      9 #include "tools/gn/parse_tree.h"
     10 #include "tools/gn/scope.h"
     11 #include "tools/gn/scope_per_file_provider.h"
     12 #include "tools/gn/value.h"
     13 
     14 Template::Template(const Scope* scope, const FunctionCallNode* def)
     15     : closure_(scope->MakeClosure()),
     16       definition_(def) {
     17 }
     18 
     19 Template::Template(scoped_ptr<Scope> scope, const FunctionCallNode* def)
     20     : closure_(scope.Pass()),
     21       definition_(def) {
     22 }
     23 
     24 Template::~Template() {
     25 }
     26 
     27 Value Template::Invoke(Scope* scope,
     28                        const FunctionCallNode* invocation,
     29                        const std::vector<Value>& args,
     30                        BlockNode* block,
     31                        Err* err) const {
     32   // Don't allow templates to be executed from imported files. Imports are for
     33   // simple values only.
     34   if (!EnsureNotProcessingImport(invocation, scope, err))
     35     return Value();
     36 
     37   // First run the invocation's block. Need to allocate the scope on the heap
     38   // so we can pass ownership to the template.
     39   scoped_ptr<Scope> invocation_scope(new Scope(scope));
     40   if (!FillTargetBlockScope(scope, invocation,
     41                             invocation->function().value().as_string(),
     42                             block, args, invocation_scope.get(), err))
     43     return Value();
     44   block->ExecuteBlockInScope(invocation_scope.get(), err);
     45   if (err->has_error())
     46     return Value();
     47 
     48   // Set up the scope to run the template and set the current directory for the
     49   // template (which ScopePerFileProvider uses to base the target-related
     50   // variables target_gen_dir and target_out_dir on) to be that of the invoker.
     51   // This way, files don't have to be rebased and target_*_dir works the way
     52   // people expect (otherwise its to easy to be putting generated files in the
     53   // gen dir corresponding to an imported file).
     54   Scope template_scope(closure_.get());
     55   template_scope.set_source_dir(scope->GetSourceDir());
     56 
     57   ScopePerFileProvider per_file_provider(&template_scope, true);
     58 
     59   // Targets defined in the template go in the collector for the invoking file.
     60   template_scope.set_item_collector(scope->GetItemCollector());
     61 
     62   // We jump through some hoops to avoid copying the invocation scope when
     63   // setting it in the template scope (since the invocation scope may have
     64   // large lists of source files in it and could be expensive to copy).
     65   //
     66   // Scope.SetValue will copy the value which will in turn copy the scope, but
     67   // if we instead create a value and then set the scope on it, the copy can
     68   // be avoided.
     69   const char kInvoker[] = "invoker";
     70   template_scope.SetValue(kInvoker, Value(NULL, scoped_ptr<Scope>()),
     71                           invocation);
     72   Value* invoker_value = template_scope.GetMutableValue(kInvoker, false);
     73   invoker_value->SetScopeValue(invocation_scope.Pass());
     74   template_scope.set_source_dir(scope->GetSourceDir());
     75 
     76   const base::StringPiece target_name("target_name");
     77   template_scope.SetValue(target_name,
     78                           Value(invocation, args[0].string_value()),
     79                           invocation);
     80 
     81   // Actually run the template code.
     82   Value result =
     83       definition_->block()->ExecuteBlockInScope(&template_scope, err);
     84   if (err->has_error())
     85     return Value();
     86 
     87   // Check for unused variables in the invocation scope. This will find typos
     88   // of things the caller meant to pass to the template but the template didn't
     89   // read out.
     90   //
     91   // This is a bit tricky because it's theoretically possible for the template
     92   // to overwrite the value of "invoker" and free the Scope owned by the
     93   // value. So we need to look it up again and don't do anything if it doesn't
     94   // exist.
     95   invoker_value = template_scope.GetMutableValue(kInvoker, false);
     96   if (invoker_value && invoker_value->type() == Value::SCOPE) {
     97     if (!invoker_value->scope_value()->CheckForUnusedVars(err))
     98       return Value();
     99   }
    100 
    101   // Check for unused variables in the template itself.
    102   if (!template_scope.CheckForUnusedVars(err))
    103     return Value();
    104 
    105   return result;
    106 }
    107 
    108 LocationRange Template::GetDefinitionRange() const {
    109   return definition_->GetRange();
    110 }
    111