diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 909dcc5e8e2157615a9e78c6a042d07bcd3bfc90..cc555860bba3675fcc0feb406019c0e061fedb79 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -147,6 +147,8 @@ public: ///< dumps in AST dumps. unsigned ASTDumpLookups : 1; ///< Whether we include lookup table ///< dumps in AST dumps. + unsigned BuildingImplicitModule : 1; ///< Whether we are performing an + ///< implicit module build. CodeCompleteOptions CodeCompleteOpts; @@ -263,6 +265,7 @@ public: FixToTemporaries(false), ARCMTMigrateEmitARCErrors(false), SkipFunctionBodies(false), UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), + BuildingImplicitModule(false), ARCMTAction(ARCMT_None), ObjCMTAction(ObjCMT_None), ProgramAction(frontend::ParseSyntaxOnly) {} diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index e830fdcf8f20ed2bab874f9a3fddd3b0542b8966..7cb91b4de16c2bdd3ee931657582b13339da78cf 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -119,6 +119,12 @@ private: /// \brief The base directory for any relative paths we emit. std::string BaseDirectory; + /// \brief Indicates whether timestamps should be written to the produced + /// module file. This is the case for files implicitly written to the + /// module cache, where we need the timestamps to determine if the module + /// file is up to date, but not otherwise. + bool IncludeTimestamps; + /// \brief Indicates when the AST writing is actively performing /// serialization, rather than just queueing updates. bool WritingAST; @@ -569,11 +575,16 @@ private: public: /// \brief Create a new precompiled header writer that outputs to /// the given bitstream. - ASTWriter(llvm::BitstreamWriter &Stream); + ASTWriter(llvm::BitstreamWriter &Stream, bool IncludeTimestamps = true); ~ASTWriter() override; const LangOptions &getLangOpts() const; + /// \brief Get a timestamp for output into the AST file. The actual timestamp + /// of the specified file may be ignored if we have been instructed to not + /// include timestamps in the output file. + time_t getTimestampForOutput(const FileEntry *E) const; + /// \brief Write a precompiled header for the given semantic analysis. /// /// \param SemaRef a reference to the semantic analysis object that processed @@ -892,7 +903,8 @@ public: PCHGenerator(const Preprocessor &PP, StringRef OutputFile, clang::Module *Module, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer, - bool AllowASTWithErrors = false); + bool AllowASTWithErrors = false, + bool IncludeTimestamps = true); ~PCHGenerator() override; void InitializeSema(Sema &S) override { SemaPtr = &S; } void HandleTranslationUnit(ASTContext &Ctx) override; diff --git a/include/clang/Serialization/Module.h b/include/clang/Serialization/Module.h index 0740cd025ec6e885624313e1c5879162af7eda7a..ee10f9b7e4de54396158401d784e44fe3410ce77 100644 --- a/include/clang/Serialization/Module.h +++ b/include/clang/Serialization/Module.h @@ -152,6 +152,9 @@ public: /// \brief Whether this precompiled header is a relocatable PCH file. bool RelocatablePCH; + /// \brief Whether timestamps are included in this module file. + bool HasTimestamps; + /// \brief The file entry for the module file. const FileEntry *File; diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index c678da982ed6eac107bc153d7926c43b25bfb959..5079353226cb813338e18c64e57e636f6dc3dae4 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -913,6 +913,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance, FrontendOpts.OutputFile = ModuleFileName.str(); FrontendOpts.DisableFree = false; FrontendOpts.GenerateGlobalModuleIndex = false; + FrontendOpts.BuildingImplicitModule = true; FrontendOpts.Inputs.clear(); InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts()); diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 057361811baa63e97cbabe241306b506f307e4e1..782b713d3e2c41bfeb943edd23d85b43b626a442 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -137,7 +137,9 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, auto Buffer = std::make_shared<PCHBuffer>(); std::vector<std::unique_ptr<ASTConsumer>> Consumers; Consumers.push_back(llvm::make_unique<PCHGenerator>( - CI.getPreprocessor(), OutputFile, Module, Sysroot, Buffer)); + CI.getPreprocessor(), OutputFile, Module, Sysroot, Buffer, + /*AllowASTWithErrors*/false, + /*IncludeTimestamps*/+CI.getFrontendOpts().BuildingImplicitModule)); Consumers.push_back( CI.getPCHContainerWriter().CreatePCHContainerGenerator( CI.getDiagnostics(), CI.getHeaderSearchOpts(), diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 547dda73f7327c1e8a8dccef4bfa18711dbf144b..6d41d155c5646ed0d051850d0c5957bd610a0329 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -1500,13 +1500,14 @@ unsigned HeaderFileInfoTrait::ComputeHash(internal_key_ref ikey) { HeaderFileInfoTrait::internal_key_type HeaderFileInfoTrait::GetInternalKey(const FileEntry *FE) { - internal_key_type ikey = { FE->getSize(), FE->getModificationTime(), - FE->getName(), /*Imported*/false }; + internal_key_type ikey = {FE->getSize(), + M.HasTimestamps ? FE->getModificationTime() : 0, + FE->getName(), /*Imported*/ false}; return ikey; } bool HeaderFileInfoTrait::EqualKey(internal_key_ref a, internal_key_ref b) { - if (a.Size != b.Size || a.ModTime != b.ModTime) + if (a.Size != b.Size || (a.ModTime && b.ModTime && a.ModTime != b.ModTime)) return false; if (llvm::sys::path::is_absolute(a.Filename) && @@ -1976,14 +1977,9 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) { // have inconsistent modification times that sometimes // erroneously trigger this error-handling path. // - // This also happens in networked file systems, so disable this - // check if validation is disabled or if we have an explicitly - // built PCM file. - // - // FIXME: Should we also do this for PCH files? They could also - // reasonably get shared across a network during a distributed build. - (StoredTime != File->getModificationTime() && !DisableValidation && - F.Kind != MK_ExplicitModule) + // FIXME: This probably also breaks HeaderFileInfo lookups on Windows. + (StoredTime && StoredTime != File->getModificationTime() && + !DisableValidation) #endif )) { if (Complain) { @@ -2164,7 +2160,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, return VersionMismatch; } - bool hasErrors = Record[5]; + bool hasErrors = Record[6]; if (hasErrors && !DisableValidation && !AllowASTWithCompilerErrors) { Diag(diag::err_pch_with_compiler_errors); return HadErrors; @@ -2175,6 +2171,8 @@ ASTReader::ReadControlBlock(ModuleFile &F, if (F.RelocatablePCH) F.BaseDirectory = isysroot.empty() ? "/" : isysroot; + F.HasTimestamps = Record[5]; + const std::string &CurBranch = getClangFullRepositoryVersion(); StringRef ASTBranch = Blob; if (StringRef(CurBranch) != ASTBranch && !DisableValidation) { diff --git a/lib/Serialization/ASTReaderInternals.h b/lib/Serialization/ASTReaderInternals.h index 5b1c4f4963e417c3501dcdc87de0224ce8e4964a..701cf7300f6fe9163b5047337cc6e579d25be042 100644 --- a/lib/Serialization/ASTReaderInternals.h +++ b/lib/Serialization/ASTReaderInternals.h @@ -226,7 +226,7 @@ public: : Reader(Reader), M(M), HS(HS), FrameworkStrings(FrameworkStrings) { } static hash_value_type ComputeHash(internal_key_ref ikey); - static internal_key_type GetInternalKey(const FileEntry *FE); + internal_key_type GetInternalKey(const FileEntry *FE); bool EqualKey(internal_key_ref a, internal_key_ref b); static std::pair<unsigned, unsigned> diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 566c7d7c9f2e866b0d8235b67c075fe2241e2f08..b774abe17e1b3500e6820e50013e2cbdc91b4e2c 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1148,6 +1148,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang maj. MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang min. MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Relocatable + MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Timestamps MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Errors MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // SVN branch/tag unsigned MetadataAbbrevCode = Stream.EmitAbbrev(MetadataAbbrev); @@ -1159,6 +1160,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, assert((!WritingModule || isysroot.empty()) && "writing module as a relocatable PCH?"); Record.push_back(!isysroot.empty()); + Record.push_back(IncludeTimestamps); Record.push_back(ASTHasCompilerErrors); Stream.EmitRecordWithBlob(MetadataAbbrevCode, Record, getClangFullRepositoryVersion()); @@ -1248,7 +1250,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, Record.push_back((unsigned)M->Kind); // FIXME: Stable encoding AddSourceLocation(M->ImportLoc, Record); Record.push_back(M->File->getSize()); - Record.push_back(M->File->getModificationTime()); + Record.push_back(getTimestampForOutput(M->File)); Record.push_back(M->Signature); AddPath(M->FileName, Record); } @@ -1512,7 +1514,7 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr, // Emit size/modification time for this file. Record.push_back(Entry.File->getSize()); - Record.push_back(Entry.File->getModificationTime()); + Record.push_back(getTimestampForOutput(Entry.File)); // Whether this file was overridden. Record.push_back(Entry.BufferOverridden); @@ -1624,15 +1626,12 @@ namespace { typedef unsigned hash_value_type; typedef unsigned offset_type; - static hash_value_type ComputeHash(key_type_ref key) { + hash_value_type ComputeHash(key_type_ref key) { // 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. - // - // FIXME: Using the mtime here will cause problems for explicit module - // imports. return llvm::hash_combine(key.FE->getSize(), - key.FE->getModificationTime()); + Writer.getTimestampForOutput(key.FE)); } std::pair<unsigned,unsigned> @@ -1653,7 +1652,7 @@ namespace { endian::Writer<little> LE(Out); LE.write<uint64_t>(key.FE->getSize()); KeyLen -= 8; - LE.write<uint64_t>(key.FE->getModificationTime()); + LE.write<uint64_t>(Writer.getTimestampForOutput(key.FE)); KeyLen -= 8; Out.write(key.Filename, KeyLen); } @@ -4058,22 +4057,21 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) { SelectorOffsets[ID - FirstSelectorID] = Offset; } -ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream) +ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream, bool IncludeTimestamps) : Stream(Stream), Context(nullptr), PP(nullptr), Chain(nullptr), - WritingModule(nullptr), WritingAST(false), - DoneWritingDeclsAndTypes(false), ASTHasCompilerErrors(false), - FirstDeclID(NUM_PREDEF_DECL_IDS), NextDeclID(FirstDeclID), - FirstTypeID(NUM_PREDEF_TYPE_IDS), NextTypeID(FirstTypeID), - FirstIdentID(NUM_PREDEF_IDENT_IDS), NextIdentID(FirstIdentID), - FirstMacroID(NUM_PREDEF_MACRO_IDS), NextMacroID(FirstMacroID), - FirstSubmoduleID(NUM_PREDEF_SUBMODULE_IDS), + WritingModule(nullptr), IncludeTimestamps(IncludeTimestamps), + WritingAST(false), DoneWritingDeclsAndTypes(false), + ASTHasCompilerErrors(false), FirstDeclID(NUM_PREDEF_DECL_IDS), + NextDeclID(FirstDeclID), FirstTypeID(NUM_PREDEF_TYPE_IDS), + NextTypeID(FirstTypeID), FirstIdentID(NUM_PREDEF_IDENT_IDS), + NextIdentID(FirstIdentID), FirstMacroID(NUM_PREDEF_MACRO_IDS), + NextMacroID(FirstMacroID), FirstSubmoduleID(NUM_PREDEF_SUBMODULE_IDS), NextSubmoduleID(FirstSubmoduleID), FirstSelectorID(NUM_PREDEF_SELECTOR_IDS), NextSelectorID(FirstSelectorID), CollectedStmts(&StmtsToEmit), NumStatements(0), NumMacros(0), NumLexicalDeclContexts(0), NumVisibleDeclContexts(0), NextCXXBaseSpecifiersID(1), NextCXXCtorInitializersID(1), - TypeExtQualAbbrev(0), - TypeFunctionProtoAbbrev(0), DeclParmVarAbbrev(0), + TypeExtQualAbbrev(0), TypeFunctionProtoAbbrev(0), DeclParmVarAbbrev(0), DeclContextLexicalAbbrev(0), DeclContextVisibleLookupAbbrev(0), UpdateVisibleAbbrev(0), DeclRecordAbbrev(0), DeclTypedefAbbrev(0), DeclVarAbbrev(0), DeclFieldAbbrev(0), DeclEnumAbbrev(0), @@ -4090,6 +4088,10 @@ const LangOptions &ASTWriter::getLangOpts() const { return Context->getLangOpts(); } +time_t ASTWriter::getTimestampForOutput(const FileEntry *E) const { + return IncludeTimestamps ? E->getModificationTime() : 0; +} + void ASTWriter::WriteAST(Sema &SemaRef, const std::string &OutputFile, Module *WritingModule, StringRef isysroot, diff --git a/lib/Serialization/GeneratePCH.cpp b/lib/Serialization/GeneratePCH.cpp index c461fe936ee4356740d6596b5c294bad01f5e46f..08660bc9675282737afdd8a652e0728de3b9ceb6 100644 --- a/lib/Serialization/GeneratePCH.cpp +++ b/lib/Serialization/GeneratePCH.cpp @@ -26,9 +26,10 @@ using namespace clang; PCHGenerator::PCHGenerator(const Preprocessor &PP, StringRef OutputFile, clang::Module *Module, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer, - bool AllowASTWithErrors) + bool AllowASTWithErrors, bool IncludeTimestamps) : PP(PP), OutputFile(OutputFile), Module(Module), isysroot(isysroot.str()), - SemaPtr(nullptr), Buffer(Buffer), Stream(Buffer->Data), Writer(Stream), + SemaPtr(nullptr), Buffer(Buffer), Stream(Buffer->Data), + Writer(Stream, IncludeTimestamps), AllowASTWithErrors(AllowASTWithErrors) { Buffer->IsComplete = false; } diff --git a/test/Modules/explicit-build-missing-files.cpp b/test/Modules/explicit-build-missing-files.cpp index ff79a116e6b41d522fcda4f4ba262e9591a65d1a..b2730e4f22725e413b3ed4492ee24af9bc07d2e2 100644 --- a/test/Modules/explicit-build-missing-files.cpp +++ b/test/Modules/explicit-build-missing-files.cpp @@ -21,6 +21,10 @@ // RUN: rm %t/other.modulemap // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s // RUN: not %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -DERRORS 2>&1 | FileCheck %s +// RUN: sleep 1 +// RUN: touch %t/a.h +// RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s +// RUN: not %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -DERRORS 2>&1 | FileCheck %s // RUN: rm %t/b.h // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s // RUN: not %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -DERRORS 2>&1 | FileCheck %s --check-prefix=MISSING-B diff --git a/test/Modules/module-map-path-hash.cpp b/test/Modules/module-map-path-hash.cpp index 620e428a2f0e55a69bcb6d344fbfcf3219ad5a1b..ccfa96ebd259a558d6962b2a5c1f7fb1f9bfd111 100644 --- a/test/Modules/module-map-path-hash.cpp +++ b/test/Modules/module-map-path-hash.cpp @@ -1,4 +1,4 @@ -// rm -rf %t +// RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -x c++ -Rmodule-build -I%S/Inputs/module-map-path-hash -fmodules-cache-path=%t -fsyntax-only %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -x c++ -Rmodule-build -I%S/Inputs//module-map-path-hash -fmodules-cache-path=%t -fsyntax-only %s 2>&1 | FileCheck -allow-empty %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -x c++ -Rmodule-build -I%S/Inputs/./module-map-path-hash -fmodules-cache-path=%t -fsyntax-only %s 2>&1 | FileCheck -allow-empty %s