Files
swift-mirror/lib/Driver/DependencyGraph.cpp
Jordan Rose a4a6b42604 [Driver] Only run compile jobs if needed.
This teaches the driver's Compilation to not run jobs where the base input
is older than the main output (r23221) when we're tracking dependencies.
After a compile command finishes, anything that depended on the file that
just got compiled will get scheduled.

This has the nice side effect of trying to rebuild changed files first.

The tests here aren't really testing the dependency graph yet, because the
files don't include any dependencies. I'll be adding several more test
scenarios in the next few commits.

Part of rdar://problem/15353101

Swift SVN r23273
2014-11-12 18:06:10 +00:00

178 lines
6.0 KiB
C++

//===--- DependencyGraph.cpp - Track intra-module dependencies ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Driver/DependencyGraph.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/YAMLParser.h"
using namespace swift;
namespace {
using DependencyMaskTy = DependencyGraphImpl::DependencyMaskTy;
enum class DependencyKind : DependencyMaskTy {
Name = 1 << 0,
Type = 1 << 1
};
enum class DependencyDirection : bool {
Depends,
Provides
};
} // end anonymous namespace
static bool
parseDependencyFileImpl(llvm::MemoryBuffer &buffer, bool providesOnly,
std::function<void(StringRef, DependencyKind,
DependencyDirection)> callback) {
using namespace llvm;
// FIXME: Switch to a format other than YAML.
llvm::SourceMgr SM;
yaml::Stream stream(buffer.getMemBufferRef(), SM);
auto I = stream.begin();
if (I == stream.end() || !I->getRoot())
return true;
auto *topLevelMap = dyn_cast<yaml::MappingNode>(I->getRoot());
if (!topLevelMap)
return !isa<yaml::NullNode>(I->getRoot());
SmallString<64> scratch;
// FIXME: LLVM's YAML support does incremental parsing in such a way that
// for-range loops break.
for (auto i = topLevelMap->begin(), e = topLevelMap->end(); i != e; ++i) {
auto *key = cast<yaml::ScalarNode>(i->getKey());
using KindPair = std::pair<DependencyKind, DependencyDirection>;
KindPair dirAndKind = llvm::StringSwitch<KindPair>(key->getValue(scratch))
.Case("top-level", std::make_pair(DependencyKind::Name,
DependencyDirection::Depends))
.Case("member-access", std::make_pair(DependencyKind::Type,
DependencyDirection::Depends))
.Case("provides", std::make_pair(DependencyKind::Name,
DependencyDirection::Provides))
.Case("nominals", std::make_pair(DependencyKind::Type,
DependencyDirection::Provides));
if (providesOnly && dirAndKind.second != DependencyDirection::Provides)
continue;
auto *entries = cast<yaml::SequenceNode>(i->getValue());
for (const yaml::Node &rawEntry : *entries) {
auto *entry = cast<yaml::ScalarNode>(&rawEntry);
callback(entry->getValue(scratch), dirAndKind.first, dirAndKind.second);
}
}
return false;
}
template <typename Fn>
static bool parseDependencyFile(llvm::MemoryBuffer &buffer, bool providesOnly,
Fn fn) {
return parseDependencyFileImpl(buffer, providesOnly, std::cref(fn));
}
bool DependencyGraphImpl::loadFromPath(const void *node, StringRef path) {
auto buffer = llvm::MemoryBuffer::getFile(path);
if (!buffer)
return true;
return loadFromBuffer(node, *buffer.get());
}
bool DependencyGraphImpl::loadFromString(const void *node, StringRef data) {
auto buffer = llvm::MemoryBuffer::getMemBuffer(data);
return loadFromBuffer(node, *buffer);
}
bool DependencyGraphImpl::loadFromBuffer(const void *node,
llvm::MemoryBuffer &buffer) {
auto &provides = Provides[node];
provides.clear();
return parseDependencyFile(buffer, /*providesOnly=*/isMarked(node),
[this, node, &provides](StringRef name,
DependencyKind kind,
DependencyDirection dir) {
auto kindAsMask = static_cast<DependencyMaskTy>(kind);
switch (dir) {
case DependencyDirection::Depends: {
auto &entries = Dependencies[name];
auto iter = std::find_if(entries.begin(), entries.end(),
[node](const DependencyPairTy &entry) -> bool {
return node == entry.second;
});
if (iter == entries.end())
entries.emplace_back(kindAsMask, node);
else
iter->first |= kindAsMask;
break;
}
case DependencyDirection::Provides: {
auto iter = std::find_if(provides.begin(), provides.end(),
[name](const ProvidesPairTy &entry) -> bool {
return name == entry.second;
});
if (iter == provides.end())
provides.emplace_back(kindAsMask, name);
else
iter->first |= kindAsMask;
break;
}
}
});
}
void
DependencyGraphImpl::markTransitive(SmallVectorImpl<const void *> &newlyMarked,
const void *node) {
assert(Provides.count(node) && "node is not in the graph");
SmallVector<const void *, 16> worklist;
auto addDependentsToWorklist = [&](const void *next) {
auto allProvided = Provides.find(next);
if (allProvided == Provides.end())
return;
for (const auto &provided : allProvided->second) {
auto allDependents = Dependencies.find(provided.second);
if (allDependents == Dependencies.end())
continue;
for (const auto &dependent : allDependents->second) {
if ((provided.first & dependent.first) == 0)
continue;
if (isMarked(dependent.second))
continue;
worklist.push_back(dependent.second);
}
}
};
// Always mark through the starting node, even if it's already marked.
Marked.insert(node);
addDependentsToWorklist(node);
while (!worklist.empty()) {
const void *next = worklist.pop_back_val();
if (!Marked.insert(next))
continue;
newlyMarked.push_back(next);
addDependentsToWorklist(next);
}
}