mirror of
https://github.com/intel/llvm.git
synced 2026-02-05 04:04:23 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user