From: Douglas Gregor Date: Thu, 10 Jan 2013 01:43:00 +0000 (+0000) Subject: Rework the realpath nonsense for framework lookups to deal more X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7005b907ea159c8e74e81f85269777429bc18d3c;p=clang Rework the realpath nonsense for framework lookups to deal more uniformly with symlinks between top-level and embedded frameworks. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172030 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index b24c6d732f..96a53849cf 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -111,6 +111,10 @@ class ModuleMap { /// framework modules from within those directories. llvm::DenseMap InferredDirectories; + /// \brief Describes whether we haved parsed a particular file as a module + /// map. + llvm::DenseMap ParsedModuleMap; + friend class ModuleMapParser; /// \brief Resolve the given export declaration into an actual export diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index cb68eb07e6..7363afc771 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -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 &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 + // + // where 'Bar' used to be embedded in 'Foo', is now a top-level framework + // which one should access with, e.g., + // + // #include + // + // 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)); - - const FileEntry *FE = FileMgr.getFile(FrameworkName.str(), - /*openFile=*/!AutomaticImport); - if (FE && AutomaticImport) - *SuggestedModule = HS.findModuleForHeader(FE); + // 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; + + // 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 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 SubmodulePath; + // Figure out the top-level framework directory and the submodule path from + // that top-level framework to the requested framework. + llvm::SmallVector 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); - - // 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; - } - } - } + const DirectoryEntry *TopFrameworkDir + = ::getTopFrameworkDir(FileMgr, Dir->getName(), SubmodulePath); - // 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. diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index d7bf45152d..b2e49ea50e 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -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::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; }