mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-01 14:58:18 +00:00
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:
parent
ae80045deb
commit
040e12662a
@ -469,9 +469,16 @@ A header declaration specifies that a particular header is associated with the e
|
||||
.. parsed-literal::
|
||||
|
||||
*header-declaration*:
|
||||
``private``:sub:`opt` ``textual``:sub:`opt` ``header`` *string-literal*
|
||||
``umbrella`` ``header`` *string-literal*
|
||||
``exclude`` ``header`` *string-literal*
|
||||
``private``:sub:`opt` ``textual``:sub:`opt` ``header`` *string-literal* *header-attrs*:sub:`opt`
|
||||
``umbrella`` ``header`` *string-literal* *header-attrs*:sub:`opt`
|
||||
``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.
|
||||
|
||||
@ -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*.
|
||||
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
An umbrella directory declaration specifies that all of the headers in the specified directory should be included within the module.
|
||||
|
@ -664,6 +664,12 @@ def warn_mmap_mismatched_top_level_private : Warning<
|
||||
InGroup<PrivateModule>;
|
||||
def note_mmap_rename_top_level_private_as_submodule : Note<
|
||||
"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<
|
||||
"treating #%select{include|import|include_next|__include_macros}0 as an "
|
||||
|
@ -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 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<
|
||||
"duplicate module file extension block name '%0'">,
|
||||
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 "
|
||||
"as a non-system module; any difference in diagnostic options will be ignored">,
|
||||
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 Component
|
||||
|
||||
|
@ -154,11 +154,19 @@ public:
|
||||
/// \brief Stored information about a header directive that was found in the
|
||||
/// module map file but has not been resolved to a file.
|
||||
struct UnresolvedHeaderDirective {
|
||||
HeaderKind Kind = HK_Normal;
|
||||
SourceLocation FileNameLoc;
|
||||
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
|
||||
/// found on the file system.
|
||||
SmallVector<UnresolvedHeaderDirective, 1> MissingHeaders;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@ -116,6 +117,11 @@ public:
|
||||
// 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,
|
||||
/// whether it was included or excluded.
|
||||
class KnownHeader {
|
||||
@ -165,7 +171,13 @@ private:
|
||||
/// \brief Mapping from each header to the module that owns the contents of
|
||||
/// that header.
|
||||
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
|
||||
/// that is generated from the umbrella header.
|
||||
///
|
||||
@ -257,22 +269,30 @@ private:
|
||||
/// resolved.
|
||||
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 Header The header directive to resolve.
|
||||
/// \param RelativePathName Filled in with the relative path name from the
|
||||
/// module to the resolved header.
|
||||
/// \return The resolved file, if any.
|
||||
const FileEntry *resolveHeader(Module *M,
|
||||
Module::UnresolvedHeaderDirective Header,
|
||||
SmallVectorImpl<char> &RelativePathName);
|
||||
const FileEntry *findHeader(Module *M,
|
||||
const Module::UnresolvedHeaderDirective &Header,
|
||||
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
|
||||
/// header.
|
||||
const FileEntry *
|
||||
resolveAsBuiltinHeader(Module *M, Module::UnresolvedHeaderDirective Header,
|
||||
SmallVectorImpl<char> &BuiltinPathName);
|
||||
/// \return \c true if a corresponding builtin header was found.
|
||||
bool resolveAsBuiltinHeader(Module *M,
|
||||
const Module::UnresolvedHeaderDirective &Header);
|
||||
|
||||
/// \brief Looks up the modules that \p File corresponds to.
|
||||
///
|
||||
@ -368,6 +388,15 @@ public:
|
||||
/// the preferred module for the header.
|
||||
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.
|
||||
///
|
||||
/// \param RequestingModule The module including a file.
|
||||
|
@ -394,11 +394,30 @@ void Module::print(raw_ostream &OS, unsigned Indent) const {
|
||||
{"exclude ", HK_Excluded}};
|
||||
|
||||
for (auto &K : Kinds) {
|
||||
assert(&K == &Kinds[K.Kind] && "kinds in wrong order");
|
||||
for (auto &H : Headers[K.Kind]) {
|
||||
OS.indent(Indent + 2);
|
||||
OS << K.Prefix << "header \"";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,14 +289,28 @@ static void addHeaderInclude(StringRef HeaderName,
|
||||
///
|
||||
/// \param Includes Will be augmented with the set of \#includes or \#imports
|
||||
/// needed to load all of the named headers.
|
||||
static std::error_code
|
||||
collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr,
|
||||
ModuleMap &ModMap, clang::Module *Module,
|
||||
SmallVectorImpl<char> &Includes) {
|
||||
static std::error_code collectModuleHeaderIncludes(
|
||||
const LangOptions &LangOpts, FileManager &FileMgr, DiagnosticsEngine &Diag,
|
||||
ModuleMap &ModMap, clang::Module *Module, SmallVectorImpl<char> &Includes) {
|
||||
// Don't collect any headers for unavailable modules.
|
||||
if (!Module->isAvailable())
|
||||
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.
|
||||
for (auto HK : {Module::HK_Normal, Module::HK_Private}) {
|
||||
for (Module::Header &H : Module->Headers[HK]) {
|
||||
@ -367,7 +381,7 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr,
|
||||
SubEnd = Module->submodule_end();
|
||||
Sub != SubEnd; ++Sub)
|
||||
if (std::error_code Err = collectModuleHeaderIncludes(
|
||||
LangOpts, FileMgr, ModMap, *Sub, Includes))
|
||||
LangOpts, FileMgr, Diag, ModMap, *Sub, Includes))
|
||||
return Err;
|
||||
|
||||
return std::error_code();
|
||||
@ -494,7 +508,7 @@ getInputBufferForModule(CompilerInstance &CI, Module *M) {
|
||||
addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents,
|
||||
CI.getLangOpts(), M->IsExternC);
|
||||
Err = collectModuleHeaderIncludes(
|
||||
CI.getLangOpts(), FileMgr,
|
||||
CI.getLangOpts(), FileMgr, CI.getDiagnostics(),
|
||||
CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M,
|
||||
HeaderContents);
|
||||
|
||||
|
@ -1114,6 +1114,8 @@ bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP,
|
||||
auto TryEnterImported = [&](void) -> bool {
|
||||
if (!ModulesEnabled)
|
||||
return false;
|
||||
// Ensure FileInfo bits are up to date.
|
||||
ModMap.resolveHeaderDirectives(File);
|
||||
// Modules with builtins are special; multiple modules use builtins as
|
||||
// modular headers, example:
|
||||
//
|
||||
|
@ -36,6 +36,37 @@
|
||||
#endif
|
||||
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
|
||||
ModuleMap::resolveExport(Module *Mod,
|
||||
const Module::UnresolvedExportDecl &Unresolved,
|
||||
@ -104,12 +135,22 @@ static void appendSubframeworkPaths(Module *Mod,
|
||||
}
|
||||
|
||||
const FileEntry *
|
||||
ModuleMap::resolveHeader(Module *M, Module::UnresolvedHeaderDirective Header,
|
||||
SmallVectorImpl<char> &RelativePathName) {
|
||||
ModuleMap::findHeader(Module *M,
|
||||
const Module::UnresolvedHeaderDirective &Header,
|
||||
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)) {
|
||||
RelativePathName.clear();
|
||||
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.
|
||||
@ -124,7 +165,7 @@ ModuleMap::resolveHeader(Module *M, Module::UnresolvedHeaderDirective Header,
|
||||
// Check whether this file is in the public headers.
|
||||
llvm::sys::path::append(RelativePathName, "Headers", Header.FileName);
|
||||
llvm::sys::path::append(FullPathName, RelativePathName);
|
||||
if (auto *File = SourceMgr.getFileManager().getFile(FullPathName))
|
||||
if (auto *File = GetFile(FullPathName))
|
||||
return File;
|
||||
|
||||
// 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",
|
||||
Header.FileName);
|
||||
llvm::sys::path::append(FullPathName, RelativePathName);
|
||||
return SourceMgr.getFileManager().getFile(FullPathName);
|
||||
return GetFile(FullPathName);
|
||||
}
|
||||
|
||||
// Lookup for normal headers.
|
||||
llvm::sys::path::append(RelativePathName, Header.FileName);
|
||||
llvm::sys::path::append(FullPathName, RelativePathName);
|
||||
return SourceMgr.getFileManager().getFile(FullPathName);
|
||||
return GetFile(FullPathName);
|
||||
}
|
||||
|
||||
const FileEntry *
|
||||
ModuleMap::resolveAsBuiltinHeader(Module *M,
|
||||
Module::UnresolvedHeaderDirective Header,
|
||||
SmallVectorImpl<char> &BuiltinPathName) {
|
||||
if (llvm::sys::path::is_absolute(Header.FileName) || M->isPartOfFramework() ||
|
||||
!M->IsSystem || Header.IsUmbrella || !BuiltinIncludeDir ||
|
||||
BuiltinIncludeDir == M->Directory || !isBuiltinHeader(Header.FileName))
|
||||
return nullptr;
|
||||
void ModuleMap::resolveHeader(Module *Mod,
|
||||
const Module::UnresolvedHeaderDirective &Header) {
|
||||
SmallString<128> RelativePathName;
|
||||
if (const FileEntry *File = findHeader(Mod, Header, RelativePathName)) {
|
||||
if (Header.IsUmbrella) {
|
||||
const DirectoryEntry *UmbrellaDir = File->getDir();
|
||||
if (Module *UmbrellaMod = UmbrellaDirs[UmbrellaDir])
|
||||
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
|
||||
// may have a counterpart (or replacement) in the set of headers
|
||||
// supplied by Clang. Find that builtin header.
|
||||
llvm::sys::path::append(BuiltinPathName, BuiltinIncludeDir->getName(),
|
||||
Header.FileName);
|
||||
return SourceMgr.getFileManager().getFile(
|
||||
StringRef(BuiltinPathName.data(), BuiltinPathName.size()));
|
||||
SmallString<128> Path;
|
||||
llvm::sys::path::append(Path, BuiltinIncludeDir->getName(), Header.FileName);
|
||||
auto *File = SourceMgr.getFileManager().getFile(Path);
|
||||
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,
|
||||
@ -246,6 +330,7 @@ bool ModuleMap::isBuiltinHeader(StringRef FileName) {
|
||||
|
||||
ModuleMap::HeadersMap::iterator
|
||||
ModuleMap::findKnownHeader(const FileEntry *File) {
|
||||
resolveHeaderDirectives(File);
|
||||
HeadersMap::iterator Known = Headers.find(File);
|
||||
if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps &&
|
||||
Known == Headers.end() && File->getDir() == BuiltinIncludeDir &&
|
||||
@ -328,8 +413,10 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
|
||||
if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule))
|
||||
return;
|
||||
|
||||
if (RequestingModule)
|
||||
if (RequestingModule) {
|
||||
resolveUses(RequestingModule, /*Complain=*/false);
|
||||
resolveHeaderDirectives(RequestingModule);
|
||||
}
|
||||
|
||||
bool Excluded = false;
|
||||
Module *Private = nullptr;
|
||||
@ -511,6 +598,7 @@ ModuleMap::findOrCreateModuleForHeaderInUmbrellaDir(const FileEntry *File) {
|
||||
|
||||
ArrayRef<ModuleMap::KnownHeader>
|
||||
ModuleMap::findAllModulesForHeader(const FileEntry *File) const {
|
||||
resolveHeaderDirectives(File);
|
||||
auto It = Headers.find(File);
|
||||
if (It == Headers.end())
|
||||
return None;
|
||||
@ -524,6 +612,7 @@ bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) const {
|
||||
bool
|
||||
ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header,
|
||||
const Module *RequestingModule) const {
|
||||
resolveHeaderDirectives(Header);
|
||||
HeadersMap::const_iterator Known = Headers.find(Header);
|
||||
if (Known != Headers.end()) {
|
||||
for (SmallVectorImpl<KnownHeader>::const_iterator
|
||||
@ -896,18 +985,63 @@ void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir,
|
||||
UmbrellaDirs[UmbrellaDir] = Mod;
|
||||
}
|
||||
|
||||
static Module::HeaderKind headerRoleToKind(ModuleMap::ModuleHeaderRole Role) {
|
||||
switch ((int)Role) {
|
||||
default: llvm_unreachable("unknown header role");
|
||||
case ModuleMap::NormalHeader:
|
||||
return Module::HK_Normal;
|
||||
case ModuleMap::PrivateHeader:
|
||||
return Module::HK_Private;
|
||||
case ModuleMap::TextualHeader:
|
||||
return Module::HK_Textual;
|
||||
case ModuleMap::PrivateHeader | ModuleMap::TextualHeader:
|
||||
return Module::HK_PrivateTextual;
|
||||
void ModuleMap::addUnresolvedHeader(Module *Mod,
|
||||
Module::UnresolvedHeaderDirective Header) {
|
||||
// If there is a builtin counterpart to this file, add it now so it can
|
||||
// wrap the system header.
|
||||
if (resolveAsBuiltinHeader(Mod, Header)) {
|
||||
// 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.
|
||||
Header.Kind = headerRoleToKind(ModuleMap::ModuleHeaderRole(
|
||||
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,
|
||||
@ -1063,6 +1197,7 @@ namespace clang {
|
||||
RequiresKeyword,
|
||||
Star,
|
||||
StringLiteral,
|
||||
IntegerLiteral,
|
||||
TextualKeyword,
|
||||
LBrace,
|
||||
RBrace,
|
||||
@ -1072,7 +1207,12 @@ namespace clang {
|
||||
|
||||
unsigned Location;
|
||||
unsigned StringLength;
|
||||
const char *StringData;
|
||||
union {
|
||||
// If Kind != IntegerLiteral.
|
||||
const char *StringData;
|
||||
// If Kind == IntegerLiteral.
|
||||
uint64_t IntegerValue;
|
||||
};
|
||||
|
||||
void clear() {
|
||||
Kind = EndOfFile;
|
||||
@ -1086,9 +1226,14 @@ namespace clang {
|
||||
SourceLocation getLocation() const {
|
||||
return SourceLocation::getFromRawEncoding(Location);
|
||||
}
|
||||
|
||||
uint64_t getInteger() const {
|
||||
return Kind == IntegerLiteral ? IntegerValue : 0;
|
||||
}
|
||||
|
||||
StringRef getString() const {
|
||||
return StringRef(StringData, StringLength);
|
||||
return Kind == IntegerLiteral ? StringRef()
|
||||
: StringRef(StringData, StringLength);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1278,6 +1423,25 @@ retry:
|
||||
Tok.StringLength = Length;
|
||||
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:
|
||||
goto retry;
|
||||
@ -1904,6 +2068,9 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
|
||||
Header.FileName = Tok.getString();
|
||||
Header.FileNameLoc = consumeToken();
|
||||
Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword;
|
||||
Header.Kind =
|
||||
(LeadingToken == MMToken::ExcludeKeyword ? Module::HK_Excluded
|
||||
: Map.headerRoleToKind(Role));
|
||||
|
||||
// Check whether we already have an umbrella.
|
||||
if (Header.IsUmbrella && ActiveModule->Umbrella) {
|
||||
@ -1913,64 +2080,62 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for this file by name if we don't have any stat information.
|
||||
SmallString<128> RelativePathName, BuiltinPathName;
|
||||
const FileEntry *File =
|
||||
Map.resolveHeader(ActiveModule, Header, RelativePathName);
|
||||
const FileEntry *BuiltinFile =
|
||||
Map.resolveAsBuiltinHeader(ActiveModule, Header, BuiltinPathName);
|
||||
// If we were given stat information, parse it so we can skip looking for
|
||||
// the file.
|
||||
if (Tok.is(MMToken::LBrace)) {
|
||||
SourceLocation LBraceLoc = consumeToken();
|
||||
|
||||
// If Clang supplies this header but the underlying system does not,
|
||||
// just silently swap in our builtin version. Otherwise, we'll end
|
||||
// up adding both (later).
|
||||
if (BuiltinFile && !File) {
|
||||
RelativePathName = BuiltinPathName;
|
||||
File = BuiltinFile;
|
||||
BuiltinFile = nullptr;
|
||||
}
|
||||
while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) {
|
||||
enum Attribute { Size, ModTime, Unknown };
|
||||
StringRef Str = Tok.getString();
|
||||
SourceLocation Loc = consumeToken();
|
||||
switch (llvm::StringSwitch<Attribute>(Str)
|
||||
.Case("size", Size)
|
||||
.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;
|
||||
|
||||
// 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 (Header.IsUmbrella) {
|
||||
const DirectoryEntry *UmbrellaDir = File->getDir();
|
||||
if (Module *UmbrellaModule = Map.UmbrellaDirs[UmbrellaDir]) {
|
||||
Diags.Report(LeadingLoc, diag::err_mmap_umbrella_clash)
|
||||
<< UmbrellaModule->getFullModuleName();
|
||||
HadError = true;
|
||||
} else {
|
||||
// Record this umbrella header.
|
||||
Map.setUmbrellaHeader(ActiveModule, File, RelativePathName.str());
|
||||
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;
|
||||
}
|
||||
} 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.
|
||||
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);
|
||||
if (Tok.is(MMToken::RBrace))
|
||||
consumeToken();
|
||||
else {
|
||||
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);
|
||||
Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);
|
||||
HadError = true;
|
||||
}
|
||||
}
|
||||
|
||||
Map.addUnresolvedHeader(ActiveModule, std::move(Header));
|
||||
}
|
||||
|
||||
static int compareModuleHeaders(const Module::Header *A,
|
||||
@ -2521,6 +2686,7 @@ bool ModuleMapParser::parseModuleMapFile() {
|
||||
case MMToken::RequiresKeyword:
|
||||
case MMToken::Star:
|
||||
case MMToken::StringLiteral:
|
||||
case MMToken::IntegerLiteral:
|
||||
case MMToken::TextualKeyword:
|
||||
case MMToken::UmbrellaKeyword:
|
||||
case MMToken::UseKeyword:
|
||||
|
@ -689,6 +689,8 @@ Preprocessor::getModuleHeaderToIncludeForDiagnostics(SourceLocation IncLoc,
|
||||
while (!Loc.isInvalid() && !SM.isInMainFile(Loc)) {
|
||||
auto ID = SM.getFileID(SM.getExpansionLoc(Loc));
|
||||
auto *FE = SM.getFileEntryForID(ID);
|
||||
if (!FE)
|
||||
break;
|
||||
|
||||
bool InTextualHeader = false;
|
||||
for (auto Header : HeaderInfo.getModuleMap().findAllModulesForHeader(FE)) {
|
||||
|
@ -1856,24 +1856,31 @@ namespace {
|
||||
// Trait used for the on-disk hash table of header search information.
|
||||
class HeaderFileInfoTrait {
|
||||
ASTWriter &Writer;
|
||||
const HeaderSearch &HS;
|
||||
|
||||
// Keep track of the framework names we've used during serialization.
|
||||
SmallVector<char, 128> FrameworkStringData;
|
||||
llvm::StringMap<unsigned> FrameworkNameOffset;
|
||||
|
||||
public:
|
||||
HeaderFileInfoTrait(ASTWriter &Writer, const HeaderSearch &HS)
|
||||
: Writer(Writer), HS(HS) { }
|
||||
|
||||
HeaderFileInfoTrait(ASTWriter &Writer) : Writer(Writer) {}
|
||||
|
||||
struct key_type {
|
||||
const FileEntry *FE;
|
||||
StringRef Filename;
|
||||
off_t Size;
|
||||
time_t ModTime;
|
||||
};
|
||||
typedef const key_type &key_type_ref;
|
||||
|
||||
using UnresolvedModule =
|
||||
llvm::PointerIntPair<Module *, 2, ModuleMap::ModuleHeaderRole>;
|
||||
|
||||
typedef HeaderFileInfo data_type;
|
||||
struct data_type {
|
||||
const HeaderFileInfo &HFI;
|
||||
ArrayRef<ModuleMap::KnownHeader> KnownHeaders;
|
||||
UnresolvedModule Unresolved;
|
||||
};
|
||||
typedef const data_type &data_type_ref;
|
||||
|
||||
typedef unsigned hash_value_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
|
||||
// match even when symlinking or excess path elements ("foo/../", "../")
|
||||
// change the form of the name. However, complete path is still the key.
|
||||
return llvm::hash_combine(key.FE->getSize(),
|
||||
Writer.getTimestampForOutput(key.FE));
|
||||
return llvm::hash_combine(key.Size, key.ModTime);
|
||||
}
|
||||
|
||||
std::pair<unsigned,unsigned>
|
||||
@ -1892,68 +1898,74 @@ namespace {
|
||||
unsigned KeyLen = key.Filename.size() + 1 + 8 + 8;
|
||||
LE.write<uint16_t>(KeyLen);
|
||||
unsigned DataLen = 1 + 2 + 4 + 4;
|
||||
for (auto ModInfo : HS.getModuleMap().findAllModulesForHeader(key.FE))
|
||||
for (auto ModInfo : Data.KnownHeaders)
|
||||
if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule()))
|
||||
DataLen += 4;
|
||||
if (Data.Unresolved.getPointer())
|
||||
DataLen += 4;
|
||||
LE.write<uint8_t>(DataLen);
|
||||
return std::make_pair(KeyLen, DataLen);
|
||||
}
|
||||
|
||||
|
||||
void EmitKey(raw_ostream& Out, key_type_ref key, unsigned KeyLen) {
|
||||
using namespace llvm::support;
|
||||
endian::Writer<little> LE(Out);
|
||||
LE.write<uint64_t>(key.FE->getSize());
|
||||
LE.write<uint64_t>(key.Size);
|
||||
KeyLen -= 8;
|
||||
LE.write<uint64_t>(Writer.getTimestampForOutput(key.FE));
|
||||
LE.write<uint64_t>(key.ModTime);
|
||||
KeyLen -= 8;
|
||||
Out.write(key.Filename.data(), KeyLen);
|
||||
}
|
||||
|
||||
|
||||
void EmitData(raw_ostream &Out, key_type_ref key,
|
||||
data_type_ref Data, unsigned DataLen) {
|
||||
using namespace llvm::support;
|
||||
endian::Writer<little> LE(Out);
|
||||
uint64_t Start = Out.tell(); (void)Start;
|
||||
|
||||
unsigned char Flags = (Data.isImport << 4)
|
||||
| (Data.isPragmaOnce << 3)
|
||||
| (Data.DirInfo << 1)
|
||||
| Data.IndexHeaderMapHeader;
|
||||
unsigned char Flags = (Data.HFI.isImport << 4)
|
||||
| (Data.HFI.isPragmaOnce << 3)
|
||||
| (Data.HFI.DirInfo << 1)
|
||||
| Data.HFI.IndexHeaderMapHeader;
|
||||
LE.write<uint8_t>(Flags);
|
||||
LE.write<uint16_t>(Data.NumIncludes);
|
||||
LE.write<uint16_t>(Data.HFI.NumIncludes);
|
||||
|
||||
if (!Data.ControllingMacro)
|
||||
LE.write<uint32_t>(Data.ControllingMacroID);
|
||||
if (!Data.HFI.ControllingMacro)
|
||||
LE.write<uint32_t>(Data.HFI.ControllingMacroID);
|
||||
else
|
||||
LE.write<uint32_t>(Writer.getIdentifierRef(Data.ControllingMacro));
|
||||
|
||||
LE.write<uint32_t>(Writer.getIdentifierRef(Data.HFI.ControllingMacro));
|
||||
|
||||
unsigned Offset = 0;
|
||||
if (!Data.Framework.empty()) {
|
||||
if (!Data.HFI.Framework.empty()) {
|
||||
// If this header refers into a framework, save the framework name.
|
||||
llvm::StringMap<unsigned>::iterator Pos
|
||||
= FrameworkNameOffset.find(Data.Framework);
|
||||
= FrameworkNameOffset.find(Data.HFI.Framework);
|
||||
if (Pos == FrameworkNameOffset.end()) {
|
||||
Offset = FrameworkStringData.size() + 1;
|
||||
FrameworkStringData.append(Data.Framework.begin(),
|
||||
Data.Framework.end());
|
||||
FrameworkStringData.append(Data.HFI.Framework.begin(),
|
||||
Data.HFI.Framework.end());
|
||||
FrameworkStringData.push_back(0);
|
||||
|
||||
FrameworkNameOffset[Data.Framework] = Offset;
|
||||
FrameworkNameOffset[Data.HFI.Framework] = Offset;
|
||||
} else
|
||||
Offset = Pos->second;
|
||||
}
|
||||
LE.write<uint32_t>(Offset);
|
||||
|
||||
// FIXME: If the header is excluded, we should write out some
|
||||
// record of that fact.
|
||||
for (auto ModInfo : HS.getModuleMap().findAllModulesForHeader(key.FE)) {
|
||||
if (uint32_t ModID =
|
||||
Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule())) {
|
||||
uint32_t Value = (ModID << 2) | (unsigned)ModInfo.getRole();
|
||||
auto EmitModule = [&](Module *M, ModuleMap::ModuleHeaderRole Role) {
|
||||
if (uint32_t ModID = Writer.getLocalOrImportedSubmoduleID(M)) {
|
||||
uint32_t Value = (ModID << 2) | (unsigned)Role;
|
||||
assert((Value >> 2) == ModID && "overflow in header module info");
|
||||
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");
|
||||
}
|
||||
@ -1968,16 +1980,71 @@ namespace {
|
||||
///
|
||||
/// \param HS The header search structure to save.
|
||||
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;
|
||||
HS.getFileMgr().GetUniqueIDMapping(FilesByUID);
|
||||
|
||||
if (FilesByUID.size() > 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) {
|
||||
const FileEntry *File = FilesByUID[UID];
|
||||
if (!File)
|
||||
@ -2004,11 +2071,16 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {
|
||||
SavedStrings.push_back(Filename.data());
|
||||
}
|
||||
|
||||
HeaderFileInfoTrait::key_type key = { File, Filename };
|
||||
Generator.insert(key, *HFI, GeneratorTrait);
|
||||
HeaderFileInfoTrait::key_type Key = {
|
||||
Filename, File->getSize(), getTimestampForOutput(File)
|
||||
};
|
||||
HeaderFileInfoTrait::data_type Data = {
|
||||
*HFI, HS.getModuleMap().findAllModulesForHeader(File), {}
|
||||
};
|
||||
Generator.insert(Key, Data, GeneratorTrait);
|
||||
++NumHeaderSearchEntries;
|
||||
}
|
||||
|
||||
|
||||
// Create the on-disk hash table in a buffer.
|
||||
SmallString<4096> TableData;
|
||||
uint32_t BucketOffset;
|
||||
|
1
clang/test/Modules/Inputs/header-attribs/bar.h
Normal file
1
clang/test/Modules/Inputs/header-attribs/bar.h
Normal file
@ -0,0 +1 @@
|
||||
extern int b;
|
1
clang/test/Modules/Inputs/header-attribs/baz.h
Normal file
1
clang/test/Modules/Inputs/header-attribs/baz.h
Normal file
@ -0,0 +1 @@
|
||||
extern int c;
|
1
clang/test/Modules/Inputs/header-attribs/foo.h
Normal file
1
clang/test/Modules/Inputs/header-attribs/foo.h
Normal file
@ -0,0 +1 @@
|
||||
extern int a;
|
@ -0,0 +1,5 @@
|
||||
module A {
|
||||
header "foo.h" { size 13 }
|
||||
header "bar.h" { size 1000 }
|
||||
header "baz.h" { mtime 1 }
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
module A {
|
||||
textual header "foo.h" { size 13 }
|
||||
textual header "bar.h" { size 1000 }
|
||||
textual header "baz.h" { mtime 1 }
|
||||
}
|
@ -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: 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
|
||||
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" {}
|
||||
}
|
||||
|
10
clang/test/Modules/header-attribs.cpp
Normal file
10
clang/test/Modules/header-attribs.cpp
Normal 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
|
7
clang/test/Modules/preprocess-missing.modulemap
Normal file
7
clang/test/Modules/preprocess-missing.modulemap
Normal 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"}}
|
@ -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/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
|
||||
// CHECK: # 1 "{{.*}}module.modulemap"
|
||||
// CHECK: module file {
|
||||
// CHECK: header "file.h"
|
||||
// CHECK: header "file2.h"
|
||||
// CHECK: header "file.h" { size
|
||||
// CHECK: header "file2.h" { size
|
||||
// CHECK: }
|
||||
|
||||
// == file.h
|
||||
@ -98,6 +107,8 @@
|
||||
__FILE *a; // expected-error {{declaration of '__FILE' must be imported}}
|
||||
#ifdef REWRITE
|
||||
// expected-note@rewrite.ii:1 {{here}}
|
||||
#elif COPY
|
||||
// expected-note@copy.ii:1 {{here}}
|
||||
#else
|
||||
// expected-note@no-rewrite.ii:1 {{here}}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user