mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
IDE: Break CMake cycle involving IDE, FrontendTool, Frontend, and Migrator.
These libraries formed a strongly connected component in the CMake build graph. The weakest link I could find was from IDE to FrontendTool and Frontend, which was necessitated by the `CompileInstance` class (https://github.com/apple/swift/pull/40645). I moved a few files out of IDE into a new IDETools library to break the cycle.
This commit is contained in:
148
lib/IDETool/DependencyChecking.cpp
Normal file
148
lib/IDETool/DependencyChecking.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
//===--- DependencyChecking.cpp -------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2021 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DependencyChecking.h"
|
||||
|
||||
#include "swift/AST/SourceFile.h"
|
||||
#include "swift/Frontend/Frontend.h"
|
||||
|
||||
using namespace swift;
|
||||
|
||||
namespace {
|
||||
/// For each dependency file in \p CI, run \p callback until the callback
|
||||
/// returns \c true. Returns \c true if any callback call returns \c true, \c
|
||||
/// false otherwise.
|
||||
static bool
|
||||
forEachDependencyUntilTrue(CompilerInstance &CI,
|
||||
Optional<unsigned> excludeBufferID,
|
||||
llvm::function_ref<bool(StringRef)> callback) {
|
||||
// Check files in the current module. If 'excludeBufferID' is None, exclude
|
||||
// all source files.
|
||||
if (excludeBufferID.hasValue()) {
|
||||
for (FileUnit *file : CI.getMainModule()->getFiles()) {
|
||||
StringRef filename;
|
||||
if (auto SF = dyn_cast<SourceFile>(file)) {
|
||||
if (SF->getBufferID() == excludeBufferID)
|
||||
continue;
|
||||
filename = SF->getFilename();
|
||||
} else if (auto LF = dyn_cast<LoadedFile>(file))
|
||||
filename = LF->getFilename();
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ignore synthesized files.
|
||||
if (filename.empty() || filename.front() == '<')
|
||||
continue;
|
||||
|
||||
if (callback(filename))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check other non-system depenencies (e.g. modules, headers).
|
||||
for (auto &dep : CI.getDependencyTracker()->getDependencies()) {
|
||||
if (callback(dep))
|
||||
return true;
|
||||
}
|
||||
for (auto dep : CI.getDependencyTracker()->getIncrementalDependencyPaths()) {
|
||||
if (callback(dep))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Collect hash codes of the dependencies into \c Map.
|
||||
static void cacheDependencyHashIfNeeded(CompilerInstance &CI,
|
||||
Optional<unsigned> excludeBufferID,
|
||||
llvm::StringMap<llvm::hash_code> &Map) {
|
||||
auto &FS = CI.getFileSystem();
|
||||
forEachDependencyUntilTrue(CI, excludeBufferID, [&](StringRef filename) {
|
||||
if (Map.count(filename))
|
||||
return false;
|
||||
|
||||
auto stat = FS.status(filename);
|
||||
if (!stat)
|
||||
return false;
|
||||
|
||||
// We will check the hash only if the modification time of the dependecy
|
||||
// is zero. See 'areAnyDependentFilesInvalidated() below'.
|
||||
if (stat->getLastModificationTime() != llvm::sys::TimePoint<>())
|
||||
return false;
|
||||
|
||||
auto buf = FS.getBufferForFile(filename);
|
||||
Map[filename] = llvm::hash_value(buf.get()->getBuffer());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/// Check if any dependent files are modified since \p timestamp.
|
||||
static bool
|
||||
areAnyDependentFilesInvalidated(CompilerInstance &CI, llvm::vfs::FileSystem &FS,
|
||||
Optional<unsigned> excludeBufferID,
|
||||
llvm::sys::TimePoint<> timestamp,
|
||||
const llvm::StringMap<llvm::hash_code> &Map) {
|
||||
|
||||
return forEachDependencyUntilTrue(
|
||||
CI, excludeBufferID, [&](StringRef filePath) {
|
||||
auto stat = FS.status(filePath);
|
||||
if (!stat)
|
||||
// Missing.
|
||||
return true;
|
||||
|
||||
auto lastModTime = stat->getLastModificationTime();
|
||||
if (lastModTime > timestamp)
|
||||
// Modified.
|
||||
return true;
|
||||
|
||||
// If the last modification time is zero, this file is probably from a
|
||||
// virtual file system. We need to check the content.
|
||||
if (lastModTime == llvm::sys::TimePoint<>()) {
|
||||
// Get the hash code of the last content.
|
||||
auto oldHashEntry = Map.find(filePath);
|
||||
if (oldHashEntry == Map.end())
|
||||
// Unreachable? Not virtual in old filesystem, but virtual in new
|
||||
// one.
|
||||
return true;
|
||||
auto oldHash = oldHashEntry->second;
|
||||
|
||||
// Calculate the hash code of the current content.
|
||||
auto newContent = FS.getBufferForFile(filePath);
|
||||
if (!newContent)
|
||||
// Unreachable? stat succeeded, but coundn't get the content.
|
||||
return true;
|
||||
|
||||
auto newHash = llvm::hash_value(newContent.get()->getBuffer());
|
||||
|
||||
if (oldHash != newHash)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ide::cacheDependencyHashIfNeeded(CompilerInstance &CI,
|
||||
Optional<unsigned> excludeBufferID,
|
||||
llvm::StringMap<llvm::hash_code> &Map) {
|
||||
return ::cacheDependencyHashIfNeeded(CI, excludeBufferID, Map);
|
||||
}
|
||||
|
||||
bool ide::areAnyDependentFilesInvalidated(
|
||||
CompilerInstance &CI, llvm::vfs::FileSystem &FS,
|
||||
Optional<unsigned> excludeBufferID, llvm::sys::TimePoint<> timestamp,
|
||||
const llvm::StringMap<llvm::hash_code> &Map) {
|
||||
return ::areAnyDependentFilesInvalidated(CI, FS, excludeBufferID, timestamp,
|
||||
Map);
|
||||
}
|
||||
Reference in New Issue
Block a user