forked from OSchip/llvm-project
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::
|
.. 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.
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
//
|
//
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
extern int b;
|
|
@ -0,0 +1 @@
|
||||||
|
extern int c;
|
|
@ -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: 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" {}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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/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
|
||||||
|
|
Loading…
Reference in New Issue