Extend -remap-file=from;to to permit mapping from a non-existent

file. This is accomplished by introducing the notion of a "virtual"
file into the file manager, which provides a FileEntry* for a named
file whose size and modification time are known but which may not
exist on disk.

Added a cute little test that remaps both a .c file and a .h file it
includes to alternative files.

llvm-svn: 90329
This commit is contained in:
Douglas Gregor 2009-12-02 18:12:28 +00:00
parent f98849eb8a
commit 407e2124bf
6 changed files with 98 additions and 29 deletions

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_FILEMANAGER_H
#define LLVM_CLANG_FILEMANAGER_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/OwningPtr.h"
@ -151,6 +152,9 @@ class FileManager {
///
unsigned NextFileUID;
/// \brief The virtual files that we have allocated.
llvm::SmallVector<FileEntry *, 4> VirtualFileEntries;
// Statistics.
unsigned NumDirLookups, NumFileLookups;
unsigned NumDirCacheMisses, NumFileCacheMisses;
@ -199,6 +203,11 @@ public:
const FileEntry *getFile(const char *FilenameStart,
const char *FilenameEnd);
/// \brief Retrieve a file entry for a "virtual" file that acts as
/// if there were a file with the given name on disk. The file
/// itself is not accessed.
const FileEntry *getVirtualFile(const llvm::StringRef &Filename,
off_t Size, time_t ModificationTime);
void PrintStats() const;
};

View File

@ -147,6 +147,12 @@ FileManager::FileManager()
FileManager::~FileManager() {
delete &UniqueDirs;
delete &UniqueFiles;
for (llvm::SmallVectorImpl<FileEntry *>::iterator
V = VirtualFileEntries.begin(),
VEnd = VirtualFileEntries.end();
V != VEnd;
++V)
delete *V;
}
void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) {
@ -184,6 +190,30 @@ void FileManager::removeStatCache(StatSysCallCache *statCache) {
assert(false && "Stat cache not found for removal");
}
/// \brief Retrieve the directory that the given file name resides in.
static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
const char *NameStart,
const char *NameEnd) {
// Figure out what directory it is in. If the string contains a / in it,
// strip off everything after it.
// FIXME: this logic should be in sys::Path.
const char *SlashPos = NameEnd-1;
while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0]))
--SlashPos;
// Ignore duplicate //'s.
while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1]))
--SlashPos;
if (SlashPos < NameStart) {
// Use the current directory if file has no path component.
const char *Name = ".";
return FileMgr.getDirectory(Name, Name+1);
} else if (SlashPos == NameEnd-1)
return 0; // If filename ends with a /, it's a directory.
else
return FileMgr.getDirectory(NameStart, SlashPos);
}
/// getDirectory - Lookup, cache, and verify the specified directory. This
/// returns null if the directory doesn't exist.
///
@ -252,33 +282,16 @@ const FileEntry *FileManager::getFile(const char *NameStart,
// By default, initialize it to invalid.
NamedFileEnt.setValue(NON_EXISTENT_FILE);
// Figure out what directory it is in. If the string contains a / in it,
// strip off everything after it.
// FIXME: this logic should be in sys::Path.
const char *SlashPos = NameEnd-1;
while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0]))
--SlashPos;
// Ignore duplicate //'s.
while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1]))
--SlashPos;
const DirectoryEntry *DirInfo;
if (SlashPos < NameStart) {
// Use the current directory if file has no path component.
const char *Name = ".";
DirInfo = getDirectory(Name, Name+1);
} else if (SlashPos == NameEnd-1)
return 0; // If filename ends with a /, it's a directory.
else
DirInfo = getDirectory(NameStart, SlashPos);
if (DirInfo == 0) // Directory doesn't exist, file can't exist.
return 0;
// Get the null-terminated file name as stored as the key of the
// FileEntries map.
const char *InterndFileName = NamedFileEnt.getKeyData();
const DirectoryEntry *DirInfo
= getDirectoryFromFile(*this, NameStart, NameEnd);
if (DirInfo == 0) // Directory doesn't exist, file can't exist.
return 0;
// FIXME: Use the directory info to prune this, before doing the stat syscall.
// FIXME: This will reduce the # syscalls.
@ -312,6 +325,44 @@ const FileEntry *FileManager::getFile(const char *NameStart,
return &UFE;
}
const FileEntry *
FileManager::getVirtualFile(const llvm::StringRef &Filename,
off_t Size, time_t ModificationTime) {
const char *NameStart = Filename.begin(), *NameEnd = Filename.end();
++NumFileLookups;
// See if there is already an entry in the map.
llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
FileEntries.GetOrCreateValue(NameStart, NameEnd);
// See if there is already an entry in the map.
if (NamedFileEnt.getValue())
return NamedFileEnt.getValue() == NON_EXISTENT_FILE
? 0 : NamedFileEnt.getValue();
++NumFileCacheMisses;
// By default, initialize it to invalid.
NamedFileEnt.setValue(NON_EXISTENT_FILE);
const DirectoryEntry *DirInfo
= getDirectoryFromFile(*this, NameStart, NameEnd);
if (DirInfo == 0) // Directory doesn't exist, file can't exist.
return 0;
FileEntry *UFE = new FileEntry();
VirtualFileEntries.push_back(UFE);
NamedFileEnt.setValue(UFE);
UFE->Name = NamedFileEnt.getKeyData();
UFE->Size = Size;
UFE->ModTime = ModificationTime;
UFE->Dir = DirInfo;
UFE->UID = NextFileUID++;
return UFE;
}
void FileManager::PrintStats() const {
llvm::errs() << "\n*** File Manager Stats:\n";
llvm::errs() << UniqueFiles.size() << " files found, "

View File

@ -19,6 +19,7 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/System/Path.h"
@ -502,11 +503,11 @@ static void InitializeFileRemapping(Diagnostic &Diags,
continue;
}
// Find the file that we're mapping from.
const FileEntry *FromFile = FileMgr.getFile(Remap->first);
// Create the file entry for the file that we're mapping from.
const FileEntry *FromFile = FileMgr.getVirtualFile(Remap->first,
ToFile->getSize(),
0);
if (!FromFile) {
// FIXME: We could actually recover from this, by faking a
// FileEntry based on the "ToFile".
Diags.Report(diag::err_fe_remap_missing_from_file)
<< Remap->first;
continue;

View File

@ -0,0 +1,3 @@
#include "nonexistent.h"
int *f() { return fp; }

View File

@ -0,0 +1,2 @@
extern float *fp;

View File

@ -1,5 +1,8 @@
// RUN: clang-cc -remap-file="%s;%S/Inputs/remapped-file" -fsyntax-only %s 2>&1 | FileCheck %s
// CHECK: remap-file.c:1:28: warning: incompatible pointer types
// RUN: clang-cc -remap-file="%s;%S/Inputs/remapped-file" -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-EXIST %s
// RUN: clang-cc -remap-file="%S/nonexistent.c;%S/Inputs/remapped-file" -fsyntax-only %S/nonexistent.c 2>&1 | FileCheck -check-prefix=CHECK-NONEXIST %s
// RUN: clang-cc -remap-file="%S/nonexistent.c;%S/Inputs/remapped-file-2" -remap-file="%S/nonexistent.h;%S/Inputs/remapped-file-3" -fsyntax-only %S/nonexistent.c 2>&1 | FileCheck -check-prefix=CHECK-HEADER %s
// CHECK-EXIST: remap-file.c:1:28: warning: incompatible pointer types
// CHECK-NONEXIST: nonexistent.c:1:28: warning: incompatible pointer types
// CHECK-HEADER: nonexistent.c:3:19: warning: incompatible pointer types
int