Rework the realpath nonsense for framework lookups to deal more

uniformly with symlinks between top-level and embedded frameworks.

llvm-svn: 172030
This commit is contained in:
Douglas Gregor
2013-01-10 01:43:00 +00:00
parent 55a7be237e
commit 4ddf2221b4
3 changed files with 142 additions and 101 deletions

View File

@@ -111,6 +111,10 @@ class ModuleMap {
/// framework modules from within those directories.
llvm::DenseMap<const DirectoryEntry *, InferredDirectory> InferredDirectories;
/// \brief Describes whether we haved parsed a particular file as a module
/// map.
llvm::DenseMap<const FileEntry *, bool> ParsedModuleMap;
friend class ModuleMapParser;
/// \brief Resolve the given export declaration into an actual export

View File

@@ -134,7 +134,7 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, bool AllowSearch) {
if (Module || !AllowSearch)
return Module;
// Look through the various header search paths to load any avai;able module
// Look through the various header search paths to load any available module
// maps, searching for a module map that describes this module.
for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) {
if (SearchDirs[Idx].isFramework()) {
@@ -263,6 +263,60 @@ const FileEntry *DirectoryLookup::LookupFile(
return Result;
}
/// \brief Given a framework directory, find the top-most framework directory.
///
/// \param FileMgr The file manager to use for directory lookups.
/// \param DirName The name of the framework directory.
/// \param SubmodulePath Will be populated with the submodule path from the
/// returned top-level module to the originally named framework.
static const DirectoryEntry *
getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
SmallVectorImpl<std::string> &SubmodulePath) {
assert(llvm::sys::path::extension(DirName) == ".framework" &&
"Not a framework directory");
#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// frameworks moving between top-level frameworks to embedded frameworks tend
// to be symlinked, and we base the logical structure of modules on the
// physical layout. In particular, we need to deal with crazy includes like
//
// #include <Foo/Frameworks/Bar.framework/Headers/Wibble.h>
//
// where 'Bar' used to be embedded in 'Foo', is now a top-level framework
// which one should access with, e.g.,
//
// #include <Bar/Wibble.h>
//
// Similar issues occur when a top-level framework has moved into an
// embedded framework.
char RealDirName[PATH_MAX];
if (realpath(DirName.str().c_str(), RealDirName))
DirName = RealDirName;
#endif
const DirectoryEntry *TopFrameworkDir = FileMgr.getDirectory(DirName);
do {
// Get the parent directory name.
DirName = llvm::sys::path::parent_path(DirName);
if (DirName.empty())
break;
// Determine whether this directory exists.
const DirectoryEntry *Dir = FileMgr.getDirectory(DirName);
if (!Dir)
break;
// If this is a framework directory, then we're a subframework of this
// framework.
if (llvm::sys::path::extension(DirName) == ".framework") {
SubmodulePath.push_back(llvm::sys::path::stem(DirName));
TopFrameworkDir = Dir;
}
} while (true);
return TopFrameworkDir;
}
/// DoFrameworkLookup - Do a lookup of the specified file in the current
/// DirectoryLookup, which is a framework directory.
@@ -334,17 +388,6 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup(
RelativePath->clear();
RelativePath->append(Filename.begin()+SlashPos+1, Filename.end());
}
// If we're allowed to look for modules, try to load or create the module
// corresponding to this framework.
Module *Module = 0;
if (SuggestedModule) {
if (const DirectoryEntry *FrameworkDir
= FileMgr.getDirectory(FrameworkName)) {
bool IsSystem = getDirCharacteristic() != SrcMgr::C_User;
Module = HS.loadFrameworkModule(ModuleName, FrameworkDir, IsSystem);
}
}
// Check "/System/Library/Frameworks/Cocoa.framework/Headers/file.h"
unsigned OrigSize = FrameworkName.size();
@@ -357,28 +400,64 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup(
SearchPath->append(FrameworkName.begin(), FrameworkName.end()-1);
}
// Determine whether this is the module we're building or not.
bool AutomaticImport = Module;
FrameworkName.append(Filename.begin()+SlashPos+1, Filename.end());
if (const FileEntry *FE = FileMgr.getFile(FrameworkName.str(),
/*openFile=*/!AutomaticImport)) {
if (AutomaticImport)
*SuggestedModule = HS.findModuleForHeader(FE);
return FE;
const FileEntry *FE = FileMgr.getFile(FrameworkName.str(),
/*openFile=*/!SuggestedModule);
if (!FE) {
// Check "/System/Library/Frameworks/Cocoa.framework/PrivateHeaders/file.h"
const char *Private = "Private";
FrameworkName.insert(FrameworkName.begin()+OrigSize, Private,
Private+strlen(Private));
if (SearchPath != NULL)
SearchPath->insert(SearchPath->begin()+OrigSize, Private,
Private+strlen(Private));
FE = FileMgr.getFile(FrameworkName.str(), /*openFile=*/!SuggestedModule);
}
// Check "/System/Library/Frameworks/Cocoa.framework/PrivateHeaders/file.h"
const char *Private = "Private";
FrameworkName.insert(FrameworkName.begin()+OrigSize, Private,
Private+strlen(Private));
if (SearchPath != NULL)
SearchPath->insert(SearchPath->begin()+OrigSize, Private,
Private+strlen(Private));
// If we found the header and are allowed to suggest a module, do so now.
if (FE && SuggestedModule) {
// Find the framework in which this header occurs.
StringRef FrameworkPath = FE->getName();
bool FoundFramework = false;
do {
// Get the parent directory name.
FrameworkPath = llvm::sys::path::parent_path(FrameworkPath);
if (FrameworkPath.empty())
break;
const FileEntry *FE = FileMgr.getFile(FrameworkName.str(),
/*openFile=*/!AutomaticImport);
if (FE && AutomaticImport)
*SuggestedModule = HS.findModuleForHeader(FE);
// Determine whether this directory exists.
const DirectoryEntry *Dir = FileMgr.getDirectory(FrameworkPath);
if (!Dir)
break;
// If this is a framework directory, then we're a subframework of this
// framework.
if (llvm::sys::path::extension(FrameworkPath) == ".framework") {
FoundFramework = true;
break;
}
} while (true);
if (FoundFramework) {
// Find the top-level framework based on this framework.
SmallVector<std::string, 4> SubmodulePath;
const DirectoryEntry *TopFrameworkDir
= ::getTopFrameworkDir(FileMgr, FrameworkPath, SubmodulePath);
// Determine the name of the top-level framework.
StringRef ModuleName = llvm::sys::path::stem(TopFrameworkDir->getName());
// Load this framework module. If that succeeds, find the suggested module
// for this header, if any.
bool IsSystem = getDirCharacteristic() != SrcMgr::C_User;
if (HS.loadFrameworkModule(ModuleName, TopFrameworkDir, IsSystem)) {
*SuggestedModule = HS.findModuleForHeader(FE);
}
} else {
*SuggestedModule = HS.findModuleForHeader(FE);
}
}
return FE;
}
@@ -898,80 +977,21 @@ Module *HeaderSearch::loadFrameworkModule(StringRef Name,
return ModMap.findModule(Name);
}
// The top-level framework directory, from which we'll infer a framework
// module.
const DirectoryEntry *TopFrameworkDir = Dir;
// The path from the module we're actually looking for back to the top-level
// framework name.
llvm::SmallVector<StringRef, 2> SubmodulePath;
// Figure out the top-level framework directory and the submodule path from
// that top-level framework to the requested framework.
llvm::SmallVector<std::string, 2> SubmodulePath;
SubmodulePath.push_back(Name);
// Walk the directory structure to find any enclosing frameworks.
#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// frameworks moving from top-level frameworks to embedded frameworks tend
// to be symlinked from the top-level location to the embedded location,
// and we need to resolve lookups as if we had found the embedded location.
char RealDirName[PATH_MAX];
StringRef DirName;
if (realpath(Dir->getName(), RealDirName))
DirName = RealDirName;
else
DirName = Dir->getName();
#else
StringRef DirName = Dir->getName();
#endif
do {
// Get the parent directory name.
DirName = llvm::sys::path::parent_path(DirName);
if (DirName.empty())
break;
// Determine whether this directory exists.
Dir = FileMgr.getDirectory(DirName);
if (!Dir)
break;
// If this is a framework directory, then we're a subframework of this
// framework.
if (llvm::sys::path::extension(DirName) == ".framework") {
SubmodulePath.push_back(llvm::sys::path::stem(DirName));
TopFrameworkDir = Dir;
}
} while (true);
const DirectoryEntry *TopFrameworkDir
= ::getTopFrameworkDir(FileMgr, Dir->getName(), SubmodulePath);
// Determine whether we're allowed to infer a module map.
bool canInfer = false;
if (llvm::sys::path::has_parent_path(TopFrameworkDir->getName())) {
// Figure out the parent path.
StringRef Parent = llvm::sys::path::parent_path(TopFrameworkDir->getName());
if (const DirectoryEntry *ParentDir = FileMgr.getDirectory(Parent)) {
// If there's a module map file in the parent directory, it can
// explicitly allow us to infer framework modules.
switch (loadModuleMapFile(ParentDir)) {
case LMM_AlreadyLoaded:
case LMM_NewlyLoaded: {
StringRef Name = llvm::sys::path::stem(TopFrameworkDir->getName());
canInfer = ModMap.canInferFrameworkModule(ParentDir, Name, IsSystem);
break;
}
case LMM_InvalidModuleMap:
case LMM_NoDirectory:
break;
}
}
}
// If we're not allowed to infer a module map, we're done.
if (!canInfer)
return 0;
// Try to infer a module map from the top-level framework directory.
Module *Result = ModMap.inferFrameworkModule(SubmodulePath.back(),
TopFrameworkDir,
IsSystem,
/*Parent=*/0);
if (!Result)
return 0;
// Follow the submodule path to find the requested (sub)framework module
// within the top-level framework module.

View File

@@ -397,10 +397,22 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName,
// If the framework has a parent path from which we're allowed to infer
// a framework module, do so.
if (!Parent) {
// Determine whether we're allowed to infer a module map.
StringRef FrameworkDirName = FrameworkDir->getName();
#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// we might be looking at an embedded framework that symlinks out to a
// top-level framework, and we need to infer as if we were naming the
// top-level framework.
char RealFrameworkDirName[PATH_MAX];
if (realpath(FrameworkDir->getName(), RealFrameworkDirName))
FrameworkDirName = RealFrameworkDirName;
#endif
bool canInfer = false;
if (llvm::sys::path::has_parent_path(FrameworkDir->getName())) {
if (llvm::sys::path::has_parent_path(FrameworkDirName)) {
// Figure out the parent path.
StringRef Parent = llvm::sys::path::parent_path(FrameworkDir->getName());
StringRef Parent = llvm::sys::path::parent_path(FrameworkDirName);
if (const DirectoryEntry *ParentDir = FileMgr.getDirectory(Parent)) {
// Check whether we have already looked into the parent directory
// for a module map.
@@ -424,7 +436,7 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName,
if (inferred->second.InferModules) {
// We're allowed to infer for this directory, but make sure it's okay
// to infer this particular module.
StringRef Name = llvm::sys::path::filename(FrameworkDir->getName());
StringRef Name = llvm::sys::path::stem(FrameworkDirName);
canInfer = std::find(inferred->second.ExcludedModules.begin(),
inferred->second.ExcludedModules.end(),
Name) == inferred->second.ExcludedModules.end();
@@ -1692,11 +1704,16 @@ bool ModuleMapParser::parseModuleMapFile() {
}
bool ModuleMap::parseModuleMapFile(const FileEntry *File) {
llvm::DenseMap<const FileEntry *, bool>::iterator Known
= ParsedModuleMap.find(File);
if (Known != ParsedModuleMap.end())
return Known->second;
assert(Target != 0 && "Missing target information");
FileID ID = SourceMgr->createFileID(File, SourceLocation(), SrcMgr::C_User);
const llvm::MemoryBuffer *Buffer = SourceMgr->getBuffer(ID);
if (!Buffer)
return true;
return ParsedModuleMap[File] = true;
// Parse this module map file.
Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, MMapLangOpts);
@@ -1705,6 +1722,6 @@ bool ModuleMap::parseModuleMapFile(const FileEntry *File) {
BuiltinIncludeDir);
bool Result = Parser.parseModuleMapFile();
Diags->getClient()->EndSourceFile();
ParsedModuleMap[File] = Result;
return Result;
}