Support lazy stat'ing of files referenced by module maps.

This patch adds support for a `header` declaration in a module map to specify
certain `stat` information (currently, size and mtime) about that header file.
This has two purposes:

- It removes the need to eagerly `stat` every file referenced by a module map.
  Instead, we track a list of unresolved header files with each size / mtime
  (actually, for simplicity, we track submodules with such headers), and when
  attempting to look up a header file based on a `FileEntry`, we check if there
  are any unresolved header directives with that `FileEntry`'s size / mtime and
  perform deferred `stat`s if so.

- It permits a preprocessed module to be compiled without the original files
  being present on disk. The only reason we used to need those files was to get
  the `stat` information in order to do header -> module lookups when using the
  module. If we're provided with the `stat` information in the preprocessed
  module, we can avoid requiring the files to exist.

Unlike most `header` directives, if a `header` directive with `stat`
information has no corresponding on-disk file the enclosing module is *not*
marked unavailable (so that behavior is consistent regardless of whether we've
resolved a header directive, and so that preprocessed modules don't get marked
unavailable). We could actually do this for all `header` directives: the only
reason we mark the module unavailable if headers are missing is to give a
diagnostic slightly earlier (rather than waiting until we actually try to build
the module / load and validate its .pcm file).

Differential Revision: https://reviews.llvm.org/D33703

llvm-svn: 304515
This commit is contained in:
Richard Smith 2017-06-02 01:55:39 +00:00
parent ae80045deb
commit 040e12662a
20 changed files with 546 additions and 152 deletions

View File

@ -469,9 +469,16 @@ A header declaration specifies that a particular header is associated with the e
.. parsed-literal:: .. parsed-literal::
*header-declaration*: *header-declaration*:
``private``:sub:`opt` ``textual``:sub:`opt` ``header`` *string-literal* ``private``:sub:`opt` ``textual``:sub:`opt` ``header`` *string-literal* *header-attrs*:sub:`opt`
``umbrella`` ``header`` *string-literal* ``umbrella`` ``header`` *string-literal* *header-attrs*:sub:`opt`
``exclude`` ``header`` *string-literal* ``exclude`` ``header`` *string-literal* *header-attrs*:sub:`opt`
*header-attrs*:
'{' *header-attr** '}'
*header-attr*:
``size`` *integer-literal*
``mtime`` *integer-literal*
A header declaration that does not contain ``exclude`` nor ``textual`` specifies a header that contributes to the enclosing module. Specifically, when the module is built, the named header will be parsed and its declarations will be (logically) placed into the enclosing submodule. A header declaration that does not contain ``exclude`` nor ``textual`` specifies a header that contributes to the enclosing module. Specifically, when the module is built, the named header will be parsed and its declarations will be (logically) placed into the enclosing submodule.
@ -504,6 +511,18 @@ A header with the ``exclude`` specifier is excluded from the module. It will not
A given header shall not be referenced by more than one *header-declaration*. A given header shall not be referenced by more than one *header-declaration*.
Two *header-declaration*\s, or a *header-declaration* and a ``#include``, are
considered to refer to the same file if the paths resolve to the same file
and the specified *header-attr*\s (if any) match the attributes of that file,
even if the file is named differently (for instance, by a relative path or
via symlinks).
.. note::
The use of *header-attr*\s avoids the need for Clang to speculatively
``stat`` every header referenced by a module map. It is recommended that
*header-attr*\s only be used in machine-generated module maps, to avoid
mismatches between attribute values and the corresponding files.
Umbrella directory declaration Umbrella directory declaration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An umbrella directory declaration specifies that all of the headers in the specified directory should be included within the module. An umbrella directory declaration specifies that all of the headers in the specified directory should be included within the module.

View File

@ -664,6 +664,12 @@ def warn_mmap_mismatched_top_level_private : Warning<
InGroup<PrivateModule>; InGroup<PrivateModule>;
def note_mmap_rename_top_level_private_as_submodule : Note< def note_mmap_rename_top_level_private_as_submodule : Note<
"make '%0' a submodule of '%1' to ensure it can be found by name">; "make '%0' a submodule of '%1' to ensure it can be found by name">;
def err_mmap_duplicate_header_attribute : Error<
"header attribute '%0' specified multiple times">;
def err_mmap_invalid_header_attribute_value : Error<
"expected integer literal as value for header attribute '%0'">;
def err_mmap_expected_header_attribute : Error<
"expected a header attribute name ('size' or 'mtime')">;
def warn_auto_module_import : Warning< def warn_auto_module_import : Warning<
"treating #%select{include|import|include_next|__include_macros}0 as an " "treating #%select{include|import|include_next|__include_macros}0 as an "

View File

@ -174,10 +174,6 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
"method %2 with %ordinal3 parameter of type %4%select{| decayed from %6}5|" "method %2 with %ordinal3 parameter of type %4%select{| decayed from %6}5|"
"method %2 with %ordinal3 parameter named %4}1">; "method %2 with %ordinal3 parameter named %4}1">;
def warn_module_uses_date_time : Warning<
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
InGroup<DiagGroup<"pch-date-time">>;
def warn_duplicate_module_file_extension : Warning< def warn_duplicate_module_file_extension : Warning<
"duplicate module file extension block name '%0'">, "duplicate module file extension block name '%0'">,
InGroup<ModuleFileExtension>; InGroup<ModuleFileExtension>;
@ -186,7 +182,15 @@ def warn_module_system_bit_conflict : Warning<
"module file '%0' was validated as a system module and is now being imported " "module file '%0' was validated as a system module and is now being imported "
"as a non-system module; any difference in diagnostic options will be ignored">, "as a non-system module; any difference in diagnostic options will be ignored">,
InGroup<ModuleConflict>; InGroup<ModuleConflict>;
} // let CategoryName
let CategoryName = "AST Serialization Issue" in {
def warn_module_uses_date_time : Warning<
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
InGroup<DiagGroup<"pch-date-time">>;
def err_module_no_size_mtime_for_header : Error<
"cannot emit module %0: %select{size|mtime}1 must be explicitly specified "
"for missing header file \"%2\"">;
} // let CategoryName } // let CategoryName
} // let Component } // let Component

View File

@ -154,11 +154,19 @@ public:
/// \brief Stored information about a header directive that was found in the /// \brief Stored information about a header directive that was found in the
/// module map file but has not been resolved to a file. /// module map file but has not been resolved to a file.
struct UnresolvedHeaderDirective { struct UnresolvedHeaderDirective {
HeaderKind Kind = HK_Normal;
SourceLocation FileNameLoc; SourceLocation FileNameLoc;
std::string FileName; std::string FileName;
bool IsUmbrella; bool IsUmbrella = false;
bool HasBuiltinHeader = false;
Optional<off_t> Size;
Optional<time_t> ModTime;
}; };
/// Headers that are mentioned in the module map file but that we have not
/// yet attempted to resolve to a file on the file system.
SmallVector<UnresolvedHeaderDirective, 1> UnresolvedHeaders;
/// \brief Headers that are mentioned in the module map file but could not be /// \brief Headers that are mentioned in the module map file but could not be
/// found on the file system. /// found on the file system.
SmallVector<UnresolvedHeaderDirective, 1> MissingHeaders; SmallVector<UnresolvedHeaderDirective, 1> MissingHeaders;

View File

@ -26,6 +26,7 @@
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
@ -116,6 +117,11 @@ public:
// Adjust ModuleMap::addHeader. // Adjust ModuleMap::addHeader.
}; };
/// Convert a header kind to a role. Requires Kind to not be HK_Excluded.
static ModuleHeaderRole headerKindToRole(Module::HeaderKind Kind);
/// Convert a header role to a kind.
static Module::HeaderKind headerRoleToKind(ModuleHeaderRole Role);
/// \brief A header that is known to reside within a given module, /// \brief A header that is known to reside within a given module,
/// whether it was included or excluded. /// whether it was included or excluded.
class KnownHeader { class KnownHeader {
@ -166,6 +172,12 @@ private:
/// that header. /// that header.
HeadersMap Headers; HeadersMap Headers;
/// Map from file sizes to modules with lazy header directives of that size.
mutable llvm::DenseMap<off_t, llvm::TinyPtrVector<Module*>> LazyHeadersBySize;
/// Map from mtimes to modules with lazy header directives with those mtimes.
mutable llvm::DenseMap<time_t, llvm::TinyPtrVector<Module*>>
LazyHeadersByModTime;
/// \brief Mapping from directories with umbrella headers to the module /// \brief Mapping from directories with umbrella headers to the module
/// that is generated from the umbrella header. /// that is generated from the umbrella header.
/// ///
@ -257,22 +269,30 @@ private:
/// resolved. /// resolved.
Module *resolveModuleId(const ModuleId &Id, Module *Mod, bool Complain) const; Module *resolveModuleId(const ModuleId &Id, Module *Mod, bool Complain) const;
/// Resolve the given header directive to an actual header file. /// Add an unresolved header to a module.
void addUnresolvedHeader(Module *Mod,
Module::UnresolvedHeaderDirective Header);
/// Look up the given header directive to find an actual header file.
/// ///
/// \param M The module in which we're resolving the header directive. /// \param M The module in which we're resolving the header directive.
/// \param Header The header directive to resolve. /// \param Header The header directive to resolve.
/// \param RelativePathName Filled in with the relative path name from the /// \param RelativePathName Filled in with the relative path name from the
/// module to the resolved header. /// module to the resolved header.
/// \return The resolved file, if any. /// \return The resolved file, if any.
const FileEntry *resolveHeader(Module *M, const FileEntry *findHeader(Module *M,
Module::UnresolvedHeaderDirective Header, const Module::UnresolvedHeaderDirective &Header,
SmallVectorImpl<char> &RelativePathName); SmallVectorImpl<char> &RelativePathName);
/// Resolve the given header directive.
void resolveHeader(Module *M,
const Module::UnresolvedHeaderDirective &Header);
/// Attempt to resolve the specified header directive as naming a builtin /// Attempt to resolve the specified header directive as naming a builtin
/// header. /// header.
const FileEntry * /// \return \c true if a corresponding builtin header was found.
resolveAsBuiltinHeader(Module *M, Module::UnresolvedHeaderDirective Header, bool resolveAsBuiltinHeader(Module *M,
SmallVectorImpl<char> &BuiltinPathName); const Module::UnresolvedHeaderDirective &Header);
/// \brief Looks up the modules that \p File corresponds to. /// \brief Looks up the modules that \p File corresponds to.
/// ///
@ -368,6 +388,15 @@ public:
/// the preferred module for the header. /// the preferred module for the header.
ArrayRef<KnownHeader> findAllModulesForHeader(const FileEntry *File) const; ArrayRef<KnownHeader> findAllModulesForHeader(const FileEntry *File) const;
/// Resolve all lazy header directives for the specified file.
///
/// This ensures that the HeaderFileInfo on HeaderSearch is up to date. This
/// is effectively internal, but is exposed so HeaderSearch can call it.
void resolveHeaderDirectives(const FileEntry *File) const;
/// Resolve all lazy header directives for the specified module.
void resolveHeaderDirectives(Module *Mod) const;
/// \brief Reports errors if a module must not include a specific file. /// \brief Reports errors if a module must not include a specific file.
/// ///
/// \param RequestingModule The module including a file. /// \param RequestingModule The module including a file.

View File

@ -394,11 +394,30 @@ void Module::print(raw_ostream &OS, unsigned Indent) const {
{"exclude ", HK_Excluded}}; {"exclude ", HK_Excluded}};
for (auto &K : Kinds) { for (auto &K : Kinds) {
assert(&K == &Kinds[K.Kind] && "kinds in wrong order");
for (auto &H : Headers[K.Kind]) { for (auto &H : Headers[K.Kind]) {
OS.indent(Indent + 2); OS.indent(Indent + 2);
OS << K.Prefix << "header \""; OS << K.Prefix << "header \"";
OS.write_escaped(H.NameAsWritten); OS.write_escaped(H.NameAsWritten);
OS << "\"\n"; OS << "\" { size " << H.Entry->getSize()
<< " mtime " << H.Entry->getModificationTime() << " }\n";
}
}
for (auto *Unresolved : {&UnresolvedHeaders, &MissingHeaders}) {
for (auto &U : *Unresolved) {
OS.indent(Indent + 2);
OS << Kinds[U.Kind].Prefix << "header \"";
OS.write_escaped(U.FileName);
OS << "\"";
if (U.Size || U.ModTime) {
OS << " {";
if (U.Size)
OS << " size " << *U.Size;
if (U.ModTime)
OS << " mtime " << *U.ModTime;
OS << " }";
}
OS << "\n";
} }
} }

View File

@ -289,14 +289,28 @@ static void addHeaderInclude(StringRef HeaderName,
/// ///
/// \param Includes Will be augmented with the set of \#includes or \#imports /// \param Includes Will be augmented with the set of \#includes or \#imports
/// needed to load all of the named headers. /// needed to load all of the named headers.
static std::error_code static std::error_code collectModuleHeaderIncludes(
collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, const LangOptions &LangOpts, FileManager &FileMgr, DiagnosticsEngine &Diag,
ModuleMap &ModMap, clang::Module *Module, ModuleMap &ModMap, clang::Module *Module, SmallVectorImpl<char> &Includes) {
SmallVectorImpl<char> &Includes) {
// Don't collect any headers for unavailable modules. // Don't collect any headers for unavailable modules.
if (!Module->isAvailable()) if (!Module->isAvailable())
return std::error_code(); return std::error_code();
// Resolve all lazy header directives to header files.
ModMap.resolveHeaderDirectives(Module);
// If any headers are missing, we can't build this module. In most cases,
// diagnostics for this should have already been produced; we only get here
// if explicit stat information was provided.
// FIXME: If the name resolves to a file with different stat information,
// produce a better diagnostic.
if (!Module->MissingHeaders.empty()) {
auto &MissingHeader = Module->MissingHeaders.front();
Diag.Report(MissingHeader.FileNameLoc, diag::err_module_header_missing)
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
return std::error_code();
}
// Add includes for each of these headers. // Add includes for each of these headers.
for (auto HK : {Module::HK_Normal, Module::HK_Private}) { for (auto HK : {Module::HK_Normal, Module::HK_Private}) {
for (Module::Header &H : Module->Headers[HK]) { for (Module::Header &H : Module->Headers[HK]) {
@ -367,7 +381,7 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr,
SubEnd = Module->submodule_end(); SubEnd = Module->submodule_end();
Sub != SubEnd; ++Sub) Sub != SubEnd; ++Sub)
if (std::error_code Err = collectModuleHeaderIncludes( if (std::error_code Err = collectModuleHeaderIncludes(
LangOpts, FileMgr, ModMap, *Sub, Includes)) LangOpts, FileMgr, Diag, ModMap, *Sub, Includes))
return Err; return Err;
return std::error_code(); return std::error_code();
@ -494,7 +508,7 @@ getInputBufferForModule(CompilerInstance &CI, Module *M) {
addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents, addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents,
CI.getLangOpts(), M->IsExternC); CI.getLangOpts(), M->IsExternC);
Err = collectModuleHeaderIncludes( Err = collectModuleHeaderIncludes(
CI.getLangOpts(), FileMgr, CI.getLangOpts(), FileMgr, CI.getDiagnostics(),
CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M, CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M,
HeaderContents); HeaderContents);

View File

@ -1114,6 +1114,8 @@ bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP,
auto TryEnterImported = [&](void) -> bool { auto TryEnterImported = [&](void) -> bool {
if (!ModulesEnabled) if (!ModulesEnabled)
return false; return false;
// Ensure FileInfo bits are up to date.
ModMap.resolveHeaderDirectives(File);
// Modules with builtins are special; multiple modules use builtins as // Modules with builtins are special; multiple modules use builtins as
// modular headers, example: // modular headers, example:
// //

View File

@ -36,6 +36,37 @@
#endif #endif
using namespace clang; using namespace clang;
Module::HeaderKind ModuleMap::headerRoleToKind(ModuleHeaderRole Role) {
switch ((int)Role) {
default: llvm_unreachable("unknown header role");
case NormalHeader:
return Module::HK_Normal;
case PrivateHeader:
return Module::HK_Private;
case TextualHeader:
return Module::HK_Textual;
case PrivateHeader | TextualHeader:
return Module::HK_PrivateTextual;
}
}
ModuleMap::ModuleHeaderRole
ModuleMap::headerKindToRole(Module::HeaderKind Kind) {
switch ((int)Kind) {
case Module::HK_Normal:
return NormalHeader;
case Module::HK_Private:
return PrivateHeader;
case Module::HK_Textual:
return TextualHeader;
case Module::HK_PrivateTextual:
return ModuleHeaderRole(PrivateHeader | TextualHeader);
case Module::HK_Excluded:
llvm_unreachable("unexpected header kind");
}
llvm_unreachable("unknown header kind");
}
Module::ExportDecl Module::ExportDecl
ModuleMap::resolveExport(Module *Mod, ModuleMap::resolveExport(Module *Mod,
const Module::UnresolvedExportDecl &Unresolved, const Module::UnresolvedExportDecl &Unresolved,
@ -104,12 +135,22 @@ static void appendSubframeworkPaths(Module *Mod,
} }
const FileEntry * const FileEntry *
ModuleMap::resolveHeader(Module *M, Module::UnresolvedHeaderDirective Header, ModuleMap::findHeader(Module *M,
const Module::UnresolvedHeaderDirective &Header,
SmallVectorImpl<char> &RelativePathName) { SmallVectorImpl<char> &RelativePathName) {
auto GetFile = [&](StringRef Filename) -> const FileEntry * {
auto *File = SourceMgr.getFileManager().getFile(Filename);
if (!File ||
(Header.Size && File->getSize() != *Header.Size) ||
(Header.ModTime && File->getModificationTime() != *Header.ModTime))
return nullptr;
return File;
};
if (llvm::sys::path::is_absolute(Header.FileName)) { if (llvm::sys::path::is_absolute(Header.FileName)) {
RelativePathName.clear(); RelativePathName.clear();
RelativePathName.append(Header.FileName.begin(), Header.FileName.end()); RelativePathName.append(Header.FileName.begin(), Header.FileName.end());
return SourceMgr.getFileManager().getFile(Header.FileName); return GetFile(Header.FileName);
} }
// Search for the header file within the module's home directory. // Search for the header file within the module's home directory.
@ -124,7 +165,7 @@ ModuleMap::resolveHeader(Module *M, Module::UnresolvedHeaderDirective Header,
// Check whether this file is in the public headers. // Check whether this file is in the public headers.
llvm::sys::path::append(RelativePathName, "Headers", Header.FileName); llvm::sys::path::append(RelativePathName, "Headers", Header.FileName);
llvm::sys::path::append(FullPathName, RelativePathName); llvm::sys::path::append(FullPathName, RelativePathName);
if (auto *File = SourceMgr.getFileManager().getFile(FullPathName)) if (auto *File = GetFile(FullPathName))
return File; return File;
// Check whether this file is in the private headers. // Check whether this file is in the private headers.
@ -141,31 +182,74 @@ ModuleMap::resolveHeader(Module *M, Module::UnresolvedHeaderDirective Header,
llvm::sys::path::append(RelativePathName, "PrivateHeaders", llvm::sys::path::append(RelativePathName, "PrivateHeaders",
Header.FileName); Header.FileName);
llvm::sys::path::append(FullPathName, RelativePathName); llvm::sys::path::append(FullPathName, RelativePathName);
return SourceMgr.getFileManager().getFile(FullPathName); return GetFile(FullPathName);
} }
// Lookup for normal headers. // Lookup for normal headers.
llvm::sys::path::append(RelativePathName, Header.FileName); llvm::sys::path::append(RelativePathName, Header.FileName);
llvm::sys::path::append(FullPathName, RelativePathName); llvm::sys::path::append(FullPathName, RelativePathName);
return SourceMgr.getFileManager().getFile(FullPathName); return GetFile(FullPathName);
} }
const FileEntry * void ModuleMap::resolveHeader(Module *Mod,
ModuleMap::resolveAsBuiltinHeader(Module *M, const Module::UnresolvedHeaderDirective &Header) {
Module::UnresolvedHeaderDirective Header, SmallString<128> RelativePathName;
SmallVectorImpl<char> &BuiltinPathName) { if (const FileEntry *File = findHeader(Mod, Header, RelativePathName)) {
if (llvm::sys::path::is_absolute(Header.FileName) || M->isPartOfFramework() || if (Header.IsUmbrella) {
!M->IsSystem || Header.IsUmbrella || !BuiltinIncludeDir || const DirectoryEntry *UmbrellaDir = File->getDir();
BuiltinIncludeDir == M->Directory || !isBuiltinHeader(Header.FileName)) if (Module *UmbrellaMod = UmbrellaDirs[UmbrellaDir])
return nullptr; Diags.Report(Header.FileNameLoc, diag::err_mmap_umbrella_clash)
<< UmbrellaMod->getFullModuleName();
else
// Record this umbrella header.
setUmbrellaHeader(Mod, File, RelativePathName.str());
} else {
Module::Header H = {RelativePathName.str(), File};
if (Header.Kind == Module::HK_Excluded)
excludeHeader(Mod, H);
else
addHeader(Mod, H, headerKindToRole(Header.Kind));
}
} else if (Header.HasBuiltinHeader && !Header.Size && !Header.ModTime) {
// There's a builtin header but no corresponding on-disk header. Assume
// this was supposed to modularize the builtin header alone.
} else if (Header.Kind == Module::HK_Excluded) {
// Ignore missing excluded header files. They're optional anyway.
} else {
// If we find a module that has a missing header, we mark this module as
// unavailable and store the header directive for displaying diagnostics.
Mod->MissingHeaders.push_back(Header);
// A missing header with stat information doesn't make the module
// unavailable; this keeps our behavior consistent as headers are lazily
// resolved. (Such a module still can't be built though, except from
// preprocessed source.)
if (!Header.Size && !Header.ModTime)
Mod->markUnavailable();
}
}
bool ModuleMap::resolveAsBuiltinHeader(
Module *Mod, const Module::UnresolvedHeaderDirective &Header) {
if (Header.Kind == Module::HK_Excluded ||
llvm::sys::path::is_absolute(Header.FileName) ||
Mod->isPartOfFramework() || !Mod->IsSystem || Header.IsUmbrella ||
!BuiltinIncludeDir || BuiltinIncludeDir == Mod->Directory ||
!isBuiltinHeader(Header.FileName))
return false;
// This is a system module with a top-level header. This header // This is a system module with a top-level header. This header
// may have a counterpart (or replacement) in the set of headers // may have a counterpart (or replacement) in the set of headers
// supplied by Clang. Find that builtin header. // supplied by Clang. Find that builtin header.
llvm::sys::path::append(BuiltinPathName, BuiltinIncludeDir->getName(), SmallString<128> Path;
Header.FileName); llvm::sys::path::append(Path, BuiltinIncludeDir->getName(), Header.FileName);
return SourceMgr.getFileManager().getFile( auto *File = SourceMgr.getFileManager().getFile(Path);
StringRef(BuiltinPathName.data(), BuiltinPathName.size())); if (!File)
return false;
auto Role = headerKindToRole(Header.Kind);
Module::Header H = {Path.str(), File};
addHeader(Mod, H, Role);
return true;
} }
ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags, ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags,
@ -246,6 +330,7 @@ bool ModuleMap::isBuiltinHeader(StringRef FileName) {
ModuleMap::HeadersMap::iterator ModuleMap::HeadersMap::iterator
ModuleMap::findKnownHeader(const FileEntry *File) { ModuleMap::findKnownHeader(const FileEntry *File) {
resolveHeaderDirectives(File);
HeadersMap::iterator Known = Headers.find(File); HeadersMap::iterator Known = Headers.find(File);
if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps && if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps &&
Known == Headers.end() && File->getDir() == BuiltinIncludeDir && Known == Headers.end() && File->getDir() == BuiltinIncludeDir &&
@ -328,8 +413,10 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule)) if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule))
return; return;
if (RequestingModule) if (RequestingModule) {
resolveUses(RequestingModule, /*Complain=*/false); resolveUses(RequestingModule, /*Complain=*/false);
resolveHeaderDirectives(RequestingModule);
}
bool Excluded = false; bool Excluded = false;
Module *Private = nullptr; Module *Private = nullptr;
@ -511,6 +598,7 @@ ModuleMap::findOrCreateModuleForHeaderInUmbrellaDir(const FileEntry *File) {
ArrayRef<ModuleMap::KnownHeader> ArrayRef<ModuleMap::KnownHeader>
ModuleMap::findAllModulesForHeader(const FileEntry *File) const { ModuleMap::findAllModulesForHeader(const FileEntry *File) const {
resolveHeaderDirectives(File);
auto It = Headers.find(File); auto It = Headers.find(File);
if (It == Headers.end()) if (It == Headers.end())
return None; return None;
@ -524,6 +612,7 @@ bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) const {
bool bool
ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header, ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header,
const Module *RequestingModule) const { const Module *RequestingModule) const {
resolveHeaderDirectives(Header);
HeadersMap::const_iterator Known = Headers.find(Header); HeadersMap::const_iterator Known = Headers.find(Header);
if (Known != Headers.end()) { if (Known != Headers.end()) {
for (SmallVectorImpl<KnownHeader>::const_iterator for (SmallVectorImpl<KnownHeader>::const_iterator
@ -896,18 +985,63 @@ void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir,
UmbrellaDirs[UmbrellaDir] = Mod; UmbrellaDirs[UmbrellaDir] = Mod;
} }
static Module::HeaderKind headerRoleToKind(ModuleMap::ModuleHeaderRole Role) { void ModuleMap::addUnresolvedHeader(Module *Mod,
switch ((int)Role) { Module::UnresolvedHeaderDirective Header) {
default: llvm_unreachable("unknown header role"); // If there is a builtin counterpart to this file, add it now so it can
case ModuleMap::NormalHeader: // wrap the system header.
return Module::HK_Normal; if (resolveAsBuiltinHeader(Mod, Header)) {
case ModuleMap::PrivateHeader: // If we have both a builtin and system version of the file, the
return Module::HK_Private; // builtin version may want to inject macros into the system header, so
case ModuleMap::TextualHeader: // force the system header to be treated as a textual header in this
return Module::HK_Textual; // case.
case ModuleMap::PrivateHeader | ModuleMap::TextualHeader: Header.Kind = headerRoleToKind(ModuleMap::ModuleHeaderRole(
return Module::HK_PrivateTextual; headerKindToRole(Header.Kind) | ModuleMap::TextualHeader));
Header.HasBuiltinHeader = true;
} }
// If possible, don't stat the header until we need to. This requires the
// user to have provided us with some stat information about the file.
// FIXME: Add support for lazily stat'ing umbrella headers and excluded
// headers.
if ((Header.Size || Header.ModTime) && !Header.IsUmbrella &&
Header.Kind != Module::HK_Excluded) {
// We expect more variation in mtime than size, so if we're given both,
// use the mtime as the key.
if (Header.ModTime)
LazyHeadersByModTime[*Header.ModTime].push_back(Mod);
else
LazyHeadersBySize[*Header.Size].push_back(Mod);
Mod->UnresolvedHeaders.push_back(Header);
return;
}
// We don't have stat information or can't defer looking this file up.
// Perform the lookup now.
resolveHeader(Mod, Header);
}
void ModuleMap::resolveHeaderDirectives(const FileEntry *File) const {
auto BySize = LazyHeadersBySize.find(File->getSize());
if (BySize != LazyHeadersBySize.end()) {
for (auto *M : BySize->second)
resolveHeaderDirectives(M);
LazyHeadersBySize.erase(BySize);
}
auto ByModTime = LazyHeadersByModTime.find(File->getModificationTime());
if (ByModTime != LazyHeadersByModTime.end()) {
for (auto *M : ByModTime->second)
resolveHeaderDirectives(M);
LazyHeadersByModTime.erase(ByModTime);
}
}
void ModuleMap::resolveHeaderDirectives(Module *Mod) const {
for (auto &Header : Mod->UnresolvedHeaders)
// This operation is logically const; we're just changing how we represent
// the header information for this file.
const_cast<ModuleMap*>(this)->resolveHeader(Mod, Header);
Mod->UnresolvedHeaders.clear();
} }
void ModuleMap::addHeader(Module *Mod, Module::Header Header, void ModuleMap::addHeader(Module *Mod, Module::Header Header,
@ -1063,6 +1197,7 @@ namespace clang {
RequiresKeyword, RequiresKeyword,
Star, Star,
StringLiteral, StringLiteral,
IntegerLiteral,
TextualKeyword, TextualKeyword,
LBrace, LBrace,
RBrace, RBrace,
@ -1072,7 +1207,12 @@ namespace clang {
unsigned Location; unsigned Location;
unsigned StringLength; unsigned StringLength;
union {
// If Kind != IntegerLiteral.
const char *StringData; const char *StringData;
// If Kind == IntegerLiteral.
uint64_t IntegerValue;
};
void clear() { void clear() {
Kind = EndOfFile; Kind = EndOfFile;
@ -1087,8 +1227,13 @@ namespace clang {
return SourceLocation::getFromRawEncoding(Location); return SourceLocation::getFromRawEncoding(Location);
} }
uint64_t getInteger() const {
return Kind == IntegerLiteral ? IntegerValue : 0;
}
StringRef getString() const { StringRef getString() const {
return StringRef(StringData, StringLength); return Kind == IntegerLiteral ? StringRef()
: StringRef(StringData, StringLength);
} }
}; };
@ -1279,6 +1424,25 @@ retry:
break; break;
} }
case tok::numeric_constant: {
// We don't support any suffixes or other complications.
SmallString<32> SpellingBuffer;
SpellingBuffer.resize(LToken.getLength() + 1);
const char *Start = SpellingBuffer.data();
unsigned Length =
Lexer::getSpelling(LToken, Start, SourceMgr, L.getLangOpts());
uint64_t Value;
if (StringRef(Start, Length).getAsInteger(0, Value)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);
HadError = true;
goto retry;
}
Tok.Kind = MMToken::IntegerLiteral;
Tok.IntegerValue = Value;
break;
}
case tok::comment: case tok::comment:
goto retry; goto retry;
@ -1904,6 +2068,9 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
Header.FileName = Tok.getString(); Header.FileName = Tok.getString();
Header.FileNameLoc = consumeToken(); Header.FileNameLoc = consumeToken();
Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword; Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword;
Header.Kind =
(LeadingToken == MMToken::ExcludeKeyword ? Module::HK_Excluded
: Map.headerRoleToKind(Role));
// Check whether we already have an umbrella. // Check whether we already have an umbrella.
if (Header.IsUmbrella && ActiveModule->Umbrella) { if (Header.IsUmbrella && ActiveModule->Umbrella) {
@ -1913,64 +2080,62 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
return; return;
} }
// Look for this file by name if we don't have any stat information. // If we were given stat information, parse it so we can skip looking for
SmallString<128> RelativePathName, BuiltinPathName; // the file.
const FileEntry *File = if (Tok.is(MMToken::LBrace)) {
Map.resolveHeader(ActiveModule, Header, RelativePathName); SourceLocation LBraceLoc = consumeToken();
const FileEntry *BuiltinFile =
Map.resolveAsBuiltinHeader(ActiveModule, Header, BuiltinPathName);
// If Clang supplies this header but the underlying system does not, while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) {
// just silently swap in our builtin version. Otherwise, we'll end enum Attribute { Size, ModTime, Unknown };
// up adding both (later). StringRef Str = Tok.getString();
if (BuiltinFile && !File) { SourceLocation Loc = consumeToken();
RelativePathName = BuiltinPathName; switch (llvm::StringSwitch<Attribute>(Str)
File = BuiltinFile; .Case("size", Size)
BuiltinFile = nullptr; .Case("mtime", ModTime)
.Default(Unknown)) {
case Size:
if (Header.Size)
Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;
if (!Tok.is(MMToken::IntegerLiteral)) {
Diags.Report(Tok.getLocation(),
diag::err_mmap_invalid_header_attribute_value) << Str;
skipUntil(MMToken::RBrace);
break;
}
Header.Size = Tok.getInteger();
consumeToken();
break;
case ModTime:
if (Header.ModTime)
Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;
if (!Tok.is(MMToken::IntegerLiteral)) {
Diags.Report(Tok.getLocation(),
diag::err_mmap_invalid_header_attribute_value) << Str;
skipUntil(MMToken::RBrace);
break;
}
Header.ModTime = Tok.getInteger();
consumeToken();
break;
case Unknown:
Diags.Report(Loc, diag::err_mmap_expected_header_attribute);
skipUntil(MMToken::RBrace);
break;
}
} }
// FIXME: We shouldn't be eagerly stat'ing every file named in a module map. if (Tok.is(MMToken::RBrace))
// Come up with a lazy way to do this. consumeToken();
if (File) { else {
if (Header.IsUmbrella) { Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);
const DirectoryEntry *UmbrellaDir = File->getDir(); Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);
if (Module *UmbrellaModule = Map.UmbrellaDirs[UmbrellaDir]) {
Diags.Report(LeadingLoc, diag::err_mmap_umbrella_clash)
<< UmbrellaModule->getFullModuleName();
HadError = true; HadError = true;
} else {
// Record this umbrella header.
Map.setUmbrellaHeader(ActiveModule, File, RelativePathName.str());
} }
} else if (LeadingToken == MMToken::ExcludeKeyword) {
Module::Header H = {RelativePathName.str(), File};
Map.excludeHeader(ActiveModule, H);
} else {
// If there is a builtin counterpart to this file, add it now so it can
// wrap the system header.
if (BuiltinFile) {
Module::Header H = { BuiltinPathName.str(), BuiltinFile };
Map.addHeader(ActiveModule, H, Role);
// If we have both a builtin and system version of the file, the
// builtin version may want to inject macros into the system header, so
// force the system header to be treated as a textual header in this
// case.
Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader);
} }
// Record this header. Map.addUnresolvedHeader(ActiveModule, std::move(Header));
Module::Header H = { RelativePathName.str(), File };
Map.addHeader(ActiveModule, H, Role);
}
} else if (LeadingToken != MMToken::ExcludeKeyword) {
// Ignore excluded header files. They're optional anyway.
// If we find a module that has a missing header, we mark this module as
// unavailable and store the header directive for displaying diagnostics.
ActiveModule->markUnavailable();
ActiveModule->MissingHeaders.push_back(Header);
}
} }
static int compareModuleHeaders(const Module::Header *A, static int compareModuleHeaders(const Module::Header *A,
@ -2521,6 +2686,7 @@ bool ModuleMapParser::parseModuleMapFile() {
case MMToken::RequiresKeyword: case MMToken::RequiresKeyword:
case MMToken::Star: case MMToken::Star:
case MMToken::StringLiteral: case MMToken::StringLiteral:
case MMToken::IntegerLiteral:
case MMToken::TextualKeyword: case MMToken::TextualKeyword:
case MMToken::UmbrellaKeyword: case MMToken::UmbrellaKeyword:
case MMToken::UseKeyword: case MMToken::UseKeyword:

View File

@ -689,6 +689,8 @@ Preprocessor::getModuleHeaderToIncludeForDiagnostics(SourceLocation IncLoc,
while (!Loc.isInvalid() && !SM.isInMainFile(Loc)) { while (!Loc.isInvalid() && !SM.isInMainFile(Loc)) {
auto ID = SM.getFileID(SM.getExpansionLoc(Loc)); auto ID = SM.getFileID(SM.getExpansionLoc(Loc));
auto *FE = SM.getFileEntryForID(ID); auto *FE = SM.getFileEntryForID(ID);
if (!FE)
break;
bool InTextualHeader = false; bool InTextualHeader = false;
for (auto Header : HeaderInfo.getModuleMap().findAllModulesForHeader(FE)) { for (auto Header : HeaderInfo.getModuleMap().findAllModulesForHeader(FE)) {

View File

@ -1856,24 +1856,31 @@ namespace {
// Trait used for the on-disk hash table of header search information. // Trait used for the on-disk hash table of header search information.
class HeaderFileInfoTrait { class HeaderFileInfoTrait {
ASTWriter &Writer; ASTWriter &Writer;
const HeaderSearch &HS;
// Keep track of the framework names we've used during serialization. // Keep track of the framework names we've used during serialization.
SmallVector<char, 128> FrameworkStringData; SmallVector<char, 128> FrameworkStringData;
llvm::StringMap<unsigned> FrameworkNameOffset; llvm::StringMap<unsigned> FrameworkNameOffset;
public: public:
HeaderFileInfoTrait(ASTWriter &Writer, const HeaderSearch &HS) HeaderFileInfoTrait(ASTWriter &Writer) : Writer(Writer) {}
: Writer(Writer), HS(HS) { }
struct key_type { struct key_type {
const FileEntry *FE;
StringRef Filename; StringRef Filename;
off_t Size;
time_t ModTime;
}; };
typedef const key_type &key_type_ref; typedef const key_type &key_type_ref;
typedef HeaderFileInfo data_type; using UnresolvedModule =
llvm::PointerIntPair<Module *, 2, ModuleMap::ModuleHeaderRole>;
struct data_type {
const HeaderFileInfo &HFI;
ArrayRef<ModuleMap::KnownHeader> KnownHeaders;
UnresolvedModule Unresolved;
};
typedef const data_type &data_type_ref; typedef const data_type &data_type_ref;
typedef unsigned hash_value_type; typedef unsigned hash_value_type;
typedef unsigned offset_type; typedef unsigned offset_type;
@ -1881,8 +1888,7 @@ namespace {
// The hash is based only on size/time of the file, so that the reader can // The hash is based only on size/time of the file, so that the reader can
// match even when symlinking or excess path elements ("foo/../", "../") // match even when symlinking or excess path elements ("foo/../", "../")
// change the form of the name. However, complete path is still the key. // change the form of the name. However, complete path is still the key.
return llvm::hash_combine(key.FE->getSize(), return llvm::hash_combine(key.Size, key.ModTime);
Writer.getTimestampForOutput(key.FE));
} }
std::pair<unsigned,unsigned> std::pair<unsigned,unsigned>
@ -1892,9 +1898,11 @@ namespace {
unsigned KeyLen = key.Filename.size() + 1 + 8 + 8; unsigned KeyLen = key.Filename.size() + 1 + 8 + 8;
LE.write<uint16_t>(KeyLen); LE.write<uint16_t>(KeyLen);
unsigned DataLen = 1 + 2 + 4 + 4; unsigned DataLen = 1 + 2 + 4 + 4;
for (auto ModInfo : HS.getModuleMap().findAllModulesForHeader(key.FE)) for (auto ModInfo : Data.KnownHeaders)
if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule())) if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule()))
DataLen += 4; DataLen += 4;
if (Data.Unresolved.getPointer())
DataLen += 4;
LE.write<uint8_t>(DataLen); LE.write<uint8_t>(DataLen);
return std::make_pair(KeyLen, DataLen); return std::make_pair(KeyLen, DataLen);
} }
@ -1902,9 +1910,9 @@ namespace {
void EmitKey(raw_ostream& Out, key_type_ref key, unsigned KeyLen) { void EmitKey(raw_ostream& Out, key_type_ref key, unsigned KeyLen) {
using namespace llvm::support; using namespace llvm::support;
endian::Writer<little> LE(Out); endian::Writer<little> LE(Out);
LE.write<uint64_t>(key.FE->getSize()); LE.write<uint64_t>(key.Size);
KeyLen -= 8; KeyLen -= 8;
LE.write<uint64_t>(Writer.getTimestampForOutput(key.FE)); LE.write<uint64_t>(key.ModTime);
KeyLen -= 8; KeyLen -= 8;
Out.write(key.Filename.data(), KeyLen); Out.write(key.Filename.data(), KeyLen);
} }
@ -1915,45 +1923,49 @@ namespace {
endian::Writer<little> LE(Out); endian::Writer<little> LE(Out);
uint64_t Start = Out.tell(); (void)Start; uint64_t Start = Out.tell(); (void)Start;
unsigned char Flags = (Data.isImport << 4) unsigned char Flags = (Data.HFI.isImport << 4)
| (Data.isPragmaOnce << 3) | (Data.HFI.isPragmaOnce << 3)
| (Data.DirInfo << 1) | (Data.HFI.DirInfo << 1)
| Data.IndexHeaderMapHeader; | Data.HFI.IndexHeaderMapHeader;
LE.write<uint8_t>(Flags); LE.write<uint8_t>(Flags);
LE.write<uint16_t>(Data.NumIncludes); LE.write<uint16_t>(Data.HFI.NumIncludes);
if (!Data.ControllingMacro) if (!Data.HFI.ControllingMacro)
LE.write<uint32_t>(Data.ControllingMacroID); LE.write<uint32_t>(Data.HFI.ControllingMacroID);
else else
LE.write<uint32_t>(Writer.getIdentifierRef(Data.ControllingMacro)); LE.write<uint32_t>(Writer.getIdentifierRef(Data.HFI.ControllingMacro));
unsigned Offset = 0; unsigned Offset = 0;
if (!Data.Framework.empty()) { if (!Data.HFI.Framework.empty()) {
// If this header refers into a framework, save the framework name. // If this header refers into a framework, save the framework name.
llvm::StringMap<unsigned>::iterator Pos llvm::StringMap<unsigned>::iterator Pos
= FrameworkNameOffset.find(Data.Framework); = FrameworkNameOffset.find(Data.HFI.Framework);
if (Pos == FrameworkNameOffset.end()) { if (Pos == FrameworkNameOffset.end()) {
Offset = FrameworkStringData.size() + 1; Offset = FrameworkStringData.size() + 1;
FrameworkStringData.append(Data.Framework.begin(), FrameworkStringData.append(Data.HFI.Framework.begin(),
Data.Framework.end()); Data.HFI.Framework.end());
FrameworkStringData.push_back(0); FrameworkStringData.push_back(0);
FrameworkNameOffset[Data.Framework] = Offset; FrameworkNameOffset[Data.HFI.Framework] = Offset;
} else } else
Offset = Pos->second; Offset = Pos->second;
} }
LE.write<uint32_t>(Offset); LE.write<uint32_t>(Offset);
// FIXME: If the header is excluded, we should write out some auto EmitModule = [&](Module *M, ModuleMap::ModuleHeaderRole Role) {
// record of that fact. if (uint32_t ModID = Writer.getLocalOrImportedSubmoduleID(M)) {
for (auto ModInfo : HS.getModuleMap().findAllModulesForHeader(key.FE)) { uint32_t Value = (ModID << 2) | (unsigned)Role;
if (uint32_t ModID =
Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule())) {
uint32_t Value = (ModID << 2) | (unsigned)ModInfo.getRole();
assert((Value >> 2) == ModID && "overflow in header module info"); assert((Value >> 2) == ModID && "overflow in header module info");
LE.write<uint32_t>(Value); LE.write<uint32_t>(Value);
} }
} };
// FIXME: If the header is excluded, we should write out some
// record of that fact.
for (auto ModInfo : Data.KnownHeaders)
EmitModule(ModInfo.getModule(), ModInfo.getRole());
if (Data.Unresolved.getPointer())
EmitModule(Data.Unresolved.getPointer(), Data.Unresolved.getInt());
assert(Out.tell() - Start == DataLen && "Wrong data length"); assert(Out.tell() - Start == DataLen && "Wrong data length");
} }
@ -1968,16 +1980,71 @@ namespace {
/// ///
/// \param HS The header search structure to save. /// \param HS The header search structure to save.
void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) { void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {
HeaderFileInfoTrait GeneratorTrait(*this);
llvm::OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator;
SmallVector<const char *, 4> SavedStrings;
unsigned NumHeaderSearchEntries = 0;
// Find all unresolved headers for the current module. We generally will
// have resolved them before we get here, but not necessarily: we might be
// compiling a preprocessed module, where there is no requirement for the
// original files to exist any more.
if (WritingModule) {
llvm::SmallVector<Module *, 16> Worklist(1, WritingModule);
while (!Worklist.empty()) {
Module *M = Worklist.pop_back_val();
if (!M->isAvailable())
continue;
// Map to disk files where possible, to pick up any missing stat
// information. This also means we don't need to check the unresolved
// headers list when emitting resolved headers in the first loop below.
// FIXME: It'd be preferable to avoid doing this if we were given
// sufficient stat information in the module map.
HS.getModuleMap().resolveHeaderDirectives(M);
// If the file didn't exist, we can still create a module if we were given
// enough information in the module map.
for (auto U : M->MissingHeaders) {
// Check that we were given enough information to build a module
// without this file existing on disk.
if (!U.Size || (!U.ModTime && IncludeTimestamps)) {
PP->Diag(U.FileNameLoc, diag::err_module_no_size_mtime_for_header)
<< WritingModule->getFullModuleName() << U.Size.hasValue()
<< U.FileName;
continue;
}
// Form the effective relative pathname for the file.
SmallString<128> Filename(M->Directory->getName());
llvm::sys::path::append(Filename, U.FileName);
PreparePathForOutput(Filename);
StringRef FilenameDup = strdup(Filename.c_str());
SavedStrings.push_back(FilenameDup.data());
HeaderFileInfoTrait::key_type Key = {
FilenameDup, *U.Size, IncludeTimestamps ? *U.ModTime : 0
};
HeaderFileInfoTrait::data_type Data = {
{}, {}, {M, ModuleMap::headerKindToRole(U.Kind)}
};
// FIXME: Deal with cases where there are multiple unresolved header
// directives in different submodules for the same header.
Generator.insert(Key, Data, GeneratorTrait);
++NumHeaderSearchEntries;
}
Worklist.append(M->submodule_begin(), M->submodule_end());
}
}
SmallVector<const FileEntry *, 16> FilesByUID; SmallVector<const FileEntry *, 16> FilesByUID;
HS.getFileMgr().GetUniqueIDMapping(FilesByUID); HS.getFileMgr().GetUniqueIDMapping(FilesByUID);
if (FilesByUID.size() > HS.header_file_size()) if (FilesByUID.size() > HS.header_file_size())
FilesByUID.resize(HS.header_file_size()); FilesByUID.resize(HS.header_file_size());
HeaderFileInfoTrait GeneratorTrait(*this, HS);
llvm::OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator;
SmallVector<const char *, 4> SavedStrings;
unsigned NumHeaderSearchEntries = 0;
for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) { for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) {
const FileEntry *File = FilesByUID[UID]; const FileEntry *File = FilesByUID[UID];
if (!File) if (!File)
@ -2004,8 +2071,13 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {
SavedStrings.push_back(Filename.data()); SavedStrings.push_back(Filename.data());
} }
HeaderFileInfoTrait::key_type key = { File, Filename }; HeaderFileInfoTrait::key_type Key = {
Generator.insert(key, *HFI, GeneratorTrait); Filename, File->getSize(), getTimestampForOutput(File)
};
HeaderFileInfoTrait::data_type Data = {
*HFI, HS.getModuleMap().findAllModulesForHeader(File), {}
};
Generator.insert(Key, Data, GeneratorTrait);
++NumHeaderSearchEntries; ++NumHeaderSearchEntries;
} }

View File

@ -0,0 +1 @@
extern int b;

View File

@ -0,0 +1 @@
extern int c;

View File

@ -0,0 +1 @@
extern int a;

View File

@ -0,0 +1,5 @@
module A {
header "foo.h" { size 13 }
header "bar.h" { size 1000 }
header "baz.h" { mtime 1 }
}

View File

@ -0,0 +1,5 @@
module A {
textual header "foo.h" { size 13 }
textual header "bar.h" { size 1000 }
textual header "baz.h" { mtime 1 }
}

View File

@ -1,4 +1,4 @@
// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/diagnostics-aux.modulemap -fmodule-map-file=%s -fsyntax-only -x c++ /dev/null 2>&1 | FileCheck %s // RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/diagnostics-aux.modulemap -fmodule-map-file=%s -fsyntax-only -x c++ /dev/null 2>&1 | FileCheck %s --implicit-check-not error:
// CHECK: In file included from {{.*}}diagnostics-aux.modulemap:3: // CHECK: In file included from {{.*}}diagnostics-aux.modulemap:3:
// CHECK: diagnostics-aux-2.modulemap:2:3: error: expected // CHECK: diagnostics-aux-2.modulemap:2:3: error: expected
@ -15,3 +15,15 @@ module bad_use {
// CHECK: diagnostics.modulemap:[[@LINE+1]]:22: error: use declarations are only allowed in top-level modules // CHECK: diagnostics.modulemap:[[@LINE+1]]:22: error: use declarations are only allowed in top-level modules
module submodule { use foo } module submodule { use foo }
} }
module header_attr {
// CHECK: diagnostics.modulemap:[[@LINE+1]]:20: error: expected a header attribute name
header "foo.h" { x }
// CHECK: diagnostics.modulemap:[[@LINE+1]]:27: error: header attribute 'size' specified multiple times
header "bar.h" { size 1 size 2 }
// CHECK: diagnostics.modulemap:[[@LINE+1]]:25: error: expected integer literal as value for header attribute 'size'
header "baz.h" { size "30 kilobytes" }
header "quux.h" { size 1 mtime 2 }
header "no_attrs.h" {}
}

View File

@ -0,0 +1,10 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -fmodules -I%S/Inputs/header-attribs -fmodule-map-file=%S/Inputs/header-attribs/textual.modulemap -fmodules-cache-path=%t -verify %s -fmodule-name=A -fmodules-strict-decluse
// RUN: not %clang_cc1 -fmodules -I%S/Inputs/header-attribs -emit-module -x c++-module-map %S/Inputs/header-attribs/modular.modulemap -fmodules-cache-path=%t -fmodule-name=A 2>&1 | FileCheck %s --check-prefix BUILD-MODULAR
#include "foo.h" // ok, stats match
#include "bar.h" // expected-error {{does not depend on a module exporting 'bar.h'}}
#include "baz.h" // expected-error {{does not depend on a module exporting 'baz.h'}}
// FIXME: Explain why the 'bar.h' found on disk doesn't match the module map.
// BUILD-MODULAR: error: header 'bar.h' not found

View File

@ -0,0 +1,7 @@
// RUN: %clang_cc1 -fmodules -fmodule-name=A -x c++-module-map %s -emit-module -o /dev/null -verify
module A {
header "does not exist" { size 12345 } // ok, do not need mtime for explicit module build
header "also does not exist" { mtime 12345 }
}
#pragma clang module contents
// expected-error@4 {{cannot emit module A: size must be explicitly specified for missing header file "also does not exist"}}

View File

@ -28,12 +28,21 @@
// RUN: %clang_cc1 -fmodules -fmodule-file=%t/no-rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DINCLUDE -I%S/Inputs/preprocess // RUN: %clang_cc1 -fmodules -fmodule-file=%t/no-rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DINCLUDE -I%S/Inputs/preprocess
// RUN: %clang_cc1 -fmodules -fmodule-file=%t/rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DREWRITE -DINCLUDE -I%S/Inputs/preprocess // RUN: %clang_cc1 -fmodules -fmodule-file=%t/rewrite.pcm %s -I%t -verify -fno-modules-error-recovery -DREWRITE -DINCLUDE -I%S/Inputs/preprocess
// Now try building the module when the header files are missing.
// RUN: cp %S/Inputs/preprocess/fwd.h %S/Inputs/preprocess/file.h %S/Inputs/preprocess/file2.h %S/Inputs/preprocess/module.modulemap %t
// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -I%t -x c++-module-map %t/module.modulemap -E -frewrite-includes -o %t/copy.ii
// RUN: rm %t/fwd.h %t/file.h %t/file2.h %t/module.modulemap
// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -x c++-module-map-cpp-output %t/copy.ii -emit-module -o %t/copy.pcm
// Finally, check that our module contains correct mapping information for the headers.
// RUN: cp %S/Inputs/preprocess/fwd.h %S/Inputs/preprocess/file.h %S/Inputs/preprocess/file2.h %S/Inputs/preprocess/module.modulemap %t
// RUN: %clang_cc1 -fmodules -fmodule-file=%t/copy.pcm %s -I%t -verify -fno-modules-error-recovery -DCOPY -DINCLUDE
// == module map // == module map
// CHECK: # 1 "{{.*}}module.modulemap" // CHECK: # 1 "{{.*}}module.modulemap"
// CHECK: module file { // CHECK: module file {
// CHECK: header "file.h" // CHECK: header "file.h" { size
// CHECK: header "file2.h" // CHECK: header "file2.h" { size
// CHECK: } // CHECK: }
// == file.h // == file.h
@ -98,6 +107,8 @@
__FILE *a; // expected-error {{declaration of '__FILE' must be imported}} __FILE *a; // expected-error {{declaration of '__FILE' must be imported}}
#ifdef REWRITE #ifdef REWRITE
// expected-note@rewrite.ii:1 {{here}} // expected-note@rewrite.ii:1 {{here}}
#elif COPY
// expected-note@copy.ii:1 {{here}}
#else #else
// expected-note@no-rewrite.ii:1 {{here}} // expected-note@no-rewrite.ii:1 {{here}}
#endif #endif