mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-04 14:56:08 +00:00

The current ModuleDependencyCollector has a AST listener to collect header files present in loaded modules, but this isn't enough to collect all headers needed in the crash reproducer. One of the reasons is that the AST writer doesn't write symbolic link header paths in the pcm modules, this makes the listeners on the reader only able to collect the real files. Since the module maps could contain submodules that use headers which are symbolic links, not collecting those forbid the reproducer scripts to regen the modules. For instance: usr/include/module.map: ... module pthread { header "pthread.h" export * module impl { header "pthread_impl.h" export * } } ... usr/include/pthread/pthread_impl.h usr/include/pthread_impl.h -> pthread/pthread_impl.h The AST dump for the module above: <SUBMODULE_HEADER abbrevid=6/> blob data = 'pthread_impl.h' <SUBMODULE_TOPHEADER abbrevid=7/> blob data = '/<path_to_sdk>/usr/include/pthread/pthread_impl.h' Note that we don't have "usr/include/pthread_impl.h" which is requested by the module.map in case we want to reconstruct the module in the reproducer. The reason the original symbolic link path isn't used is because the headers are kept by name and requested through the FileManager, which unique files and returns the real path only. To fix that, add a callback to be invoked everytime a header is added while parsing module maps and hook that up to the module dependecy collector. This callback is only registered when generating the reproducer. Differential Revision: http://reviews.llvm.org/D18585 rdar://problem/24499339 llvm-svn: 264971
178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Collect the dependencies of a set of modules.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Frontend/Utils.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Serialization/ASTReader.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
/// Private implementations for ModuleDependencyCollector
|
|
class ModuleDependencyListener : public ASTReaderListener {
|
|
ModuleDependencyCollector &Collector;
|
|
public:
|
|
ModuleDependencyListener(ModuleDependencyCollector &Collector)
|
|
: Collector(Collector) {}
|
|
bool needsInputFileVisitation() override { return true; }
|
|
bool needsSystemInputFileVisitation() override { return true; }
|
|
bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden,
|
|
bool IsExplicitModule) override {
|
|
Collector.addFile(Filename);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks {
|
|
ModuleDependencyCollector &Collector;
|
|
ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector)
|
|
: Collector(Collector) {}
|
|
|
|
void moduleMapAddHeader(const FileEntry &File) override {
|
|
StringRef HeaderPath = File.getName();
|
|
if (llvm::sys::path::is_absolute(HeaderPath))
|
|
Collector.addFile(HeaderPath);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void ModuleDependencyCollector::attachToASTReader(ASTReader &R) {
|
|
R.addListener(llvm::make_unique<ModuleDependencyListener>(*this));
|
|
}
|
|
|
|
void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) {
|
|
PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
|
|
llvm::make_unique<ModuleDependencyMMCallbacks>(*this));
|
|
}
|
|
|
|
void ModuleDependencyCollector::writeFileMap() {
|
|
if (Seen.empty())
|
|
return;
|
|
|
|
SmallString<256> Dest = getDest();
|
|
llvm::sys::path::append(Dest, "vfs.yaml");
|
|
|
|
// Default to use relative overlay directories in the VFS yaml file. This
|
|
// allows crash reproducer scripts to work across machines.
|
|
VFSWriter.setOverlayDir(getDest());
|
|
|
|
std::error_code EC;
|
|
llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text);
|
|
if (EC) {
|
|
HasErrors = true;
|
|
return;
|
|
}
|
|
VFSWriter.write(OS);
|
|
}
|
|
|
|
// TODO: move this to Support/Path.h and check for HAVE_REALPATH?
|
|
static bool real_path(StringRef SrcPath, SmallVectorImpl<char> &RealPath) {
|
|
#ifdef LLVM_ON_UNIX
|
|
char CanonicalPath[PATH_MAX];
|
|
|
|
// TODO: emit a warning in case this fails...?
|
|
if (!realpath(SrcPath.str().c_str(), CanonicalPath))
|
|
return false;
|
|
|
|
SmallString<256> RPath(CanonicalPath);
|
|
RealPath.swap(RPath);
|
|
return true;
|
|
#else
|
|
// FIXME: Add support for systems without realpath.
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool ModuleDependencyCollector::getRealPath(StringRef SrcPath,
|
|
SmallVectorImpl<char> &Result) {
|
|
using namespace llvm::sys;
|
|
SmallString<256> RealPath;
|
|
StringRef FileName = path::filename(SrcPath);
|
|
std::string Dir = path::parent_path(SrcPath).str();
|
|
auto DirWithSymLink = SymLinkMap.find(Dir);
|
|
|
|
// Use real_path to fix any symbolic link component present in a path.
|
|
// Computing the real path is expensive, cache the search through the
|
|
// parent path directory.
|
|
if (DirWithSymLink == SymLinkMap.end()) {
|
|
if (!real_path(Dir, RealPath))
|
|
return false;
|
|
SymLinkMap[Dir] = RealPath.str();
|
|
} else {
|
|
RealPath = DirWithSymLink->second;
|
|
}
|
|
|
|
path::append(RealPath, FileName);
|
|
Result.swap(RealPath);
|
|
return true;
|
|
}
|
|
|
|
std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src) {
|
|
using namespace llvm::sys;
|
|
|
|
// We need an absolute path to append to the root.
|
|
SmallString<256> AbsoluteSrc = Src;
|
|
fs::make_absolute(AbsoluteSrc);
|
|
// Canonicalize to a native path to avoid mixed separator styles.
|
|
path::native(AbsoluteSrc);
|
|
// Remove redundant leading "./" pieces and consecutive separators.
|
|
AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc);
|
|
|
|
// Canonicalize path by removing "..", "." components.
|
|
SmallString<256> CanonicalPath = AbsoluteSrc;
|
|
path::remove_dots(CanonicalPath, /*remove_dot_dot=*/true);
|
|
|
|
// If a ".." component is present after a symlink component, remove_dots may
|
|
// lead to the wrong real destination path. Let the source be canonicalized
|
|
// like that but make sure the destination uses the real path.
|
|
bool HasDotDotInPath =
|
|
std::count(path::begin(AbsoluteSrc), path::end(AbsoluteSrc), "..") > 0;
|
|
SmallString<256> RealPath;
|
|
bool HasRemovedSymlinkComponent = HasDotDotInPath &&
|
|
getRealPath(AbsoluteSrc, RealPath) &&
|
|
!StringRef(CanonicalPath).equals(RealPath);
|
|
|
|
// Build the destination path.
|
|
SmallString<256> Dest = getDest();
|
|
path::append(Dest, path::relative_path(HasRemovedSymlinkComponent ? RealPath
|
|
: CanonicalPath));
|
|
|
|
// Copy the file into place.
|
|
if (std::error_code EC = fs::create_directories(path::parent_path(Dest),
|
|
/*IgnoreExisting=*/true))
|
|
return EC;
|
|
if (std::error_code EC = fs::copy_file(
|
|
HasRemovedSymlinkComponent ? RealPath : CanonicalPath, Dest))
|
|
return EC;
|
|
|
|
// Use the canonical path under the root for the file mapping. Also create
|
|
// an additional entry for the real path.
|
|
addFileMapping(CanonicalPath, Dest);
|
|
if (HasRemovedSymlinkComponent)
|
|
addFileMapping(RealPath, Dest);
|
|
|
|
return std::error_code();
|
|
}
|
|
|
|
void ModuleDependencyCollector::addFile(StringRef Filename) {
|
|
if (insertSeen(Filename))
|
|
if (copyToRoot(Filename))
|
|
HasErrors = true;
|
|
}
|