diff --git a/.gitignore b/.gitignore index 8fa35e3..817c733 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,404 @@ -Thumbs.db -*.obj -*.exe -*.pdb +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + + +# User-specific files +*.rsuser +*.suo *.user -*.aps -*.pch -*.vspscc +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*.ncb -*.suo -*.sln.docstates +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr *.tlb +*.tli *.tlh -*.bak -*.cache -*.ilk +*.tmp +*.tmp_proj +*_wpftmp.csproj *.log -[Bb]in -[Dd]ebug*/ -*.lib -*.sbr -obj/ -[Rr]elease*/ -_ReSharper*/ -[Tt]est[Rr]esult* +*.tlog +*.vspscc *.vssscc -$tf*/ -packages/*/ +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +Output/ +Output/* +*-cache/ +.vscode/ +package-lock.json \ No newline at end of file diff --git a/App.config b/App.config index 87b8caf..4bfa005 100644 --- a/App.config +++ b/App.config @@ -1,6 +1,6 @@ - + diff --git a/CmdArguments.cs b/CmdArguments.cs index b88af97..c60df73 100644 --- a/CmdArguments.cs +++ b/CmdArguments.cs @@ -1,8 +1,6 @@ // Copyright (c) 2015 Abel Cheng . Licensed under the MIT license. // Repository: https://nupkgmerge.codeplex.com/ -using System.Linq; -using System.Collections.Generic; using CommandLine; using CommandLine.Text; diff --git a/NuGetPackageMerge.csproj b/NuGetPackageMerge.csproj index 8b0667e..7dc6e0e 100644 --- a/NuGetPackageMerge.csproj +++ b/NuGetPackageMerge.csproj @@ -8,11 +8,12 @@ Exe Properties NuGetPackageMerge - NupkgMerge - v4.0 + NupkgMerge3 + v4.8 512 true - Client + + false .\ true @@ -40,6 +41,8 @@ DEBUG;TRACE prompt 4 + false + latest AnyCPU @@ -50,22 +53,14 @@ prompt 4 false + false + latest - - packages\CommandLineParser.1.9.71\lib\net40\CommandLine.dll - True - - - packages\Microsoft.Web.Xdt.2.1.1\lib\net40\Microsoft.Web.XmlTransform.dll - True - - - packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll - True - + + @@ -75,6 +70,7 @@ + @@ -83,7 +79,6 @@ Designer - @@ -107,6 +102,17 @@ true + + + 1.9.71 + + + 3.1.0 + + + 6.9.1 + + diff --git a/NuGetPackageMerge.nuspec b/NuGetPackageMerge.nuspec index 5a41c2d..327fd70 100644 --- a/NuGetPackageMerge.nuspec +++ b/NuGetPackageMerge.nuspec @@ -11,7 +11,7 @@ false $description$ http://nupkgmerge.codeplex.com/ - Copyright 2015-2017 Abel Cheng + Copyright 2015-2024 Abel Cheng, BitKFu nuget packaging nupkg packages merge tool command line true diff --git a/NupkgMerge.cs b/NupkgMerge.cs index 4495012..e6336d8 100644 --- a/NupkgMerge.cs +++ b/NupkgMerge.cs @@ -4,210 +4,282 @@ using System; using System.IO; using System.Linq; -using System.Runtime.Versioning; using System.Collections.Generic; using System.Collections.ObjectModel; -using NuGet; +using System.IO.Compression; +using System.Threading; +using NuGet.Packaging; +using NuGet.Packaging.Core; namespace NuGetPackageMerge { class NupkgMerge { - private readonly PackageBuilder _Builder; - + private readonly PackageBuilder _builder; + private List _folderToRemove = []; + private List _filesToRemove = []; + public NupkgMerge(string primaryNupkg) { - _Builder = new PackageBuilder(); + _builder = new PackageBuilder(); Merge(primaryNupkg, true); } - private static void ReplaceMetadata(PackageBuilder builder, IPackage primarySource) + private static void ReplaceMetadata(PackageBuilder builder, NuspecReader primarySource) { - if (!string.IsNullOrEmpty(primarySource.Id)) - builder.Id = primarySource.Id; - if (primarySource.Version != null) - builder.Version = primarySource.Version; - if (!string.IsNullOrEmpty(primarySource.Title)) - builder.Title = primarySource.Title; - if (primarySource.IconUrl != null) - builder.IconUrl = primarySource.IconUrl; - if (primarySource.LicenseUrl != null) - builder.LicenseUrl = primarySource.LicenseUrl; - if (primarySource.ProjectUrl != null) - builder.ProjectUrl = primarySource.ProjectUrl; - if (primarySource.RequireLicenseAcceptance) - builder.RequireLicenseAcceptance = primarySource.RequireLicenseAcceptance; - if (primarySource.DevelopmentDependency) - builder.DevelopmentDependency = primarySource.DevelopmentDependency; - if (!string.IsNullOrEmpty(primarySource.Description)) - builder.Description = primarySource.Description; - if (!string.IsNullOrEmpty(primarySource.Summary)) - builder.Summary = primarySource.Summary; - if (!string.IsNullOrEmpty(primarySource.ReleaseNotes)) - builder.ReleaseNotes = primarySource.ReleaseNotes; - if (!string.IsNullOrEmpty(primarySource.Copyright)) - builder.Copyright = primarySource.Copyright; - if (!string.IsNullOrEmpty(primarySource.Language)) - builder.Language = primarySource.Language; - if (primarySource.MinClientVersion != null) - builder.MinClientVersion = primarySource.MinClientVersion; - if (!string.IsNullOrEmpty(primarySource.Tags)) - builder.Tags.AddRange(SplitTags(primarySource.Tags)); - builder.Authors.AddRange(primarySource.Authors.Except(builder.Authors)); - builder.Owners.AddRange(primarySource.Owners.Except(builder.Owners)); - - MergeDependencySets(builder.DependencySets, primarySource); - MergeFrameworkReferences(builder.FrameworkReferences, primarySource.FrameworkAssemblies); - MergePackageAssemblyReferences(builder.PackageAssemblyReferences, primarySource.PackageAssemblyReferences); - } + if (!string.IsNullOrEmpty(primarySource.GetId())) + builder.Id = primarySource.GetId(); + if (primarySource.GetVersion() != null) + builder.Version = primarySource.GetVersion(); + if (!string.IsNullOrEmpty(primarySource.GetTitle())) + builder.Title = primarySource.GetTitle(); + if (!string.IsNullOrEmpty(primarySource.GetIconUrl())) + builder.IconUrl = new Uri(primarySource.GetIconUrl()); + if (!string.IsNullOrEmpty(primarySource.GetLicenseUrl())) + builder.LicenseUrl = new Uri(primarySource.GetLicenseUrl()); + if (!string.IsNullOrEmpty(primarySource.GetProjectUrl())) + builder.ProjectUrl = new Uri(primarySource.GetProjectUrl()); + if (primarySource.GetRequireLicenseAcceptance()) + builder.RequireLicenseAcceptance = primarySource.GetRequireLicenseAcceptance(); + if (primarySource.GetDevelopmentDependency()) + builder.DevelopmentDependency = primarySource.GetDevelopmentDependency(); + if (!string.IsNullOrEmpty(primarySource.GetDescription())) + builder.Description = primarySource.GetDescription(); + if (!string.IsNullOrEmpty(primarySource.GetSummary())) + builder.Summary = primarySource.GetSummary(); + if (!string.IsNullOrEmpty(primarySource.GetReleaseNotes())) + builder.ReleaseNotes = primarySource.GetReleaseNotes(); + if (!string.IsNullOrEmpty(primarySource.GetCopyright())) + builder.Copyright = primarySource.GetCopyright(); + if (!string.IsNullOrEmpty(primarySource.GetLanguage())) + builder.Language = primarySource.GetLanguage(); + if (primarySource.GetMinClientVersion() != null) + builder.MinClientVersion = primarySource.GetMinClientVersion().Version; + if (!string.IsNullOrEmpty(primarySource.GetTags())) + builder.Tags.AddRange(SplitTags(primarySource.GetTags())); + builder.Authors.AddRange(primarySource.GetAuthors().Split(',').Except(builder.Authors)); + builder.Owners.AddRange(primarySource.GetOwners().Split(',').Except(builder.Owners)); - private static void MergeMetadata(PackageBuilder builder, IPackage secondSource) - { - if (string.IsNullOrEmpty(builder.Id) && !string.IsNullOrEmpty(secondSource.Id)) - builder.Id = secondSource.Id; - if (builder.Version == null && secondSource.Version != null) - builder.Version = secondSource.Version; - if (string.IsNullOrEmpty(builder.Title) && !string.IsNullOrEmpty(secondSource.Title)) - builder.Title = secondSource.Title; - if (builder.IconUrl == null && secondSource.IconUrl != null) - builder.IconUrl = secondSource.IconUrl; - if (builder.LicenseUrl == null && secondSource.LicenseUrl != null) - builder.LicenseUrl = secondSource.LicenseUrl; - if (builder.ProjectUrl == null && secondSource.ProjectUrl != null) - builder.ProjectUrl = secondSource.ProjectUrl; - if (!builder.RequireLicenseAcceptance && secondSource.RequireLicenseAcceptance) - builder.RequireLicenseAcceptance = secondSource.RequireLicenseAcceptance; - if (!builder.DevelopmentDependency && secondSource.DevelopmentDependency) - builder.DevelopmentDependency = secondSource.DevelopmentDependency; - if (string.IsNullOrEmpty(builder.Description) && !string.IsNullOrEmpty(secondSource.Description)) - builder.Description = secondSource.Description; - if (string.IsNullOrEmpty(builder.Summary) && !string.IsNullOrEmpty(secondSource.Summary)) - builder.Summary = secondSource.Summary; - if (string.IsNullOrEmpty(builder.ReleaseNotes) && !string.IsNullOrEmpty(secondSource.ReleaseNotes)) - builder.ReleaseNotes = secondSource.ReleaseNotes; - if (string.IsNullOrEmpty(builder.Copyright) && !string.IsNullOrEmpty(secondSource.Copyright)) - builder.Copyright = secondSource.Copyright; - if (string.IsNullOrEmpty(builder.Language) && !string.IsNullOrEmpty(secondSource.Language)) - builder.Language = secondSource.Language; - if (builder.MinClientVersion == null && secondSource.MinClientVersion != null) - builder.MinClientVersion = secondSource.MinClientVersion; - if (!string.IsNullOrEmpty(secondSource.Tags)) - builder.Tags.AddRange(SplitTags(secondSource.Tags).Except(builder.Tags)); - builder.Authors.AddRange(secondSource.Authors.Except(builder.Authors)); - builder.Owners.AddRange(secondSource.Owners.Except(builder.Owners)); - - MergeDependencySets(builder.DependencySets, secondSource); - MergeFrameworkReferences(builder.FrameworkReferences, secondSource.FrameworkAssemblies); - MergePackageAssemblyReferences(builder.PackageAssemblyReferences, secondSource.PackageAssemblyReferences); - } + MergeDependencySets(builder.DependencyGroups, primarySource.GetDependencyGroups()); + MergeFrameworkReferences(builder.FrameworkReferenceGroups, primarySource.GetFrameworkRefGroups()); + MergePackageAssemblyReferences(builder.FrameworkReferences, primarySource.GetFrameworkAssemblyGroups()); + MergeContentFiles(builder.ContentFiles, primarySource.GetContentFiles()); + } - private static void MergeDependencySets(Collection dependencySets, IPackage secondSource) + private static void MergeMetadata(PackageBuilder builder, NuspecReader secondSource) { - var secondDependencies = TargetDependencies(secondSource); + if (string.IsNullOrEmpty(builder.Id) && !string.IsNullOrEmpty(secondSource.GetId())) + builder.Id = secondSource.GetId(); + if (builder.Version == null && secondSource.GetVersion() != null) + builder.Version = secondSource.GetVersion(); + if (string.IsNullOrEmpty(builder.Title) && !string.IsNullOrEmpty(secondSource.GetTitle())) + builder.Title = secondSource.GetTitle(); + if (builder.IconUrl == null && !string.IsNullOrEmpty(secondSource.GetIconUrl())) + builder.IconUrl = new Uri(secondSource.GetIconUrl()); + if (builder.LicenseUrl == null && !string.IsNullOrEmpty(secondSource.GetLicenseUrl())) + builder.LicenseUrl = new Uri(secondSource.GetLicenseUrl()); + if (builder.ProjectUrl == null && !string.IsNullOrEmpty(secondSource.GetProjectUrl())) + builder.ProjectUrl = new Uri(secondSource.GetProjectUrl()); + if (!builder.RequireLicenseAcceptance && secondSource.GetRequireLicenseAcceptance()) + builder.RequireLicenseAcceptance = secondSource.GetRequireLicenseAcceptance(); + if (!builder.DevelopmentDependency && secondSource.GetDevelopmentDependency()) + builder.DevelopmentDependency = secondSource.GetDevelopmentDependency(); + if (string.IsNullOrEmpty(builder.Description) && !string.IsNullOrEmpty(secondSource.GetDescription())) + builder.Description = secondSource.GetDescription(); + if (string.IsNullOrEmpty(builder.Summary) && !string.IsNullOrEmpty(secondSource.GetSummary())) + builder.Summary = secondSource.GetSummary(); + if (string.IsNullOrEmpty(builder.ReleaseNotes) && !string.IsNullOrEmpty(secondSource.GetReleaseNotes())) + builder.ReleaseNotes = secondSource.GetReleaseNotes(); + if (string.IsNullOrEmpty(builder.Copyright) && !string.IsNullOrEmpty(secondSource.GetCopyright())) + builder.Copyright = secondSource.GetCopyright(); + if (string.IsNullOrEmpty(builder.Language) && !string.IsNullOrEmpty(secondSource.GetLanguage())) + builder.Language = secondSource.GetLanguage(); + if (builder.MinClientVersion == null && secondSource.GetMinClientVersion() != null) + builder.MinClientVersion = secondSource.GetMinClientVersion().Version; + if (!string.IsNullOrEmpty(secondSource.GetTags())) + builder.Tags.AddRange(SplitTags(secondSource.GetTags()).Except(builder.Tags)); + builder.Authors.AddRange(secondSource.GetAuthors().Split(',').Except(builder.Authors)); + builder.Owners.AddRange(secondSource.GetOwners().Split(',').Except(builder.Owners)); + + MergeDependencySets(builder.DependencyGroups, secondSource.GetDependencyGroups()); + MergeFrameworkReferences(builder.FrameworkReferenceGroups, secondSource.GetFrameworkRefGroups()); + MergePackageAssemblyReferences(builder.FrameworkReferences, secondSource.GetFrameworkAssemblyGroups()); + MergeContentFiles(builder.ContentFiles, secondSource.GetContentFiles()); + } + + private static void MergeDependencySets(Collection dependencySets, IEnumerable secondDependencies) + { if (dependencySets.Count == 0) dependencySets.AddRange(secondDependencies); else { - PackageDependencySet oldDepSet; - - foreach (var newDepSet in secondDependencies) - { - oldDepSet = dependencySets.FirstOrDefault(d => d.TargetFramework == newDepSet.TargetFramework); + foreach (var newDepSet in secondDependencies) + { + var oldDepSet = dependencySets.FirstOrDefault(d => d.TargetFramework == newDepSet.TargetFramework); - if (oldDepSet == null) + if (oldDepSet == null) dependencySets.Add(newDepSet); else - { - foreach (var dep in newDepSet.Dependencies) - if (!oldDepSet.Dependencies.Any(d => d.Id.Equals(dep.Id, StringComparison.OrdinalIgnoreCase))) - oldDepSet.Dependencies.Add(dep); - } - } + { + var packages = oldDepSet.Packages.ToList(); + + foreach (var dep in newDepSet.Packages) + if (!oldDepSet.Packages.Any(d => d.Id.Equals(dep.Id, StringComparison.OrdinalIgnoreCase))) + packages.Add(dep); + + dependencySets.Remove(oldDepSet); + dependencySets.Add(new PackageDependencyGroup(oldDepSet.TargetFramework, packages)); + } + } } } - private static IEnumerable TargetDependencies(IPackage source) + private static void MergeFrameworkReferences(Collection frameworkReferences, IEnumerable secondFrameworkReferences) { - List supportedFrameworks = source.GetSupportedFrameworks().ToList(); + foreach (var newRefSet in secondFrameworkReferences) + { + var existingFrameworkReference = frameworkReferences.FirstOrDefault(p => p.TargetFramework == newRefSet.TargetFramework) + ?? new FrameworkReferenceGroup(newRefSet.TargetFramework, newRefSet.FrameworkReferences); - var dependencySets = source.DependencySets.Where(d => d.Dependencies.Count > 0); + // Merge references + var copiedReferences = existingFrameworkReference.FrameworkReferences.ToList(); + foreach (var r in newRefSet.FrameworkReferences) + if (!copiedReferences.Contains(r)) + copiedReferences.Add(r); - if (supportedFrameworks.Count == 1) - return dependencySets.Select(d => TargetDependency(d, supportedFrameworks[0])); - else - return dependencySets; + // Remove the old package reference and add the merged + frameworkReferences.Remove(existingFrameworkReference); + frameworkReferences.Add(new FrameworkReferenceGroup(existingFrameworkReference.TargetFramework, copiedReferences)); + } } - private static PackageDependencySet TargetDependency(PackageDependencySet packageDependencySet, FrameworkName targetFramework) + private static void MergePackageAssemblyReferences(Collection packageAssemblyReferences, IEnumerable secondPackageAssemblyReferences) { - if (packageDependencySet.TargetFramework == null && packageDependencySet.Dependencies.Count > 0) - return new PackageDependencySet(targetFramework, packageDependencySet.Dependencies); - else - return packageDependencySet; - } + foreach (var newPrSet in secondPackageAssemblyReferences) + { + foreach (var assemblyFile in newPrSet.Items) + { + FrameworkAssemblyReference newReference = null; - private static void MergeFrameworkReferences(Collection frameworkReferences, IEnumerable secondFrameworkReferences) - { - if (frameworkReferences.Count == 0) - frameworkReferences.AddRange(secondFrameworkReferences); - else - { - foreach (var fr in secondFrameworkReferences) - if (!frameworkReferences.Any(f => f.AssemblyName.Equals(fr.AssemblyName, StringComparison.OrdinalIgnoreCase))) - frameworkReferences.Add(fr); - } - } - - private static void MergePackageAssemblyReferences(ICollection packageAssemblyReferences, IEnumerable secondPackageAssemblyReferences) - { - if (packageAssemblyReferences.Count == 0) - packageAssemblyReferences.AddRange(secondPackageAssemblyReferences); - else - { - PackageReferenceSet oldPrSet; + var assemblyReference = packageAssemblyReferences.FirstOrDefault(p => p.AssemblyName == assemblyFile); + if (assemblyReference != null && !assemblyReference.SupportedFrameworks.Contains(newPrSet.TargetFramework)) + newReference = new FrameworkAssemblyReference(assemblyFile, assemblyReference.SupportedFrameworks.Concat([newPrSet.TargetFramework])); + else + { + if (assemblyReference == null) + newReference = new FrameworkAssemblyReference(assemblyFile, [newPrSet.TargetFramework]); + } - foreach (var newPrSet in secondPackageAssemblyReferences) - { - oldPrSet = packageAssemblyReferences.FirstOrDefault(p => p.TargetFramework == newPrSet.TargetFramework); + // Remove and add again + if (newReference == null) + continue; - if (oldPrSet == null) - packageAssemblyReferences.Add(newPrSet); - else - { - foreach (var r in newPrSet.References) - if (!oldPrSet.References.Contains(r, StringComparer.OrdinalIgnoreCase)) - oldPrSet.References.Add(r); - } - } - } + packageAssemblyReferences.Remove(packageAssemblyReferences.FirstOrDefault(p => p.AssemblyName == assemblyFile)); + packageAssemblyReferences.Add(newReference); + } + } } - public void Merge(string secondNupkg, bool replaceMetadata = false) + private static void MergeContentFiles(ICollection builderContentFiles, IEnumerable secondContentFiles) + { + foreach (var newContentFile in secondContentFiles) + { + var mfc = new ManifestContentFiles() + { + BuildAction = newContentFile.BuildAction, + CopyToOutput = newContentFile.CopyToOutput?.ToString(), + Exclude = newContentFile.Exclude, + Flatten = newContentFile.Flatten?.ToString(), + Include = newContentFile.Include + }; + builderContentFiles.Add(mfc); + } + } + + + public void Merge(string secondNupkg, bool replaceMetadata = false) { - var secondPackage = new ZipPackage(secondNupkg); + var secondPackage = new PackageArchiveReader(new ZipArchive(new FileStream(secondNupkg, FileMode.Open))); if (replaceMetadata) - ReplaceMetadata(_Builder, secondPackage); + ReplaceMetadata(_builder, secondPackage.NuspecReader); else - MergeMetadata(_Builder, secondPackage); + MergeMetadata(_builder, secondPackage.NuspecReader); + var tempPath = Directory.GetCurrentDirectory(); + secondPackage.CopyFiles(tempPath, secondPackage.GetFiles(), CopyProgress, null, CancellationToken.None); foreach (var packageFile in secondPackage.GetFiles()) - if (!_Builder.Files.Any(e => e.Path.Equals(packageFile.Path, StringComparison.OrdinalIgnoreCase))) - _Builder.Files.Add(packageFile); - } + if (!_builder.Files.Any(e => e.Path.Equals(packageFile, StringComparison.OrdinalIgnoreCase))) + { + var nugetPackageFile = new PhysicalPackageFile() { TargetPath = packageFile, SourcePath = Path.Combine(tempPath, packageFile)}; + _builder.Files.Add(nugetPackageFile); + } + } - public void Save(string outputNupkg) - { - using (Stream stream = File.Create(outputNupkg)) - { - _Builder.Save(stream); - } - } + private string CopyProgress(string sourcefile, string targetFile, Stream filestream) + { + Console.WriteLine($"{sourcefile} -> {targetFile}"); + + byte[] target = new byte[filestream.Length]; + filestream.Read(target, 0, (int)filestream.Length); + + var targetPath = Path.GetDirectoryName(targetFile); + _folderToRemove.Add(targetPath); + + if (!Directory.Exists(targetPath)) + Directory.CreateDirectory(targetPath); + + File.WriteAllBytes(targetFile, target); + _filesToRemove.Add(targetFile); + return sourcefile; + } + + public void CleanUpLocalFiles() + { + foreach (var fileName in _filesToRemove) + { + File.Delete(fileName); + } + + foreach (var folderName in _folderToRemove) + { + try + { + var innerFolder = folderName; + while (innerFolder != null && Directory.GetFiles(innerFolder).Length == 0) + { + try + { + Directory.Delete(innerFolder); + innerFolder = Directory.GetParent(innerFolder)?.FullName; + } + catch + { + // thrown if the folder is not empty + innerFolder = null; + } + } + } + catch + { + // Folder not found (because we deleted it earlier) + } + } + + _filesToRemove = []; + _folderToRemove = []; + } + + public void Save(string outputNupkg) + { + using Stream stream = File.Create(outputNupkg); + _builder.Save(stream); + } private static IEnumerable SplitTags(string tags) { - return (tags == null) ? Enumerable.Empty() : tags.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + return tags?.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) ?? Enumerable.Empty(); } } } diff --git a/PackageFileStub.cs b/PackageFileStub.cs new file mode 100644 index 0000000..5b3051b --- /dev/null +++ b/PackageFileStub.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; +using NuGet.Frameworks; +using NuGet.Packaging; + +namespace NuGetPackageMerge +{ + internal class PackageFileStub : IPackageFile + { + #region Implementation of IPackageFile + + public Stream GetStream() + { + return null; + } + + public string Path { get; set; } + public string EffectivePath { get; set; } + public FrameworkName TargetFramework { get; set; } + public NuGetFramework NuGetFramework { get; set; } + public DateTimeOffset LastWriteTime { get; set; } + + #endregion + } +} diff --git a/Program.cs b/Program.cs index c63b01a..58baabb 100644 --- a/Program.cs +++ b/Program.cs @@ -15,9 +15,10 @@ static int Main(string[] args) { if (cmdArguments.Parse(args)) { - NupkgMerge nupkgMerge = new NupkgMerge(cmdArguments.PrimaryNupkg); + var nupkgMerge = new NupkgMerge(cmdArguments.PrimaryNupkg); nupkgMerge.Merge(cmdArguments.SecondNupkg); nupkgMerge.Save(cmdArguments.OutputNupkg); + nupkgMerge.CleanUpLocalFiles(); Console.WriteLine("Successfully merged '{0}' with '{1}' into '{2}'.", cmdArguments.PrimaryNupkg, cmdArguments.SecondNupkg, cmdArguments.OutputNupkg); diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 8ab08e9..863dddf 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -8,9 +8,9 @@ [assembly: AssemblyTitle("NuGet Packages Merge Tool")] [assembly: AssemblyDescription("This is a NuGet packages merge utility which lets you merge two .nupkg packages (two .NET Framework Versions) into a single .nupkg package.")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Abel Cheng")] +[assembly: AssemblyCompany("Abel Cheng, BitKFu")] [assembly: AssemblyProduct("NuGetPackagesMerge")] -[assembly: AssemblyCopyright("Copyright 2015-2017 Abel Cheng - http://nupkgmerge.codeplex.com")] +[assembly: AssemblyCopyright("Copyright 2015-2024 Abel Cheng - https://github.com/DataBooster/NupkgMerge, BitKFu - https://github.com/BitKFu/NupkgMerge")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.1.0")] -[assembly: AssemblyFileVersion("1.0.1.0")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")]