#include "gn/import_manager.h"
#include <memory>
#include "gn/err.h"
#include "gn/parse_tree.h"
#include "gn/scheduler.h"
#include "gn/scope_per_file_provider.h"
#include "gn/trace.h"
#include "util/ticks.h"
namespace {
std::unique_ptr<Scope> UncachedImport(const Settings* settings,
const SourceFile& file,
const ParseNode* node_for_err,
Err* err) {
ScopedTrace load_trace(TraceItem::TRACE_IMPORT_LOAD, file.value());
load_trace.SetToolchain(settings->toolchain_label());
const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
node_for_err->GetRange(), settings->build_settings(), file, err);
if (!node)
return nullptr;
std::unique_ptr<Scope> scope =
std::make_unique<Scope>(settings->base_config());
scope->set_source_dir(file.GetDir());
ScopePerFileProvider per_file_provider(scope.get(), false);
scope->SetProcessingImport();
node->Execute(scope.get(), err);
if (err->has_error()) {
err->AppendSubErr(Err(node_for_err, "whence it was imported."));
return nullptr;
}
scope->ClearProcessingImport();
return scope;
}
}
struct ImportManager::ImportInfo {
ImportInfo() = default;
~ImportInfo() = default;
std::mutex load_lock;
std::unique_ptr<const Scope> scope;
Err load_result;
};
ImportManager::ImportManager() = default;
ImportManager::~ImportManager() = default;
bool ImportManager::DoImport(const SourceFile& file,
const ParseNode* node_for_err,
Scope* scope,
Err* err) {
std::stringstream ss;
ss << std::this_thread::get_id() << file.value();
std::string key = ss.str();
ImportInfo* import_info = nullptr;
{
std::lock_guard<std::mutex> lock(imports_lock_);
std::unique_ptr<ImportInfo>& info_ptr = imports_[file];
if (!info_ptr)
info_ptr = std::make_unique<ImportInfo>();
import_info = info_ptr.get();
if (imports_in_progress_.find(key) != imports_in_progress_.end()) {
*err = Err(Location(), file.value() + " is part of an import loop.");
return false;
}
imports_in_progress_.insert(key);
}
const Scope* import_scope = nullptr;
{
Ticks import_block_begin = TicksNow();
std::lock_guard<std::mutex> lock(import_info->load_lock);
if (!import_info->scope) {
if (!import_info->load_result.has_error()) {
import_info->scope = UncachedImport(
scope->settings(), file, node_for_err, &import_info->load_result);
}
if (import_info->load_result.has_error()) {
*err = import_info->load_result;
return false;
}
} else {
Ticks import_block_end = TicksNow();
constexpr auto kImportBlockTraceThresholdMS = 20;
if (TracingEnabled() &&
TicksDelta(import_block_end, import_block_begin).InMilliseconds() >
kImportBlockTraceThresholdMS) {
auto import_block_trace = std::make_unique<TraceItem>(
TraceItem::TRACE_IMPORT_BLOCK, file.value(),
std::this_thread::get_id());
import_block_trace->set_begin(import_block_begin);
import_block_trace->set_end(import_block_end);
import_block_trace->set_toolchain(
scope->settings()->toolchain_label().GetUserVisibleName(false));
AddTrace(std::move(import_block_trace));
}
}
import_scope = import_info->scope.get();
}
Scope::MergeOptions options;
options.skip_private_vars = true;
options.mark_dest_used = true;
{
std::lock_guard<std::mutex> lock(imports_lock_);
imports_in_progress_.erase(key);
}
return import_scope->NonRecursiveMergeTo(scope, options, node_for_err,
"import", err);
}
std::vector<SourceFile> ImportManager::GetImportedFiles() const {
std::vector<SourceFile> imported_files;
imported_files.resize(imports_.size());
std::transform(imports_.begin(), imports_.end(), imported_files.begin(),
[](const ImportMap::value_type& val) { return val.first; });
return imported_files;
}