private void ProcessArchiveNuSpec(string path) { using (var archive = ZipFile.Open(path, ZipArchiveMode.Read)) { var nuspecFiles = archive.Entries .Where(e => e.FullName.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)) .Select(e => e.FullName) .ToArray(); if (nuspecFiles.Length > 0) { using (var packageReader = new PackageArchiveReader(archive)) { var packageIdentity = packageReader.GetIdentity(); // Read nupkg for ps1's and extract the nupkgs into the same folder as nupkg foreach (var nuspecFile in nuspecFiles) { using (var fileStream = archive.GetEntry(nuspecFile).Open()) { // Parse NuSpec to find metadata/contentfiles tag var nuspec = new NuspecReader(fileStream); if (nuspec.GetContentFiles().Count() > 0) { AddToPackageCollection(packageIdentity, nuspecFile, ""); } } } } } } }
/// <summary> /// Apply build actions from the nuspec to items from the contentFiles folder. /// </summary> internal static List <LockFileContentFile> GetContentFileGroup( NuspecReader nuspec, List <ContentItemGroup> contentFileGroups) { var results = new List <LockFileContentFile>(contentFileGroups.Count); var rootFolderPathLength = ContentFilesFolderName.Length; // Read the contentFiles section of the nuspec // Read the entries so that the bottom entry has priority var nuspecContentFiles = nuspec.GetContentFiles().ToList(); // Initialize mappings var entryMappings = new Dictionary <string, List <ContentFilesEntry> >(StringComparer.OrdinalIgnoreCase); var languageMappings = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var group in contentFileGroups) { var codeLanguage = group.Properties[ManagedCodeConventions.PropertyNames.CodeLanguage] as string; foreach (var item in group.Items) { if (!entryMappings.ContainsKey(item.Path)) { entryMappings.Add(item.Path, new List <ContentFilesEntry>()); languageMappings.Add(item.Path, codeLanguage); } } } // Virtual root for file globbing var rootDirectory = new VirtualFileInfo(VirtualFileProvider.RootDir, isDirectory: true); // Apply all nuspec property mappings to the files returned by content model foreach (var filesEntry in nuspecContentFiles) { // this is validated in the nuspec reader Debug.Assert(filesEntry.Include != null, "invalid contentFiles entry"); // Create a filesystem matcher for globbing patterns var matcher = new Matcher(StringComparison.OrdinalIgnoreCase); matcher.AddInclude(filesEntry.Include); if (filesEntry.Exclude != null) { matcher.AddExclude(filesEntry.Exclude); } // Check each file against the patterns foreach (var file in entryMappings.Keys) { // Remove contentFiles/ from the string Debug.Assert(file.StartsWith(ContentFilesFolderName, StringComparison.OrdinalIgnoreCase), "invalid file path: " + file); // All files should begin with the same root folder if (file.Length > rootFolderPathLength) { var relativePath = file.Substring(rootFolderPathLength, file.Length - rootFolderPathLength); // Check if the nuspec group include/exclude patterns apply to the file var virtualDirectory = new VirtualFileProvider(new List <string>() { relativePath }); var globbingDirectory = new FileProviderGlobbingDirectory( virtualDirectory, fileInfo: rootDirectory, parent: null); // Currently Matcher only returns the file name not the full path, each file must be // check individually. var matchResults = matcher.Execute(globbingDirectory); if (matchResults.Files.Any()) { entryMappings[file].Add(filesEntry); } } } } // Create lock file entries for each item in the contentFiles folder foreach (var file in entryMappings.Keys) { // defaults var action = BuildAction.Parse(PackagingConstants.ContentFilesDefaultBuildAction); var copyToOutput = false; var flatten = false; // _._ is needed for empty codeLanguage groups if (file.EndsWith(PackagingCoreConstants.ForwardSlashEmptyFolder, StringComparison.Ordinal)) { action = BuildAction.None; } else { // apply each entry // entries may not have all the attributes, if a value is null // ignore it and continue using the previous value. foreach (var filesEntry in entryMappings[file]) { if (!string.IsNullOrEmpty(filesEntry.BuildAction)) { action = BuildAction.Parse(filesEntry.BuildAction); } if (filesEntry.CopyToOutput.HasValue) { copyToOutput = filesEntry.CopyToOutput.Value; } if (filesEntry.Flatten.HasValue) { flatten = filesEntry.Flatten.Value; } } } // Add attributes to the lock file item var lockFileItem = new LockFileContentFile(file); // Add the language from the directory path lockFileItem.CodeLanguage = languageMappings[file].ToLowerInvariant(); if (!action.IsKnown) { // Throw an error containing the package identity, invalid action, and file where it occurred. var message = string.Format(CultureInfo.CurrentCulture, Strings.Error_UnknownBuildAction, nuspec.GetIdentity(), action, file); throw new PackagingException(message); } lockFileItem.BuildAction = action; lockFileItem.CopyToOutput = copyToOutput; // Check if this is a .pp transform. If the filename is ".pp" ignore it since it will // have no file name after the transform. var isPP = lockFileItem.Path.EndsWith(".pp", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Path.GetFileNameWithoutExtension(lockFileItem.Path)); if (copyToOutput) { string destination = null; if (flatten) { destination = Path.GetFileName(lockFileItem.Path); } else { // Find path relative to the TxM // Ex: contentFiles/cs/net45/config/config.xml -> config/config.xml destination = GetContentFileFolderRelativeToFramework(file); } if (isPP) { // Remove .pp from the output file path destination = destination.Substring(0, destination.Length - 3); } lockFileItem.OutputPath = destination; } // Add the pp transform file if one exists if (isPP) { var destination = lockFileItem.Path.Substring(0, lockFileItem.Path.Length - 3); destination = GetContentFileFolderRelativeToFramework(destination); lockFileItem.PPOutputPath = destination; } results.Add(lockFileItem); } return(results); }