in src/Utilities/TrackedDependencies/CanonicalTrackedInputFiles.cs [626:897]
private void ConstructDependencyTable()
{
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)
{
foreach (ITaskItem tlogFileName in _tlogFiles)
{
if (!FileUtilities.FileExistsNoThrow(tlogFileName.ItemSpec))
{
FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_SingleLogFileNotAvailable", tlogFileName.ItemSpec);
}
}
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, string>>)cachedEntry.DependencyTable;
// Log information about what we're using
FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_ReadTrackingCached");
foreach (ITaskItem tlogItem in cachedEntry.TlogFiles)
{
FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", tlogItem.ItemSpec);
}
return;
}
// Now we need to construct a dependency table for the primary sources 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;
FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_ReadTrackingLogs");
foreach (ITaskItem tlogFileName in _tlogFiles)
{
try
{
FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", tlogFileName.ItemSpec);
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] != '#') // command marker
{
bool rootingRecord = false;
// If this is a rooting record, remove the rooting marker
if (tlogEntry[0] == '^')
{
tlogEntry = tlogEntry.Substring(1);
if (tlogEntry.Length == 0)
{
encounteredInvalidTLogContents = true;
invalidTLogName = tlogFileName.ItemSpec;
break;
}
rootingRecord = true;
}
// found one of our primary sources
if (rootingRecord)
{
// dependency table for the source file
Dictionary<string, string> dependencies;
Dictionary<string, string> primaryFiles;
if (!_maintainCompositeRootingMarkers)
{
primaryFiles = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (tlogEntry.Contains("|"))
{
foreach (ITaskItem file in _sourceFiles)
{
if (!primaryFiles.ContainsKey(FileUtilities.NormalizePath(file.ItemSpec)))
{
primaryFiles.Add(FileUtilities.NormalizePath(file.ItemSpec), null);
}
}
}
else
{
primaryFiles.Add(tlogEntry, null);
}
}
else
{
primaryFiles = null;
}
// We haven't seen this source before in the tracking log
// so create a new dependency table and add the source file(s)
if (!_dependencyTable.TryGetValue(tlogEntry, out dependencies))
{
dependencies = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (!_maintainCompositeRootingMarkers)
{
dependencies.Add(tlogEntry, null);
}
_dependencyTable.Add(tlogEntry, dependencies);
}
tlogEntry = tlog.ReadLine();
if (_maintainCompositeRootingMarkers)
{
// Process each file encountered until we reach:
// the end of the or,
// A command marker or,
// we hit a rooting marker
while (tlogEntry != null)
{
if (tlogEntry.Length == 0)
{
encounteredInvalidTLogContents = true;
invalidTLogName = tlogFileName.ItemSpec;
break;
}
else if ((tlogEntry[0] != '#') && (tlogEntry[0] != '^'))
{
if (!dependencies.ContainsKey(tlogEntry))
{
if (FileTracker.FileIsUnderPath(tlogEntry, currentProjectDirectory) || !FileTracker.FileIsExcludedFromDependencies(tlogEntry))
{
dependencies.Add(tlogEntry, null);
}
}
}
else
{
break;
}
tlogEntry = tlog.ReadLine();
}
}
else
{
while (tlogEntry != null)
{
if (tlogEntry.Length == 0)
{
encounteredInvalidTLogContents = true;
invalidTLogName = tlogFileName.ItemSpec;
break;
}
else if (tlogEntry[0] != '#' && tlogEntry[0] != '^')
{
if (primaryFiles.ContainsKey(tlogEntry))
{
// if this is a primary file, we need to add it to the dependency table, and we need
// to reset "dependencies" so that the following dependencies get written into this
// primary file's table instead of the previous one.
if (!_dependencyTable.TryGetValue(tlogEntry, out dependencies))
{
dependencies = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
dependencies.Add(tlogEntry, null);
_dependencyTable.Add(tlogEntry, dependencies);
}
}
else if (!dependencies.ContainsKey(tlogEntry))
{
// however, if it's not a primary file, just add it to the current dependency table
if (FileTracker.FileIsUnderPath(tlogEntry, currentProjectDirectory) || !FileTracker.FileIsExcludedFromDependencies(tlogEntry))
{
dependencies.Add(tlogEntry, null);
}
}
}
else
{
break;
}
tlogEntry = tlog.ReadLine();
}
}
}
else // don't know what this entry is, so skip it
{
tlogEntry = tlog.ReadLine();
}
}
else // skip over the initial '#' line
{
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, string>>(StringComparer.OrdinalIgnoreCase);
}
else
{
// Record the newly built dependency table in the cache
DependencyTableCache.DependencyTable[tLogRootingMarker] = new DependencyTableCacheEntry(_tlogFiles, _dependencyTable);
}
}
}