| //===--- 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; |
| } |