internal static void ExecuteFilesAutoCleanup()

in Sharpmake/Util.cs [449:560]


        internal static void ExecuteFilesAutoCleanup(bool addDBToAlternateDB)
        {
            if (!FilesAutoCleanupActive && !s_forceFilesCleanup)
                return; // Auto cleanup not active. Nothing to do.

            if (string.IsNullOrWhiteSpace(s_overrideFilesAutoCleanupDBPath) && !Directory.Exists(FilesAutoCleanupDBPath))
                throw new Exception($"Unable to find directory {FilesAutoCleanupDBPath} used to store auto-cleanup database. Is proper path set?");

            string databaseFilename = GetDatabaseFilename(FilesAutoCleanupDBSuffix);
            Dictionary<string, DateTime> dbFiles = ReadCleanupDatabase(databaseFilename);

            // Note: We must take into account all databases when doing the cleanup otherwise we might end up deleting files still used in other contexts.
            List<Dictionary<string, DateTime>> alternateDatabases = new List<Dictionary<string, DateTime>>();

            // Try to load all alternate db contexts
            var alternateDBFullPaths = FilesAlternatesAutoCleanupDBSuffixes.Select(alternateDBSuffix => GetDatabaseFilename(alternateDBSuffix))
                .Concat(_FilesAlternatesAutoCleanupDBFullPaths.AsEnumerable());

            foreach (string alternateDatabaseFilename in alternateDBFullPaths)
            {
                Dictionary<string, DateTime> alternateDBFiles = ReadCleanupDatabase(alternateDatabaseFilename);
                if (alternateDBFiles != null)
                    alternateDatabases.Add(alternateDBFiles);
            }

            Dictionary<string, DateTime> newDbFiles = new Dictionary<string, DateTime>(s_writtenFiles, StringComparer.InvariantCultureIgnoreCase);

            if (dbFiles != null)
            {
                // Deleting all files that are no longer used.
                DateTime now = DateTime.Now;
                foreach (KeyValuePair<string, DateTime> filenameDate in dbFiles)
                {
                    if (s_forceFilesCleanup)
                    {
                        if (!File.Exists(filenameDate.Key))
                            continue;

                        LogWrite(@"Force deleting '{0}'", filenameDate.Key);
                        if (!TryDeleteFile(filenameDate.Key, removeIfReadOnly: true))
                        {
                            // Failed to delete the file... Keep it for now... Maybe later we will be able to delete it!
                            LogWrite(@"Failed to delete '{0}'", filenameDate.Key);
                        }
                        continue;
                    }

                    if (newDbFiles.ContainsKey(filenameDate.Key))
                        continue;

                    if (FilesAutoCleanupIgnoredEndings.Any(x => filenameDate.Key.EndsWith(x, StringComparison.OrdinalIgnoreCase)))
                        continue;

                    TimeSpan diff = now - filenameDate.Value;
                    if (diff >= FilesAutoCleanupDelay)
                    {
                        if (!File.Exists(filenameDate.Key))
                            continue;

                        bool foundFileInAlternateDatabase = false;
                        // Check alternateDatabases files
                        foreach (var alternateDB in alternateDatabases)
                        {
                            if (alternateDB.ContainsKey(filenameDate.Key))
                            {
                                // Another database still uses the file. Simply remove the file from the current database and don't attempt to delete this file.
                                foundFileInAlternateDatabase = true;
                                break;
                            }
                        }
                        if (!foundFileInAlternateDatabase)
                        {
                            if (!FilesToBeExplicitlyRemovedFromDB.Contains(filenameDate.Key))
                            {
                                // Exclude files that were modified since the beginning of the current Sharpmake run.
                                // This should avoid regressions when a generated file is not added to cleanup database anymore.
                                // Example: replacing a call to Util.FileWriteIfDifferent() with File.WriteAll()
                                // From the previous run, Util.FileWriteIfDifferent() added the file in the cleanup database.
                                // In the new run, File.WriteAll() wrote the file, but the cleanup system would want to delete it.
                                if (File.GetLastWriteTime(filenameDate.Key) >= ProgramStartTime)
                                {
                                    LogWrite(@"Skip deleting old file (updated during this run): {0}", filenameDate.Key);
                                    newDbFiles.Add(filenameDate.Key, filenameDate.Value);
                                    continue;
                                }

                                LogWrite(@"Deleting old file: {0}", filenameDate.Key);
                                if (!TryDeleteFile(filenameDate.Key))
                                {
                                    // Failed to delete the file... Keep it for now... Maybe later we will be able to delete it!
                                    LogWrite(@"Failed to delete {0}. Keep it in the database.", filenameDate.Key);
                                    newDbFiles.Add(filenameDate.Key, filenameDate.Value);
                                }
                            }
                        }
                    }
                    else
                    {
                        LogWrite(@"Skip deleting old file (delayed): {0}", filenameDate.Key);
                        newDbFiles.Add(filenameDate.Key, filenameDate.Value);
                    }
                }
            }

            WriteCleanupDatabase(databaseFilename, newDbFiles);
            if (addDBToAlternateDB)
                _FilesAlternatesAutoCleanupDBFullPaths.Add(databaseFilename);

            // We are done! Clear the list of files to avoid problems as this context is now considered as complete.
            // For example if generating debug solution and then executing normal generation
            s_writtenFiles.Clear();
        }