/// <summary> /// Build an ObsoleteFiles instance searching a set of /// ManifestReferences and a FileMetadataSet for old files. /// Old files are bundled into unreferenced (i.e not referenced by a /// manifest that is not pending deletion) and reference (referenced /// by an active manifest). /// </summary> /// <param name="manifestReferencesList">List of manifests to query /// for obsolete files.</param> /// <param name="metadataSet">Set of metadata to query for obsolete /// files.<param> /// <returns>ObsoleteFiles instance which references the discovered /// obsolete files.</returns> public ObsoleteFiles( List<ManifestReferences> manifestReferencesList, FileMetadataSet metadataSet) { // Combine all currently referenced and obsolete files into a // global sets. var currentFiles = new HashSet<string>(); var obsoleteFiles = new HashSet<string>(); foreach (var manifestReferences in manifestReferencesList) { currentFiles.UnionWith(manifestReferences.currentFiles); obsoleteFiles.UnionWith(manifestReferences.obsoleteFiles); } // Fold in obsolete files that are not referenced by manifests. foreach (var metadataByVersion in metadataSet.Values) { obsoleteFiles.UnionWith( metadataByVersion.FindObsoleteVersions()); } // Filter the obsoleteFiles set for all obsolete files currently // in use and add to a dictionary indexed by filename // which contains a list of manifest filenames which reference // each file. var referencedObsoleteFiles = new Dictionary<string, List<string>>(); var obsoleteFilesToDelete = new HashSet<string>(); foreach (var obsoleteFile in obsoleteFiles) { var manifestsReferencingFile = new List<string>(); foreach (var manifestReferences in manifestReferencesList) { if (manifestReferences.currentFiles.Contains( obsoleteFile)) { manifestsReferencingFile.Add( manifestReferences.currentMetadata.filename); } } // If the referenced file doesn't exist, ignore it. if (!File.Exists(obsoleteFile)) { continue; } if (manifestsReferencingFile.Count > 0) { referencedObsoleteFiles[obsoleteFile] = manifestsReferencingFile; } else { obsoleteFilesToDelete.Add(obsoleteFile); } } unreferenced = obsoleteFilesToDelete; referenced = referencedObsoleteFiles; }
/// <summary> /// Find and read all package manifests. /// </summary> /// <param name="metadataSet">Set to query for manifest files.</param> /// <returns>List of ManifestReferences which contain current and /// obsolete files referenced in each manifest file.</returns> public static List<ManifestReferences> FindAndReadManifests( FileMetadataSet metadataSet) { var manifestReferencesList = new List<ManifestReferences>(); foreach (var metadataByVersion in metadataSet.Values) { ManifestReferences manifestReferences = new ManifestReferences(); if (manifestReferences.ParseManifests(metadataByVersion, metadataSet)) { manifestReferencesList.Add(manifestReferences); } } return manifestReferencesList; }
/// <summary> /// Filter the a set for files which have multiple versions and those /// with metadata that selects the set of target platforms. /// </summary> /// <param name="metadataSet">Set to filter.</param> /// <returns>Filtered MetadataSet. public static FileMetadataSet FindWithPendingUpdates( FileMetadataSet metadataSet) { FileMetadataSet outMetadataSet = new FileMetadataSet(); foreach (var filenameAndMetadata in metadataSet.metadataByCanonicalFilename) { var metadataByVersion = filenameAndMetadata.Value.Values; bool needsUpdate = metadataByVersion.Count > 1; if (!needsUpdate) { foreach (var metadata in metadataByVersion) { if ((metadata.targets != null && metadata.targets.Length > 0) || metadata.isManifest) { needsUpdate = true; break; } } } if (needsUpdate) { outMetadataSet.metadataByCanonicalFilename[ filenameAndMetadata.Key] = filenameAndMetadata.Value; } } return outMetadataSet; }
/// <summary> /// Parse current and obsolete file references from a package's /// manifest files. /// </summary> /// <param name="metadataByVersion">Metadata for files ordered by /// version number. If the metadata does not have the isManifest /// attribute it is ignored.</param> /// <param name="metadataSet">Set of all metadata files in the /// project. This is used to handle file renaming in the parsed /// manifest. If the manifest contains files that have been /// renamed it's updated with the new filenames.</param> /// <returns>true if data was parsed from the specified file metadata, /// false otherwise.</returns> public bool ParseManifests(FileMetadataByVersion metadataByVersion, FileMetadataSet metadataSet) { currentFiles = new HashSet<string>(); obsoleteFiles = new HashSet<string>(); int versionIndex = 0; int numberOfVersions = metadataByVersion.Values.Count; foreach (FileMetadata metadata in metadataByVersion.Values) { versionIndex++; if (!metadata.isManifest) return false; bool manifestNeedsUpdate = false; HashSet<string> filesInManifest = versionIndex < numberOfVersions ? obsoleteFiles : currentFiles; StreamReader manifestFile = new StreamReader(metadata.filename); string line; while ((line = manifestFile.ReadLine()) != null) { var manifestFileMetadata = new FileMetadata(line.Trim()); string filename = manifestFileMetadata.filename; // Check for a renamed file. var existingFileMetadata = metadataSet.FindMetadata( manifestFileMetadata.filenameCanonical, manifestFileMetadata.CalculateVersion()); if (existingFileMetadata != null && !manifestFileMetadata.filename.Equals( existingFileMetadata.filename)) { filename = existingFileMetadata.filename; manifestNeedsUpdate = true; } filesInManifest.Add(filename); } manifestFile.Close(); // If this is the most recent manifest version, remove all // current files from the set to delete. if (versionIndex == numberOfVersions) { currentMetadata = metadata; foreach (var currentFile in filesInManifest) { obsoleteFiles.Remove(currentFile); } } // Rewrite the manifest to track renamed files. if (manifestNeedsUpdate) { File.Delete(metadata.filename); var writer = new StreamWriter(metadata.filename); foreach (var filename in filesInManifest) { writer.WriteLine(filename); } writer.Close(); } } this.filenameCanonical = metadataByVersion.filenameCanonical; return true; }
/// <summary> /// Parse metadata from a set of filenames. /// </summary> /// <param name="assetFiles">Filenames to parse.</param> /// <returns>FileMetadataSet referencing metadata parsed from filenames /// ordered by version and bucketed by canonical filename. /// </returns> public static FileMetadataSet ParseFromFilenames(string[] filenames) { FileMetadataSet metadataSet = new FileMetadataSet(); // Parse metadata from filenames and bucket by version. foreach (string filename in filenames) { metadataSet.Add(new FileMetadata(filename)); } return metadataSet; }