diff --git a/eng/restore-toolset.ps1 b/eng/restore-toolset.ps1 index 720c6de5b2e9..7ea33783bd3b 100644 --- a/eng/restore-toolset.ps1 +++ b/eng/restore-toolset.ps1 @@ -18,7 +18,7 @@ function InitializeCustomSDKToolset { InstallDotNetSharedFramework "1.0.5" InstallDotNetSharedFramework "1.1.2" InstallDotNetSharedFramework "2.1.0" - InstallDotNetSharedFramework "2.2.3" + InstallDotNetSharedFramework "2.2.4" CreateBuildEnvScript InstallNuget diff --git a/eng/restore-toolset.sh b/eng/restore-toolset.sh index 7d71703492e5..842fd6bc60e3 100644 --- a/eng/restore-toolset.sh +++ b/eng/restore-toolset.sh @@ -13,7 +13,7 @@ function InitializeCustomSDKToolset { InstallDotNetSharedFramework "1.0.5" InstallDotNetSharedFramework "1.1.2" InstallDotNetSharedFramework "2.1.0" - InstallDotNetSharedFramework "2.2.3" + InstallDotNetSharedFramework "2.2.4" } # Installs additional shared frameworks for testing purposes diff --git a/global.json b/global.json index baccdabda3c0..be3bf4e6c91c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "3.0.100-preview5-011162", + "dotnet": "3.0.100-preview5-011317", "vs-opt": { "version": "15.9" } diff --git a/src/Tasks/Common/ConflictResolution/ConflictItem.cs b/src/Tasks/Common/ConflictResolution/ConflictItem.cs index c3d05cad024e..c4643e2a0d3c 100644 --- a/src/Tasks/Common/ConflictResolution/ConflictItem.cs +++ b/src/Tasks/Common/ConflictResolution/ConflictItem.cs @@ -158,11 +158,33 @@ public string PackageId { if (_packageId == null) { - _packageId = OriginalItem?.GetMetadata(MetadataNames.NuGetPackageId) ?? String.Empty; + _packageId = OriginalItem?.GetMetadata(MetadataNames.NuGetPackageId); + + if (string.IsNullOrEmpty(_packageId)) + { + _packageId = OriginalItem?.GetMetadata(MetadataKeys.PackageName) ?? string.Empty; + } if (_packageId.Length == 0) { - _packageId = NuGetUtils.GetPackageIdFromSourcePath(SourcePath) ?? String.Empty; + // We want to move away from using the heuristic of walking up the folder tree until + // we find a .nuspec in order to determine the package ID of a file. However, we + // don't want to accidentally stop having a package ID for a file that the heuristic + // would have found the package ID for. So to catch those cases, we throw an + // exception if the heuristic finds a package ID but we don't have the package ID + // from any other source + string packageIdFromPath = NuGetUtils.GetPackageIdFromSourcePath(SourcePath); + if (!string.IsNullOrEmpty(packageIdFromPath)) + { + string path = OriginalItem?.ItemSpec; + if (string.IsNullOrEmpty(path)) + { + path = SourcePath; + } + throw new InvalidOperationException("NuGetPackageId metadata not set on " + path); + } + + _packageId = string.Empty; } } diff --git a/src/Tasks/Common/ConflictResolution/FrameworkListReader.cs b/src/Tasks/Common/ConflictResolution/FrameworkListReader.cs index 0c15dce06a03..ea61e33ad994 100644 --- a/src/Tasks/Common/ConflictResolution/FrameworkListReader.cs +++ b/src/Tasks/Common/ConflictResolution/FrameworkListReader.cs @@ -98,7 +98,7 @@ private static IEnumerable LoadConflictItems(string frameworkListP } ret.Add(new ConflictItem(assemblyName + ".dll", - packageId: null, + packageId: "TargetingPack", assemblyVersion: assemblyVersion, fileVersion: null)); } diff --git a/src/Tasks/Common/ConflictResolution/PackageOverride.cs b/src/Tasks/Common/ConflictResolution/PackageOverride.cs index 60e6fb1b7d75..b7519120b082 100644 --- a/src/Tasks/Common/ConflictResolution/PackageOverride.cs +++ b/src/Tasks/Common/ConflictResolution/PackageOverride.cs @@ -45,7 +45,7 @@ private static IEnumerable> CreateOverriddenPackages(stri if (!string.IsNullOrEmpty(overriddenPackagesString)) { overriddenPackagesString = overriddenPackagesString.Trim(); - string[] overriddenPackagesAndVersions = overriddenPackagesString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + string[] overriddenPackagesAndVersions = overriddenPackagesString.Split(new char[] { ';', '\r', '\n', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string overriddenPackagesAndVersion in overriddenPackagesAndVersions) { string trimmedOverriddenPackagesAndVersion = overriddenPackagesAndVersion.Trim(); diff --git a/src/Tasks/Common/ConflictResolution/ResolvePackageFileConflicts.cs b/src/Tasks/Common/ConflictResolution/ResolvePackageFileConflicts.cs index a4185b7d9945..fbde7f7c62ee 100644 --- a/src/Tasks/Common/ConflictResolution/ResolvePackageFileConflicts.cs +++ b/src/Tasks/Common/ConflictResolution/ResolvePackageFileConflicts.cs @@ -202,6 +202,7 @@ private ITaskItem CreateConflictTaskItem(ConflictItem conflict) if (conflict.PackageId != null) { item.SetMetadata(nameof(ConflictItemType), conflict.ItemType.ToString()); + item.SetMetadata(MetadataKeys.NuGetPackageId, conflict.PackageId); } return item; diff --git a/src/Tasks/Common/MetadataKeys.cs b/src/Tasks/Common/MetadataKeys.cs index a47c5603a2db..413083a869d9 100644 --- a/src/Tasks/Common/MetadataKeys.cs +++ b/src/Tasks/Common/MetadataKeys.cs @@ -17,6 +17,7 @@ internal static class MetadataKeys public const string IsImplicitlyDefined = "IsImplicitlyDefined"; public const string IsTopLevelDependency = "IsTopLevelDependency"; public const string AllowExplicitVersion = "AllowExplicitVersion"; + public const string RelativePath = "RelativePath"; // Target Metadata public const string RuntimeIdentifier = "RuntimeIdentifier"; @@ -61,7 +62,7 @@ internal static class MetadataKeys public const string NuGetPackageId = "NuGetPackageId"; public const string NuGetPackageVersion = "NuGetPackageVersion"; public const string NuGetSourceType = "NuGetSourceType"; - public const string RelativePath = "RelativePath"; + public const string PathInPackage = "PathInPackage"; public const string PackageDirectory = "PackageDirectory"; // References diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.osx.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.osx.deps.json index cc76e7aa992e..1b2d6468a6a4 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.osx.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.osx.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0/osx.10.11-x64", - "signature": "6c0ed2851936790712df303bf7e27fc94dfbd644" + "signature": "" }, "compilationOptions": {}, "targets": { @@ -83,4 +83,4 @@ "hashPath": "system.spatial.5.7.0.nupkg.sha512" } } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.portable.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.portable.deps.json index 76736e6ecb0e..12ecd20f9bad 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.portable.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/all.asset.types.portable.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0", - "signature": "92c4a7720ef26003e081a02c73de57101baa7210" + "signature": "" }, "compilationOptions": {}, "targets": { @@ -81,4 +81,4 @@ "hashPath": "system.spatial.5.7.0.nupkg.sha512" } } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.deps.json index 46f7e2de2f87..cc0b2da3e706 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0", - "signature": "da39a3ee5e6b4b0d3255bfef95601890afd80709" + "signature": "" }, "compilationOptions": {}, "targets": { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.resources.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.resources.deps.json index bba68389721f..187856fa5be3 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.resources.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/dotnet.new.resources.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0", - "signature": "da39a3ee5e6b4b0d3255bfef95601890afd80709" + "signature": "" }, "compilationOptions": {}, "targets": { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.compilerOptions.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.compilerOptions.deps.json index fe81cddf6bd8..652e797e7965 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.compilerOptions.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.compilerOptions.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0", - "signature": "a813617eb7d3bb5d69fa51d48f64bc6455d8ca5f" + "signature": "" }, "compilationOptions": { "defines": [ @@ -2494,4 +2494,4 @@ "hashPath": "system.xml.xpath.xdocument.4.0.1.nupkg.sha512" } } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.deps.json index ff64bd0b836c..29254fdefd0a 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0", - "signature": "a813617eb7d3bb5d69fa51d48f64bc6455d8ca5f" + "signature": "" }, "compilationOptions": {}, "targets": { @@ -63,4 +63,4 @@ "hashPath": "system.runtime.serialization.primitives.4.1.1.nupkg.sha512" } } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.directReference.deps.json b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.directReference.deps.json index 647bc29cac87..7b7986e2c87f 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.directReference.deps.json +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/simple.dependencies.directReference.deps.json @@ -1,7 +1,7 @@ { "runtimeTarget": { "name": ".NETCoreApp,Version=v1.0", - "signature": "a813617eb7d3bb5d69fa51d48f64bc6455d8ca5f" + "signature": "" }, "compilationOptions": { "defines": [ @@ -2513,4 +2513,4 @@ "hashPath": "system.xml.xpath.xdocument.4.0.1.nupkg.sha512" } } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs b/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs index 95ad6c0366f9..707d1916e5aa 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs @@ -17,6 +17,8 @@ namespace Microsoft.NET.Build.Tasks { internal class DependencyContextBuilder { + private const string _runtimePackPrefix = "runtimepack."; + private readonly VersionFolderPathResolver _versionFolderPathResolver; private readonly SingleProjectInfo _mainProjectInfo; private readonly ProjectContext _projectContext; @@ -34,6 +36,7 @@ internal class DependencyContextBuilder private bool _includeMainProjectInDepsFile = true; private HashSet _usedLibraryNames; private Dictionary _referenceLibraryNames; + private Dictionary _compilationTargetLibraries; public DependencyContextBuilder(SingleProjectInfo mainProjectInfo, ProjectContext projectContext, bool includeRuntimeFileVersions) { @@ -50,6 +53,12 @@ public DependencyContextBuilder(SingleProjectInfo mainProjectInfo, ProjectContex // it needs to read the file versions _packageResolver = NuGetPackageResolver.CreateResolver(projectContext.LockFile); } + + if (_projectContext.CompilationLockFileTarget != _projectContext.LockFileTarget) + { + _compilationTargetLibraries = _projectContext.CompilationLockFileTarget.Libraries + .ToDictionary(l => l.Name, StringComparer.OrdinalIgnoreCase); + } } /// @@ -169,7 +178,7 @@ public DependencyContext Build() var libraryLookup = new LockFileLookup(_projectContext.LockFile); - var runtimeSignature = GenerateRuntimeSignature(runtimeExports); + var runtimeSignature = string.Empty; IEnumerable runtimeLibraries = Enumerable.Empty(); if (_includeMainProjectInDepsFile) @@ -224,30 +233,6 @@ public DependencyContext Build() new RuntimeFallbacks[] { }); } - private static string GenerateRuntimeSignature(IEnumerable runtimeExports) - { - var sha1 = SHA1.Create(); - var builder = new StringBuilder(); - var packages = runtimeExports - .Where(libraryExport => libraryExport.IsPackage()); - var separator = "|"; - foreach (var libraryExport in packages) - { - builder.Append(libraryExport.Name); - builder.Append(separator); - builder.Append(libraryExport.Version.ToString()); - builder.Append(separator); - } - var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(builder.ToString())); - - builder.Clear(); - foreach (var hashByte in hash) - { - builder.AppendFormat("{0:x2}", hashByte); - } - return builder.ToString(); - } - private List GetProjectDependencies( ProjectContext projectContext, Dictionary dependencyLookup, @@ -324,7 +309,7 @@ private RuntimeLibrary GetProjectRuntimeLibrary( List dependencies = GetProjectDependencies(projectContext, dependencyLookup, includeCompilationLibraries); foreach (var runtimePackGroup in _runtimePackAssets.GroupBy(asset => asset.PackageName + "/" + asset.PackageVersion)) { - dependencies.Add(new Dependency("runtimepack." + runtimePackGroup.First().PackageName, runtimePackGroup.First().PackageVersion)); + dependencies.Add(new Dependency(_runtimePackPrefix + runtimePackGroup.First().PackageName, runtimePackGroup.First().PackageVersion)); } return CreateRuntimeLibrary( @@ -376,7 +361,7 @@ private IEnumerable GetRuntimePackLibraries(IEnumerable assemblies = GetCompileTimeAssemblies(export, referenceProjectInfo); + IEnumerable assemblies = Enumerable.Empty(); + + // In some situations, the assets file will include compilation assets under the RID-specific + // target, but not under the RID-less target. The RID-less target is what is used for project + // compilation, so make sure we get those assets when writing the compile references to the assets + // file. + // This can happen when the runtime graph adds dependencies which don't have compile assets excluded. + // This was encountered with the 4.3.0 System.Security.Claims, System.Security.Principal.Windows, and + // System.Threading.Overlapped packages. + LockFileTargetLibrary exportWithCompileAssets; + if (_compilationTargetLibraries != null) + { + _compilationTargetLibraries.TryGetValue(export.Name, out exportWithCompileAssets); + } + else + { + exportWithCompileAssets = export; + } + if (exportWithCompileAssets != null) + { + assemblies = GetCompileTimeAssemblies(exportWithCompileAssets, referenceProjectInfo); + } return new CompilationLibrary( type.ToLowerInvariant(), diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder2.cs b/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder2.cs new file mode 100644 index 000000000000..c3a04d6083a8 --- /dev/null +++ b/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder2.cs @@ -0,0 +1,863 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Extensions.DependencyModel; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.ProjectModel; +using NuGet.Versioning; + +namespace Microsoft.NET.Build.Tasks +{ + internal class DependencyContextBuilder2 + { + private readonly SingleProjectInfo _mainProjectInfo; + private readonly bool _includeRuntimeFileVersions; + private IEnumerable _referenceAssemblies; + private IEnumerable _directReferences; + private IEnumerable _dependencyReferences; + private Dictionary> _compileReferences; + private Dictionary> _resolvedNuGetFiles; + private Dictionary _referenceProjectInfos; + private IEnumerable _excludeFromPublishPackageIds; + private Dictionary> _runtimePackAssets; + private CompilationOptions _compilationOptions; + private string _referenceAssembliesPath; + private Dictionary _filteredPackages; + private bool _includeMainProjectInDepsFile = true; + private Dictionary _dependencyLibraries; + private Dictionary> _libraryDependencies; + private List _mainProjectDependencies; + private HashSet _packagesToBeFiltered; + private bool _isFrameworkDependent; + private string _platformLibrary; + private string _dotnetFrameworkName; + private string _runtimeIdentifier; + private bool _isPortable; + private HashSet _usedLibraryNames; + + private Dictionary _referenceLibraryNames; + + // This resolver is only used for building file names, so that base path is not required. + private readonly VersionFolderPathResolver _versionFolderPathResolver = new VersionFolderPathResolver(rootPath: null); + + private const string NetCorePlatformLibrary = "Microsoft.NETCore.App"; + + public DependencyContextBuilder2(SingleProjectInfo mainProjectInfo, ProjectContext projectContext, bool includeRuntimeFileVersions) + { + _mainProjectInfo = mainProjectInfo; + _includeRuntimeFileVersions = includeRuntimeFileVersions; + + var libraryLookup = new LockFileLookup(projectContext.LockFile); + + _dependencyLibraries = projectContext.LockFileTarget.Libraries + .Select(lockFileTargetLibrary => + { + var dependencyLibrary = new DependencyLibrary(lockFileTargetLibrary.Name, lockFileTargetLibrary.Version, lockFileTargetLibrary.Type); + + LockFileLibrary library; + if (libraryLookup.TryGetLibrary(lockFileTargetLibrary, out library)) + { + dependencyLibrary.Sha512 = library.Sha512; + dependencyLibrary.Path = library.Path; + dependencyLibrary.MSBuildProject = library.MSBuildProject; + } + + return dependencyLibrary; + }).ToDictionary(d => d.Name, StringComparer.OrdinalIgnoreCase); + + _libraryDependencies = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var library in projectContext.LockFileTarget.Libraries) + { + _libraryDependencies[library.Name] = library.Dependencies + .Select(d => new LibraryDependency() + { + Name = d.Id, + MinVersion = d.VersionRange.MinVersion + }).ToList(); + } + + _mainProjectDependencies = projectContext.GetTopLevelDependencies().ToList(); + _packagesToBeFiltered = projectContext.PackagesToBeFiltered; + + _isFrameworkDependent = projectContext.IsFrameworkDependent; + _platformLibrary = projectContext.PlatformLibrary?.Name; + _dotnetFrameworkName = projectContext.LockFileTarget.TargetFramework.DotNetFrameworkName; + _runtimeIdentifier = projectContext.LockFileTarget.RuntimeIdentifier; + _isPortable = projectContext.IsPortable; + + _usedLibraryNames = new HashSet(_dependencyLibraries.Keys, StringComparer.OrdinalIgnoreCase); + } + + private bool IncludeCompilationLibraries => _compilationOptions != null; + + private Dictionary ReferenceLibraryNames + { + get + { + if (_referenceLibraryNames == null) + { + _referenceLibraryNames = new Dictionary(); + } + + return _referenceLibraryNames; + } + } + + public DependencyContextBuilder2 WithReferenceAssemblies(IEnumerable referenceAssemblies) + { + // note: ReferenceAssembly libraries only export compile-time stuff + // since they assume the runtime library is present already + _referenceAssemblies = referenceAssemblies; + return this; + } + + public DependencyContextBuilder2 WithDirectReferences(IEnumerable directReferences) + { + _directReferences = directReferences; + return this; + } + + public DependencyContextBuilder2 WithDependencyReferences(IEnumerable dependencyReferences) + { + _dependencyReferences = dependencyReferences; + return this; + } + + public DependencyContextBuilder2 WithCompileReferences(IEnumerable compileReferences) + { + _compileReferences = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var group in compileReferences.GroupBy(r => r.PackageName, StringComparer.OrdinalIgnoreCase)) + { + _compileReferences.Add(group.Key, group.ToList()); + } + + return this; + } + + public DependencyContextBuilder2 WithResolvedNuGetFiles(IEnumerable resolvedNuGetFiles) + { + _resolvedNuGetFiles = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var group in resolvedNuGetFiles.GroupBy(f => f.PackageName, StringComparer.OrdinalIgnoreCase)) + { + _resolvedNuGetFiles.Add(group.Key, group.ToList()); + } + + return this; + } + + public DependencyContextBuilder2 WithReferenceProjectInfos(Dictionary referenceProjectInfos) + { + _referenceProjectInfos = referenceProjectInfos; + return this; + } + + public DependencyContextBuilder2 WithMainProjectInDepsFile(bool includeMainProjectInDepsFile) + { + _includeMainProjectInDepsFile = includeMainProjectInDepsFile; + return this; + } + + public DependencyContextBuilder2 WithExcludeFromPublishAssets(IEnumerable excludeFromPublishPackageIds) + { + _excludeFromPublishPackageIds = excludeFromPublishPackageIds; + return this; + } + + public DependencyContextBuilder2 WithRuntimePackAssets(IEnumerable runtimePackAssets) + { + _runtimePackAssets = new Dictionary>(); + foreach (var runtimePackGroup in runtimePackAssets.GroupBy(a => a.PackageName)) + { + var dependencyLibrary = new DependencyLibrary("runtimepack." + runtimePackGroup.Key, + NuGetVersion.Parse(runtimePackGroup.First().PackageVersion), + "runtimepack"); + + _dependencyLibraries.Add(dependencyLibrary.Name, dependencyLibrary); + + _runtimePackAssets[dependencyLibrary.Name] = runtimePackGroup.ToList(); + } + return this; + } + + public DependencyContextBuilder2 WithCompilationOptions(CompilationOptions compilationOptions) + { + _compilationOptions = compilationOptions; + return this; + } + + public DependencyContextBuilder2 WithReferenceAssembliesPath(string referenceAssembliesPath) + { + // if the path is empty, we want to use the original string instead of a single trailing character. + if (string.IsNullOrEmpty(referenceAssembliesPath) || + referenceAssembliesPath[referenceAssembliesPath.Length - 1] == Path.DirectorySeparatorChar) + { + _referenceAssembliesPath = referenceAssembliesPath; + } + else + { + _referenceAssembliesPath = referenceAssembliesPath + Path.DirectorySeparatorChar; + } + + return this; + } + + public DependencyContextBuilder2 WithPackagesThatWereFiltered(Dictionary packagesThatWhereFiltered) + { + _filteredPackages = packagesThatWhereFiltered; + return this; + } + + public DependencyContext Build() + { + CalculateExcludedLibraries(); + + List runtimeLibraries = new List(); + + if (_includeMainProjectInDepsFile) + { + runtimeLibraries.Add(GetProjectRuntimeLibrary()); + } + + runtimeLibraries.AddRange(GetRuntimePackLibraries()); + + foreach (var library in _dependencyLibraries.Values + .Where(l => !l.ExcludeFromRuntime && l.Type != "runtimepack")) + { + var runtimeLibrary = GetRuntimeLibrary(library); + if (runtimeLibrary != null) + { + runtimeLibraries.Add(runtimeLibrary); + } + } + + var directAndDependencyReferences = _directReferences ?? Enumerable.Empty(); + if (_dependencyReferences != null) + { + directAndDependencyReferences = directAndDependencyReferences.Concat(_dependencyReferences); + } + + foreach (var directReference in directAndDependencyReferences) + { + var runtimeLibrary = new RuntimeLibrary( + type: "reference", + name: GetReferenceLibraryName(directReference), + version: directReference.Version, + hash: string.Empty, + runtimeAssemblyGroups: new[] { new RuntimeAssetGroup(string.Empty, new[] { CreateRuntimeFile(directReference.FileName, directReference.FullPath) }) }, + nativeLibraryGroups: new RuntimeAssetGroup[] { }, + resourceAssemblies: CreateResourceAssemblies(directReference.ResourceAssemblies), + dependencies: Enumerable.Empty(), + path: null, + hashPath: null, + runtimeStoreManifestName: null, + serviceable: false); + + runtimeLibraries.Add(runtimeLibrary); + } + + List compilationLibraries = new List(); + if (IncludeCompilationLibraries) + { + if (_includeMainProjectInDepsFile) + { + var dependencies = GetProjectDependencies(); + + var projectCompilationLibrary = new CompilationLibrary( + type: "project", + name: _mainProjectInfo.Name, + version: _mainProjectInfo.Version, + hash: string.Empty, + assemblies: new[] { _mainProjectInfo.OutputName }, + dependencies: dependencies, + serviceable: false); + + compilationLibraries.Add(projectCompilationLibrary); + } + + if (_referenceAssemblies != null) + { + foreach (var referenceAssembly in _referenceAssemblies) + { + string resolvedPath; + if (!string.IsNullOrEmpty(_referenceAssembliesPath) && + referenceAssembly.FullPath?.StartsWith(_referenceAssembliesPath) == true) + { + resolvedPath = referenceAssembly.FullPath.Substring(_referenceAssembliesPath.Length); + } + else + { + resolvedPath = Path.GetFileName(referenceAssembly.FullPath); + } + + compilationLibraries.Add(new CompilationLibrary( + type: "referenceassembly", + name: GetReferenceLibraryName(referenceAssembly), + version: referenceAssembly.Version, + hash: string.Empty, + assemblies: new[] { resolvedPath }, + dependencies: Enumerable.Empty(), + serviceable: false)); + } + } + + foreach (var library in _dependencyLibraries.Values + .Where(l => !l.ExcludeFromCompilation && l.Type != "runtimepack")) + { + var compilationLibrary = GetCompilationLibrary(library); + if (compilationLibrary != null) + { + compilationLibraries.Add(compilationLibrary); + } + } + + if (_directReferences != null) + { + foreach (var directReference in _directReferences) + { + compilationLibraries.Add(new CompilationLibrary( + type: "reference", + name: GetReferenceLibraryName(directReference), + version: directReference.Version, + hash: string.Empty, + assemblies: new[] { directReference.FileName }, + dependencies: Enumerable.Empty(), + serviceable: false)); + } + } + } + + + var targetInfo = new TargetInfo( + _dotnetFrameworkName, + _runtimeIdentifier, + runtimeSignature: string.Empty, + _isPortable); + + return new DependencyContext( + targetInfo, + _compilationOptions ?? CompilationOptions.Default, + compilationLibraries, + runtimeLibraries, + new RuntimeFallbacks[] { }); + } + + private RuntimeLibrary GetProjectRuntimeLibrary() + { + RuntimeAssetGroup[] runtimeAssemblyGroups = new[] { new RuntimeAssetGroup(string.Empty, _mainProjectInfo.OutputName) }; + + var dependencies = GetProjectDependencies(); + + // Runtime pack assets only get added as dependencies to the runtime (not the compile) project + if (_runtimePackAssets != null) + { + foreach (var runtimePackName in _runtimePackAssets.Keys) + { + dependencies.Add(_dependencyLibraries[runtimePackName].Dependency); + } + } + + return new RuntimeLibrary( + type: "project", + name: _mainProjectInfo.Name, + version: _mainProjectInfo.Version, + hash: string.Empty, + runtimeAssemblyGroups: runtimeAssemblyGroups, + nativeLibraryGroups: Array.Empty(), + resourceAssemblies: CreateResourceAssemblies(_mainProjectInfo.ResourceAssemblies), + dependencies: dependencies, + path: null, + hashPath: null, + runtimeStoreManifestName: GetRuntimeStoreManifestName(_mainProjectInfo.Name, _mainProjectInfo.Version), + serviceable: false); + } + + private List GetProjectDependencies() + { + List dependencies = new List(); + foreach (var dependencyName in _mainProjectDependencies) + { + if (_dependencyLibraries.TryGetValue(dependencyName, out var dependencyLibrary)) + { + // Include dependency if it would be included either as a runtime or compilation + // (if compilation libraries are being included) library + if (!dependencyLibrary.ExcludeFromRuntime || + (IncludeCompilationLibraries && !dependencyLibrary.ExcludeFromCompilation)) + { + dependencies.Add(dependencyLibrary.Dependency); + } + } + } + + var references = _directReferences; + if (IncludeCompilationLibraries && _referenceAssemblies != null) + { + if (references == null) + { + references = _referenceAssemblies; + } + else + { + references = references.Concat(_referenceAssemblies); + } + } + + if (references != null) + { + foreach (var directReference in references) + { + dependencies.Add( + new Dependency( + GetReferenceLibraryName(directReference), + directReference.Version)); + } + } + + return dependencies; + } + + private IEnumerable GetRuntimePackLibraries() + { + if (_runtimePackAssets == null) + { + return Enumerable.Empty(); + } + return _runtimePackAssets.Select(runtimePack => + { + var runtimeAssemblyGroup = new RuntimeAssetGroup(string.Empty, + runtimePack.Value.Where(asset => asset.AssetType == AssetType.Runtime) + .Select(asset => CreateRuntimeFile(asset.DestinationSubPath, asset.SourcePath))); + + var nativeLibraryGroup = new RuntimeAssetGroup(string.Empty, + runtimePack.Value.Where(asset => asset.AssetType == AssetType.Native) + .Select(asset => CreateRuntimeFile(asset.DestinationSubPath, asset.SourcePath))); + + return new RuntimeLibrary( + type: "runtimepack", + name: runtimePack.Key, + version: runtimePack.Value.First().PackageVersion, + hash: string.Empty, + runtimeAssemblyGroups: new[] { runtimeAssemblyGroup }, + nativeLibraryGroups: new[] { nativeLibraryGroup }, + resourceAssemblies: Enumerable.Empty(), + dependencies: Enumerable.Empty(), + serviceable: false); + }); + } + + private RuntimeLibrary GetRuntimeLibrary(DependencyLibrary library) + { + GetCommonLibraryProperties(library, + out string hash, + out HashSet libraryDependencies, + out bool serviceable, + out string path, + out string hashPath, + out SingleProjectInfo referenceProjectInfo); + + if (referenceProjectInfo is UnreferencedProjectInfo) + { + // unreferenced ProjectInfos will be added later as simple dll dependencies + return null; + } + + List runtimeAssemblyGroups = new List(); + List nativeLibraryGroups = new List(); + List resourceAssemblies = new List(); + + if (library.Type == "project" && !(referenceProjectInfo is UnreferencedProjectInfo)) + { + runtimeAssemblyGroups.Add(new RuntimeAssetGroup(string.Empty, referenceProjectInfo.OutputName)); + + resourceAssemblies.AddRange(referenceProjectInfo.ResourceAssemblies + .Select(r => new ResourceAssembly(r.RelativePath, r.Culture))); + } + else + { + if (_resolvedNuGetFiles != null && _resolvedNuGetFiles.TryGetValue(library.Name, out var resolvedNuGetFiles)) + { + var runtimeFiles = resolvedNuGetFiles.Where(f => f.Asset == AssetType.Runtime && + !f.IsRuntimeTarget); + + runtimeAssemblyGroups.Add(new RuntimeAssetGroup(string.Empty, + runtimeFiles.Select(CreateRuntimeFile))); + + var nativeFiles = resolvedNuGetFiles.Where(f => f.Asset == AssetType.Native && + !f.IsRuntimeTarget); + + nativeLibraryGroups.Add(new RuntimeAssetGroup(string.Empty, + nativeFiles.Select(CreateRuntimeFile))); + + var resourceFiles = resolvedNuGetFiles.Where(f => f.Asset == AssetType.Resources && + !f.IsRuntimeTarget); + + resourceAssemblies.AddRange(resourceFiles.Select(f => new ResourceAssembly(f.PathInPackage, f.Culture))); + + var runtimeTargets = resolvedNuGetFiles.Where(f => f.IsRuntimeTarget) + .GroupBy(f => f.RuntimeIdentifier); + + foreach (var runtimeIdentifierGroup in runtimeTargets) + { + var managedRuntimeTargetsFiles = runtimeIdentifierGroup.Where(f => f.Asset == AssetType.Runtime).ToList(); + if (managedRuntimeTargetsFiles.Any()) + { + runtimeAssemblyGroups.Add(new RuntimeAssetGroup(runtimeIdentifierGroup.Key, + managedRuntimeTargetsFiles.Select(CreateRuntimeFile))); + } + + var nativeRuntimeTargetsFiles = runtimeIdentifierGroup.Where(f => f.Asset == AssetType.Native).ToList(); + if (nativeRuntimeTargetsFiles.Any()) + { + nativeLibraryGroups.Add(new RuntimeAssetGroup(runtimeIdentifierGroup.Key, + nativeRuntimeTargetsFiles.Select(CreateRuntimeFile))); + } + } + } + + } + + var runtimeLibrary = new RuntimeLibrary( + type: library.Type, + name: library.Name, + version: library.Version.ToString(), + hash: hash, + runtimeAssemblyGroups: runtimeAssemblyGroups, + nativeLibraryGroups: nativeLibraryGroups, + resourceAssemblies: resourceAssemblies, + dependencies: libraryDependencies, + path: path, + hashPath: hashPath, + runtimeStoreManifestName: GetRuntimeStoreManifestName(library.Name, library.Version.ToString()), + serviceable: serviceable); + + return runtimeLibrary; + } + + private CompilationLibrary GetCompilationLibrary(DependencyLibrary library) + { + GetCommonLibraryProperties(library, + out string hash, + out HashSet libraryDependencies, + out bool serviceable, + out string path, + out string hashPath, + out SingleProjectInfo referenceProjectInfo); + + List assemblies = new List(); + + if (library.Type == "project" && !(referenceProjectInfo is UnreferencedProjectInfo)) + { + assemblies.Add(referenceProjectInfo.OutputName); + } + else if (_compileReferences != null && _compileReferences.TryGetValue(library.Name, out var compileReferences)) + { + foreach (var compileReference in compileReferences) + { + assemblies.Add(compileReference.PathInPackage); + } + } + + return new CompilationLibrary( + type: library.Type, + name: library.Name, + version: library.Version.ToString(), + hash, + assemblies, + libraryDependencies, + serviceable, + path, + hashPath); + } + + private void GetCommonLibraryProperties(DependencyLibrary library, + out string hash, + out HashSet dependencies, + out bool serviceable, + out string path, + out string hashPath, + out SingleProjectInfo referenceProjectInfo) + { + serviceable = true; + referenceProjectInfo = null; + + dependencies = new HashSet(); + List libraryDependencies; + if (_libraryDependencies.TryGetValue(library.Name, out libraryDependencies)) + { + foreach (var dependency in libraryDependencies) + { + if (_dependencyLibraries.TryGetValue(dependency.Name, out var libraryDependency)) + { + if (!libraryDependency.ExcludeFromRuntime || + (!libraryDependency.ExcludeFromCompilation && IncludeCompilationLibraries)) + { + dependencies.Add(libraryDependency.Dependency); + } + } + } + } + + hash = string.Empty; + path = null; + hashPath = null; + if (library.Type == "package") + { + // TEMPORARY: All packages are serviceable in RC2 + // See https://github.com/dotnet/cli/issues/2569 + serviceable = true; + if (!string.IsNullOrEmpty(library.Sha512)) + { + hash = "sha512-" + library.Sha512; + hashPath = _versionFolderPathResolver.GetHashFileName(library.Name, library.Version); + } + + path = library.Path; + } + else if (library.Type == "project") + { + serviceable = false; + referenceProjectInfo = GetProjectInfo(library); + + foreach (var dependencyReference in referenceProjectInfo.DependencyReferences) + { + dependencies.Add( + new Dependency( + GetReferenceLibraryName(dependencyReference), + dependencyReference.Version)); + } + } + } + + private RuntimeFile CreateRuntimeFile(ResolvedFile resolvedFile) + { + string relativePath = resolvedFile.PathInPackage; + if (string.IsNullOrEmpty(relativePath)) + { + relativePath = resolvedFile.DestinationSubPath; + } + return CreateRuntimeFile(relativePath, resolvedFile.SourcePath); + } + + private RuntimeFile CreateRuntimeFile(string path, string fullPath) + { + if (_includeRuntimeFileVersions) + { + string fileVersion = FileUtilities.GetFileVersion(fullPath).ToString(); + string assemblyVersion = FileUtilities.TryGetAssemblyVersion(fullPath)?.ToString(); + return new RuntimeFile(path, assemblyVersion, fileVersion); + } + else + { + return new RuntimeFile(path, null, null); + } + } + + private static IEnumerable CreateResourceAssemblies(IEnumerable resourceAssemblyInfos) + { + return resourceAssemblyInfos + .Select(r => new ResourceAssembly(r.RelativePath, r.Culture)); + } + + private SingleProjectInfo GetProjectInfo(DependencyLibrary library) + { + string projectPath = library.MSBuildProject; + if (string.IsNullOrEmpty(projectPath)) + { + throw new BuildErrorException(Strings.CannotFindProjectInfo, library.Name); + } + + string mainProjectDirectory = Path.GetDirectoryName(_mainProjectInfo.ProjectPath); + string fullProjectPath = Path.GetFullPath(Path.Combine(mainProjectDirectory, projectPath)); + + SingleProjectInfo referenceProjectInfo = null; + if (_referenceProjectInfos?.TryGetValue(fullProjectPath, out referenceProjectInfo) != true || + referenceProjectInfo == null) + { + return UnreferencedProjectInfo.Default; + } + + return referenceProjectInfo; + } + + private void CalculateExcludedLibraries() + { + Dictionary libraries = _dependencyLibraries; + + HashSet runtimeExclusionList = new HashSet(StringComparer.OrdinalIgnoreCase); + + if (_isFrameworkDependent && !string.IsNullOrEmpty(_platformLibrary)) + { + // Exclude platform library and dependencies. + runtimeExclusionList.Add(_platformLibrary); + + Stack dependenciesToWalk = new Stack(_libraryDependencies[_platformLibrary]); + + // If the platform library is not Microsoft.NETCore.App, treat it as an implicit dependency. + // This makes it so Microsoft.AspNet.* 2.x platforms also exclude Microsoft.NETCore.App files. + if (!string.Equals(_platformLibrary, NetCorePlatformLibrary, StringComparison.OrdinalIgnoreCase)) + { + if (_dependencyLibraries.TryGetValue(NetCorePlatformLibrary, out var netCoreDependencyLibrary)) + { + dependenciesToWalk.Push(new LibraryDependency() + { + Name = netCoreDependencyLibrary.Name, + MinVersion = netCoreDependencyLibrary.Version + }); + } + } + + while (dependenciesToWalk.Any()) + { + var dependency = dependenciesToWalk.Pop(); + if (runtimeExclusionList.Contains(dependency.Name)) + { + continue; + } + + // Resolved version of library has to match dependency version exactly, so that we + // don't exclude newer versions of libraries that are part of the platform + if (_dependencyLibraries[dependency.Name].Version == dependency.MinVersion) + { + runtimeExclusionList.Add(dependency.Name); + foreach (var newDependency in _libraryDependencies[dependency.Name]) + { + dependenciesToWalk.Push(newDependency); + } + } + } + } + + if (_packagesToBeFiltered != null) + { + foreach (var packageToFilter in _packagesToBeFiltered) + { + if (_dependencyLibraries.TryGetValue(packageToFilter.Id, out var library)) + { + if (library.Type == "package" && + _dependencyLibraries[packageToFilter.Id].Version == packageToFilter.Version) + { + runtimeExclusionList.Add(packageToFilter.Id); + } + } + } + } + + foreach (var packageToExcludeFromRuntime in runtimeExclusionList) + { + _dependencyLibraries[packageToExcludeFromRuntime].ExcludeFromRuntime = true; + } + + if (_excludeFromPublishPackageIds != null && _excludeFromPublishPackageIds.Any()) + { + // Include transitive dependencies of all top-level dependencies which are not + // excluded from publish + + Dictionary includedDependencies = new Dictionary(StringComparer.OrdinalIgnoreCase); + + HashSet excludeFromPublishPackageIds = new HashSet(_excludeFromPublishPackageIds); + + Stack dependenciesToWalk = new Stack( + _mainProjectDependencies.Except(_excludeFromPublishPackageIds, StringComparer.OrdinalIgnoreCase)); + + while (dependenciesToWalk.Any()) + { + var dependencyName = dependenciesToWalk.Pop(); + if (!includedDependencies.ContainsKey(dependencyName)) + { + includedDependencies.Add(dependencyName, _dependencyLibraries[dependencyName]); + foreach (var newDependency in _libraryDependencies[dependencyName]) + { + dependenciesToWalk.Push(newDependency.Name); + } + } + } + + foreach (var dependencyLibrary in _dependencyLibraries.Values) + { + // Libraries explicitly marked as exclude from publish should be excluded from + // publish even if there are other transitive dependencies to them + if (!includedDependencies.ContainsKey(dependencyLibrary.Name) || + excludeFromPublishPackageIds.Contains(dependencyLibrary.Name)) + { + dependencyLibrary.ExcludeFromCompilation = true; + dependencyLibrary.ExcludeFromRuntime = true; + } + } + } + } + + private string GetReferenceLibraryName(ReferenceInfo reference) + { + if (!ReferenceLibraryNames.TryGetValue(reference, out string name)) + { + // Reference names can conflict with PackageReference names, so + // ensure that the Reference names are unique when creating libraries + name = GetUniqueReferenceName(reference.Name); + + ReferenceLibraryNames.Add(reference, name); + _usedLibraryNames.Add(name); + } + + return name; + } + + private string GetUniqueReferenceName(string name) + { + if (_usedLibraryNames.Contains(name)) + { + string startingName = $"{name}.Reference"; + name = startingName; + + int suffix = 1; + while (_usedLibraryNames.Contains(name)) + { + name = $"{startingName}{suffix++}"; + } + } + + return name; + } + + private string GetRuntimeStoreManifestName(string packageName, string packageVersion) + { + string runtimeStoreManifestName = null; + if (_filteredPackages != null && _filteredPackages.Any()) + { + var pkg = new PackageIdentity(packageName, NuGetVersion.Parse(packageVersion)); + _filteredPackages?.TryGetValue(pkg, out runtimeStoreManifestName); + } + return runtimeStoreManifestName; + } + + private class DependencyLibrary + { + public string Name { get; } + public NuGetVersion Version { get; } + public string Type { get; } + public Dependency Dependency { get; } + public string Sha512 { get; set; } + public string Path { get; set; } + public string MSBuildProject { get; set; } + + public bool ExcludeFromRuntime { get; set; } + + public bool ExcludeFromCompilation { get; set; } + + public DependencyLibrary(string name, NuGetVersion version, string type) + { + Name = name; + Version = version; + Type = type; + Dependency = new Dependency(name, version.ToString()); + } + } + + private struct LibraryDependency + { + public string Name { get; set; } + public NuGetVersion MinVersion { get; set; } + } + } +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs index fbcb77e151ce..b35d5a5969c6 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs @@ -5,6 +5,7 @@ using Microsoft.Build.Utilities; using Microsoft.Extensions.DependencyModel; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NuGet.Packaging.Core; using NuGet.ProjectModel; using NuGet.Versioning; @@ -71,10 +72,22 @@ public class GenerateDepsFile : TaskBase public ITaskItem[] RuntimeStorePackages { get; set; } + [Required] + public ITaskItem[] CompileReferences { get; set; } + + // NativeCopyLocalItems, ResourceCopyLocalItems, RuntimeCopyLocalItems + [Required] + public ITaskItem[] ResolvedNuGetFiles { get; set; } + + [Required] + public ITaskItem[] ResolvedRuntimeTargetsFiles { get; set; } + public bool IsSelfContained { get; set; } public bool IncludeRuntimeFileVersions { get; set; } + public string DepsFileGenerationMode { get; set; } = "both"; + List _filesWritten = new List(); [Output] @@ -104,7 +117,7 @@ private Dictionary GetFilteredPackages() return filteredPackages; } - protected override void ExecuteCore() + private void WriteDepsFileOld(string depsFilePath) { LoadFilesToSkip(); @@ -163,12 +176,137 @@ protected override void ExecuteCore() } var writer = new DependencyContextWriter(); - using (var fileStream = File.Create(DepsFilePath)) + using (var fileStream = File.Create(depsFilePath)) { writer.Write(dependencyContext, fileStream); } - _filesWritten.Add(new TaskItem(DepsFilePath)); + _filesWritten.Add(new TaskItem(depsFilePath)); + } + + private void WriteDepsFileNew(string depsFilePath) + { + LockFile lockFile = new LockFileCache(this).GetLockFile(AssetsFilePath); + CompilationOptions compilationOptions = CompilationOptionsConverter.ConvertFrom(CompilerOptions); + + SingleProjectInfo mainProject = SingleProjectInfo.Create( + ProjectPath, + AssemblyName, + AssemblyExtension, + AssemblyVersion, + AssemblySatelliteAssemblies); + + IEnumerable referenceAssemblyInfos = + ReferenceInfo.CreateReferenceInfos(ReferenceAssemblies); + + IEnumerable directReferences = + ReferenceInfo.CreateDirectReferenceInfos(ReferencePaths, ReferenceSatellitePaths); + + IEnumerable dependencyReferences = + ReferenceInfo.CreateDependencyReferenceInfos(ReferenceDependencyPaths, ReferenceSatellitePaths); + + Dictionary referenceProjects = SingleProjectInfo.CreateProjectReferenceInfos( + ReferencePaths, + ReferenceDependencyPaths, + ReferenceSatellitePaths); + + IEnumerable excludeFromPublishAssets = PackageReferenceConverter.GetPackageIds(ExcludeFromPublishPackageReferences); + + IEnumerable runtimePackAssets = + RuntimePackAssets.Select(item => RuntimePackAssetInfo.FromItem(item)); + + ProjectContext projectContext = lockFile.CreateProjectContext( + NuGetUtils.ParseFrameworkName(TargetFramework), + RuntimeIdentifier, + PlatformLibraryName, + RuntimeFrameworks, + IsSelfContained); + + var builder = new DependencyContextBuilder2(mainProject, projectContext, IncludeRuntimeFileVersions); + + builder = builder + .WithMainProjectInDepsFile(IncludeMainProject) + .WithReferenceAssemblies(referenceAssemblyInfos) + .WithDirectReferences(directReferences) + .WithDependencyReferences(dependencyReferences) + .WithReferenceProjectInfos(referenceProjects) + .WithExcludeFromPublishAssets(excludeFromPublishAssets) + .WithRuntimePackAssets(runtimePackAssets) + .WithCompilationOptions(compilationOptions) + .WithReferenceAssembliesPath(FrameworkReferenceResolver.GetDefaultReferenceAssembliesPath()) + .WithPackagesThatWereFiltered(GetFilteredPackages()); + + if (CompileReferences.Length > 0) + { + builder = builder.WithCompileReferences(ReferenceInfo.CreateReferenceInfos(CompileReferences)); + } + + var resolvedNuGetFiles = ResolvedNuGetFiles.Select(f => new ResolvedFile(f, false)) + .Concat(ResolvedRuntimeTargetsFiles.Select(f => new ResolvedFile(f, true))); + builder = builder.WithResolvedNuGetFiles(resolvedNuGetFiles); + + DependencyContext dependencyContext = builder.Build(); + + var writer = new DependencyContextWriter(); + using (var fileStream = File.Create(depsFilePath)) + { + writer.Write(dependencyContext, fileStream); + } + _filesWritten.Add(new TaskItem(depsFilePath)); + } + + bool _loggedLocalError = false; + + public override bool Execute() + { + if (!base.Execute() || _loggedLocalError) + { + return false; + } + return true; + } + protected override void ExecuteCore() + { + if (DepsFileGenerationMode.Equals("old", StringComparison.InvariantCultureIgnoreCase)) + { + WriteDepsFileOld(DepsFilePath); + } + else if (DepsFileGenerationMode.Equals("new", StringComparison.InvariantCultureIgnoreCase)) + { + WriteDepsFileNew(DepsFilePath); + } + else + { + var newDepsFilePath = Path.ChangeExtension(DepsFilePath, ".new.json"); + + WriteDepsFileOld(DepsFilePath); + + WriteDepsFileNew(newDepsFilePath); + + var oldJson = File.ReadAllText(DepsFilePath); + var newJson = File.ReadAllText(newDepsFilePath); + + if (oldJson != newJson) + { + string message = "Internal error: new deps file generation logic did not produce the same result as the old logic." + Environment.NewLine + + " Please file an issue for this at https://github.com/dotnet/sdk and include the following two files: " + Environment.NewLine + + " Deps file from old logic: " + DepsFilePath + Environment.NewLine + + " Deps file from new logic: " + newDepsFilePath + Environment.NewLine + + " You can work around this by setting the DepsFileGenerationMode MSBuild property to 'old'"; + + // This is a temporary error message that we won't localize or assign an SDK + // error code to. So use the Task classes Log property instead of our wrapper + // around it (which would force it to have an error code) + ((Task) this).Log.LogError(message); + + _loggedLocalError = true; + } + else + { + // If the files matched, then delete the .new.json file + File.Delete(newDepsFilePath); + } + } } private void LoadFilesToSkip() @@ -236,7 +374,11 @@ private IEnumerable TrimAssetGroups(IEnumerable _lockFile; public LockFileTarget LockFileTarget => _lockFileTarget; + public LockFileTarget CompilationLockFileTarget { get; } + public ProjectContext(LockFile lockFile, LockFileTarget lockFileTarget, // Trimmed from publish output, and if there are no runtimeFrameworks, written to runtimeconfig.json LockFileTargetLibrary platformLibrary, @@ -71,6 +73,14 @@ public ProjectContext(LockFile lockFile, LockFileTarget lockFileTarget, _lockFile = lockFile; _lockFileTarget = lockFileTarget; + if (string.IsNullOrEmpty(lockFileTarget.RuntimeIdentifier)) + { + CompilationLockFileTarget = lockFileTarget; + } + else + { + CompilationLockFileTarget = lockFile.GetTargetAndThrowIfNotFound(lockFileTarget.TargetFramework, null); + } PlatformLibrary = platformLibrary; RuntimeFrameworks = runtimeFrameworks; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ReferenceInfo.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ReferenceInfo.cs index 39883008747d..cf39b20300b3 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ReferenceInfo.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ReferenceInfo.cs @@ -16,17 +16,25 @@ internal class ReferenceInfo public string FullPath { get; } public string FileName => Path.GetFileName(FullPath); + public string PackageName { get; } + public string PackageVersion { get; } + public string PathInPackage { get; } + private List _resourceAssemblies; public IEnumerable ResourceAssemblies { get { return _resourceAssemblies; } } - private ReferenceInfo(string name, string version, string fullPath) + private ReferenceInfo(string name, string version, string fullPath, + string packageName, string packageVersion, string pathInPackage) { Name = name; Version = version; FullPath = fullPath; + PackageName = packageName; + PackageVersion = packageVersion; + PathInPackage = pathInPackage; _resourceAssemblies = new List(); } @@ -107,7 +115,22 @@ internal static ReferenceInfo CreateReferenceInfo(ITaskItem referencePath) string name = Path.GetFileNameWithoutExtension(fullPath); string version = GetVersion(referencePath); - return new ReferenceInfo(name, version, fullPath); + var packageName = referencePath.GetMetadata(MetadataKeys.NuGetPackageId); + if (string.IsNullOrEmpty(packageName)) + { + packageName = referencePath.GetMetadata(MetadataKeys.PackageName); + } + + var packageVersion = referencePath.GetMetadata(MetadataKeys.NuGetPackageVersion); + if (string.IsNullOrEmpty(packageVersion)) + { + packageVersion = referencePath.GetMetadata(MetadataKeys.PackageVersion); + } + + var pathInPackage = referencePath.GetMetadata(MetadataKeys.PathInPackage); + + return new ReferenceInfo(name, version, fullPath, + packageName, packageVersion, pathInPackage); } private static string GetVersion(ITaskItem referencePath) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveAppHosts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveAppHosts.cs index 5cf64de4264e..9d74a349b3a3 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveAppHosts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveAppHosts.cs @@ -227,7 +227,7 @@ private ITaskItem GetHostItem(string runtimeIdentifier, appHostItem.SetMetadata(MetadataKeys.PackageVersion, appHostPackVersion); } - appHostItem.SetMetadata(MetadataKeys.RelativePath, hostRelativePathInPackage); + appHostItem.SetMetadata(MetadataKeys.PathInPackage, hostRelativePathInPackage); appHostItem.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimeIdentifier); return appHostItem; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveCopyLocalAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveCopyLocalAssets.cs index f7fefc216066..fb298e08256a 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveCopyLocalAssets.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveCopyLocalAssets.cs @@ -78,8 +78,8 @@ protected override void ExecuteCore() item.SetMetadata(MetadataKeys.DestinationSubPath, resolvedFile.DestinationSubPath); item.SetMetadata(MetadataKeys.DestinationSubDirectory, resolvedFile.DestinationSubDirectory); item.SetMetadata(MetadataKeys.AssetType, resolvedFile.Asset.ToString().ToLowerInvariant()); - item.SetMetadata(MetadataKeys.PackageName, resolvedFile.Package.Id.ToString()); - item.SetMetadata(MetadataKeys.PackageVersion, resolvedFile.Package.Version.ToString().ToLowerInvariant()); + item.SetMetadata(MetadataKeys.PackageName, resolvedFile.PackageName); + item.SetMetadata(MetadataKeys.PackageVersion, resolvedFile.PackageVersion.ToLowerInvariant()); if (resolvedFile.Asset == AssetType.Resources) { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveFrameworkReferences.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveFrameworkReferences.cs index 4ecf355cb424..0016504f3fda 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveFrameworkReferences.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveFrameworkReferences.cs @@ -108,6 +108,7 @@ protected override void ExecuteCore() targetingPack.SetMetadata(MetadataKeys.PackageVersion, targetingPackVersion); targetingPack.SetMetadata("TargetingPackFormat", knownFrameworkReference.TargetingPackFormat); targetingPack.SetMetadata("TargetFramework", knownFrameworkReference.TargetFramework.GetShortFolderName()); + targetingPack.SetMetadata("RuntimeFrameworkName", knownFrameworkReference.RuntimeFrameworkName); string targetingPackPath = null; if (!string.IsNullOrEmpty(TargetingPackRoot)) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs index d86f158fefd7..d33a0f6ec7c7 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs @@ -262,7 +262,7 @@ public sealed class ResolvePackageAssets : TaskBase //////////////////////////////////////////////////////////////////////////////////////////////////// private const int CacheFormatSignature = ('P' << 0) | ('K' << 8) | ('G' << 16) | ('A' << 24); - private const int CacheFormatVersion = 6; + private const int CacheFormatVersion = 7; private static readonly Encoding TextEncoding = Encoding.UTF8; private const int SettingsHashLength = 256 / 8; private HashAlgorithm CreateSettingsHash() => SHA256.Create(); @@ -947,6 +947,7 @@ private void WriteNativeLibraries() package => package.NativeLibraries, writeMetadata: (package, asset) => { + WriteMetadata(MetadataKeys.AssetType, "native"); if (ShouldCopyLocalPackageAssets(package)) { WriteCopyLocalMetadata(package, Path.GetFileName(asset.Path), "native"); @@ -1011,6 +1012,7 @@ private void WriteResourceAssemblies() string.Equals(asset.Properties["locale"], lang.ItemSpec, StringComparison.OrdinalIgnoreCase))), writeMetadata: (package, asset) => { + WriteMetadata(MetadataKeys.AssetType, "resources"); string locale = asset.Properties["locale"]; if (ShouldCopyLocalPackageAssets(package)) { @@ -1035,6 +1037,7 @@ private void WriteRuntimeAssemblies() package => package.RuntimeAssemblies, writeMetadata: (package, asset) => { + WriteMetadata(MetadataKeys.AssetType, "runtime"); if (ShouldCopyLocalPackageAssets(package)) { WriteCopyLocalMetadata(package, Path.GetFileName(asset.Path), "runtime"); @@ -1054,6 +1057,7 @@ private void WriteRuntimeTargets() package => package.RuntimeTargets, writeMetadata: (package, asset) => { + WriteMetadata(MetadataKeys.AssetType, asset.AssetType.ToLowerInvariant()); if (ShouldCopyLocalPackageAssets(package)) { WriteCopyLocalMetadata( @@ -1066,6 +1070,7 @@ private void WriteRuntimeTargets() { WriteMetadata(MetadataKeys.DestinationSubDirectory, Path.GetDirectoryName(asset.Path) + Path.DirectorySeparatorChar); } + WriteMetadata(MetadataKeys.RuntimeIdentifier, asset.Runtime); }); } @@ -1121,6 +1126,10 @@ private void WriteItems( string itemSpec = _packageResolver.ResolvePackageAssetPath(library, asset.Path); WriteItem(itemSpec, library); + WriteMetadata(MetadataKeys.PathInPackage, asset.Path); + WriteMetadata(MetadataKeys.PackageName, library.Name); + WriteMetadata(MetadataKeys.PackageVersion, library.Version.ToString().ToLowerInvariant()); + writeMetadata?.Invoke(library, asset); } } @@ -1161,9 +1170,6 @@ private void WriteCopyLocalMetadata(LockFileTargetLibrary package, string assets { WriteMetadata(MetadataKeys.DestinationSubDirectory, destinationSubDirectory); } - WriteMetadata(MetadataKeys.AssetType, assetType); - WriteMetadata(MetadataKeys.PackageName, package.Name); - WriteMetadata(MetadataKeys.PackageVersion, package.Version.ToString().ToLowerInvariant()); } private int GetMetadataIndex(string value) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveTargetingPackAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveTargetingPackAssets.cs index e84d8d74e9a6..0a4521db72d5 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolveTargetingPackAssets.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolveTargetingPackAssets.cs @@ -93,6 +93,8 @@ protected override void ExecuteCore() string platformManifestPath = possibleManifestPaths.FirstOrDefault(File.Exists); + string packageOverridesPath = Path.Combine(targetingPackDataPath, "PackageOverrides.txt"); + foreach (var dll in Directory.GetFiles(targetingPackDllPath, "*.dll")) { var reference = CreateReferenceItem(dll, targetingPack); @@ -105,6 +107,11 @@ protected override void ExecuteCore() platformManifests.Add(new TaskItem(platformManifestPath)); } + if (File.Exists(packageOverridesPath)) + { + packageConflictOverrides.Add(CreatePackageOverride(targetingPack.GetMetadata("RuntimeFrameworkName"), packageOverridesPath)); + } + if (targetingPack.ItemSpec.Equals("Microsoft.NETCore.App", StringComparison.OrdinalIgnoreCase)) { // Hardcode this for now. Load this from the targeting pack once we have "real" targeting packs @@ -125,6 +132,13 @@ protected override void ExecuteCore() PackageConflictOverrides = packageConflictOverrides.ToArray(); } + private TaskItem CreatePackageOverride(string runtimeFrameworkName, string packageOverridesPath) + { + TaskItem packageOverride = new TaskItem(runtimeFrameworkName); + packageOverride.SetMetadata("OverriddenPackages", File.ReadAllText(packageOverridesPath)); + return packageOverride; + } + private void AddNetStandardTargetingPackAssets(ITaskItem targetingPack, string targetingPackRoot, List referencesToAdd) { string targetingPackTargetFramework = targetingPack.GetMetadata("TargetFramework"); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvedFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvedFile.cs index 7ba8ea38eff2..ffa8ae0ee1a5 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvedFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvedFile.cs @@ -4,6 +4,8 @@ using System.IO; using System; using NuGet.Packaging.Core; +using Microsoft.Build.Framework; + namespace Microsoft.NET.Build.Tasks { internal enum AssetType @@ -17,9 +19,14 @@ internal enum AssetType internal class ResolvedFile { public string SourcePath { get; } - public PackageIdentity Package { get; } + public string PackageName { get; } + public string PackageVersion { get; } + public string PathInPackage { get; } public string DestinationSubDirectory { get; } public AssetType Asset{ get; } + public bool IsRuntimeTarget { get; } + public string RuntimeIdentifier { get; } + public string Culture { get; } public string FileName { get { return Path.GetFileName(SourcePath); } @@ -40,7 +47,51 @@ public ResolvedFile(string sourcePath, string destinationSubDirectory, PackageId SourcePath = Path.GetFullPath(sourcePath); DestinationSubDirectory = destinationSubDirectory; Asset = assetType; - Package = package; + PackageName = package.Id; + PackageVersion = package.Version.ToString(); + + } + + public ResolvedFile(ITaskItem item, bool isRuntimeTarget) + { + SourcePath = item.ItemSpec; + DestinationSubDirectory = item.GetMetadata(MetadataKeys.DestinationSubDirectory); + string assetType = item.GetMetadata(MetadataKeys.AssetType); + if (assetType.Equals("runtime", StringComparison.OrdinalIgnoreCase)) + { + Asset = AssetType.Runtime; + } + else if (assetType.Equals("native", StringComparison.OrdinalIgnoreCase)) + { + Asset = AssetType.Native; + } + else if (assetType.Equals("resources", StringComparison.OrdinalIgnoreCase)) + { + Asset = AssetType.Resources; + } + else + { + throw new InvalidOperationException($"Unrecognized AssetType '{assetType}' for {SourcePath}"); + } + + PackageName = item.GetMetadata(MetadataKeys.NuGetPackageId); + if (string.IsNullOrEmpty(PackageName)) + { + PackageName = item.GetMetadata(MetadataKeys.PackageName); + } + + PackageVersion = item.GetMetadata(MetadataKeys.NuGetPackageVersion); + if (string.IsNullOrEmpty(PackageVersion)) + { + PackageVersion = item.GetMetadata(MetadataKeys.PackageVersion); + } + + PathInPackage = item.GetMetadata(MetadataKeys.PathInPackage); + + RuntimeIdentifier = item.GetMetadata(MetadataKeys.RuntimeIdentifier); + Culture = item.GetMetadata(MetadataKeys.Culture); + + IsRuntimeTarget = isRuntimeTarget; } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DesignerSupport.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DesignerSupport.targets index 05ab860153a0..a83481bd1311 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DesignerSupport.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DesignerSupport.targets @@ -64,7 +64,11 @@ Copyright (c) .NET Foundation. All rights reserved. PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" ProjectPath="$(MSBuildProjectFullPath)" RuntimeFrameworks="@(RuntimeFramework)" + CompileReferences="@(ResolvedCompileFileDefinitions)" + ResolvedNuGetFiles="@(NativeCopyLocalItems);@(ResourceCopyLocalItems);@(RuntimeCopyLocalItems)" + ResolvedRuntimeTargetsFiles="@(RuntimeTargetsCopyLocalItems)" TargetFramework="$(TargetFrameworkMoniker)" + DepsFileGenerationMode="$(DepsFileGenerationMode)" /> diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets index 62122f2c181e..130492517ee7 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets @@ -789,6 +789,15 @@ Copyright (c) .NET Foundation. All rights reserved. $(PublishDir)$(ProjectDepsFileName) + + + + + + + + + + IncludeRuntimeFileVersions="$(IncludeFileVersionsInDependencyFile)" + DepsFileGenerationMode="$(DepsFileGenerationMode)"/> diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.TargetingPackResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.TargetingPackResolution.targets index c6877fe34410..cf4d0ff37339 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.TargetingPackResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.TargetingPackResolution.targets @@ -214,14 +214,14 @@ Copyright (c) .NET Foundation. All rights reserved. - <_ApphostsForShimRuntimeIdentifiers Include="%(_ApphostsForShimRuntimeIdentifiersGetPackageDirectory.PackageDirectory)\%(_ApphostsForShimRuntimeIdentifiersGetPackageDirectory.RelativePath)" > + <_ApphostsForShimRuntimeIdentifiers Include="%(_ApphostsForShimRuntimeIdentifiersGetPackageDirectory.PackageDirectory)\%(_ApphostsForShimRuntimeIdentifiersGetPackageDirectory.PathInPackage)" > %(_ApphostsForShimRuntimeIdentifiersGetPackageDirectory.RuntimeIdentifier) - %(ResolvedAppHostPack.PackageDirectory)\%(ResolvedAppHostPack.RelativePath) + %(ResolvedAppHostPack.PackageDirectory)\%(ResolvedAppHostPack.PathInPackage) @@ -231,7 +231,7 @@ Copyright (c) .NET Foundation. All rights reserved. - %(ResolvedComHostPack.PackageDirectory)\%(ResolvedComHostPack.RelativePath) + %(ResolvedComHostPack.PackageDirectory)\%(ResolvedComHostPack.PathInPackage) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index 2335aa4eabb4..05b9790b4a23 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -133,6 +133,15 @@ Copyright (c) .NET Foundation. All rights reserved. Inputs="$(ProjectAssetsFile);$(MSBuildAllProjects)" Outputs="$(ProjectDepsFilePath)"> + + + + + + + + + + IncludeRuntimeFileVersions="$(IncludeFileVersionsInDependencyFile)" + DepsFileGenerationMode="$(DepsFileGenerationMode)"> diff --git a/src/Tests/Microsoft.NET.Build.Tests/DepsFileSkipTests.cs b/src/Tests/Microsoft.NET.Build.Tests/DepsFileSkipTests.cs new file mode 100644 index 000000000000..5189f3878739 --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Tests/DepsFileSkipTests.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Xml.Linq; +using FluentAssertions; +using Microsoft.Extensions.DependencyModel; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using Microsoft.NET.TestFramework.ProjectConstruction; +using Newtonsoft.Json.Linq; +using NuGet.Frameworks; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NET.Build.Tests +{ + public class DepsFileSkipTests : SdkTest + { + public DepsFileSkipTests(ITestOutputHelper log) : base(log) + { + + } + + [Fact] + public void RuntimeAssemblyFromPackageCanBeSkipped() + { + var testProject = new TestProject() + { + Name = "SkipRuntimeAssemblyFromPackage", + TargetFrameworks = "netcoreapp3.0", + IsSdkProject = true, + IsExe = true + }; + + testProject.PackageReferences.Add(new TestPackageReference("Newtonsoft.Json", "12.0.1")); + + string filenameToSkip = "Newtonsoft.Json.dll"; + + TestSkippingFile(testProject, filenameToSkip, "runtime"); + } + + [Fact] + public void RuntimeAssemblyFromRuntimePackCanBeSkipped() + { + var testProject = new TestProject() + { + Name = "SkipRuntimeAssemblyFromRuntimePack", + TargetFrameworks = "netcoreapp3.0", + IsSdkProject = true, + IsExe = true + }; + + testProject.RuntimeIdentifier = EnvironmentInfo.GetCompatibleRid(testProject.TargetFrameworks); + + string filenameToSkip = "Microsoft.CSharp.dll"; + + TestSkippingFile(testProject, filenameToSkip, "runtime"); + } + + [Fact] + public void NativeAssetFromPackageCanBeSkipped() + { + var testProject = new TestProject() + { + Name = "SkipNativeAssetFromPackage", + TargetFrameworks = "netcoreapp3.0", + IsSdkProject = true, + IsExe = true + }; + + testProject.RuntimeIdentifier = EnvironmentInfo.GetCompatibleRid(testProject.TargetFrameworks); + + testProject.PackageReferences.Add(new TestPackageReference("sqlite", "3.13.0")); + + string filenameToSkip = FileConstants.DynamicLibPrefix + "sqlite3" + FileConstants.DynamicLibSuffix; + + TestSkippingFile(testProject, filenameToSkip, "native"); + } + + [Fact] + public void RuntimeTargetFromPackageCanBeSkipped() + { + var testProject = new TestProject() + { + Name = "SkipNativeAssetFromPackage", + TargetFrameworks = "netcoreapp3.0", + IsSdkProject = true, + IsExe = true + }; + + testProject.PackageReferences.Add(new TestPackageReference("sqlite", "3.13.0")); + + string filenameToSkip = FileConstants.DynamicLibPrefix + "sqlite3" + FileConstants.DynamicLibSuffix; + + TestSkippingFile(testProject, filenameToSkip, "runtimeTargets"); + } + + [Fact] + public void NativeAssetFromRuntimePackCanBeSkipped() + { + var testProject = new TestProject() + { + Name = "SkipNativeAssetFromRuntimePack", + TargetFrameworks = "netcoreapp3.0", + IsSdkProject = true, + IsExe = true + }; + + testProject.RuntimeIdentifier = EnvironmentInfo.GetCompatibleRid(testProject.TargetFrameworks); + + string filenameToSkip = FileConstants.DynamicLibPrefix + "coreclr" + FileConstants.DynamicLibSuffix; + + TestSkippingFile(testProject, filenameToSkip, "native"); + } + + [Fact] + public void ResourceAssetFromPackageCanBeSkipped() + { + var testProject = new TestProject() + { + Name = "SkipResourceFromPackage", + TargetFrameworks = "netcoreapp3.0", + IsSdkProject = true, + IsExe = true + }; + + testProject.PackageReferences.Add(new TestPackageReference("Humanizer", "2.2.0")); + + string filenameToSkip = "de/Humanizer.resources.dll"; + string filenameNotToSkip = "es/Humanizer.resources.dll"; + string assetType = "resources"; + + testProject.AdditionalProperties["DepsFileGenerationMode"] = "new"; + + var testAsset = _testAssetsManager.CreateTestProject(testProject, testProject.Name) + .WithProjectChanges(project => AddSkipTarget(project, filenameToSkip)) + .Restore(Log, testProject.Name); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + + buildCommand + .Execute() + .Should() + .Pass(); + + string outputFolder = buildCommand.GetOutputDirectory(testProject.TargetFrameworks, + runtimeIdentifier: testProject.RuntimeIdentifier).FullName; + + string depsJsonPath = Path.Combine(outputFolder, $"{testProject.Name}.deps.json"); + + var resourceAssets = GetDepsJsonAssets(depsJsonPath, testProject, assetType) + .Select(GetDepsJsonLocalizedResourceRelativePath) + .ToList(); + + resourceAssets.Should().Contain(filenameToSkip); + resourceAssets.Should().Contain(filenameNotToSkip); + + // Force deps.json to be regenerated, otherwise it would be considered up-to-date + File.Delete(depsJsonPath); + + buildCommand + .Execute("/p:AddFileToSkip=true") + .Should() + .Pass(); + + resourceAssets = GetDepsJsonAssets(depsJsonPath, testProject, assetType) + .Select(GetDepsJsonLocalizedResourceRelativePath) + .ToList(); + + resourceAssets.Should().NotContain(filenameToSkip); + resourceAssets.Should().Contain(filenameNotToSkip); + } + + private void TestSkippingFile(TestProject testProject, string filenameToSkip, string assetType) + { + testProject.AdditionalProperties["DepsFileGenerationMode"] = "new"; + + var testAsset = _testAssetsManager.CreateTestProject(testProject, testProject.Name) + .WithProjectChanges(project => AddSkipTarget(project, filenameToSkip)) + .Restore(Log, testProject.Name); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + + buildCommand + .Execute() + .Should() + .Pass(); + + string outputFolder = buildCommand.GetOutputDirectory(testProject.TargetFrameworks, + runtimeIdentifier: testProject.RuntimeIdentifier).FullName; + + string depsJsonPath = Path.Combine(outputFolder, $"{testProject.Name}.deps.json"); + + var assets = GetDepsJsonAssets(depsJsonPath, testProject, assetType) + .Select(GetDepsJsonFilename) + .ToList(); + + assets.Should().Contain(filenameToSkip); + + // Force deps.json to be regenerated, otherwise it would be considered up-to-date + File.Delete(depsJsonPath); + + buildCommand + .Execute("/p:AddFileToSkip=true") + .Should() + .Pass(); + + assets = GetDepsJsonAssets(depsJsonPath, testProject, assetType) + .Select(GetDepsJsonFilename) + .ToList(); + + assets.Should().NotContain(filenameToSkip); + } + + private void AddSkipTarget(XDocument project, string filenameToSkip) + { + var ns = project.Root.Name.Namespace; + + var target = new XElement(ns + "Target", + new XAttribute("Name", "AddFilesToSkip"), + new XAttribute("BeforeTargets", "GenerateBuildDependencyFile"), + new XAttribute("Condition", "'$(AddFileToSkip)' == 'true'")); + + project.Root.Add(target); + + var itemGroup = new XElement(ns + "ItemGroup"); + target.Add(itemGroup); + + if (filenameToSkip.Contains('/')) + { + string filenameToSkipWithCorrectSlash = filenameToSkip.Replace('/', Path.DirectorySeparatorChar); + + // This is a localized resource we need to skip + var fileToSkipItem = new XElement(ns + "_FileToSkip", + new XAttribute("Include", "@(ResourceCopyLocalItems)"), + new XAttribute("Condition", $"'%(DestinationSubPath)' == '{filenameToSkipWithCorrectSlash}'")); + + itemGroup.Add(fileToSkipItem); + } + else + { + var fileToSkipItem = new XElement(ns + "_FileToSkip", + new XAttribute("Include", "@(ReferencePath);@(ReferenceDependencyPaths);@(RuntimePackAsset);@(NativeCopyLocalItems);@(ResourceCopyLocalItems);@(RuntimeTargetsCopyLocalItems)"), + new XAttribute("Condition", $"'%(Filename)%(Extension)' == '{filenameToSkip}'")); + + itemGroup.Add(fileToSkipItem); + } + + var conflictItem = new XElement(ns + "_ConflictPackageFiles", + new XAttribute("Include", "@(_FileToSkip)"), + new XAttribute("KeepMetadata", "-None-")); + + itemGroup.Add(conflictItem); + } + + public static List GetDepsJsonAssets(string depsJsonPath, TestProject testProject, string assetType) + { + string frameworkName = NuGetFramework.Parse(testProject.TargetFrameworks).DotNetFrameworkName; + string targetName; + if (testProject.RuntimeIdentifier == null) + { + targetName = frameworkName; + } + else + { + targetName = frameworkName + "/" + testProject.RuntimeIdentifier; + } + + string depsJsonContents = File.ReadAllText(depsJsonPath); + JObject depsJson = JObject.Parse(depsJsonContents); + var target = (JObject)(JObject)(JObject)depsJson["targets"][targetName]; + var assets = target.Properties().SelectMany(lib => lib.Value[assetType] ?? Enumerable.Empty()).ToList(); + var assetNames = assets.Select(library => ((JProperty)library).Name).ToList(); + return assetNames; + } + + public static string GetDepsJsonFilename(string depsJsonFilePath) + { + return depsJsonFilePath.Split('/', StringSplitOptions.RemoveEmptyEntries).Last(); + } + + private string GetDepsJsonLocalizedResourceRelativePath(string depsJsonFilePath) + { + // Covert a path such as: lib/netstandard1.0/de/Humanizer.resources.dll + // To a path such as: de/Humanizer.resources.dll + return string.Join('/', depsJsonFilePath.Split('/', StringSplitOptions.RemoveEmptyEntries).TakeLast(2)); + } + + } +} diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs index 9e4f16e3fe38..8516d2ae17ce 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs @@ -46,6 +46,10 @@ public void It_only_publish_selected_ResourceLanguages(string targetFramework, b testProject.AdditionalProperties.Add("CopyLocalLockFileAssemblies", "true"); } + // Old deps file generation code still included all satellite languages in deps.json, + // so don't compare both versions + testProject.AdditionalProperties["DepsFileGenerationMode"] = "new"; + var testProjectInstance = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework) .Restore(Log, testProject.Name); diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs index 5eeda7d62c7f..2a3a81d72bca 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using FluentAssertions; using Microsoft.NET.TestFramework; @@ -102,5 +103,44 @@ private void GetReferences(TestProject testProject, bool expectConflicts, out Li referenceCopyLocalPaths = getReferenceCopyLocalPathsCommand.GetValues(); } + + [Fact] + public void CompileConflictsAreNotRemovedFromRuntimeDepsAssets() + { + TestProject testProject = new TestProject() + { + Name = "NetStandard2Library", + TargetFrameworks = "netstandard2.0", + IsSdkProject = true, + // In deps file, assets are under the ".NETStandard,Version=v2.0/" target (ie with empty RID) for some reason + RuntimeIdentifier = string.Empty + }; + + testProject.PackageReferences.Add(new TestPackageReference("Microsoft.AspNetCore.Mvc.Razor", "2.0.1")); + + var testAsset = _testAssetsManager.CreateTestProject(testProject) + .Restore(Log, testProject.Name); + + string projectFolder = Path.Combine(testAsset.Path, testProject.Name); + + var buildCommand = new BuildCommand(Log, projectFolder); + + buildCommand + .Execute() + .Should() + .Pass(); + + string outputFolder = buildCommand.GetOutputDirectory(testProject.TargetFrameworks, + runtimeIdentifier: testProject.RuntimeIdentifier).FullName; + + string depsJsonPath = Path.Combine(outputFolder, $"{testProject.Name}.deps.json"); + + var assets = DepsFileSkipTests.GetDepsJsonAssets(depsJsonPath, testProject, "runtime") + .Select(DepsFileSkipTests.GetDepsJsonFilename) + .ToList(); + + assets.Should().Contain("System.ValueTuple.dll"); + + } } } diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs index aec7cfa01f59..bd4f0b6d86e5 100644 --- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs +++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToFilterSatelliteAssemblies.cs @@ -35,6 +35,10 @@ public void It_only_publishes_selected_ResourceLanguages() testProject.PackageReferences.Add(new TestPackageReference("System.Spatial", "5.8.3")); testProject.AdditionalProperties.Add("SatelliteResourceLanguages", "en-US;it;fr"); + // Old deps file generation code still included all satellite languages in deps.json, + // so don't compare both versions + testProject.AdditionalProperties["DepsFileGenerationMode"] = "new"; + var testProjectInstance = _testAssetsManager.CreateTestProject(testProject) .Restore(Log, testProject.Name); @@ -66,6 +70,10 @@ public void It_publishes_all_satellites_when_not_filtered() IsSdkProject = true }; + // Old deps file generation code still included all satellite languages in deps.json, + // so don't compare both versions + testProject.AdditionalProperties["DepsFileGenerationMode"] = "new"; + testProject.PackageReferences.Add(new TestPackageReference("System.Spatial", "5.8.3")); var testProjectInstance = _testAssetsManager.CreateTestProject(testProject) diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPreserveCompilationContext.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPreserveCompilationContext.cs index 7bf6e5633754..a0b8624c45bd 100644 --- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPreserveCompilationContext.cs +++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPreserveCompilationContext.cs @@ -373,19 +373,16 @@ public void It_excludes_runtime_store_packages_from_the_refs_folder() System.Runtime.InteropServices.RuntimeInformation.dll System.Runtime.Numerics.dll System.Runtime.Serialization.Primitives.dll -System.Security.Claims.dll System.Security.Cryptography.Algorithms.dll System.Security.Cryptography.Encoding.dll System.Security.Cryptography.OpenSsl.dll System.Security.Cryptography.Primitives.dll System.Security.Cryptography.X509Certificates.dll System.Security.Principal.dll -System.Security.Principal.Windows.dll System.Text.Encoding.dll System.Text.Encoding.Extensions.dll System.Text.RegularExpressions.dll System.Threading.dll -System.Threading.Overlapped.dll System.Threading.Tasks.dll System.Threading.Tasks.Dataflow.dll System.Threading.Tasks.Extensions.dll