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
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 2cf115d..cbaa407 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -382,8 +382,9 @@
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 @@
"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_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 262e50d..2d95255 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -181,7 +181,11 @@
/// \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 17c9992..41ef74e 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -507,22 +507,24 @@
/// \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 190cdba..5dabe81 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 @@
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 e4c9d77..c9efbf0 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -346,6 +346,11 @@
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 @@
bool parseModuleId(ModuleId &Id);
void parseModuleDecl();
void parseHeaderDecl(SourceLocation UmbrellaLoc);
+ void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
void parseExportDecl();
void parseInferredSubmoduleDecl(bool Explicit);
@@ -796,9 +802,14 @@
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 @@
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,11 +945,64 @@
}
} 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.
///
/// export-declaration:
@@ -998,8 +1061,8 @@
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 21be3aa..236c2bd 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -3088,7 +3088,7 @@
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 @@
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 1bd9050..83ad885 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1900,7 +1900,7 @@
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 @@
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 @@
// 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 91e4bf7..e17f44a 100644
--- a/test/Modules/Inputs/normal-module-map/module.map
+++ b/test/Modules/Inputs/normal-module-map/module.map
@@ -6,3 +6,8 @@
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 0000000..ab180fe
--- /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 0000000..a903f5d
--- /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 1929495..5a7d549 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 @@
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;
+}