private static bool FilesExistAndRecordRequestedWriteTime(ICollection <ITaskItem> files, TaskLoggingHelper log, bool getNewest, out DateTime requestedTime, out string requestedFilename) { bool allExist = true; requestedTime = getNewest ? DateTime.MinValue : DateTime.MaxValue; requestedFilename = string.Empty; // No output files for the source were tracked // safely assume that this is because we didn't track them because they weren't compiled if (files == null || files.Count == 0) { allExist = false; } else { foreach (ITaskItem item in files) { DateTime lastWriteTime = NativeMethodsShared.GetLastWriteFileUtcTime(item.ItemSpec); // If the file does not exist if (lastWriteTime == DateTime.MinValue) { FileTracker.LogMessageFromResources(log, MessageImportance.Low, "Tracking_OutputDoesNotExist", item.ItemSpec); allExist = false; break; } if (getNewest && lastWriteTime > requestedTime || !getNewest && lastWriteTime < requestedTime) { requestedTime = lastWriteTime; requestedFilename = item.ItemSpec; } } } return(allExist); }
/// <summary> /// Update the current state of entry details for the dependency table /// </summary> public void UpdateFileEntryDetails() { _oldestFileName = String.Empty; _oldestFileTimeUtc = DateTime.MaxValue; _newestFileName = String.Empty; _newestFileTimeUtc = DateTime.MinValue; _newestTLogFileName = String.Empty; _newestTLogTimeUtc = DateTime.MinValue; this.MissingFiles.Clear(); // First update the details of our Tlogs foreach (ITaskItem tlogFileName in _tlogFiles) { DateTime tlogLastWriteTimeUtc = NativeMethodsShared.GetLastWriteFileUtcTime(tlogFileName.ItemSpec); if (tlogLastWriteTimeUtc > _newestTLogTimeUtc) { _newestTLogTimeUtc = tlogLastWriteTimeUtc; _newestTLogFileName = tlogFileName.ItemSpec; } } // Now for each entry in the table foreach (string entry in this.DependencyTable.Keys) { RecordEntryDetails(entry, false); } }
public void GetLastWriteFileUtcTimeReturnsMinValueForDirectory() { string directory = FileUtilities.GetTemporaryDirectory(createDirectory: true); DateTime directoryTime = NativeMethodsShared.GetLastWriteFileUtcTime(directory); Assert.Equal(DateTime.MinValue, directoryTime); }
public void GetLastWriteFileUtcTimeReturnsMinValueForMissingFile() { string nonexistentFile = FileUtilities.GetTemporaryFile(); // Make sure that the file does not, in fact, exist. File.Delete(nonexistentFile); DateTime nonexistentFileTime = NativeMethodsShared.GetLastWriteFileUtcTime(nonexistentFile); Assert.Equal(DateTime.MinValue, nonexistentFileTime); }
/// <summary> /// Returns cached value for last write time of file. Update the cache if it is the first /// time someone asking for that file /// </summary> public DateTime GetLastWriteTimeUtc(string file) { DateTime fileModifiedTimeUtc = DateTime.MinValue; if (!_lastWriteTimeUtcCache.TryGetValue(file, out fileModifiedTimeUtc)) { fileModifiedTimeUtc = NativeMethodsShared.GetLastWriteFileUtcTime(file); _lastWriteTimeUtcCache[file] = fileModifiedTimeUtc; } return(fileModifiedTimeUtc); }
public override DateTime GetLastWriteTimeUtc(string path) { var fileLastWriteTime = NativeMethodsShared.GetLastWriteFileUtcTime(path); if (fileLastWriteTime != DateTime.MinValue) { return(fileLastWriteTime); } else { NativeMethodsShared.GetLastWriteDirectoryUtcTime(path, out var directoryLastWriteTime); return(directoryLastWriteTime); } }
/// <summary> /// Returns true if task execution is not necessary. Executed after ValidateParameters /// </summary> protected override bool SkipTaskExecution() { if (!String.IsNullOrEmpty(OutputWindowsMetadataFile)) { var outputWriteTime = NativeMethodsShared.GetLastWriteFileUtcTime(OutputWindowsMetadataFile); var winMDModuleWriteTime = NativeMethodsShared.GetLastWriteFileUtcTime(WinMDModule); // If the last write time of the input file is less than the last write time of the output file // then the output is newer then the input so we do not need to re-run the tool. if (outputWriteTime > winMDModuleWriteTime) { return(true); } } return(false); }
/// <summary> /// Determine if a cache entry is up to date /// </summary> /// <param name="dependencyTable">The cache entry to check</param> /// <returns>true if up to date</returns> private static bool DependencyTableIsUpToDate(DependencyTableCacheEntry dependencyTable) { DateTime tableTime = dependencyTable.TableTime; foreach (ITaskItem tlogFile in dependencyTable.TlogFiles) { string tlogFilename = FileUtilities.NormalizePath(tlogFile.ItemSpec); DateTime lastWriteTime = NativeMethodsShared.GetLastWriteFileUtcTime(tlogFilename); if (lastWriteTime > tableTime) { // one of the tlog files is newer than the table, so return false return(false); } } return(true); }
/// <summary> /// This method adds a computed output for the given source key to the dictionary specified /// </summary> /// <param name="dependencies">The dictionary to add outputs to</param> /// <param name="computedOutput">The computed outputs for this source key</param> private void AddOutput(Dictionary <string, DateTime> dependencies, string computedOutput) { string fullComputedOutput = FileUtilities.NormalizePath(computedOutput).ToUpperInvariant(); if (!dependencies.ContainsKey(fullComputedOutput)) { DateTime fileModifiedTime; if (FileUtilities.FileExistsNoThrow(fullComputedOutput)) { fileModifiedTime = NativeMethodsShared.GetLastWriteFileUtcTime(fullComputedOutput); } else { fileModifiedTime = DateTime.MinValue; } dependencies.Add(fullComputedOutput, fileModifiedTime); } }
/// <summary> /// Construct a new entry /// </summary> /// <param name="tlogFiles">The tlog files used to build this dependency table</param> /// <param name="dependencyTable">The dependency table to be cached</param> internal DependencyTableCacheEntry(ITaskItem[] tlogFiles, IDictionary dependencyTable) { TlogFiles = new ITaskItem[tlogFiles.Length]; TableTime = DateTime.MinValue; // Our cache's knowledge of the tlog items needs their full path for (int tlogItemCount = 0; tlogItemCount < tlogFiles.Length; tlogItemCount++) { string tlogFilename = FileUtilities.NormalizePath(tlogFiles[tlogItemCount].ItemSpec); TlogFiles[tlogItemCount] = new TaskItem(tlogFilename); // Our cache entry needs to use the last modified time of the latest tlog // involved so that our cache can be invalidated if any tlog is updated DateTime modifiedTime = NativeMethodsShared.GetLastWriteFileUtcTime(tlogFilename); if (modifiedTime > TableTime) { TableTime = modifiedTime; } } DependencyTable = dependencyTable; }
/// <summary> /// Construct our dependency table for our source files /// </summary> private void ConstructOutputTable() { string tLogRootingMarker = null; try { // construct a rooting marker from the tlog files tLogRootingMarker = DependencyTableCache.FormatNormalizedTlogRootingMarker(_tlogFiles); } catch (ArgumentException e) { FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", e.Message); return; } // Record the current directory (which under normal circumstances will be the project directory) // so that we can compare tracked paths against it for inclusion in the dependency graph string currentProjectDirectory = FileUtilities.EnsureTrailingSlash(Directory.GetCurrentDirectory()); if (!_tlogAvailable) { FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_TrackingLogNotAvailable"); lock (DependencyTableCache.DependencyTable) { // The tracking logs are not available, they may have been deleted at some point. // Be safe and remove any references from the cache. if (DependencyTableCache.DependencyTable.ContainsKey(tLogRootingMarker)) { DependencyTableCache.DependencyTable.Remove(tLogRootingMarker); } } return; } DependencyTableCacheEntry cachedEntry = null; lock (DependencyTableCache.DependencyTable) { // Look in the dependency table cache to see if its available and up to date cachedEntry = DependencyTableCache.GetCachedEntry(tLogRootingMarker); } // We have an up to date cached entry if (cachedEntry != null) { _dependencyTable = (Dictionary <string, System.Collections.Generic.Dictionary <string, System.DateTime> >)cachedEntry.DependencyTable; // Log information about what we're using FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_WriteTrackingCached"); foreach (ITaskItem tlogItem in cachedEntry.TlogFiles) { FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", tlogItem.ItemSpec); } return; } FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_WriteTrackingLogs"); // Now we need to construct the rest of the table from the TLOG files // If there are any errors in the tlogs, we want to warn, stop parsing tlogs, and empty // out the dependency table, essentially forcing a rebuild. bool encounteredInvalidTLogContents = false; bool exceptionCaught = false; string invalidTLogName = null; foreach (ITaskItem tlogFileName in _tlogFiles) { FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", tlogFileName.ItemSpec); try { using (StreamReader tlog = File.OpenText(tlogFileName.ItemSpec)) { string tlogEntry = tlog.ReadLine(); while (tlogEntry != null) { if (tlogEntry.Length == 0) { encounteredInvalidTLogContents = true; invalidTLogName = tlogFileName.ItemSpec; break; } if (tlogEntry[0] == '^') // This is a rooting record, follow the outputs for it { Dictionary <string, DateTime> dependencies; tlogEntry = tlogEntry.Substring(1); if (tlogEntry.Length == 0) { encounteredInvalidTLogContents = true; invalidTLogName = tlogFileName.ItemSpec; break; } if (!_dependencyTable.TryGetValue(tlogEntry, out dependencies)) { dependencies = new Dictionary <string, DateTime>(StringComparer.OrdinalIgnoreCase); _dependencyTable.Add(tlogEntry, dependencies); } // Process each file encountered until we hit a rooting marker do { tlogEntry = tlog.ReadLine(); if (tlogEntry != null) { if (tlogEntry.Length == 0) { encounteredInvalidTLogContents = true; invalidTLogName = tlogFileName.ItemSpec; break; } else if ((tlogEntry[0] != '^') && (tlogEntry[0] != '#') && (!dependencies.ContainsKey(tlogEntry))) { // Allows incremental build of projects existing under temp, only for those reads / writes that // either are not under temp, or are recursively beneath the current project directory. if (FileTracker.FileIsUnderPath(tlogEntry, currentProjectDirectory) || !FileTracker.FileIsExcludedFromDependencies(tlogEntry)) { DateTime fileModifiedTime = NativeMethodsShared.GetLastWriteFileUtcTime(tlogEntry); dependencies.Add(tlogEntry, fileModifiedTime); } } } } while ((tlogEntry != null) && (tlogEntry[0] != '^')); if (encounteredInvalidTLogContents) { break; } } else // don't know what this entry is, so skip it { tlogEntry = tlog.ReadLine(); } } } } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", e.Message); break; } if (encounteredInvalidTLogContents) { FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLogContents", invalidTLogName); break; } } lock (DependencyTableCache.DependencyTable) { // There were problems with the tracking logs -- we've already warned or errored; now we want to make // sure that we essentially force a rebuild of this particular root. if (encounteredInvalidTLogContents || exceptionCaught) { if (DependencyTableCache.DependencyTable.ContainsKey(tLogRootingMarker)) { DependencyTableCache.DependencyTable.Remove(tLogRootingMarker); } _dependencyTable = new Dictionary <string, Dictionary <string, DateTime> >(StringComparer.OrdinalIgnoreCase); } else { // Record the newly built valid dependency table in the cache DependencyTableCache.DependencyTable[tLogRootingMarker] = new DependencyTableCacheEntry(_tlogFiles, _dependencyTable); } } }
/// <summary> /// Construct our dependency table for our source files /// </summary> private void ConstructFileTable() { string tLogRootingMarker = null; try { // construct a rooting marker from the tlog files tLogRootingMarker = DependencyTableCache.FormatNormalizedTlogRootingMarker(_tlogFiles); } catch (ArgumentException e) { FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", e.Message); return; } if (!_tlogsAvailable) { lock (DependencyTableCache.DependencyTable) { // The tracking logs are not available, they may have been deleted at some point. // Be safe and remove any references from the cache. if (DependencyTableCache.DependencyTable.ContainsKey(tLogRootingMarker)) { DependencyTableCache.DependencyTable.Remove(tLogRootingMarker); } } return; } DependencyTableCacheEntry cachedEntry = null; lock (DependencyTableCache.DependencyTable) { // Look in the dependency table cache to see if its available and up to date cachedEntry = DependencyTableCache.GetCachedEntry(tLogRootingMarker); } // We have an up to date cached entry if (cachedEntry != null) { _dependencyTable = (IDictionary <string, DateTime>)cachedEntry.DependencyTable; // We may have stored the dependency table in the cache, but all the other information // (newest file time, number of missing files, etc.) has been reset to default. Refresh // the data. this.UpdateFileEntryDetails(); // Log information about what we're using FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_TrackingCached"); foreach (ITaskItem tlogItem in cachedEntry.TlogFiles) { FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", tlogItem.ItemSpec); } return; } FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_TrackingLogs"); // Now we need to construct the rest of the table from the TLOG files // If there are any errors in the tlogs, we want to warn, stop parsing tlogs, and empty // out the dependency table, essentially forcing a rebuild. bool encounteredInvalidTLogContents = false; bool exceptionCaught = false; string invalidTLogName = null; foreach (ITaskItem tlogFileName in _tlogFiles) { try { FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", tlogFileName.ItemSpec); DateTime tlogLastWriteTimeUtc = NativeMethodsShared.GetLastWriteFileUtcTime(tlogFileName.ItemSpec); if (tlogLastWriteTimeUtc > _newestTLogTimeUtc) { _newestTLogTimeUtc = tlogLastWriteTimeUtc; _newestTLogFileName = tlogFileName.ItemSpec; } using (StreamReader tlog = File.OpenText(tlogFileName.ItemSpec)) { string tlogEntry = tlog.ReadLine(); while (tlogEntry != null) { if (tlogEntry.Length == 0) // empty lines are a sign that something has gone wrong { encounteredInvalidTLogContents = true; invalidTLogName = tlogFileName.ItemSpec; break; } // Preprocessing for the line entry else if (tlogEntry[0] == '#') // a comment marker should be skipped { tlogEntry = tlog.ReadLine(); continue; } else if (tlogEntry[0] == '^' && TreatRootMarkersAsEntries && tlogEntry.IndexOf('|') < 0) // This is a rooting non composite record, and we should keep it { tlogEntry = tlogEntry.Substring(1); if (tlogEntry.Length == 0) { encounteredInvalidTLogContents = true; invalidTLogName = tlogFileName.ItemSpec; break; } } else if (tlogEntry[0] == '^') // root marker is not being treated as an entry, skip it { tlogEntry = tlog.ReadLine(); continue; } // If we haven't seen this file before, then record it if (!_dependencyTable.ContainsKey(tlogEntry)) { // It may be that this is one of the locations that we should ignore if (!FileTracker.FileIsExcludedFromDependencies(tlogEntry)) { RecordEntryDetails(tlogEntry, true); } } tlogEntry = tlog.ReadLine(); } } } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", e.Message); break; } if (encounteredInvalidTLogContents) { FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLogContents", invalidTLogName); break; } } lock (DependencyTableCache.DependencyTable) { // There were problems with the tracking logs -- we've already warned or errored; now we want to make // sure that we essentially force a rebuild of this particular root. if (encounteredInvalidTLogContents || exceptionCaught) { if (DependencyTableCache.DependencyTable.ContainsKey(tLogRootingMarker)) { DependencyTableCache.DependencyTable.Remove(tLogRootingMarker); } _dependencyTable = new Dictionary <string, DateTime>(StringComparer.OrdinalIgnoreCase); } else { // Record the newly built dependency table in the cache DependencyTableCache.DependencyTable[tLogRootingMarker] = new DependencyTableCacheEntry(_tlogFiles, (IDictionary)_dependencyTable); } } }