private void ConstructDependencyTable()

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);
                }
            }
        }