diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-12-08 19:11:24 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-12-08 19:11:24 +0000 |
commit | 77d029f6a24dbf70d97e61757945df53fb250ea0 (patch) | |
tree | a5b04a494d0f58aa79d0523e53213a842ffe5dd2 | |
parent | d5c3fa29c6a06ed9c74e03b4a96f786cbc156b7c (diff) |
Implement umbrella directories for modules, which are similar to
umbrella headers in the sense that all of the headers within that
directory (and eventually its subdirectories) are considered to be
part of the module with that umbrella directory. However, unlike
umbrella headers, which are expected to include all of the headers
within their subdirectories, Clang will automatically include all of
the headers it finds in the named subdirectory.
The intent here is to allow a module map to trivially turn a
subdirectory into a module, where the module's structure can mimic the
directory structure.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146165 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticLexKinds.td | 11 | ||||
-rw-r--r-- | include/clang/Lex/ModuleMap.h | 6 | ||||
-rw-r--r-- | include/clang/Serialization/ASTBitCodes.h | 16 | ||||
-rw-r--r-- | lib/Frontend/FrontendActions.cpp | 23 | ||||
-rw-r--r-- | lib/Lex/ModuleMap.cpp | 83 | ||||
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 24 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 16 | ||||
-rw-r--r-- | test/Modules/Inputs/normal-module-map/module.map | 5 | ||||
-rw-r--r-- | test/Modules/Inputs/normal-module-map/nested_umbrella/a.h | 2 | ||||
-rw-r--r-- | test/Modules/Inputs/normal-module-map/nested_umbrella/b.h | 2 | ||||
-rw-r--r-- | test/Modules/normal-module-map.cpp | 18 |
11 files changed, 179 insertions, 27 deletions
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 2cf115d728..cbaa407d3f 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -382,8 +382,9 @@ def err_mmap_expected_lbrace : Error<"expected '{' to start module '%0'">; def err_mmap_expected_rbrace : Error<"expected '}'">; def note_mmap_lbrace_match : Note<"to match this '{'">; def err_mmap_expected_member : Error< - "expected umbrella header, header, submodule, or module export">; + "expected umbrella, header, submodule, or module export">; def err_mmap_expected_header : Error<"expected a header name after '%0'">; +def err_mmap_expected_dir : Error<"expected a directory name after '%0'">; def err_mmap_module_redefinition : Error< "redefinition of module '%0'">; def note_mmap_prev_definition : Note<"previously defined here">; @@ -391,10 +392,10 @@ def err_mmap_header_conflict : Error< "header '%0' is already part of module '%1'">; def err_mmap_header_not_found : Error< "%select{|umbrella }0header '%1' not found">; -def err_mmap_umbrella_header_conflict : Error< - "module '%0' already has an umbrella header ('%1')">; +def err_mmap_umbrella_dir_not_found : Error< + "umbrella directory '%0' not found">; def err_mmap_umbrella_clash : Error< - "umbrella header for module '%0' already covers this directory">; + "umbrella for module '%0' already covers this directory">; def err_mmap_export_module_id : Error< "expected an exported module name or '*'">; def err_mmap_missing_module_unqualified : Error< @@ -404,7 +405,7 @@ def err_mmap_missing_module_qualified : Error< def err_mmap_top_level_inferred_submodule : Error< "only submodules may be inferred with wildcard syntax">; def err_mmap_inferred_no_umbrella : Error< - "inferred submodules require a module with an umbrella header">; + "inferred submodules require a module with an umbrella">; def err_mmap_inferred_redef : Error< "redefinition of inferred submodule">; def err_mmap_expected_lbrace_wildcard : Error< diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 262e50db23..2d95255290 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -181,7 +181,11 @@ public: /// \brief Sets the umbrella header of the given module to the given /// header. void setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader); - + + /// \brief Sets the umbrella directory of the given module to the given + /// directory. + void setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir); + /// \brief Adds this header to the given module. void addHeader(Module *Mod, const FileEntry *Header); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 17c9992a6c..41ef74ee0a 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -507,22 +507,24 @@ namespace clang { /// \brief Record types used within a submodule description block. enum SubmoduleRecordTypes { + /// \brief Metadata for submodules as a whole. + SUBMODULE_METADATA = 0, /// \brief Defines the major attributes of a submodule, including its /// name and parent. - SUBMODULE_DEFINITION = 0, + SUBMODULE_DEFINITION = 1, /// \brief Specifies the umbrella header used to create this module, /// if any. - SUBMODULE_UMBRELLA = 1, + SUBMODULE_UMBRELLA_HEADER = 2, /// \brief Specifies a header that falls into this (sub)module. - SUBMODULE_HEADER = 2, - /// \brief Metadata for submodules as a whole. - SUBMODULE_METADATA = 3, + SUBMODULE_HEADER = 3, + /// \brief Specifies an umbrella directory. + SUBMODULE_UMBRELLA_DIR = 4, /// \brief Specifies the submodules that are imported by this /// submodule. - SUBMODULE_IMPORTS = 4, + SUBMODULE_IMPORTS = 5, /// \brief Specifies the submodules that are re-exported from this /// submodule. - SUBMODULE_EXPORTS = 5 + SUBMODULE_EXPORTS = 6 }; /// \defgroup ASTAST AST file AST constants diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 190cdba582..5dabe81996 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -21,6 +21,7 @@ #include "clang/Frontend/Utils.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" @@ -155,6 +156,28 @@ static void collectModuleHeaderIncludes(const LangOptions &LangOpts, Includes += UmbrellaHeader->getName(); Includes += "\"\n"; } + } else if (const DirectoryEntry *UmbrellaDir = Module->getUmbrellaDir()) { + // Add all of the headers we find in this subdirectory (FIXME: recursively!). + llvm::error_code EC; + llvm::SmallString<128> DirNative; + llvm::sys::path::native(UmbrellaDir->getName(), DirNative); + for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this entry has an extension typically associated with + // headers. + if (!llvm::StringSwitch<bool>(llvm::sys::path::extension(Dir->path())) + .Cases(".h", ".H", ".hh", ".hpp", true) + .Default(false)) + continue; + + // Include this header umbrella header for submodules. + if (LangOpts.ObjC1) + Includes += "#import \""; + else + Includes += "#include \""; + Includes += Dir->path(); + Includes += "\"\n"; + } } // Recurse into submodules. diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index e4c9d77ad6..c9efbf0b53 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -346,6 +346,11 @@ void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader){ UmbrellaDirs[UmbrellaDir] = Mod; } +void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir) { + Mod->Umbrella = UmbrellaDir; + UmbrellaDirs[UmbrellaDir] = Mod; +} + void ModuleMap::addHeader(Module *Mod, const FileEntry *Header) { Mod->Headers.push_back(Header); Headers[Header] = Mod; @@ -494,6 +499,7 @@ namespace clang { bool parseModuleId(ModuleId &Id); void parseModuleDecl(); void parseHeaderDecl(SourceLocation UmbrellaLoc); + void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); void parseExportDecl(); void parseInferredSubmoduleDecl(bool Explicit); @@ -796,9 +802,14 @@ void ModuleMapParser::parseModuleDecl() { parseExportDecl(); break; - case MMToken::UmbrellaKeyword: - parseHeaderDecl(consumeToken()); + case MMToken::UmbrellaKeyword: { + SourceLocation UmbrellaLoc = consumeToken(); + if (Tok.is(MMToken::HeaderKeyword)) + parseHeaderDecl(UmbrellaLoc); + else + parseUmbrellaDirDecl(UmbrellaLoc); break; + } case MMToken::HeaderKeyword: parseHeaderDecl(SourceLocation()); @@ -863,11 +874,10 @@ void ModuleMapParser::parseHeaderDecl(SourceLocation UmbrellaLoc) { std::string FileName = Tok.getString(); SourceLocation FileNameLoc = consumeToken(); - // Check whether we already have an umbrella header. - if (Umbrella && ActiveModule->getUmbrellaHeader()) { - Diags.Report(FileNameLoc, diag::err_mmap_umbrella_header_conflict) - << ActiveModule->getFullModuleName() - << ActiveModule->getUmbrellaHeader()->getName(); + // Check whether we already have an umbrella. + if (Umbrella && ActiveModule->Umbrella) { + Diags.Report(FileNameLoc, diag::err_mmap_umbrella_clash) + << ActiveModule->getFullModuleName(); HadError = true; return; } @@ -935,9 +945,62 @@ void ModuleMapParser::parseHeaderDecl(SourceLocation UmbrellaLoc) { } } else { Diags.Report(FileNameLoc, diag::err_mmap_header_not_found) - << false << FileName; + << Umbrella << FileName; + HadError = true; + } +} + +/// \brief Parse an umbrella directory declaration. +/// +/// umbrella-dir-declaration: +/// umbrella string-literal +void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { + // Parse the directory name. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << "umbrella"; + HadError = true; + return; + } + + std::string DirName = Tok.getString(); + SourceLocation DirNameLoc = consumeToken(); + + // Check whether we already have an umbrella. + if (ActiveModule->Umbrella) { + Diags.Report(DirNameLoc, diag::err_mmap_umbrella_clash) + << ActiveModule->getFullModuleName(); + HadError = true; + return; + } + + // Look for this file. + const DirectoryEntry *Dir = 0; + if (llvm::sys::path::is_absolute(DirName)) + Dir = SourceMgr.getFileManager().getDirectory(DirName); + else { + llvm::SmallString<128> PathName; + PathName = Directory->getName(); + llvm::sys::path::append(PathName, DirName); + Dir = SourceMgr.getFileManager().getDirectory(PathName); + } + + if (!Dir) { + Diags.Report(DirNameLoc, diag::err_mmap_umbrella_dir_not_found) + << DirName; HadError = true; + return; } + + if (Module *OwningModule = Map.UmbrellaDirs[Dir]) { + Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash) + << OwningModule->getFullModuleName(); + HadError = true; + return; + } + + // Record this umbrella directory. + Map.setUmbrellaDir(ActiveModule, Dir); } /// \brief Parse a module export declaration. @@ -998,8 +1061,8 @@ void ModuleMapParser::parseInferredSubmoduleDecl(bool Explicit) { Failed = true; } - // Inferred modules must have umbrella headers. - if (!Failed && !ActiveModule->getUmbrellaHeader()) { + // Inferred modules must have umbrella directories. + if (!Failed && !ActiveModule->getUmbrellaDir()) { Diags.Report(StarLoc, diag::err_mmap_inferred_no_umbrella); Failed = true; } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 21be3aa50c..236c2bd1f5 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3088,7 +3088,7 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { break; } - case SUBMODULE_UMBRELLA: { + case SUBMODULE_UMBRELLA_HEADER: { if (First) { Error("missing submodule metadata record at beginning of block"); return Failure; @@ -3129,6 +3129,28 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { break; } + case SUBMODULE_UMBRELLA_DIR: { + if (First) { + Error("missing submodule metadata record at beginning of block"); + return Failure; + } + + if (!CurrentModule) + break; + + StringRef DirName(BlobStart, BlobLen); + if (const DirectoryEntry *Umbrella + = PP.getFileManager().getDirectory(DirName)) { + if (!CurrentModule->getUmbrellaDir()) + ModMap.setUmbrellaDir(CurrentModule, Umbrella); + else if (CurrentModule->getUmbrellaDir() != Umbrella) { + Error("mismatched umbrella directories in submodule"); + return Failure; + } + } + break; + } + case SUBMODULE_METADATA: { if (!First) { Error("submodule metadata record not at beginning of block"); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 1bd9050fe0..83ad885969 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1900,7 +1900,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { unsigned DefinitionAbbrev = Stream.EmitAbbrev(Abbrev); Abbrev = new BitCodeAbbrev(); - Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_UMBRELLA)); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_UMBRELLA_HEADER)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned UmbrellaAbbrev = Stream.EmitAbbrev(Abbrev); @@ -1908,7 +1908,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_HEADER)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned HeaderAbbrev = Stream.EmitAbbrev(Abbrev); - + + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_UMBRELLA_DIR)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + unsigned UmbrellaDirAbbrev = Stream.EmitAbbrev(Abbrev); + // Write the submodule metadata block. RecordData Record; Record.push_back(getNumberOfModules(WritingModule)); @@ -1943,9 +1948,14 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { // Emit the umbrella header, if there is one. if (const FileEntry *UmbrellaHeader = Mod->getUmbrellaHeader()) { Record.clear(); - Record.push_back(SUBMODULE_UMBRELLA); + Record.push_back(SUBMODULE_UMBRELLA_HEADER); Stream.EmitRecordWithBlob(UmbrellaAbbrev, Record, UmbrellaHeader->getName()); + } else if (const DirectoryEntry *UmbrellaDir = Mod->getUmbrellaDir()) { + Record.clear(); + Record.push_back(SUBMODULE_UMBRELLA_DIR); + Stream.EmitRecordWithBlob(UmbrellaDirAbbrev, Record, + UmbrellaDir->getName()); } // Emit the headers. diff --git a/test/Modules/Inputs/normal-module-map/module.map b/test/Modules/Inputs/normal-module-map/module.map index 91e4bf7178..e17f44a8f4 100644 --- a/test/Modules/Inputs/normal-module-map/module.map +++ b/test/Modules/Inputs/normal-module-map/module.map @@ -6,3 +6,8 @@ module libA { module libB { header "b1.h" } + +module nested_umbrella { + umbrella "nested_umbrella" + module * { } +} diff --git a/test/Modules/Inputs/normal-module-map/nested_umbrella/a.h b/test/Modules/Inputs/normal-module-map/nested_umbrella/a.h new file mode 100644 index 0000000000..ab180fe073 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/nested_umbrella/a.h @@ -0,0 +1,2 @@ +int nested_umbrella_a; + diff --git a/test/Modules/Inputs/normal-module-map/nested_umbrella/b.h b/test/Modules/Inputs/normal-module-map/nested_umbrella/b.h new file mode 100644 index 0000000000..a903f5d8ab --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/nested_umbrella/b.h @@ -0,0 +1,2 @@ +int nested_umbrella_b; + diff --git a/test/Modules/normal-module-map.cpp b/test/Modules/normal-module-map.cpp index 19294950d4..5a7d549053 100644 --- a/test/Modules/normal-module-map.cpp +++ b/test/Modules/normal-module-map.cpp @@ -1,3 +1,5 @@ +// Note: inside the module. expected-note{{ 'nested_umbrella_a' declared here}} + // RUN: rm -rf %t // RUN: %clang_cc1 -x objective-c -fmodule-cache-path %t -fauto-module-import -I %S/Inputs/normal-module-map %s -verify #include "Umbrella/umbrella_sub.h" @@ -15,3 +17,19 @@ __import_module__ Umbrella2; int test() { return a1 + b1 + nested2; } + +__import_module__ nested_umbrella.a; + +int testNestedUmbrellaA() { + return nested_umbrella_a; +} + +int testNestedUmbrellaBFail() { + return nested_umbrella_b; // expected-error{{use of undeclared identifier 'nested_umbrella_b'; did you mean 'nested_umbrella_a'?}} +} + +__import_module__ nested_umbrella.b; + +int testNestedUmbrellaB() { + return nested_umbrella_b; +} |