1// Copyright (c) 2013 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/import_manager.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/stl_util.h"
9#include "tools/gn/parse_tree.h"
10#include "tools/gn/scheduler.h"
11#include "tools/gn/scope_per_file_provider.h"
12
13namespace {
14
15// Returns a newly-allocated scope on success, null on failure.
16Scope* UncachedImport(const Settings* settings,
17                      const SourceFile& file,
18                      const ParseNode* node_for_err,
19                      Err* err) {
20  const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
21      node_for_err->GetRange(), settings->build_settings(), file, err);
22  if (!node)
23    return NULL;
24  const BlockNode* block = node->AsBlock();
25  CHECK(block);
26
27  scoped_ptr<Scope> scope(new Scope(settings->base_config()));
28  scope->set_source_dir(file.GetDir());
29
30  // Don't allow ScopePerFileProvider to provide target-related variables.
31  // These will be relative to the imported file, which is probably not what
32  // people mean when they use these.
33  ScopePerFileProvider per_file_provider(scope.get(), false);
34
35  scope->SetProcessingImport();
36  block->ExecuteBlockInScope(scope.get(), err);
37  if (err->has_error())
38    return NULL;
39  scope->ClearProcessingImport();
40
41  return scope.release();
42}
43
44}  // namesapce
45
46ImportManager::ImportManager() {
47}
48
49ImportManager::~ImportManager() {
50  STLDeleteContainerPairSecondPointers(imports_.begin(), imports_.end());
51}
52
53bool ImportManager::DoImport(const SourceFile& file,
54                             const ParseNode* node_for_err,
55                             Scope* scope,
56                             Err* err) {
57  // See if we have a cached import, but be careful to actually do the scope
58  // copying outside of the lock.
59  const Scope* imported_scope = NULL;
60  {
61    base::AutoLock lock(lock_);
62    ImportMap::const_iterator found = imports_.find(file);
63    if (found != imports_.end())
64      imported_scope = found->second;
65  }
66
67  if (!imported_scope) {
68    // Do a new import of the file.
69    imported_scope = UncachedImport(scope->settings(), file,
70                                    node_for_err, err);
71    if (!imported_scope)
72      return false;
73
74    // We loaded the file outside the lock. This means that there could be a
75    // race and the file was already loaded on a background thread. Recover
76    // from this and use the existing one if that happens.
77    {
78      base::AutoLock lock(lock_);
79      ImportMap::const_iterator found = imports_.find(file);
80      if (found != imports_.end()) {
81        delete imported_scope;
82        imported_scope = found->second;
83      } else {
84        imports_[file] = imported_scope;
85      }
86    }
87  }
88
89  Scope::MergeOptions options;
90  options.skip_private_vars = true;
91  options.mark_used = true;  // Don't require all imported values be used.
92  return imported_scope->NonRecursiveMergeTo(scope, options, node_for_err,
93                                             "import", err);
94}
95