| File: PackageFileExtractor.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using NuGet.Common; namespace NuGet.Packaging { public class PackageFileExtractor { private readonly HashSet<string>? _intellisenseXmlFiles; private readonly XmlDocFileSaveMode _xmlDocFileSaveMode; public PackageFileExtractor(IEnumerable<string> packageFiles, XmlDocFileSaveMode xmlDocFileSaveMode) { _xmlDocFileSaveMode = xmlDocFileSaveMode; if (xmlDocFileSaveMode == XmlDocFileSaveMode.Skip || xmlDocFileSaveMode == XmlDocFileSaveMode.Compress) { _intellisenseXmlFiles = GatherIntellisenseXmlFiles(packageFiles); } } private static HashSet<string> GatherIntellisenseXmlFiles(IEnumerable<string> packageFiles) { var intellisenseXmlFiles = new HashSet<string>(StringComparer.Ordinal); var refAndLibFiles = packageFiles.Where( f => f.StartsWith("lib/", StringComparison.OrdinalIgnoreCase) || f.StartsWith("ref/", StringComparison.OrdinalIgnoreCase)) .ToArray(); foreach (var file in refAndLibFiles) { if (!file.EndsWith(".xml", PathUtility.GetStringComparisonBasedOnOS())) { continue; } // Xml files located next to the neutral language dll. var dllFile = Path.ChangeExtension(file, ".dll"); if (refAndLibFiles.Contains(dllFile, StringComparer.OrdinalIgnoreCase)) { intellisenseXmlFiles.Add(file); continue; } // For satellite assemblies, look for a corresponding .resources.dll var resourceDll = Path.ChangeExtension(file, ".resources.dll"); if (refAndLibFiles.Contains(resourceDll, StringComparer.OrdinalIgnoreCase)) { intellisenseXmlFiles.Add(file); continue; } // Xml files located in a language specific directory. var languageDllFile = GetBinaryForLanguageSpecificXml(file); if (languageDllFile != null && refAndLibFiles.Contains(languageDllFile, StringComparer.Ordinal)) { intellisenseXmlFiles.Add(file); } } return intellisenseXmlFiles; } private static string? GetBinaryForLanguageSpecificXml(string file) { // For xml files located in language specific directories, look for a corresponding binary // in the parent directory. // e.g. ref/net45/fr/MyBinary.xml -> ref/net45/MyBinary.dll var fileSeparatorIndex = file.LastIndexOf('/'); if (fileSeparatorIndex > 0) { var directorySeparatorIndex = file.LastIndexOf('/', fileSeparatorIndex - 1); if (directorySeparatorIndex >= 0) { var fileAtParentDirectory = file.Substring(0, directorySeparatorIndex) + file.Substring(fileSeparatorIndex); return Path.ChangeExtension(fileAtParentDirectory, ".dll"); } } return null; } public string? ExtractPackageFile(string source, string target, Stream stream) { if ((_xmlDocFileSaveMode == XmlDocFileSaveMode.Skip) && _intellisenseXmlFiles!.Contains(source)) { return null; } var extractDirectory = Path.GetDirectoryName(target)!; Directory.CreateDirectory(extractDirectory); if ((_xmlDocFileSaveMode == XmlDocFileSaveMode.Compress) && _intellisenseXmlFiles!.Contains(source)) { // If the package contains a file named {BinaryName}.xml.zip already exists, the result would be // ambigious. var targetZip = Path.ChangeExtension(target, ".xml.zip"); using (var outputStream = NuGetExtractionFileIO.CreateFile(targetZip)) using (var zipArchive = new ZipArchive(outputStream, ZipArchiveMode.Create)) { var entry = zipArchive.CreateEntry(Path.GetFileName(source)); using (var entryStream = entry.Open()) { stream.CopyTo(entryStream); } } return targetZip; } if (Path.IsPathRooted(source)) { // Copying files stream-to-stream is less efficient. Attempt to copy using File.Copy if we the source // is a file on disk. File.Copy(source, target, overwrite: true); } else { stream.CopyToFile(target); } return target; } } }