Browse Source

[modules] When explicitly building a module file, don't include timestamps in
the produced pcm file for stable file creation across distributed build
systems.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@245199 91177308-0d34-0410-b5e6-96231b3b80d8

Richard Smith 10 years ago
parent
commit
c9d67f175e

+ 3 - 0
include/clang/Frontend/FrontendOptions.h

@@ -147,6 +147,8 @@ public:
                                            ///< dumps in AST dumps.
                                            ///< dumps in AST dumps.
   unsigned ASTDumpLookups : 1;             ///< Whether we include lookup table
   unsigned ASTDumpLookups : 1;             ///< Whether we include lookup table
                                            ///< dumps in AST dumps.
                                            ///< dumps in AST dumps.
+  unsigned BuildingImplicitModule : 1;     ///< Whether we are performing an
+                                           ///< implicit module build.
 
 
   CodeCompleteOptions CodeCompleteOpts;
   CodeCompleteOptions CodeCompleteOpts;
 
 
@@ -263,6 +265,7 @@ public:
     FixToTemporaries(false), ARCMTMigrateEmitARCErrors(false),
     FixToTemporaries(false), ARCMTMigrateEmitARCErrors(false),
     SkipFunctionBodies(false), UseGlobalModuleIndex(true),
     SkipFunctionBodies(false), UseGlobalModuleIndex(true),
     GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false),
     GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false),
+    BuildingImplicitModule(false),
     ARCMTAction(ARCMT_None), ObjCMTAction(ObjCMT_None),
     ARCMTAction(ARCMT_None), ObjCMTAction(ObjCMT_None),
     ProgramAction(frontend::ParseSyntaxOnly)
     ProgramAction(frontend::ParseSyntaxOnly)
   {}
   {}

+ 14 - 2
include/clang/Serialization/ASTWriter.h

@@ -119,6 +119,12 @@ private:
   /// \brief The base directory for any relative paths we emit.
   /// \brief The base directory for any relative paths we emit.
   std::string BaseDirectory;
   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
   /// \brief Indicates when the AST writing is actively performing
   /// serialization, rather than just queueing updates.
   /// serialization, rather than just queueing updates.
   bool WritingAST;
   bool WritingAST;
@@ -569,11 +575,16 @@ private:
 public:
 public:
   /// \brief Create a new precompiled header writer that outputs to
   /// \brief Create a new precompiled header writer that outputs to
   /// the given bitstream.
   /// the given bitstream.
-  ASTWriter(llvm::BitstreamWriter &Stream);
+  ASTWriter(llvm::BitstreamWriter &Stream, bool IncludeTimestamps = true);
   ~ASTWriter() override;
   ~ASTWriter() override;
 
 
   const LangOptions &getLangOpts() const;
   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.
   /// \brief Write a precompiled header for the given semantic analysis.
   ///
   ///
   /// \param SemaRef a reference to the semantic analysis object that processed
   /// \param SemaRef a reference to the semantic analysis object that processed
@@ -892,7 +903,8 @@ public:
   PCHGenerator(const Preprocessor &PP, StringRef OutputFile,
   PCHGenerator(const Preprocessor &PP, StringRef OutputFile,
                clang::Module *Module, StringRef isysroot,
                clang::Module *Module, StringRef isysroot,
                std::shared_ptr<PCHBuffer> Buffer,
                std::shared_ptr<PCHBuffer> Buffer,
-               bool AllowASTWithErrors = false);
+               bool AllowASTWithErrors = false,
+               bool IncludeTimestamps = true);
   ~PCHGenerator() override;
   ~PCHGenerator() override;
   void InitializeSema(Sema &S) override { SemaPtr = &S; }
   void InitializeSema(Sema &S) override { SemaPtr = &S; }
   void HandleTranslationUnit(ASTContext &Ctx) override;
   void HandleTranslationUnit(ASTContext &Ctx) override;

+ 3 - 0
include/clang/Serialization/Module.h

@@ -152,6 +152,9 @@ public:
   /// \brief Whether this precompiled header is a relocatable PCH file.
   /// \brief Whether this precompiled header is a relocatable PCH file.
   bool RelocatablePCH;
   bool RelocatablePCH;
 
 
+  /// \brief Whether timestamps are included in this module file.
+  bool HasTimestamps;
+
   /// \brief The file entry for the module file.
   /// \brief The file entry for the module file.
   const FileEntry *File;
   const FileEntry *File;
 
 

+ 1 - 0
lib/Frontend/CompilerInstance.cpp

@@ -913,6 +913,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
   FrontendOpts.OutputFile = ModuleFileName.str();
   FrontendOpts.OutputFile = ModuleFileName.str();
   FrontendOpts.DisableFree = false;
   FrontendOpts.DisableFree = false;
   FrontendOpts.GenerateGlobalModuleIndex = false;
   FrontendOpts.GenerateGlobalModuleIndex = false;
+  FrontendOpts.BuildingImplicitModule = true;
   FrontendOpts.Inputs.clear();
   FrontendOpts.Inputs.clear();
   InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts());
   InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts());
 
 

+ 3 - 1
lib/Frontend/FrontendActions.cpp

@@ -137,7 +137,9 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
   auto Buffer = std::make_shared<PCHBuffer>();
   auto Buffer = std::make_shared<PCHBuffer>();
   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
   Consumers.push_back(llvm::make_unique<PCHGenerator>(
   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(
   Consumers.push_back(
       CI.getPCHContainerWriter().CreatePCHContainerGenerator(
       CI.getPCHContainerWriter().CreatePCHContainerGenerator(
           CI.getDiagnostics(), CI.getHeaderSearchOpts(),
           CI.getDiagnostics(), CI.getHeaderSearchOpts(),

+ 10 - 12
lib/Serialization/ASTReader.cpp

@@ -1500,13 +1500,14 @@ unsigned HeaderFileInfoTrait::ComputeHash(internal_key_ref ikey) {
 
 
 HeaderFileInfoTrait::internal_key_type 
 HeaderFileInfoTrait::internal_key_type 
 HeaderFileInfoTrait::GetInternalKey(const FileEntry *FE) {
 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;
   return ikey;
 }
 }
     
     
 bool HeaderFileInfoTrait::EqualKey(internal_key_ref a, internal_key_ref b) {
 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;
     return false;
 
 
   if (llvm::sys::path::is_absolute(a.Filename) &&
   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
        // have inconsistent modification times that sometimes
        // erroneously trigger this error-handling path.
        // 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
 #endif
        )) {
        )) {
     if (Complain) {
     if (Complain) {
@@ -2164,7 +2160,7 @@ ASTReader::ReadControlBlock(ModuleFile &F,
         return VersionMismatch;
         return VersionMismatch;
       }
       }
 
 
-      bool hasErrors = Record[5];
+      bool hasErrors = Record[6];
       if (hasErrors && !DisableValidation && !AllowASTWithCompilerErrors) {
       if (hasErrors && !DisableValidation && !AllowASTWithCompilerErrors) {
         Diag(diag::err_pch_with_compiler_errors);
         Diag(diag::err_pch_with_compiler_errors);
         return HadErrors;
         return HadErrors;
@@ -2175,6 +2171,8 @@ ASTReader::ReadControlBlock(ModuleFile &F,
       if (F.RelocatablePCH)
       if (F.RelocatablePCH)
         F.BaseDirectory = isysroot.empty() ? "/" : isysroot;
         F.BaseDirectory = isysroot.empty() ? "/" : isysroot;
 
 
+      F.HasTimestamps = Record[5];
+
       const std::string &CurBranch = getClangFullRepositoryVersion();
       const std::string &CurBranch = getClangFullRepositoryVersion();
       StringRef ASTBranch = Blob;
       StringRef ASTBranch = Blob;
       if (StringRef(CurBranch) != ASTBranch && !DisableValidation) {
       if (StringRef(CurBranch) != ASTBranch && !DisableValidation) {

+ 1 - 1
lib/Serialization/ASTReaderInternals.h

@@ -226,7 +226,7 @@ public:
   : Reader(Reader), M(M), HS(HS), FrameworkStrings(FrameworkStrings) { }
   : Reader(Reader), M(M), HS(HS), FrameworkStrings(FrameworkStrings) { }
   
   
   static hash_value_type ComputeHash(internal_key_ref ikey);
   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);
   bool EqualKey(internal_key_ref a, internal_key_ref b);
   
   
   static std::pair<unsigned, unsigned>
   static std::pair<unsigned, unsigned>

+ 20 - 18
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 maj.
   MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang min.
   MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang min.
   MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Relocatable
   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::Fixed, 1)); // Errors
   MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // SVN branch/tag
   MetadataAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // SVN branch/tag
   unsigned MetadataAbbrevCode = Stream.EmitAbbrev(MetadataAbbrev);
   unsigned MetadataAbbrevCode = Stream.EmitAbbrev(MetadataAbbrev);
@@ -1159,6 +1160,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context,
   assert((!WritingModule || isysroot.empty()) &&
   assert((!WritingModule || isysroot.empty()) &&
          "writing module as a relocatable PCH?");
          "writing module as a relocatable PCH?");
   Record.push_back(!isysroot.empty());
   Record.push_back(!isysroot.empty());
+  Record.push_back(IncludeTimestamps);
   Record.push_back(ASTHasCompilerErrors);
   Record.push_back(ASTHasCompilerErrors);
   Stream.EmitRecordWithBlob(MetadataAbbrevCode, Record,
   Stream.EmitRecordWithBlob(MetadataAbbrevCode, Record,
                             getClangFullRepositoryVersion());
                             getClangFullRepositoryVersion());
@@ -1248,7 +1250,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context,
       Record.push_back((unsigned)M->Kind); // FIXME: Stable encoding
       Record.push_back((unsigned)M->Kind); // FIXME: Stable encoding
       AddSourceLocation(M->ImportLoc, Record);
       AddSourceLocation(M->ImportLoc, Record);
       Record.push_back(M->File->getSize());
       Record.push_back(M->File->getSize());
-      Record.push_back(M->File->getModificationTime());
+      Record.push_back(getTimestampForOutput(M->File));
       Record.push_back(M->Signature);
       Record.push_back(M->Signature);
       AddPath(M->FileName, Record);
       AddPath(M->FileName, Record);
     }
     }
@@ -1512,7 +1514,7 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
 
 
     // Emit size/modification time for this file.
     // Emit size/modification time for this file.
     Record.push_back(Entry.File->getSize());
     Record.push_back(Entry.File->getSize());
-    Record.push_back(Entry.File->getModificationTime());
+    Record.push_back(getTimestampForOutput(Entry.File));
 
 
     // Whether this file was overridden.
     // Whether this file was overridden.
     Record.push_back(Entry.BufferOverridden);
     Record.push_back(Entry.BufferOverridden);
@@ -1624,15 +1626,12 @@ namespace {
     typedef unsigned hash_value_type;
     typedef unsigned hash_value_type;
     typedef unsigned offset_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
       // 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.
-      //
-      // FIXME: Using the mtime here will cause problems for explicit module
-      // imports.
       return llvm::hash_combine(key.FE->getSize(),
       return llvm::hash_combine(key.FE->getSize(),
-                                key.FE->getModificationTime());
+                                Writer.getTimestampForOutput(key.FE));
     }
     }
     
     
     std::pair<unsigned,unsigned>
     std::pair<unsigned,unsigned>
@@ -1653,7 +1652,7 @@ namespace {
       endian::Writer<little> LE(Out);
       endian::Writer<little> LE(Out);
       LE.write<uint64_t>(key.FE->getSize());
       LE.write<uint64_t>(key.FE->getSize());
       KeyLen -= 8;
       KeyLen -= 8;
-      LE.write<uint64_t>(key.FE->getModificationTime());
+      LE.write<uint64_t>(Writer.getTimestampForOutput(key.FE));
       KeyLen -= 8;
       KeyLen -= 8;
       Out.write(key.Filename, KeyLen);
       Out.write(key.Filename, KeyLen);
     }
     }
@@ -4058,22 +4057,21 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) {
   SelectorOffsets[ID - FirstSelectorID] = 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),
     : 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),
       NextSubmoduleID(FirstSubmoduleID),
       FirstSelectorID(NUM_PREDEF_SELECTOR_IDS), NextSelectorID(FirstSelectorID),
       FirstSelectorID(NUM_PREDEF_SELECTOR_IDS), NextSelectorID(FirstSelectorID),
       CollectedStmts(&StmtsToEmit), NumStatements(0), NumMacros(0),
       CollectedStmts(&StmtsToEmit), NumStatements(0), NumMacros(0),
       NumLexicalDeclContexts(0), NumVisibleDeclContexts(0),
       NumLexicalDeclContexts(0), NumVisibleDeclContexts(0),
       NextCXXBaseSpecifiersID(1), NextCXXCtorInitializersID(1),
       NextCXXBaseSpecifiersID(1), NextCXXCtorInitializersID(1),
-      TypeExtQualAbbrev(0),
-      TypeFunctionProtoAbbrev(0), DeclParmVarAbbrev(0),
+      TypeExtQualAbbrev(0), TypeFunctionProtoAbbrev(0), DeclParmVarAbbrev(0),
       DeclContextLexicalAbbrev(0), DeclContextVisibleLookupAbbrev(0),
       DeclContextLexicalAbbrev(0), DeclContextVisibleLookupAbbrev(0),
       UpdateVisibleAbbrev(0), DeclRecordAbbrev(0), DeclTypedefAbbrev(0),
       UpdateVisibleAbbrev(0), DeclRecordAbbrev(0), DeclTypedefAbbrev(0),
       DeclVarAbbrev(0), DeclFieldAbbrev(0), DeclEnumAbbrev(0),
       DeclVarAbbrev(0), DeclFieldAbbrev(0), DeclEnumAbbrev(0),
@@ -4090,6 +4088,10 @@ const LangOptions &ASTWriter::getLangOpts() const {
   return Context->getLangOpts();
   return Context->getLangOpts();
 }
 }
 
 
+time_t ASTWriter::getTimestampForOutput(const FileEntry *E) const {
+  return IncludeTimestamps ? E->getModificationTime() : 0;
+}
+
 void ASTWriter::WriteAST(Sema &SemaRef,
 void ASTWriter::WriteAST(Sema &SemaRef,
                          const std::string &OutputFile,
                          const std::string &OutputFile,
                          Module *WritingModule, StringRef isysroot,
                          Module *WritingModule, StringRef isysroot,

+ 3 - 2
lib/Serialization/GeneratePCH.cpp

@@ -26,9 +26,10 @@ using namespace clang;
 PCHGenerator::PCHGenerator(const Preprocessor &PP, StringRef OutputFile,
 PCHGenerator::PCHGenerator(const Preprocessor &PP, StringRef OutputFile,
                            clang::Module *Module, StringRef isysroot,
                            clang::Module *Module, StringRef isysroot,
                            std::shared_ptr<PCHBuffer> Buffer,
                            std::shared_ptr<PCHBuffer> Buffer,
-                           bool AllowASTWithErrors)
+                           bool AllowASTWithErrors, bool IncludeTimestamps)
     : PP(PP), OutputFile(OutputFile), Module(Module), isysroot(isysroot.str()),
     : 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) {
       AllowASTWithErrors(AllowASTWithErrors) {
   Buffer->IsComplete = false;
   Buffer->IsComplete = false;
 }
 }

+ 4 - 0
test/Modules/explicit-build-missing-files.cpp

@@ -21,6 +21,10 @@
 // RUN: rm %t/other.modulemap
 // RUN: rm %t/other.modulemap
 // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s
 // 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: 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: rm %t/b.h
 // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s
 // 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
 // RUN: not %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -DERRORS 2>&1 | FileCheck %s --check-prefix=MISSING-B

+ 1 - 1
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
 // 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
 // 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