//===--- 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/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 implementation 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; } }; } void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { R.addListener(llvm::make_unique(*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 &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 &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; }