From 595276662862d87f0cf212862bc785b3193c3fe6 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 15 Oct 2012 06:28:11 +0000 Subject: [PATCH] Introduce the notion of excluded headers into the module map description. Previously, one could emulate this behavior by placing the header in an always-unavailable submodule, but Argyrios guilted me into expressing this idea properly. llvm-svn: 165921 --- clang/include/clang/Basic/Module.h | 3 + clang/include/clang/Lex/ModuleMap.h | 35 +++++++++- clang/include/clang/Serialization/ASTBitCodes.h | 5 +- clang/lib/Basic/Module.cpp | 7 ++ clang/lib/Lex/ModuleMap.cpp | 76 +++++++++++++--------- clang/lib/Serialization/ASTReader.cpp | 22 ++++++- clang/lib/Serialization/ASTWriter.cpp | 12 ++++ .../Modules/Inputs/NoUmbrella.framework/module.map | 5 +- 8 files changed, 126 insertions(+), 39 deletions(-) diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index faaca22..b6b088c 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -73,6 +73,9 @@ public: /// \brief The headers that are part of this module. llvm::SmallVector Headers; + /// \brief The headers that are explicitly excluded from this module. + llvm::SmallVector ExcludedHeaders; + /// \brief The top-level headers associated with this module. llvm::SmallSetVector TopHeaders; diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index fe5abdf..dc9670f 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -52,10 +52,37 @@ class ModuleMap { /// \brief The top-level modules that are known. llvm::StringMap Modules; - + + /// \brief A header that is known to reside within a given module, + /// whether it was included or excluded. + class KnownHeader { + llvm::PointerIntPair Storage; + + public: + KnownHeader() : Storage(0, false) { } + KnownHeader(Module *M, bool Excluded) : Storage(M, Excluded) { } + + /// \brief Retrieve the module the header is stored in. + Module *getModule() const { return Storage.getPointer(); } + + /// \brief Whether this header is explicitly excluded from the module. + bool isExcluded() const { return Storage.getInt(); } + + /// \brief Whether this header is available in the module. + bool isAvailable() const { + return !isExcluded() && getModule()->isAvailable(); + } + + // \brief Whether this known header is valid (i.e., it has an + // associated module). + operator bool() const { return Storage.getPointer() != 0; } + }; + + typedef llvm::DenseMap HeadersMap; + /// \brief Mapping from each header to the module that owns the contents of the /// that header. - llvm::DenseMap Headers; + HeadersMap Headers; /// \brief Mapping from directories with umbrella headers to the module /// that is generated from the umbrella header. @@ -215,7 +242,9 @@ public: void setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir); /// \brief Adds this header to the given module. - void addHeader(Module *Mod, const FileEntry *Header); + /// \param Excluded Whether this header is explicitly excluded from the + /// module; otherwise, it's included in the module. + void addHeader(Module *Mod, const FileEntry *Header, bool Excluded); /// \brief Parse the given module map file, and record any modules we /// encounter. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index a0c62bb..68b8bf0 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -565,7 +565,10 @@ namespace clang { /// submodule. SUBMODULE_EXPORTS = 7, /// \brief Specifies a required feature. - SUBMODULE_REQUIRES = 8 + SUBMODULE_REQUIRES = 8, + /// \brief Specifies a header that has been explicitly excluded + /// from this submodule. + SUBMODULE_EXCLUDED_HEADER = 9 }; /// \brief Record types used within a comments block. diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 91b6207..76c7f8b 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -219,6 +219,13 @@ void Module::print(llvm::raw_ostream &OS, unsigned Indent) const { OS.write_escaped(Headers[I]->getName()); OS << "\"\n"; } + + for (unsigned I = 0, N = ExcludedHeaders.size(); I != N; ++I) { + OS.indent(Indent + 2); + OS << "exclude header \""; + OS.write_escaped(ExcludedHeaders[I]->getName()); + OS << "\"\n"; + } for (submodule_const_iterator MI = submodule_begin(), MIEnd = submodule_end(); MI != MIEnd; ++MI) diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 514f036..826c498 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -146,15 +146,13 @@ static StringRef sanitizeFilenameAsIdentifier(StringRef Name, } Module *ModuleMap::findModuleForHeader(const FileEntry *File) { - llvm::DenseMap::iterator Known - = Headers.find(File); + HeadersMap::iterator Known = Headers.find(File); if (Known != Headers.end()) { - // If a header corresponds to an unavailable module, don't report - // that it maps to anything. - if (!Known->second->isAvailable()) + // If a header is not available, don't report that it maps to anything. + if (!Known->second.isAvailable()) return 0; - return Known->second; + return Known->second.getModule(); } const DirectoryEntry *Dir = File->getDir(); @@ -218,7 +216,7 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) { UmbrellaDirs[SkippedDirs[I]] = Result; } - Headers[File] = Result; + Headers[File] = KnownHeader(Result, /*Excluded=*/false); // If a header corresponds to an unavailable module, don't report // that it maps to anything. @@ -243,10 +241,9 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) { } bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) { - llvm::DenseMap::iterator Known - = Headers.find(Header); + HeadersMap::iterator Known = Headers.find(Header); if (Known != Headers.end()) - return !Known->second->isAvailable(); + return !Known->second.isAvailable(); const DirectoryEntry *Dir = Header->getDir(); llvm::SmallVector SkippedDirs; @@ -381,7 +378,7 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName, // umbrella header "umbrella-header-name" Result->Umbrella = UmbrellaHeader; - Headers[UmbrellaHeader] = Result; + Headers[UmbrellaHeader] = KnownHeader(Result, /*Excluded=*/false); UmbrellaDirs[UmbrellaHeader->getDir()] = Result; // export * @@ -447,7 +444,7 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName, } void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader){ - Headers[UmbrellaHeader] = Mod; + Headers[UmbrellaHeader] = KnownHeader(Mod, /*Excluded=*/false); Mod->Umbrella = UmbrellaHeader; UmbrellaDirs[UmbrellaHeader->getDir()] = Mod; } @@ -457,9 +454,13 @@ void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir) { UmbrellaDirs[UmbrellaDir] = Mod; } -void ModuleMap::addHeader(Module *Mod, const FileEntry *Header) { - Mod->Headers.push_back(Header); - Headers[Header] = Mod; +void ModuleMap::addHeader(Module *Mod, const FileEntry *Header, + bool Excluded) { + if (Excluded) + Mod->ExcludedHeaders.push_back(Header); + else + Mod->Headers.push_back(Header); + Headers[Header] = KnownHeader(Mod, Excluded); } const FileEntry * @@ -479,12 +480,10 @@ void ModuleMap::dump() { M->getValue()->print(llvm::errs(), 2); llvm::errs() << "Headers:"; - for (llvm::DenseMap::iterator - H = Headers.begin(), - HEnd = Headers.end(); + for (HeadersMap::iterator H = Headers.begin(), HEnd = Headers.end(); H != HEnd; ++H) { llvm::errs() << " \"" << H->first->getName() << "\" -> " - << H->second->getFullModuleName() << "\n"; + << H->second.getModule()->getFullModuleName() << "\n"; } } @@ -545,6 +544,7 @@ namespace clang { EndOfFile, HeaderKeyword, Identifier, + ExcludeKeyword, ExplicitKeyword, ExportKeyword, FrameworkKeyword, @@ -623,7 +623,7 @@ namespace clang { bool parseModuleId(ModuleId &Id); void parseModuleDecl(); void parseRequiresDecl(); - void parseHeaderDecl(SourceLocation UmbrellaLoc); + void parseHeaderDecl(SourceLocation UmbrellaLoc, SourceLocation ExcludeLoc); void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); void parseExportDecl(); void parseInferredSubmoduleDecl(bool Explicit); @@ -666,6 +666,7 @@ retry: Tok.StringLength = LToken.getLength(); Tok.Kind = llvm::StringSwitch(Tok.getString()) .Case("header", MMToken::HeaderKeyword) + .Case("exclude", MMToken::ExcludeKeyword) .Case("explicit", MMToken::ExplicitKeyword) .Case("export", MMToken::ExportKeyword) .Case("framework", MMToken::FrameworkKeyword) @@ -1044,14 +1045,25 @@ void ModuleMapParser::parseModuleDecl() { case MMToken::UmbrellaKeyword: { SourceLocation UmbrellaLoc = consumeToken(); if (Tok.is(MMToken::HeaderKeyword)) - parseHeaderDecl(UmbrellaLoc); + parseHeaderDecl(UmbrellaLoc, SourceLocation()); else parseUmbrellaDirDecl(UmbrellaLoc); break; } + case MMToken::ExcludeKeyword: { + SourceLocation ExcludeLoc = consumeToken(); + if (Tok.is(MMToken::HeaderKeyword)) { + parseHeaderDecl(SourceLocation(), ExcludeLoc); + } else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << "exclude"; + } + break; + } + case MMToken::HeaderKeyword: - parseHeaderDecl(SourceLocation()); + parseHeaderDecl(SourceLocation(), SourceLocation()); break; default: @@ -1153,12 +1165,15 @@ static bool isBuiltinHeader(StringRef FileName) { /// /// header-declaration: /// 'umbrella'[opt] 'header' string-literal -void ModuleMapParser::parseHeaderDecl(SourceLocation UmbrellaLoc) { +/// 'exclude'[opt] 'header' string-literal +void ModuleMapParser::parseHeaderDecl(SourceLocation UmbrellaLoc, + SourceLocation ExcludeLoc) { assert(Tok.is(MMToken::HeaderKeyword)); consumeToken(); bool Umbrella = UmbrellaLoc.isValid(); - + bool Exclude = ExcludeLoc.isValid(); + assert(!(Umbrella && Exclude) && "Cannot have both 'umbrella' and 'exclude'"); // Parse the header name. if (!Tok.is(MMToken::StringLiteral)) { Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) @@ -1236,15 +1251,15 @@ void ModuleMapParser::parseHeaderDecl(SourceLocation UmbrellaLoc) { // FIXME: We shouldn't be eagerly stat'ing every file named in a module map. // Come up with a lazy way to do this. if (File) { - if (const Module *OwningModule = Map.Headers[File]) { + if (ModuleMap::KnownHeader OwningModule = Map.Headers[File]) { Diags.Report(FileNameLoc, diag::err_mmap_header_conflict) - << FileName << OwningModule->getFullModuleName(); + << FileName << OwningModule.getModule()->getFullModuleName(); HadError = true; } else if (Umbrella) { const DirectoryEntry *UmbrellaDir = File->getDir(); - if ((OwningModule = Map.UmbrellaDirs[UmbrellaDir])) { + if (Module *UmbrellaModule = Map.UmbrellaDirs[UmbrellaDir]) { Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash) - << OwningModule->getFullModuleName(); + << UmbrellaModule->getFullModuleName(); HadError = true; } else { // Record this umbrella header. @@ -1252,11 +1267,11 @@ void ModuleMapParser::parseHeaderDecl(SourceLocation UmbrellaLoc) { } } else { // Record this header. - Map.addHeader(ActiveModule, File); + Map.addHeader(ActiveModule, File, Exclude); // If there is a builtin counterpart to this file, add it now. if (BuiltinFile) - Map.addHeader(ActiveModule, BuiltinFile); + Map.addHeader(ActiveModule, BuiltinFile, Exclude); } } else { Diags.Report(FileNameLoc, diag::err_mmap_header_not_found) @@ -1489,6 +1504,7 @@ bool ModuleMapParser::parseModuleMapFile() { break; case MMToken::Comma: + case MMToken::ExcludeKeyword: case MMToken::ExportKeyword: case MMToken::HeaderKeyword: case MMToken::Identifier: diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index d22e85b..a62f257 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3322,7 +3322,27 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { if (std::find(CurrentModule->Headers.begin(), CurrentModule->Headers.end(), File) == CurrentModule->Headers.end()) - ModMap.addHeader(CurrentModule, File); + ModMap.addHeader(CurrentModule, File, false); + } + break; + } + + case SUBMODULE_EXCLUDED_HEADER: { + if (First) { + Error("missing submodule metadata record at beginning of block"); + return Failure; + } + + if (!CurrentModule) + break; + + // FIXME: Be more lazy about this! + StringRef FileName(BlobStart, BlobLen); + if (const FileEntry *File = PP.getFileManager().getFile(FileName)) { + if (std::find(CurrentModule->Headers.begin(), + CurrentModule->Headers.end(), + File) == CurrentModule->Headers.end()) + ModMap.addHeader(CurrentModule, File, true); } break; } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 2fbe4bf6..d04753a 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1983,6 +1983,11 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Feature unsigned RequiresAbbrev = Stream.EmitAbbrev(Abbrev); + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_EXCLUDED_HEADER)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + unsigned ExcludedHeaderAbbrev = Stream.EmitAbbrev(Abbrev); + // Write the submodule metadata block. RecordData Record; Record.push_back(getNumberOfModules(WritingModule)); @@ -2044,6 +2049,13 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Stream.EmitRecordWithBlob(HeaderAbbrev, Record, Mod->Headers[I]->getName()); } + // Emit the excluded headers. + for (unsigned I = 0, N = Mod->ExcludedHeaders.size(); I != N; ++I) { + Record.clear(); + Record.push_back(SUBMODULE_EXCLUDED_HEADER); + Stream.EmitRecordWithBlob(ExcludedHeaderAbbrev, Record, + Mod->ExcludedHeaders[I]->getName()); + } for (unsigned I = 0, N = Mod->TopHeaders.size(); I != N; ++I) { Record.clear(); Record.push_back(SUBMODULE_TOPHEADER); diff --git a/clang/test/Modules/Inputs/NoUmbrella.framework/module.map b/clang/test/Modules/Inputs/NoUmbrella.framework/module.map index 4a4d970..03a8a17 100644 --- a/clang/test/Modules/Inputs/NoUmbrella.framework/module.map +++ b/clang/test/Modules/Inputs/NoUmbrella.framework/module.map @@ -2,8 +2,5 @@ framework module NoUmbrella [system] { umbrella "Headers" module * { } - module unavailable { - requires unavailable - header "Boom.h" - } + exclude header "Boom.h" } -- 2.7.4