diff options
-rw-r--r-- | clang/include/clang-c/Index.h | 2 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticDriverKinds.td | 4 | ||||
-rw-r--r-- | clang/include/clang/Driver/Options.td | 7 | ||||
-rw-r--r-- | clang/include/clang/Frontend/Utils.h | 11 | ||||
-rw-r--r-- | clang/include/clang/Lex/HeaderSearchOptions.h | 15 | ||||
-rw-r--r-- | clang/include/clang/Serialization/Module.h | 10 | ||||
-rw-r--r-- | clang/lib/Driver/Tools.cpp | 10 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInstance.cpp | 25 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInvocation.cpp | 26 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 49 | ||||
-rw-r--r-- | clang/lib/Serialization/ModuleManager.cpp | 10 | ||||
-rw-r--r-- | clang/test/Driver/modules.m | 10 | ||||
-rw-r--r-- | clang/tools/c-index-test/c-index-test.c | 10 | ||||
-rw-r--r-- | clang/tools/libclang/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/tools/libclang/libclang.exports | 1 |
15 files changed, 166 insertions, 25 deletions
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index d98c1e66fdd..0f152169002 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -30,7 +30,7 @@ * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable. */ #define CINDEX_VERSION_MAJOR 0 -#define CINDEX_VERSION_MINOR 21 +#define CINDEX_VERSION_MINOR 22 #define CINDEX_VERSION_ENCODE(major, minor) ( \ ((major) * 10000) \ diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index dbc5ce994c6..cc7d8969f37 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -166,4 +166,8 @@ def err_analyzer_config_no_value : Error< "analyzer-config option '%0' has a key but no value">; def err_analyzer_config_multiple_values : Error< "analyzer-config option '%0' should contain only one '='">; + +def err_drv_modules_validate_once_requires_timestamp : Error< + "option '-fmodules-validate-once-per-build-session' requires " + "'-fbuild-session-timestamp=<seconds since Epoch>'">; } diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 52cd9efdd42..ed0fc0315ea 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -581,6 +581,13 @@ def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group<i def fmodules_prune_after : Joined<["-"], "fmodules-prune-after=">, Group<i_Group>, Flags<[CC1Option]>, MetaVarName<"<seconds>">, HelpText<"Specify the interval (in seconds) after which a module file will be considered unused">; +def fbuild_session_timestamp : Joined<["-"], "fbuild-session-timestamp=">, + Group<i_Group>, Flags<[CC1Option]>, MetaVarName<"<time since Epoch in seconds>">, + HelpText<"Time when the current build session started">; +def fmodules_validate_once_per_build_session : Flag<["-"], "fmodules-validate-once-per-build-session">, + Group<i_Group>, Flags<[CC1Option]>, + HelpText<"Don't verify input files for the modules if the module has been " + "successfully validate or loaded during this build session">; def fmodules : Flag <["-"], "fmodules">, Group<f_Group>, Flags<[DriverOption, CC1Option]>, HelpText<"Enable the 'modules' language feature">; diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index db52d5cf0dd..471d9ac82b0 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -123,6 +123,17 @@ inline int getLastArgIntValue(const llvm::opt::ArgList &Args, return getLastArgIntValue(Args, Id, Default, &Diags); } +uint64_t getLastArgUInt64Value(const llvm::opt::ArgList &Args, + llvm::opt::OptSpecifier Id, uint64_t Default, + DiagnosticsEngine *Diags = 0); + +inline uint64_t getLastArgUInt64Value(const llvm::opt::ArgList &Args, + llvm::opt::OptSpecifier Id, + uint64_t Default, + DiagnosticsEngine &Diags) { + return getLastArgUInt64Value(Args, Id, Default, &Diags); +} + // When Clang->getFrontendOpts().DisableFree is set we don't delete some of the // global objects, but we don't want LeakDetectors to complain, so we bury them // in a globally visible array. diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index 0b21c0dd349..b471b8cafe4 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -116,6 +116,12 @@ public: /// regenerated often. unsigned ModuleCachePruneAfter; + /// \brief The time in seconds when the build session started. + /// + /// This time is used by other optimizations in header search and module + /// loading. + uint64_t BuildSessionTimestamp; + /// \brief The set of macro names that should be ignored for the purposes /// of computing the module hash. llvm::SetVector<std::string> ModulesIgnoreMacros; @@ -138,14 +144,21 @@ public: /// Whether header search information should be output as for -v. unsigned Verbose : 1; + /// \brief If true, skip verifying input files used by modules if the + /// module was already verified during this build session (see + /// \c BuildSessionTimestamp). + unsigned ModulesValidateOncePerBuildSession : 1; + public: HeaderSearchOptions(StringRef _Sysroot = "/") : Sysroot(_Sysroot), DisableModuleHash(0), ModuleMaps(0), ModuleCachePruneInterval(7*24*60*60), ModuleCachePruneAfter(31*24*60*60), + BuildSessionTimestamp(0), UseBuiltinIncludes(true), UseStandardSystemIncludes(true), UseStandardCXXIncludes(true), - UseLibcxx(false), Verbose(false) {} + UseLibcxx(false), Verbose(false), + ModulesValidateOncePerBuildSession(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. void AddPath(StringRef Path, frontend::IncludeDirGroup Group, diff --git a/clang/include/clang/Serialization/Module.h b/clang/include/clang/Serialization/Module.h index f4c07553d6b..821c9cdfb51 100644 --- a/clang/include/clang/Serialization/Module.h +++ b/clang/include/clang/Serialization/Module.h @@ -115,6 +115,10 @@ public: /// \brief The file name of the module file. std::string FileName; + std::string getTimestampFilename() const { + return FileName + ".timestamp"; + } + /// \brief The original source file name that was used to build the /// primary AST file, which may have been modified for /// relocatable-pch support. @@ -185,6 +189,12 @@ public: /// \brief The input files that have been loaded from this AST file. std::vector<InputFile> InputFilesLoaded; + /// \brief If non-zero, specifies the time when we last validated input + /// files. Zero means we never validated them. + /// + /// The time is specified in seconds since the start of the Epoch. + uint64_t InputFilesValidationTimestamp; + // === Source Locations === /// \brief Cursor used to read source location entries. diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 9e71c31c623..73437f68f35 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -3289,6 +3289,16 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fmodules_prune_interval); Args.AddLastArg(CmdArgs, options::OPT_fmodules_prune_after); + Args.AddLastArg(CmdArgs, options::OPT_fbuild_session_timestamp); + + if (Args.getLastArg(options::OPT_fmodules_validate_once_per_build_session)) { + if (!Args.getLastArg(options::OPT_fbuild_session_timestamp)) + D.Diag(diag::err_drv_modules_validate_once_requires_timestamp); + + Args.AddLastArg(CmdArgs, + options::OPT_fmodules_validate_once_per_build_session); + } + // -faccess-control is default. if (Args.hasFlag(options::OPT_fno_access_control, options::OPT_faccess_control, diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index d1211801253..61fe26c9222 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1057,41 +1057,38 @@ static void pruneModuleCache(const HeaderSearchOptions &HSOpts) { continue; // Walk all of the files within this directory. - bool RemovedAllFiles = true; for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd; File != FileEnd && !EC; File.increment(EC)) { // We only care about module and global module index files. - if (llvm::sys::path::extension(File->path()) != ".pcm" && - llvm::sys::path::filename(File->path()) != "modules.idx") { - RemovedAllFiles = false; + StringRef Extension = llvm::sys::path::extension(File->path()); + if (Extension != ".pcm" && Extension != ".timestamp" && + llvm::sys::path::filename(File->path()) != "modules.idx") continue; - } // Look at this file. If we can't stat it, there's nothing interesting // there. - if (::stat(File->path().c_str(), &StatBuf)) { - RemovedAllFiles = false; + if (::stat(File->path().c_str(), &StatBuf)) continue; - } // If the file has been used recently enough, leave it there. time_t FileAccessTime = StatBuf.st_atime; if (CurrentTime - FileAccessTime <= time_t(HSOpts.ModuleCachePruneAfter)) { - RemovedAllFiles = false; continue; } // Remove the file. - bool Existed; - if (llvm::sys::fs::remove(File->path(), Existed) || !Existed) { - RemovedAllFiles = false; - } + llvm::sys::fs::remove(File->path()); + + // Remove the timestamp file. + std::string TimpestampFilename = File->path() + ".timestamp"; + llvm::sys::fs::remove(TimpestampFilename); } // If we removed all of the files in the directory, remove the directory // itself. - if (RemovedAllFiles) + if (llvm::sys::fs::directory_iterator(Dir->path(), EC) == + llvm::sys::fs::directory_iterator() && !EC) llvm::sys::fs::remove(Dir->path()); } } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5320415ecb4..5b26df037f6 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -924,6 +924,10 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60); Opts.ModuleCachePruneAfter = getLastArgIntValue(Args, OPT_fmodules_prune_after, 31 * 24 * 60 * 60); + Opts.ModulesValidateOncePerBuildSession = + Args.hasArg(OPT_fmodules_validate_once_per_build_session); + Opts.BuildSessionTimestamp = + getLastArgUInt64Value(Args, OPT_fbuild_session_timestamp, 0); for (arg_iterator it = Args.filtered_begin(OPT_fmodules_ignore_macro), ie = Args.filtered_end(); it != ie; ++it) { @@ -1837,10 +1841,11 @@ std::string CompilerInvocation::getModuleHash() const { namespace clang { -// Declared in clang/Frontend/Utils.h. -int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default, - DiagnosticsEngine *Diags) { - int Res = Default; +template<typename IntTy> +static IntTy getLastArgIntValueImpl(const ArgList &Args, OptSpecifier Id, + IntTy Default, + DiagnosticsEngine *Diags) { + IntTy Res = Default; if (Arg *A = Args.getLastArg(Id)) { if (StringRef(A->getValue()).getAsInteger(10, Res)) { if (Diags) @@ -1851,6 +1856,19 @@ int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default, return Res; } + +// Declared in clang/Frontend/Utils.h. +int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default, + DiagnosticsEngine *Diags) { + return getLastArgIntValueImpl<int>(Args, Id, Default, Diags); +} + +uint64_t getLastArgUInt64Value(const ArgList &Args, OptSpecifier Id, + uint64_t Default, + DiagnosticsEngine *Diags) { + return getLastArgIntValueImpl<uint64_t>(Args, Id, Default, Diags); +} + void BuryPointer(const void *Ptr) { // This function may be called only a small fixed amount of times per each // invocation, otherwise we do actually have a leak which we want to report. diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 674aefeb1c5..fb1d7edcbad 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -49,6 +49,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" #include <algorithm> #include <cstdio> @@ -1827,16 +1828,28 @@ ASTReader::ReadControlBlock(ModuleFile &F, case llvm::BitstreamEntry::Error: Error("malformed block record in AST file"); return Failure; - case llvm::BitstreamEntry::EndBlock: - // Validate all of the non-system input files. - if (!DisableValidation) { + case llvm::BitstreamEntry::EndBlock: { + // Validate input files. + const HeaderSearchOptions &HSOpts = + PP.getHeaderSearchInfo().getHeaderSearchOpts(); + if (!DisableValidation && + (!HSOpts.ModulesValidateOncePerBuildSession || + F.InputFilesValidationTimestamp <= HSOpts.BuildSessionTimestamp)) { bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0; // All user input files reside at the index range [0, Record[1]), and // system input files reside at [Record[1], Record[0]). // Record is the one from INPUT_FILE_OFFSETS. + // + // If we are reading a module, we will create a verification timestamp, + // so we verify all input files. Otherwise, verify only user input + // files. unsigned NumInputs = Record[0]; unsigned NumUserInputs = Record[1]; - unsigned N = ValidateSystemInputs ? NumInputs : NumUserInputs; + unsigned N = ValidateSystemInputs || + (HSOpts.ModulesValidateOncePerBuildSession && + F.Kind == MK_Module) + ? NumInputs + : NumUserInputs; for (unsigned I = 0; I < N; ++I) { InputFile IF = getInputFile(F, I+1, Complain); if (!IF.getFile() || IF.isOutOfDate()) @@ -1844,7 +1857,8 @@ ASTReader::ReadControlBlock(ModuleFile &F, } } return Success; - + } + case llvm::BitstreamEntry::SubBlock: switch (Entry.ID) { case INPUT_FILES_BLOCK_ID: @@ -2922,6 +2936,16 @@ bool ASTReader::isGlobalIndexUnavailable() const { !hasGlobalIndex() && TriedLoadingGlobalIndex; } +static void updateModuleTimestamp(ModuleFile &MF) { + // Overwrite the timestamp file contents so that file's mtime changes. + std::string TimestampFilename = MF.getTimestampFilename(); + std::string ErrorInfo; + llvm::raw_fd_ostream OS(TimestampFilename.c_str(), ErrorInfo); + if (!ErrorInfo.empty()) + return; + OS << "Timestamp file\n"; +} + ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, ModuleKind Type, SourceLocation ImportLoc, @@ -3080,6 +3104,21 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, PreviousGeneration); } + if (PP.getHeaderSearchInfo() + .getHeaderSearchOpts() + .ModulesValidateOncePerBuildSession) { + // Now we are certain that the module and all modules it depends on are + // up to date. Create or update timestamp files for modules that are + // located in the module cache (not for PCH files that could be anywhere + // in the filesystem). + for (unsigned I = 0, N = Loaded.size(); I != N; ++I) { + ImportedModule &M = Loaded[I]; + if (M.Mod->Kind == MK_Module) { + updateModuleTimestamp(*M.Mod); + } + } + } + return Success; } diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index 9c4b3d92bef..711afcd2dca 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -86,6 +86,16 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, NewModule = true; ModuleEntry = New; + New->InputFilesValidationTimestamp = 0; + if (New->Kind == MK_Module) { + std::string TimestampFilename = New->getTimestampFilename(); + llvm::sys::fs::file_status Status; + // A cached stat value would be fine as well. + if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) + New->InputFilesValidationTimestamp = + Status.getLastModificationTime().toEpochTime(); + } + // Load the contents of the module if (llvm::MemoryBuffer *Buffer = lookupBuffer(FileName)) { // The buffer was already provided for us. diff --git a/clang/test/Driver/modules.m b/clang/test/Driver/modules.m index b93054dbf87..7b14f21443f 100644 --- a/clang/test/Driver/modules.m +++ b/clang/test/Driver/modules.m @@ -4,3 +4,13 @@ // RUN: %clang -fmodules -fno-modules -fmodules -### %s 2>&1 | FileCheck -check-prefix=CHECK-HAS-MODULES %s // CHECK-HAS-MODULES: -fmodules +// RUN: %clang -fbuild-session-timestamp=123 -### %s 2>&1 | FileCheck -check-prefix=TIMESTAMP_ONLY %s +// TIMESTAMP_ONLY: -fbuild-session-timestamp=123 + +// RUN: %clang -fbuild-session-timestamp=123 -fmodules-validate-once-per-build-session -### %s 2>&1 | FileCheck -check-prefix=MODULES_VALIDATE_ONCE %s +// MODULES_VALIDATE_ONCE: -fbuild-session-timestamp=123 +// MODULES_VALIDATE_ONCE: -fmodules-validate-once-per-build-session + +// RUN: %clang -fmodules-validate-once-per-build-session -### %s 2>&1 | FileCheck -check-prefix=MODULES_VALIDATE_ONCE_ERR %s +// MODULES_VALIDATE_ONCE_ERR: option '-fmodules-validate-once-per-build-session' requires '-fbuild-session-timestamp=<seconds since Epoch>' + diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index 9288f92d17d..7f5bf4c2edf 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -2,6 +2,7 @@ #include "clang-c/Index.h" #include "clang-c/CXCompilationDatabase.h" +#include "clang-c/BuildSystem.h" #include "llvm/Config/config.h" #include <ctype.h> #include <stdlib.h> @@ -3800,6 +3801,11 @@ static int read_diagnostics(const char *filename) { return 0; } +static int perform_print_build_session_timestamp(void) { + printf("%lld\n", clang_getBuildSessionTimestamp()); + return 0; +} + /******************************************************************************/ /* Command line processing. */ /******************************************************************************/ @@ -3856,6 +3862,8 @@ static void print_usage(void) { fprintf(stderr, " c-index-test -compilation-db [lookup <filename>] database\n"); fprintf(stderr, + " c-index-test -print-build-session-timestamp\n"); + fprintf(stderr, " c-index-test -read-diagnostics <file>\n\n"); fprintf(stderr, " <symbol filter> values:\n%s", @@ -3955,6 +3963,8 @@ int cindextest_main(int argc, const char **argv) { return write_pch_file(argv[2], argc - 3, argv + 3); else if (argc > 2 && strcmp(argv[1], "-compilation-db") == 0) return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2); + else if (argc == 2 && strcmp(argv[1], "-print-build-session-timestamp") == 0) + return perform_print_build_session_timestamp(); print_usage(); return 1; diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index 97203b0df4d..95a8a6ccbaf 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS set(SOURCES ARCMigrate.cpp + BuildSystem.cpp CIndex.cpp CIndexCXX.cpp CIndexCodeCompletion.cpp diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports index 809cf03a10b..975993adb35 100644 --- a/clang/tools/libclang/libclang.exports +++ b/clang/tools/libclang/libclang.exports @@ -119,6 +119,7 @@ clang_formatDiagnostic clang_getArgType clang_getArrayElementType clang_getArraySize +clang_getBuildSessionTimestamp clang_getCString clang_getCXTUResourceUsage clang_getCXXAccessSpecifier |