From bc7b4b9f65185014be45e5327a76cc1d9bbdb2f9 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 15 Mar 2017 17:51:31 -0700 Subject: [PATCH 01/34] Add conflict resolution and trimming tasks Copied from dotnet/standard repo, commit a6be13f4bfa9dda899f41dd0983347f2436c084d --- .../docs/trimming.md | 148 ++++++++ .../pkg/Microsoft.Packaging.Tools.pkgproj | 20 ++ .../tasks/ConflictItem.cs | 188 ++++++++++ .../tasks/ConflictResolver.cs | 184 ++++++++++ .../tasks/FileUtilities.cs | 35 ++ .../tasks/FileUtilities.net45.cs | 24 ++ .../tasks/FileUtilities.netstandard.cs | 42 +++ .../tasks/HandlePackageFileConflicts.cs | 184 ++++++++++ .../Microsoft.Packaging.Tools/tasks/ILog.cs | 86 +++++ .../tasks/ItemUtilities.cs | 115 +++++++ .../tasks/MSBuildLog.cs | 39 +++ .../tasks/MSBuildUtilities.cs | 69 ++++ .../tasks/MetadataNames.cs | 20 ++ .../Microsoft.Packaging.Tools.Tasks.builds | 17 + .../Microsoft.Packaging.Tools.Tasks.csproj | 57 +++ .../tasks/NuGetUtilities.cs | 57 +++ .../tasks/PackageRank.cs | 49 +++ .../tasks/PlatformManifestReader.cs | 70 ++++ .../tasks/ReferenceComparer.cs | 35 ++ .../tasks/RemoveDepsFileConflicts.cs | 157 +++++++++ .../tasks/Trimming/FileNode.cs | 162 +++++++++ .../tasks/Trimming/IIsIncluded.cs | 11 + .../BuildErrorException.cs | 33 ++ .../LockFileCache.cs | 62 ++++ .../tasks/Trimming/NuGetPackageNode.cs | 68 ++++ .../tasks/Trimming/TrimFiles.cs | 324 ++++++++++++++++++ .../tasks/Trimming/Trimmable.cs | 48 +++ .../tasks/project.json | 30 ++ ...Microsoft.Packaging.Tools.Trimming.targets | 80 +++++ .../targets/Microsoft.Packaging.Tools.targets | 122 +++++++ 30 files changed, 2536 insertions(+) create mode 100644 src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md create mode 100644 src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictItem.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictResolver.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/HandlePackageFileConflicts.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/ILog.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/ItemUtilities.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildLog.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildUtilities.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/MetadataNames.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/NuGetUtilities.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/PackageRank.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/PlatformManifestReader.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/ReferenceComparer.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/RemoveDepsFileConflicts.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/project.json create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets create mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets diff --git a/src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md b/src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md new file mode 100644 index 000000000000..916458d70d8a --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md @@ -0,0 +1,148 @@ +#Dependency Trimming + +This package provides build infrastructure for trimming the output of an application. + +It determines what is used by the application by examining static dependencies of the application binary as well as any directly referenced packages. For any file that is unused it will be removed from the set of files copied to the output and publish folders and removed from the application's dependency file(`deps.json`) in the case of a .NET Core application. + +Applications which rely on dynamic dependencies, for example using reflection or runtime compilation like ASP.NET MVC, can specify their dynamic dependencies by referencing packages that contain those dependencies or specifying dependent files as *[roots](#roots)*. + +## How to use +First install the `Microsoft.Packaging.Tools` package in your application. + +You must use *Visual Studio 2017* or later, or *.NET Core command-line (CLI) tools 1.0* or later. + +### From the commandline +Specify `/p:TrimUnusedDependencies=true` when building the project with either `dotnet` or `msbuild`. + +Examples: +``` +dotnet build /p:TrimUnusedDependencies=true +dotnet publish /p:TrimUnusedDependencies=true +msbuild /p:TrimUnusedDependencies=true +msbuild /t:Publish /p:TrimUnusedDependencies=true +``` + +**Important:** Specify TrimUnusedDependencies for both *build* and *publish*, otherwise *build* will produce an application that is not trimmed and debugging will run against an untrimmed application that may hide any problems introduced by trimming, like missing dynamic dependencies. + +### From the IDE or committing the change to your project + +In your project (*.csproj* file) make the following change. + +```xml + + true + +``` + +### Additional options +`@(TrimFilesRootFiles)` - Additional *root* files to consider part of the application. See [roots](#roots). +`@(TrimFilesRootPackages)` - Additional *root* packages to consider part of the application. See [roots](#roots). +`@(TrimmableFiles)` - Files which should be trimmed from the application. See [trimmable](#trimmable). +`@(TrimmablePackages)` - Packages which should be trimmed from the application. See [trimmable](#trimmable). +`$(TrimFilesPreferNativeImages)` - Prefer a file with the `.ni.dll` extension over a file with the `.dll` extension. `.ni.dll` files are native images and significantly larger than a managed assembly but will load faster since they don't need to be JIT compiled. Default is `false`. +`$(RootPackageReference)` - Set to `false` to indicate that `PackageReferences` should not be considered as *[roots](roots)*. Default is `true`. +`$(TreatMetaPackagesAsTrimmable)` - When set to `true` indicates that meta-packages (packages without any file assets) should be treated as *[trimmable](#trimmable)*. Default is `true`. + +**Examples:** +- Specify TrimFilesRootFiles to include file `System.IO.Pipes.dll`. + +```xml + + + +``` + +- Specify TrimmablePackages to indicate that the `NuGet.Client` package should be considered trimmable and only the files in its closure that are actually used should be included. + +```xml + + + +``` + +- Specify TrimFilesPreferNativeImages to prefer faster and larger native images if they exist. + +```xml + + true + +``` + +- Specify RootPackageReference to prefer avoid *rooting* packages directly reference by the project. + +```xml + + false + +``` + +## How it works +The trimming task examines all of the binaries and packages that make up your project and constructs a graph of the two that is related. We start by identifying roots that are included in the application then we traverse the relationships between those to determine if other files or packages should be included in the app. + +### Roots +By default the application is a *root*, as well as all `PackageReference`s from the project file. + +The direct packages references may be excluded from the set of *roots* by specifying the property `RootPackageReference=false`. + +Additional file *roots* may be specified using the `TrimFilesRootFiles` item. +Additional package *roots* may be specified using the `TrimFilesRootPackages` item. + +### Trimmable +Files or packages may be treated as *trimmable*. Essentially this means that when the file or package is encountered while examining dependencies, that file or package will not be included nor will its dependencies unless otherwise referenced. + +If a file is *trimmable* this means that the file will not be included in the application. This takes precedence over all other indirect or direct references, including *roots*. + +If a package is *trimmable* this means that a package's files will not be included in the application unless those files are directly referenced by another file or as a root. + +Additional *trimmable* files may be specified using the `TrimmableFiles` item. +Additional *trimmable* packages may be specified using the `TrimmablePackages` item. + +### File relationships +Managed assemblies are related to other managed assemblies by assembly references in the compiled assembly. Managed assemblies are related to native libraries by DllImports, P-Invokes, to those libraries. + +#### Adding file relationships explicitly +Not all relationships can be discovered statically. A file may define relationships to other files by placing a text file next to it, with the `.dependencies` extension and list other files that it depends on. + +For example: +Suppose `foo.dll` depends on `somelibrary.dll` but that dependency is dynamic. The developer of `foo.dll` can specify this dependency by placing a file foo.dll.dependencies next to foo.dll where the content of that file is a single line: `somelibrary.dll`. + +### Package relationships +Packages are related to other packages by dependencies. Files are related to packages if they are contained in a package. + +Package relationships are established by the dependencies of a package. In this way if a file (a.dll) has a dynamic dependency on another file (b.dll) which is contained in a package (B), that file may be included in a package (A) with a dependency on the other package (b). + +## How to identify and fix missing dynamic dependencies +The best way to identify dynamic dependencies is to run your application with trimming enabled and without. If it fails only with trimming enabled then the cause of the failure is likely trimming. + +A missing assembly may cause the application to fail with an exception like the following: + +``` +Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly +'AssemblyName, Culture=culture, PublicKeyToken=0123456789abcdef' or one of its dependencies. +The system cannot find the file specified. +``` + +To fix this you can *root* the assembly `'AssemblyName, Culture=culture, PublicKeyToken=0123456789abcdef'` by adding the following to your project file. + +```xml + + + +``` + +A missing native library may cause the application to fail with an exception like the following: + +``` +Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'native.dll': +The specified module could not be found. (Exception from HRESULT: 0x8007007E) +``` + +To fix this you can *root* the native library `'native.dll'` by adding the following to your project file. + +```xml + + + +``` + +**Note:** Just because you see these exceptions doesn't necessarily mean trimming is the root cause. If you don't see the exception when running the application with trimming disabled then trimming is the likely cause. If you see the exception when running the application with trimming disabled then the cause could be a missing pre-requisite or an undeclared dependency from some package. \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj b/src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj new file mode 100644 index 000000000000..27b4d6947d8d --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj @@ -0,0 +1,20 @@ + + + + + + 1.0.0 + true + true + + + + + + + build + + + + + diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictItem.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictItem.cs new file mode 100644 index 000000000000..33428301497c --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictItem.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using System; +using System.IO; + +namespace Microsoft.DotNet.Build.Tasks +{ + internal enum ConflictItemType + { + Reference, + CopyLocal, + Runtime, + Platform + } + // Wraps an ITask item and adds lazy evaluated properties used by Conflict resolution. + internal class ConflictItem + { + public ConflictItem(ITaskItem originalItem, ConflictItemType itemType) + { + OriginalItem = originalItem; + ItemType = itemType; + } + + public ConflictItem(string fileName, string packageId, Version assemblyVersion, Version fileVersion) + { + OriginalItem = null; + ItemType = ConflictItemType.Platform; + FileName = fileName; + SourcePath = fileName; + PackageId = packageId; + AssemblyVersion = assemblyVersion; + FileVersion = fileVersion; + } + + private bool hasAssemblyVersion; + private Version assemblyVersion; + public Version AssemblyVersion + { + get + { + if (!hasAssemblyVersion) + { + assemblyVersion = null; + + var assemblyVersionString = OriginalItem?.GetMetadata(nameof(AssemblyVersion)) ?? String.Empty; + + if (assemblyVersionString.Length != 0) + { + Version.TryParse(assemblyVersionString, out assemblyVersion); + } + else + { + assemblyVersion = FileUtilities.TryGetAssemblyVersion(SourcePath); + } + + // assemblyVersion may be null but don't try to recalculate it + hasAssemblyVersion = true; + } + + return assemblyVersion; + } + private set + { + assemblyVersion = value; + hasAssemblyVersion = true; + } + } + + public ConflictItemType ItemType { get; } + + private bool? exists; + public bool Exists + { + get + { + if (exists == null) + { + exists = ItemType == ConflictItemType.Platform || File.Exists(SourcePath); + } + + return exists.Value; + } + } + + private string fileName; + public string FileName + { + get + { + if (fileName == null) + { + fileName = OriginalItem == null ? String.Empty : OriginalItem.GetMetadata(MetadataNames.FileName) + OriginalItem.GetMetadata(MetadataNames.Extension); + } + return fileName; + } + private set { fileName = value; } + } + + private bool hasFileVersion; + private Version fileVersion; + public Version FileVersion + { + get + { + if (!hasFileVersion) + { + fileVersion = null; + + var fileVersionString = OriginalItem?.GetMetadata(nameof(FileVersion)) ?? String.Empty; + + if (fileVersionString.Length != 0) + { + Version.TryParse(fileVersionString, out fileVersion); + } + else + { + fileVersion = FileUtilities.GetFileVersion(SourcePath); + } + + // fileVersion may be null but don't try to recalculate it + hasFileVersion = true; + } + + return fileVersion; + } + private set + { + fileVersion = value; + hasFileVersion = true; + } + } + + public ITaskItem OriginalItem { get; } + + private string packageId; + public string PackageId + { + get + { + if (packageId == null) + { + packageId = OriginalItem?.GetMetadata(MetadataNames.NuGetPackageId) ?? String.Empty; + + if (packageId.Length == 0) + { + packageId = NuGetUtilities.GetPackageIdFromSourcePath(SourcePath) ?? String.Empty; + } + } + + return packageId.Length == 0 ? null : packageId; + } + private set { packageId = value; } + } + + + private string sourcePath; + public string SourcePath + { + get + { + if (sourcePath == null) + { + sourcePath = ItemUtilities.GetSourcePath(OriginalItem) ?? String.Empty; + } + + return sourcePath.Length == 0 ? null : sourcePath; + } + private set { sourcePath = value; } + } + + public string displayName; + public string DisplayName + { + get + { + if (displayName == null) + { + var itemSpec = OriginalItem == null ? FileName : OriginalItem.ItemSpec; + displayName = $"{ItemType}:{itemSpec}"; + } + return displayName; + } + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictResolver.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictResolver.cs new file mode 100644 index 000000000000..518111be13d7 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictResolver.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Build.Tasks +{ + class ConflictResolver + { + private Dictionary winningItemsByKey = new Dictionary(); + private ILog log; + private PackageRank packageRank; + + public ConflictResolver(PackageRank packageRank, ILog log) + { + this.log = log; + this.packageRank = packageRank; + } + + public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, Action foundConflict, bool commitWinner = true) + { + if (conflictItems == null) + { + return; + } + + foreach (var conflictItem in conflictItems) + { + var itemKey = getItemKey(conflictItem); + + if (String.IsNullOrEmpty(itemKey)) + { + continue; + } + + ConflictItem existingItem; + + if (winningItemsByKey.TryGetValue(itemKey, out existingItem)) + { + // a conflict was found, determine the winner. + var winner = ResolveConflict(existingItem, conflictItem); + + if (winner == null) + { + // no winner, skip it. + // don't add to conflict list and just use the existing item for future conflicts. + continue; + } + + ConflictItem loser = conflictItem; + if (!ReferenceEquals(winner, existingItem)) + { + // replace existing item + if (commitWinner) + { + winningItemsByKey[itemKey] = conflictItem; + } + else + { + winningItemsByKey.Remove(itemKey); + } + loser = existingItem; + + } + + foundConflict(loser); + } + else if (commitWinner) + { + winningItemsByKey[itemKey] = conflictItem; + } + } + } + + private ConflictItem ResolveConflict(ConflictItem item1, ConflictItem item2) + { + var conflictMessage = $"Encountered conflict between {item1.DisplayName} and {item2.DisplayName}."; + + var exists1 = item1.Exists; + var exists2 = item2.Exists; + + if (!exists1 || !exists2) + { + var fileMessage = !exists1 ? + !exists2 ? + "both files do" : + $"{item1.DisplayName} does" : + $"{item2.DisplayName} does"; + + log.LogMessage($"{conflictMessage}. Could not determine winner because {fileMessage} not exist."); + return null; + } + + var assemblyVersion1 = item1.AssemblyVersion; + var assemblyVersion2 = item2.AssemblyVersion; + + // if only one is missing version stop: something is wrong when we have a conflict between assembly and non-assembly + if (assemblyVersion1 == null ^ assemblyVersion2 == null) + { + var nonAssembly = assemblyVersion1 == null ? item1.DisplayName : item2.DisplayName; + log.LogMessage($"{conflictMessage}. Could not determine a winner because {nonAssembly} is not an assembly."); + return null; + } + + // only handle cases where assembly version is different, and not null (implicit here due to xor above) + if (assemblyVersion1 != assemblyVersion2) + { + if (assemblyVersion1 > assemblyVersion2) + { + log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because AssemblyVersion {assemblyVersion1} is greater than {assemblyVersion2}."); + return item1; + } + + if (assemblyVersion2 > assemblyVersion1) + { + log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because AssemblyVersion {assemblyVersion2} is greater than {assemblyVersion1}."); + return item2; + } + } + + var fileVersion1 = item1.FileVersion; + var fileVersion2 = item2.FileVersion; + + // if only one is missing version + if (fileVersion1 == null ^ fileVersion2 == null) + { + var nonVersion = fileVersion1 == null ? item1.DisplayName : item2.DisplayName; + log.LogMessage($"{conflictMessage}. Could not determine a winner because {nonVersion} has no file version."); + return null; + } + + if (fileVersion1 != fileVersion2) + { + if (fileVersion1 > fileVersion2) + { + log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because file version {fileVersion1} is greater than {fileVersion2}."); + return item1; + } + + if (fileVersion2 > fileVersion1) + { + log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because file version {fileVersion2} is greater than {fileVersion1}."); + return item2; + } + } + + var packageRank1 = packageRank.GetPackageRank(item1.PackageId); + var packageRank2 = packageRank.GetPackageRank(item2.PackageId); + + if (packageRank1 < packageRank2) + { + log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because package it comes from a package that is preferred."); + return item1; + } + + if (packageRank2 < packageRank1) + { + log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because package it comes from a package that is preferred."); + return item2; + } + + var isPlatform1 = item1.ItemType == ConflictItemType.Platform; + var isPlatform2 = item2.ItemType == ConflictItemType.Platform; + + if (isPlatform1 && !isPlatform2) + { + log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because it is a platform item."); + return item1; + } + + if (!isPlatform1 && isPlatform2) + { + log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because it is a platform item."); + return item1; + } + + log.LogMessage($"{conflictMessage}. Could not determine winner due to equal file and assembly versions."); + return null; + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.cs new file mode 100644 index 000000000000..6f374bf8f930 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.DotNet.Build.Tasks +{ + static partial class FileUtilities + { + public static Version GetFileVersion(string sourcePath) + { + var fvi = FileVersionInfo.GetVersionInfo(sourcePath); + + if (fvi != null) + { + return new Version(fvi.FileMajorPart, fvi.FileMinorPart, fvi.FileBuildPart, fvi.FilePrivatePart); + } + + return null; + } + + static readonly HashSet s_assemblyExtensions = new HashSet(new[] { ".dll", ".exe", ".winmd" }, StringComparer.OrdinalIgnoreCase); + public static Version TryGetAssemblyVersion(string sourcePath) + { + var extension = Path.GetExtension(sourcePath); + + return s_assemblyExtensions.Contains(extension) ? GetAssemblyVersion(sourcePath) : null; + } + + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs new file mode 100644 index 000000000000..59b417d374dc --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; + +namespace Microsoft.DotNet.Build.Tasks +{ + static partial class FileUtilities + { + private static Version GetAssemblyVersion(string sourcePath) + { + try + { + return AssemblyName.GetAssemblyName(sourcePath)?.Version; + } + catch(BadImageFormatException) + { + return null; + } + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs new file mode 100644 index 000000000000..c8368ca45d78 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + +namespace Microsoft.DotNet.Build.Tasks +{ + static partial class FileUtilities + { + private static Version GetAssemblyVersion(string sourcePath) + { + using (var assemblyStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) + { + Version result = null; + try + { + using (PEReader peReader = new PEReader(assemblyStream, PEStreamOptions.LeaveOpen)) + { + if (peReader.HasMetadata) + { + MetadataReader reader = peReader.GetMetadataReader(); + if (reader.IsAssembly) + { + result = reader.GetAssemblyDefinition().Version; + } + } + } + } + catch (BadImageFormatException) + { + // not a PE + } + + return result; + } + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/HandlePackageFileConflicts.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/HandlePackageFileConflicts.cs new file mode 100644 index 000000000000..45fda6a1ab5e --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/HandlePackageFileConflicts.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Build.Tasks +{ + public partial class HandlePackageFileConflicts : Task + { + HashSet referenceConflicts = new HashSet(); + HashSet copyLocalConflicts = new HashSet(); + HashSet allConflicts = new HashSet(); + + public ITaskItem[] References { get; set; } + + public ITaskItem[] ReferenceCopyLocalPaths { get; set; } + + public ITaskItem[] OtherRuntimeItems { get; set; } + + public ITaskItem[] PlatformManifests { get; set; } + + /// + /// NuGet3 and later only. In the case of a conflict with identical file version information a file from the most preferred package will be chosen. + /// + public string[] PreferredPackages { get; set; } + + [Output] + public ITaskItem[] ReferencesWithoutConflicts { get; set; } + + [Output] + public ITaskItem[] ReferenceCopyLocalPathsWithoutConflicts { get; set; } + + [Output] + public ITaskItem[] Conflicts { get; set; } + + public override bool Execute() + { + var log = new MSBuildLog(Log); + var packageRanks = new PackageRank(PreferredPackages); + + // resolve conflicts at compile time + var referenceItems = GetConflictTaskItems(References, ConflictItemType.Reference).ToArray(); + + var compileConflictScope = new ConflictResolver(packageRanks, log); + + compileConflictScope.ResolveConflicts(referenceItems, + ci => ItemUtilities.GetReferenceFileName(ci.OriginalItem), + HandleCompileConflict); + + // resolve conflicts that class in output + var runtimeConflictScope = new ConflictResolver(packageRanks, log); + + runtimeConflictScope.ResolveConflicts(referenceItems, + ci => ItemUtilities.GetReferenceTargetPath(ci.OriginalItem), + HandleRuntimeConflict); + + var copyLocalItems = GetConflictTaskItems(ReferenceCopyLocalPaths, ConflictItemType.CopyLocal).ToArray(); + + runtimeConflictScope.ResolveConflicts(copyLocalItems, + ci => ItemUtilities.GetTargetPath(ci.OriginalItem), + HandleRuntimeConflict); + + var otherRuntimeItems = GetConflictTaskItems(OtherRuntimeItems, ConflictItemType.Runtime).ToArray(); + + runtimeConflictScope.ResolveConflicts(otherRuntimeItems, + ci => ItemUtilities.GetTargetPath(ci.OriginalItem), + HandleRuntimeConflict); + + + // resolve conflicts with platform (eg: shared framework) items + // we only commit the platform items since its not a conflict if other items share the same filename. + var platformConflictScope = new ConflictResolver(packageRanks, log); + var platformItems = PlatformManifests?.SelectMany(pm => PlatformManifestReader.LoadConflictItems(pm.ItemSpec, log)) ?? Enumerable.Empty(); + + platformConflictScope.ResolveConflicts(platformItems, pi => pi.FileName, pi => { }); + platformConflictScope.ResolveConflicts(referenceItems.Where(ri => !referenceConflicts.Contains(ri.OriginalItem)), + ri => ItemUtilities.GetReferenceTargetFileName(ri.OriginalItem), + HandleRuntimeConflict, + commitWinner:false); + platformConflictScope.ResolveConflicts(copyLocalItems.Where(ci => !copyLocalConflicts.Contains(ci.OriginalItem)), + ri => ri.FileName, + HandleRuntimeConflict, + commitWinner: false); + platformConflictScope.ResolveConflicts(otherRuntimeItems, + ri => ri.FileName, + HandleRuntimeConflict, + commitWinner: false); + + ReferencesWithoutConflicts = RemoveConflicts(References, referenceConflicts); + ReferenceCopyLocalPathsWithoutConflicts = RemoveConflicts(ReferenceCopyLocalPaths, copyLocalConflicts); + Conflicts = CreateConflictTaskItems(allConflicts); + + return !Log.HasLoggedErrors; + } + + private ITaskItem[] CreateConflictTaskItems(ICollection conflicts) + { + var conflictItems = new ITaskItem[conflicts.Count]; + + int i = 0; + foreach(var conflict in conflicts) + { + conflictItems[i++] = CreateConflictTaskItem(conflict); + } + + return conflictItems; + } + + private ITaskItem CreateConflictTaskItem(ConflictItem conflict) + { + var item = new TaskItem(conflict.SourcePath); + + if (conflict.PackageId != null) + { + item.SetMetadata(nameof(ConflictItemType), conflict.ItemType.ToString()); + } + + return item; + } + + private IEnumerable GetConflictTaskItems(ITaskItem[] items, ConflictItemType itemType) + { + return (items != null) ? items.Select(i => new ConflictItem(i, itemType)) : Enumerable.Empty(); + } + + private void HandleCompileConflict(ConflictItem conflictItem) + { + if (conflictItem.ItemType == ConflictItemType.Reference) + { + referenceConflicts.Add(conflictItem.OriginalItem); + } + allConflicts.Add(conflictItem); + } + + private void HandleRuntimeConflict(ConflictItem conflictItem) + { + if (conflictItem.ItemType == ConflictItemType.Reference) + { + conflictItem.OriginalItem.SetMetadata(MetadataNames.Private, "False"); + } + else if (conflictItem.ItemType == ConflictItemType.CopyLocal) + { + copyLocalConflicts.Add(conflictItem.OriginalItem); + } + allConflicts.Add(conflictItem); + } + + /// + /// Filters conflicts from original, maintaining order. + /// + /// + /// + /// + private ITaskItem[] RemoveConflicts(ITaskItem[] original, ICollection conflicts) + { + if (conflicts.Count == 0) + { + return original; + } + + var result = new ITaskItem[original.Length - conflicts.Count]; + int index = 0; + + foreach(var originalItem in original) + { + if (!conflicts.Contains(originalItem)) + { + if (index >= result.Length) + { + throw new ArgumentException($"Items from {nameof(conflicts)} were missing from {nameof(original)}"); + } + result[index++] = originalItem; + } + } + + return result; + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ILog.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/ILog.cs new file mode 100644 index 000000000000..272fc9136171 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/ILog.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; + +namespace Microsoft.DotNet.Build.Tasks +{ + public enum LogImportance + { + Low = MessageImportance.Low, + Normal = MessageImportance.Normal, + High = MessageImportance.High + } + + + public interface ILog + { + // + // Summary: + // Logs an error with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogError(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string and importance. + // + // Parameters: + // importance: + // One of the enumeration values that specifies the importance of the message. + // + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(LogImportance importance, string message, params object[] messageArgs); + + // + // Summary: + // Logs a warning with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogWarning(string message, params object[] messageArgs); + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ItemUtilities.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/ItemUtilities.cs new file mode 100644 index 000000000000..b95d2944b5f6 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/ItemUtilities.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using System; +using System.IO; + +namespace Microsoft.DotNet.Build.Tasks +{ + static partial class ItemUtilities + { + /// + /// Get's the filename to use for identifying reference conflicts + /// + /// + /// + public static string GetReferenceFileName(ITaskItem item) + { + var aliases = item.GetMetadata(MetadataNames.Aliases); + + if (!String.IsNullOrEmpty(aliases)) + { + // skip compile-time conflict detection for aliased assemblies. + // An alias is the way to avoid a conflict + // eg: System, v1.0.0.0 in global will not conflict with System, v2.0.0.0 in `private` alias + // We could model each alias scope and try to check for conflicts within that scope, + // but this is a ton of complexity for a fringe feature. + // Instead, we'll treat an alias as an indication that the developer has opted out of + // conflict resolution. + return null; + } + + // We only handle references that have path information since we're only concerned + // with resolving conflicts between file references. If conflicts exist between + // named references that are found from AssemblySearchPaths we'll leave those to + // RAR to handle or not as it sees fit. + var sourcePath = GetSourcePath(item); + + if (String.IsNullOrEmpty(sourcePath)) + { + return null; + } + + try + { + return Path.GetFileName(sourcePath); + } + catch (ArgumentException) + { + // We won't even try to resolve a conflict if we can't open the file, so ignore invalid paths + return null; + } + } + + public static string GetReferenceTargetPath(ITaskItem item) + { + // Determine if the reference will be copied local. + // We're only dealing with primary file references. For these RAR will + // copy local if Private is true or unset. + + var isPrivate = MSBuildUtilities.ConvertStringToBool(item.GetMetadata(MetadataNames.Private), defaultValue: true); + + if (!isPrivate) + { + // Private = false means the reference shouldn't be copied. + return null; + } + + return GetTargetPath(item); + } + + public static string GetReferenceTargetFileName(ITaskItem item) + { + var targetPath = GetReferenceTargetPath(item); + + return targetPath != null ? Path.GetFileName(targetPath) : null; + } + + public static string GetSourcePath(ITaskItem item) + { + var sourcePath = item.GetMetadata(MetadataNames.HintPath); + + if (String.IsNullOrWhiteSpace(sourcePath)) + { + // assume item-spec points to the file. + // this won't work if it comes from a targeting pack or SDK, but + // in that case the file won't exist and we'll skip it. + sourcePath = item.ItemSpec; + } + + return sourcePath; + } + + static readonly string[] s_targetPathMetadata = new[] { MetadataNames.TargetPath, MetadataNames.DestinationSubPath, MetadataNames.Path }; + public static string GetTargetPath(ITaskItem item) + { + // first use TargetPath, DestinationSubPath, then Path, then fallback to filename+extension alone + foreach (var metadata in s_targetPathMetadata) + { + var value = item.GetMetadata(metadata); + + if (!String.IsNullOrWhiteSpace(value)) + { + // normalize path + return value.Replace('\\', '/'); + } + } + + var sourcePath = GetSourcePath(item); + + return Path.GetFileName(sourcePath); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildLog.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildLog.cs new file mode 100644 index 000000000000..da12b82b186b --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildLog.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Build.Tasks +{ + internal class MSBuildLog : ILog + { + private TaskLoggingHelper logger; + public MSBuildLog(TaskLoggingHelper logger) + { + this.logger = logger; + } + + public void LogError(string message, params object[] messageArgs) + { + logger.LogError(message, messageArgs); + } + + public void LogMessage(string message, params object[] messageArgs) + { + logger.LogMessage(message, messageArgs); + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + logger.LogMessage((MessageImportance)importance, message, messageArgs); + } + + public void LogWarning(string message, params object[] messageArgs) + { + logger.LogWarning(message, messageArgs); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildUtilities.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildUtilities.cs new file mode 100644 index 000000000000..2af2c2e27d9c --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildUtilities.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; + +namespace Microsoft.DotNet.Build.Tasks +{ + /// + /// Internal utilties copied from microsoft/MSBuild repo. + /// + class MSBuildUtilities + { + /// + /// Converts a string to a bool. We consider "true/false", "on/off", and + /// "yes/no" to be valid boolean representations in the XML. + /// Modified from its original version to not throw, but return a default value. + /// + /// The string to convert. + /// Boolean true or false, corresponding to the string. + internal static bool ConvertStringToBool(string parameterValue, bool defaultValue = false) + { + if (String.IsNullOrEmpty(parameterValue)) + { + return defaultValue; + } + else if (ValidBooleanTrue(parameterValue)) + { + return true; + } + else if (ValidBooleanFalse(parameterValue)) + { + return false; + } + else + { + // Unsupported boolean representation. + return defaultValue; + } + } + + /// + /// Returns true if the string represents a valid MSBuild boolean true value, + /// such as "on", "!false", "yes" + /// + private static bool ValidBooleanTrue(string parameterValue) + { + return ((String.Compare(parameterValue, "true", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "on", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "yes", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "!false", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "!off", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "!no", StringComparison.OrdinalIgnoreCase) == 0)); + } + + /// + /// Returns true if the string represents a valid MSBuild boolean false value, + /// such as "!on" "off" "no" "!true" + /// + private static bool ValidBooleanFalse(string parameterValue) + { + return ((String.Compare(parameterValue, "false", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "off", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "no", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "!true", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "!on", StringComparison.OrdinalIgnoreCase) == 0) || + (String.Compare(parameterValue, "!yes", StringComparison.OrdinalIgnoreCase) == 0)); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/MetadataNames.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/MetadataNames.cs new file mode 100644 index 000000000000..b205ed500d37 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/MetadataNames.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.DotNet.Build.Tasks + +{ + static class MetadataNames + { + public const string Aliases = "Aliases"; + public const string DestinationSubPath = "DestinationSubPath"; + public const string Extension = "Extension"; + public const string FileName = "FileName"; + public const string HintPath = "HintPath"; + public const string NuGetPackageId = "NuGetPackageId"; + public const string Path = "Path"; + public const string Private = "Private"; + public const string TargetPath = "TargetPath"; + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds b/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds new file mode 100644 index 000000000000..26422204cda2 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds @@ -0,0 +1,17 @@ + + + + + + true + + + + + net451 + + + + + diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj b/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj new file mode 100644 index 000000000000..3913a41b8eb3 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj @@ -0,0 +1,57 @@ + + + + + Library + .NETStandard,Version=v1.5 + .NETFramework,Version=v4.5.1 + false + {65E58605-AE96-46C2-8C6C-F28A5EB9B565} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/NuGetUtilities.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/NuGetUtilities.cs new file mode 100644 index 000000000000..463d36723644 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/NuGetUtilities.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using System; +using System.IO; + +namespace Microsoft.DotNet.Build.Tasks +{ + static partial class NuGetUtilities + { + /// + /// Gets PackageId from sourcePath. + /// + /// + /// + public static string GetPackageIdFromSourcePath(string sourcePath) + { + string packageId, unused; + GetPackageParts(sourcePath, out packageId, out unused); + return packageId; + } + + /// + /// Gets PackageId and package subpath from source path + /// + /// full path to package file + /// package ID + /// subpath of asset within package + public static void GetPackageParts(string fullPath, out string packageId, out string packageSubPath) + { + packageId = null; + packageSubPath = null; + try + { + // this method is just a temporary heuristic until we get metadata added to items created by the .NETCore SDK + for (var dir = Directory.GetParent(fullPath); dir != null; dir = dir.Parent) + { + var nuspecs = dir.GetFiles("*.nuspec"); + + if (nuspecs.Length > 0) + { + packageId = Path.GetFileNameWithoutExtension(nuspecs[0].Name); + packageSubPath = fullPath.Substring(dir.FullName.Length + 1).Replace('\\', '/'); + break; + } + } + } + catch (Exception) + { } + + return; + + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/PackageRank.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/PackageRank.cs new file mode 100644 index 000000000000..f6446f558744 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/PackageRank.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Build.Tasks +{ + class PackageRank + { + private Dictionary packageRanks; + + public PackageRank(string[] packageIds) + { + var numPackages = packageIds?.Length ?? 0; + + // cache ranks for fast lookup + packageRanks = new Dictionary(numPackages, StringComparer.OrdinalIgnoreCase); + + for (int i = numPackages - 1; i >= 0; i--) + { + var preferredPackageId = packageIds[i].Trim(); + + if (preferredPackageId.Length != 0) + { + // overwrite any duplicates, lowest rank will win. + packageRanks[preferredPackageId] = i; + } + } + } + + /// + /// Get's the rank of a package, lower packages are preferred + /// + /// id of package + /// rank of package + public int GetPackageRank(string packageId) + { + int rank; + if (packageId != null && packageRanks.TryGetValue(packageId, out rank)) + { + return rank; + } + + return int.MaxValue; + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/PlatformManifestReader.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/PlatformManifestReader.cs new file mode 100644 index 000000000000..126ed5b698f9 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/PlatformManifestReader.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.DotNet.Build.Tasks +{ + static class PlatformManifestReader + { + static readonly char[] s_manifestLineSeparator = new[] { '|' }; + public static IEnumerable LoadConflictItems(string manifestPath, ILog log) + { + if (manifestPath == null) + { + throw new ArgumentNullException(nameof(manifestPath)); + } + + if (!File.Exists(manifestPath)) + { + log.LogError($"Could not load PlatformManifest from {manifestPath} because it did not exist"); + yield break; + } + + using (var manfiestStream = File.Open(manifestPath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) + using (var manifestReader = new StreamReader(manfiestStream)) + { + for (int lineNumber = 0; !manifestReader.EndOfStream; lineNumber++) + { + var line = manifestReader.ReadLine().Trim(); + + if (line.Length == 0 || line[0] == '#') + { + continue; + } + + var lineParts = line.Split(s_manifestLineSeparator); + + if (lineParts.Length != 4) + { + log.LogError($"Error parsing PlatformManifest from {manifestPath} line {lineNumber}. Lines must have the format fileName|packageId|assemblyVersion|fileVersion"); + yield break; + } + + var fileName = lineParts[0].Trim(); + var packageId = lineParts[1].Trim(); + var assemblyVersionString = lineParts[2].Trim(); + var fileVersionString = lineParts[3].Trim(); + + Version assemblyVersion = null, fileVersion = null; + + if (assemblyVersionString.Length != 0 && !Version.TryParse(assemblyVersionString, out assemblyVersion)) + { + log.LogError($"Error parsing PlatformManfiest from {manifestPath} line {lineNumber}. AssemblyVersion {assemblyVersionString} was invalid."); + } + + if (fileVersionString.Length != 0 && !Version.TryParse(fileVersionString, out fileVersion)) + { + log.LogError($"Error parsing PlatformManifest from {manifestPath} line {lineNumber}. FileVersion {fileVersionString} was invalid."); + } + + yield return new ConflictItem(fileName, packageId, assemblyVersion, fileVersion); + } + } + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ReferenceComparer.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/ReferenceComparer.cs new file mode 100644 index 000000000000..befd3d8ee44a --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/ReferenceComparer.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Microsoft.DotNet.Build.Tasks +{ + internal class ReferenceComparer : IEqualityComparer, IEqualityComparer + { + public static ReferenceComparer Instance { get; } = new ReferenceComparer(); + + public bool Equals(T x, T y) + { + return ReferenceEquals(x, y); + } + + bool IEqualityComparer.Equals(object x, object y) + { + return ReferenceEquals(x, y); + } + + public int GetHashCode(T obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + + public int GetHashCode(object obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/RemoveDepsFileConflicts.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/RemoveDepsFileConflicts.cs new file mode 100644 index 000000000000..bcb1cc61de51 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/RemoveDepsFileConflicts.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Extensions.DependencyModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Build.Tasks +{ + public partial class RemoveDepsFileConflicts : Task + { + Dictionary> compileConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); + Dictionary> runtimeConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + [Required] + public string DepsFilePath { get; set; } + + [Required] + public ITaskItem[] Conflicts { get; set; } + + public override bool Execute() + { + LoadConflicts(); + + DependencyContext sourceDeps; + using (var sourceStream = File.Open(DepsFilePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) + { + sourceDeps = new DependencyContextJsonReader().Read(sourceStream); + } + + DependencyContext trimmedDeps = TrimConflicts(sourceDeps); + + var writer = new DependencyContextWriter(); + using (var fileStream = File.Create(DepsFilePath)) + { + writer.Write(trimmedDeps, fileStream); + } + + return !Log.HasLoggedErrors; + } + + private void LoadConflicts() + { + foreach (var conflict in Conflicts) + { + string packageId, packageSubPath; + NuGetUtilities.GetPackageParts(conflict.ItemSpec, out packageId, out packageSubPath); + + if (String.IsNullOrEmpty(packageId) || String.IsNullOrEmpty(packageSubPath)) + { + continue; + } + + var itemType = conflict.GetMetadata(nameof(ConflictItemType)); + var conflictPackages = (itemType == nameof(ConflictItemType.Reference)) ? compileConflicts : runtimeConflicts; + + HashSet conflictFiles; + if (!conflictPackages.TryGetValue(packageId, out conflictFiles)) + { + conflictPackages[packageId] = conflictFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + } + + conflictFiles.Add(packageSubPath); + } + } + + private DependencyContext TrimConflicts(DependencyContext sourceDeps) + { + return new DependencyContext(sourceDeps.Target, + sourceDeps.CompilationOptions, + TrimCompilationLibraries(sourceDeps.CompileLibraries), + TrimRuntimeLibraries(sourceDeps.RuntimeLibraries), + sourceDeps.RuntimeGraph); + } + + private IEnumerable TrimRuntimeLibraries(IReadOnlyList runtimeLibraries) + { + foreach(var runtimeLibrary in runtimeLibraries) + { + HashSet conflictFiles; + if (runtimeConflicts.TryGetValue(runtimeLibrary.Name, out conflictFiles)) + { + yield return new RuntimeLibrary(runtimeLibrary.Type, + runtimeLibrary.Name, + runtimeLibrary.Version, + runtimeLibrary.Hash, + TrimAssetGroups(runtimeLibrary.RuntimeAssemblyGroups, conflictFiles).ToArray(), + TrimAssetGroups(runtimeLibrary.NativeLibraryGroups, conflictFiles).ToArray(), + TrimResourceAssemblies(runtimeLibrary.ResourceAssemblies, conflictFiles), + runtimeLibrary.Dependencies, + runtimeLibrary.Serviceable); + } + else + { + yield return runtimeLibrary; + } + } + } + + private IEnumerable TrimAssetGroups(IEnumerable assetGroups, ISet conflicts) + { + foreach (var assetGroup in assetGroups) + { + yield return new RuntimeAssetGroup(assetGroup.Runtime, TrimAssemblies(assetGroup.AssetPaths, conflicts)); + } + } + + private IEnumerable TrimResourceAssemblies(IEnumerable resourceAssemblies, ISet conflicts) + { + foreach(var resourceAssembly in resourceAssemblies) + { + if (!conflicts.Contains(resourceAssembly.Path)) + { + yield return resourceAssembly; + } + } + } + + private IEnumerable TrimCompilationLibraries(IReadOnlyList compileLibraries) + { + foreach (var compileLibrary in compileLibraries) + { + HashSet conflictFiles; + if (compileConflicts.TryGetValue(compileLibrary.Name, out conflictFiles)) + { + yield return new CompilationLibrary(compileLibrary.Type, + compileLibrary.Name, + compileLibrary.Version, + compileLibrary.Hash, + TrimAssemblies(compileLibrary.Assemblies, conflictFiles), + compileLibrary.Dependencies, + compileLibrary.Serviceable); + } + else + { + yield return compileLibrary; + } + } + } + + private IEnumerable TrimAssemblies(IEnumerable assemblies, ISet conflicts) + { + foreach(var assembly in assemblies) + { + if (!conflicts.Contains(assembly)) + { + yield return assembly; + } + } + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs new file mode 100644 index 000000000000..bafbd55bc185 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; + +namespace Microsoft.DotNet.Build.Tasks +{ + internal class FileNode : IIsIncluded + { + IEnumerable _dependencies; + + internal const string NuGetPackageIdMetadata = "NuGetPackageId"; + internal const string NuGetPackageVersionMetadata = "NuGetPackageVersion"; + internal const string AdditionalDependenciesFileSuffix = ".dependencies"; + + public FileNode(ITaskItem fileItem, IDictionary allPackages) + { + Name = fileItem.GetMetadata("Filename") + fileItem.GetMetadata("Extension"); + OriginalItem = fileItem; + PackageId = fileItem.GetMetadata(NuGetPackageIdMetadata); + SourceFile = fileItem.GetMetadata("FullPath"); + + if (string.IsNullOrEmpty(PackageId)) + { + PackageId = NuGetUtilities.GetPackageIdFromSourcePath(SourceFile); + } + + if (!string.IsNullOrEmpty(PackageId)) + { + NuGetPackageNode package; + + if (!allPackages.TryGetValue(PackageId, out package)) + { + // some file came from a package that wasn't found in the lock file + } + else + { + Package = package; + Package.Files.Add(this); + } + } + } + + public bool IsIncluded { get; set; } + public string Name { get; } + public ITaskItem OriginalItem { get; } + public string PackageId { get; } + public string SourceFile { get; } + public NuGetPackageNode Package { get; } + public IEnumerable Dependencies { get { return _dependencies; } } + + public void PopulateDependencies(Dictionary allFiles, bool preferNativeImage, ILog log) + { + List dependencies = new List(); + + try + { + using (var peReader = new PEReader(new FileStream(SourceFile, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))) + { + if (peReader.HasMetadata) + { + var reader = peReader.GetMetadataReader(); + foreach (var handle in reader.AssemblyReferences) + { + var reference = reader.GetAssemblyReference(handle); + var referenceName = reader.GetString(reference.Name); + + var referenceCandidates = preferNativeImage ? + new[] { referenceName + ".ni.dll", referenceName + ".dll" } : + new[] { referenceName + ".dll", referenceName + ".ni.dll" }; + + FileNode referencedFile = null; + foreach (var referenceCandidate in referenceCandidates) + { + if (allFiles.TryGetValue(referenceCandidate, out referencedFile)) + { + break; + } + } + + if (referencedFile != null) + { + dependencies.Add(referencedFile); + } + else + { + // static dependency that wasn't satisfied, this can happen if folks use + // lightup code to guard the static dependency. + // this can also happen when referencing a package that isn't implemented + // on this platform but don't fail the build here + log.LogMessage(LogImportance.Low, $"Could not locate assembly dependency {referenceName} of {SourceFile}."); + } + } + + for (int i = 1, count = reader.GetTableRowCount(TableIndex.ModuleRef); i <= count; i++) + { + var moduleRef = reader.GetModuleReference(MetadataTokens.ModuleReferenceHandle(i)); + var moduleName = reader.GetString(moduleRef.Name); + + var moduleRefCandidates = new[] { moduleName, moduleName + ".dll", moduleName + ".so", moduleName + ".dylib" }; + + FileNode referencedNativeFile = null; + foreach (var moduleRefCandidate in moduleRefCandidates) + { + if (allFiles.TryGetValue(moduleRefCandidate, out referencedNativeFile)) + { + break; + } + } + + if (referencedNativeFile != null) + { + dependencies.Add(referencedNativeFile); + } + else + { + // DLLImport that wasn't satisfied + } + } + } + } + } + catch(BadImageFormatException) + { + // not a PE + } + + // allow for components to specify their dependencies themselves, by placing a file next to their source file. + var additionalDependenciesFile = SourceFile + AdditionalDependenciesFileSuffix; + + if (File.Exists(additionalDependenciesFile)) + { + foreach(var additionalDependency in File.ReadAllLines(additionalDependenciesFile)) + { + if (additionalDependency.Length == 0 || additionalDependency[0] == '#') + { + continue; + } + + FileNode additionalDependencyFile; + if (allFiles.TryGetValue(additionalDependency, out additionalDependencyFile)) + { + dependencies.Add(additionalDependencyFile); + } + else + { + log.LogMessage(LogImportance.Low, $"Could not locate explicit dependency {additionalDependency} of {SourceFile} specified in {additionalDependenciesFile}."); + } + } + } + + _dependencies = dependencies; + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs new file mode 100644 index 000000000000..5643648805ec --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.DotNet.Build.Tasks +{ + interface IIsIncluded + { + bool IsIncluded { get; set; } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs new file mode 100644 index 000000000000..3dd0adb5ff01 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; + +namespace Microsoft.NET.Build.Tasks +{ + /// + /// Represents an error that is neither avoidable in all cases nor indicative of a bug in this library. + /// It will be logged as a plain build error without the exception type or stack. + /// + internal class BuildErrorException : Exception + { + public BuildErrorException() + { + } + + public BuildErrorException(string message) : base(message) + { + } + + public BuildErrorException(string message, Exception innerException) : base(message, innerException) + { + } + + public BuildErrorException(string format, params string[] args) + : this(string.Format(CultureInfo.CurrentCulture, format, args)) + { + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs new file mode 100644 index 000000000000..60a5e93adfc8 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using Microsoft.Build.Framework; +using NuGet.Common; +using NuGet.ProjectModel; + +namespace Microsoft.NET.Build.Tasks +{ + internal class LockFileCache + { + private IBuildEngine4 _buildEngine; + + public LockFileCache(IBuildEngine4 buildEngine) + { + _buildEngine = buildEngine; + } + + public LockFile GetLockFile(string path) + { + if (!Path.IsPathRooted(path)) + { + throw new BuildErrorException("Assets file path '{0}' is not rooted. Only full paths are supported.", path); + } + + string lockFileKey = GetTaskObjectKey(path); + + LockFile result; + object existingLockFileTaskObject = _buildEngine.GetRegisteredTaskObject(lockFileKey, RegisteredTaskObjectLifetime.Build); + if (existingLockFileTaskObject == null) + { + result = LoadLockFile(path); + + _buildEngine.RegisterTaskObject(lockFileKey, result, RegisteredTaskObjectLifetime.Build, true); + } + else + { + result = (LockFile)existingLockFileTaskObject; + } + + return result; + } + + private static string GetTaskObjectKey(string lockFilePath) + { + return $"{nameof(LockFileCache)}:{lockFilePath}"; + } + + private LockFile LoadLockFile(string path) + { + if (!File.Exists(path)) + { + throw new BuildErrorException("Assets file '{0}' not found. Run a NuGet package restore to generate this file.", path); + } + + // TODO - https://github.com/dotnet/sdk/issues/18 adapt task logger to Nuget Logger + return LockFileUtilities.GetLockFile(path, NullLogger.Instance); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs new file mode 100644 index 000000000000..452558830711 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.DotNet.Build.Tasks +{ + /// + /// Metedata and information for a package listed in the lock file. + /// + internal sealed class NuGetPackageNode : IIsIncluded + { + private const string NuGetPackageDependencies = "dependencies"; + private List _dependencies = new List(); + private IEnumerable _dependencyIds; + + public NuGetPackageNode(string id, string version, IEnumerable dependencyIds) + { + Id = id; + IsRuntimePackage = id.StartsWith("runtime."); + Version = version; + _dependencyIds = dependencyIds; + } + + public string Id { get; } + private bool IsRuntimePackage { get; } + public bool IsIncluded { get; set; } + public string Version { get; } + public bool IsProject { get; } + public bool IsMetaPackage { get { return Files.Count == 0; } } + public IEnumerable Dependencies { get { return _dependencies; } } + public IList Files { get; } = new List(); + + public void PopulateDependencies(Dictionary allPackages, ILog log) + { + foreach(var dependencyId in _dependencyIds) + { + NuGetPackageNode dependencyNode; + if (!allPackages.TryGetValue(dependencyId, out dependencyNode)) + { + // package declared a dependency but NuGet was missing the dependent package + // in the lock file. This indicates a broken restore, but don't fail trimming + log.LogMessage(LogImportance.Low, $"Could not locate dependency {dependencyId} of package {Id}."); + } + else + { + _dependencies.Add(dependencyNode); + + // Runtime packages may be brought in by a file-based dependency, + // but runtime packages may be missing the dependencies needed since those are + // often declared by the idenity package since it is in the compile graph + // and capable of bringing in other runtime-split packages. + + // Map back up to the identity package so that we can root it and its dependencies. + // This creates an artificial cycle, but our graph walk doesn't care about cycles. + if (dependencyNode.IsRuntimePackage) + { + dependencyNode._dependencies.Add(this); + } + } + } + + _dependencyIds = null; + } + + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs new file mode 100644 index 000000000000..0dd557ec0fd2 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs @@ -0,0 +1,324 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.NET.Build.Tasks; +using NuGet.Frameworks; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Build.Tasks +{ + public partial class TrimFiles : Task + { + /// + /// Files to consider as a roots for trimming. These files and their dependency closure will be preserved. + /// Typically the project's output assembly. + /// + [Required] + public ITaskItem[] RootFiles { get; set; } + + /// + /// Packages to consider as roots for trimming. These packages and their dependency closure will be preserved. + /// Identity either package ID or packageID/version. + /// + [Required] + public ITaskItem[] RootPackages { get; set; } + + /// + /// Optional additional packages which can be trimmed. Default is common meta-packages. + /// Identity is package ID. + /// + public ITaskItem[] TrimmablePackages { get; set; } + + /// + /// Optional additional files which can be trimmed. Default is none. + /// + public ITaskItem[] TrimmableFiles { get; set; } + + /// + /// project.assets.json file for this project to be used for finding package dependencies. + /// + [Required] + public string AssetsFilePath { get; set; } + + /// + /// Target framework to use when determining package dependencies. + /// + [Required] + public string TargetFramework { get; set; } + + /// + /// RuntimeIdentifier to use when determining package dependencies. + /// + public string RuntimeIdentifier { get; set; } + + /// + /// Resolved runtime items to consider for trimming. + /// Should have metadata (where appropriate): + /// NuGetPackageId + /// NuGetPackageVersion + /// + [Required] + public ITaskItem[] RuntimeItems { get; set; } + + /// + /// Resolved runtime items to consider for trimming. + /// Should have metadata (where appropriate): + /// NuGetPackageId + /// NuGetPackageVersion + /// + public ITaskItem[] OtherRuntimeItems { get; set; } + + /// + /// True to prefer a native image ('.ni.dll') over non-native image when walking references. + /// + public bool PreferNativeImages { get; set; } + + /// + /// True to treat packages with only package references as trimmable. + /// + public bool TreatMetaPackagesAsTrimmable { get; set; } + + /// + /// A subset of ReferenceCopyLocalPaths after trimming has been done. + /// + [Output] + public ITaskItem[] RuntimeItemsAfterTrimming { get; set; } + + [Output] + public ITaskItem[] TrimmedItems { get; set; } + + private ILog log; + private Trimmable trimmable; + + public override bool Execute() + { + log = new MSBuildLog(Log); + + // Build the package graph + var packages = GetPackagesFromAssetsFile(); + + // Build file graph + var files = GetFiles(packages); + + trimmable = new Trimmable(TrimmablePackages?.Select(i => i.ItemSpec), + TrimmableFiles?.Select(i => GetFileNameFromItem(i))); + + Queue packageRoots = GetPackageRoots(packages, trimmable); + Queue fileRoots = GetFileRoots(files, trimmable); + + while (packageRoots.Count > 0 || fileRoots.Count > 0) + { + while (fileRoots.Count > 0) + { + var fileNode = fileRoots.Dequeue(); + + foreach(var file in fileNode.Dependencies.Where(f => !trimmable.IsFileTrimmable(f.Name))) + { + IncludeNode(fileRoots, file); + } + + if (fileNode.Package != null && !IsPackageTrimmable(fileNode.Package)) + { + IncludeNode(packageRoots, fileNode.Package); + } + } + + while (packageRoots.Count > 0) + { + var packageNode = packageRoots.Dequeue(); + + foreach(var dependency in packageNode.Dependencies.Where(d => !IsPackageTrimmable(d))) + { + IncludeNode(packageRoots, dependency); + } + + foreach(var file in packageNode.Files.Where(f => !trimmable.IsFileTrimmable(f.Name))) + { + IncludeNode(fileRoots, file); + } + } + } + + var excludedItems = files.Values.Where(f => !f.IsIncluded).Select(f => f.OriginalItem); + TrimmedItems = excludedItems.ToArray(); + RuntimeItemsAfterTrimming = RuntimeItems.Except(excludedItems).ToArray(); + + LogResults(files.Values); + + return !Log.HasLoggedErrors; + } + + private void LogResults(IEnumerable results) + { + int numIncluded = 0, numExcluded = 0; + long sizeIncluded = 0, sizeExcluded = 0; + foreach (var file in results) + { + if (file.IsIncluded) + { + numIncluded++; + sizeIncluded += GetFileSize(file); + } + else + { + numExcluded++; + sizeExcluded += GetFileSize(file); + } + } + + Log.LogMessage(MessageImportance.High, $"Trimmed {numExcluded} out of {numExcluded + numIncluded} files for a savings of {(double)sizeExcluded / (1024*1024):0.##} MB"); + Log.LogMessage(MessageImportance.High, $"Final app size is {(double)sizeIncluded / (1024 * 1024):0.##} MB"); + } + + private long GetFileSize(FileNode file) + { + return new FileInfo(file.SourceFile).Length; + } + + Queue GetPackageRoots(IDictionary packages, Trimmable trimmable) + { + var packageRootQueue = new Queue(); + + if (RootPackages != null) + { + var rootPackageIds = RootPackages.Select(i => GetPackageIdFromItemSpec(i.ItemSpec)); + + foreach (var rootPackageId in rootPackageIds) + { + NuGetPackageNode rootPackage; + + if (!packages.TryGetValue(rootPackageId, out rootPackage)) + { + throw new Exception($"Root package {rootPackageId} was specified but was not found in {AssetsFilePath}"); + } + + if (!IsPackageTrimmable(rootPackage)) + { + IncludeNode(packageRootQueue, rootPackage); + } + } + } + + return packageRootQueue; + } + + private static string GetPackageIdFromItemSpec(string itemSpec) + { + var separatorIndex = itemSpec.IndexOf('/'); + + return separatorIndex > 0 ? itemSpec.Substring(0, separatorIndex) : itemSpec; + } + + Queue GetFileRoots(IDictionary files, Trimmable trimmable) + { + var fileRootQueue = new Queue(); + + var trimmedRootFilenames = RootFiles.Select(i => GetFileNameFromItem(i)).Where(f => !trimmable.IsFileTrimmable(f)); + + foreach(var rootFilename in trimmedRootFilenames) + { + FileNode rootFile; + if (files.TryGetValue(rootFilename, out rootFile)) + { + IncludeNode(fileRootQueue, rootFile); + } + else + { + Log.LogMessage($"Root file {rootFilename} was specified but was not found in the file index."); + } + } + + return fileRootQueue; + } + internal IDictionary GetPackagesFromAssetsFile() + { + var lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath); + var lockFileTarget = lockFile.GetTarget(NuGetFramework.Parse(TargetFramework), RuntimeIdentifier); + + if (lockFileTarget == null) + { + var targetString = string.IsNullOrEmpty(RuntimeIdentifier) ? TargetFramework : $"{TargetFramework}/{RuntimeIdentifier}"; + + throw new Exception($"Missing target section {targetString} from assets file {AssetsFilePath}. Ensure you have restored this project previously."); + } + + Dictionary packages = new Dictionary(lockFileTarget.Libraries.Count, StringComparer.OrdinalIgnoreCase); + + foreach (var library in lockFileTarget.Libraries) + { + var dependencyIds = library.Dependencies.Select(d => d.Id); + + packages.Add(library.Name, new NuGetPackageNode(library.Name, library.Version.ToString(), dependencyIds)); + } + + // Connect the graph + foreach (var package in packages.Values) + { + package.PopulateDependencies(packages, log); + } + + return packages; + } + + internal IDictionary GetFiles(IDictionary packages) + { + var files = new Dictionary(RuntimeItems.Length, StringComparer.OrdinalIgnoreCase); + IEnumerable runtimeItems = RuntimeItems; + + if (OtherRuntimeItems != null) + { + runtimeItems = runtimeItems.Concat(OtherRuntimeItems); + } + + foreach(var runtimeItem in runtimeItems) + { + var fileNode = new FileNode(runtimeItem, packages); + files.Add(fileNode.Name, fileNode); + } + + // root files are likely not in the RuntimeItems + foreach (var rootFile in RootFiles) + { + var fileNode = new FileNode(rootFile, packages); + if (!files.ContainsKey(fileNode.Name) && File.Exists(fileNode.SourceFile)) + { + files.Add(fileNode.Name, fileNode); + } + } + + // connect the graph + foreach(var file in files.Values) + { + file.PopulateDependencies(files, PreferNativeImages, log); + } + + return files; + } + + private bool IsPackageTrimmable(NuGetPackageNode package) + { + return trimmable.IsPackageTrimmable(package.Id) || + (TreatMetaPackagesAsTrimmable && package.IsMetaPackage); + } + + private static void IncludeNode(Queue queue, T node) where T : IIsIncluded + { + if (!node.IsIncluded) + { + node.IsIncluded = true; + queue.Enqueue(node); + } + } + + private static string GetFileNameFromItem(ITaskItem item) + { + return item.GetMetadata("FileName") + item.GetMetadata("Extension"); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs new file mode 100644 index 000000000000..189e1701336a --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Build.Tasks +{ + class Trimmable + { + private static readonly string[] defaultPackages = { "Microsoft.NETCore.App", "NETStandard.Library", "Microsoft.NETCore.UniversalWindowsPlatform"}; + + private HashSet _trimmablePackages; + private HashSet _trimmableFiles; + public Trimmable(IEnumerable additionalPackages, IEnumerable trimmableFiles) + { + _trimmablePackages = new HashSet(defaultPackages, StringComparer.OrdinalIgnoreCase); + _trimmableFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + + if (additionalPackages != null) + { + foreach(var additionalPackage in additionalPackages) + { + _trimmablePackages.Add(additionalPackage); + } + } + + if (trimmableFiles != null) + { + foreach(var trimmableFile in trimmableFiles) + { + _trimmableFiles.Add(trimmableFile); + } + } + } + + public bool IsPackageTrimmable(string packageId) + { + return _trimmablePackages.Contains(packageId); + } + + public bool IsFileTrimmable(string fileName) + { + return _trimmableFiles.Contains(fileName); + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/project.json b/src/Tasks/Microsoft.Packaging.Tools/tasks/project.json new file mode 100644 index 000000000000..3869cf8c060d --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/project.json @@ -0,0 +1,30 @@ +{ + "dependencies": { + "Microsoft.DotNet.PlatformAbstractions": "1.0.1-beta-000933", + "Microsoft.Extensions.DependencyModel": "1.0.1-beta-000933", + "NuGet.ProjectModel": "4.0.0-rtm-2241", + "NETStandard.Library": "1.6.0", + "System.Reflection.Metadata": "1.3.0" + }, + "frameworks": { + "netstandard1.5": { + "dependencies": { + "Microsoft.Build": "0.1.0-preview-00022", + "Microsoft.Build.Framework": "0.1.0-preview-00022", + "Microsoft.Build.Tasks.Core": "0.1.0-preview-00022", + "Microsoft.Build.Utilities.Core": "0.1.0-preview-00022", + "Microsoft.Tpl.Dataflow": { + "version": "4.5.24", + "exclude": "all" + }, + "System.Diagnostics.FileVersionInfo": "4.0.0" + }, + "imports": [ "dnxcore50", "portable-net45+win8+wpa81" ] + }, + "net451": { + "dependencies": { + "Microsoft.TargetingPack.NETFramework.v4.5.1": "1.0.1" + } + } + } +} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets b/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets new file mode 100644 index 000000000000..5375d642312d --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets @@ -0,0 +1,80 @@ + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + <_trimOtherRuntimeItems Include="@(_LockFileAssemblies)" Exclude="@(_ConflictPackageFiles)" /> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets b/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets new file mode 100644 index 000000000000..beab07ddb0d6 --- /dev/null +++ b/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets @@ -0,0 +1,122 @@ + + + + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory)desktop/ + + + + + + + + + + ResolvePackageDependenciesForBuild + RunResolvePublishAssemblies + GenerateBuildDependencyFile + GeneratePublishDependencyFile + + _GetLockFileAssemblies + + <_supportsTrimming>true + + + + + + ResolveNuGetPackageAssets + + + + + + $(ResolveAssemblyReferencesDependsOn);HandlePackageFileConflicts + HandlePackageFileConflicts;$(PrepareResourcesDependsOn) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_RemoveDepsFileConflictsSemaphore>$(IntermediateOutputPath)\RemoveDepsFileConflicts.semaphore + + + + + + + + + + + + + + + + + + + + + + + <_RemovePublishDepsFileConflictsSemaphore>$(IntermediateOutputPath)\RemovePublishDepsFileConflicts.semaphore + + + + + + + + + + + \ No newline at end of file From 7543da3fa8d231818873608729d18a952456f30e Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 15 Mar 2017 19:24:49 -0700 Subject: [PATCH 02/34] Add conflict resolution tasks to Microsoft.NET.Build.Tasks project --- build/DependencyVersions.props | 5 +++++ .../ConflictResolution}/ConflictItem.cs | 0 .../ConflictResolution}/ConflictResolver.cs | 0 .../ConflictResolution}/FileUtilities.cs | 0 .../ConflictResolution}/FileUtilities.net45.cs | 4 ++++ .../ConflictResolution}/FileUtilities.netstandard.cs | 4 ++++ .../ConflictResolution}/HandlePackageFileConflicts.cs | 0 .../ConflictResolution}/ILog.cs | 0 .../ConflictResolution}/ItemUtilities.cs | 0 .../ConflictResolution}/MSBuildLog.cs | 0 .../ConflictResolution}/MSBuildUtilities.cs | 0 .../ConflictResolution}/MetadataNames.cs | 0 .../ConflictResolution}/NuGetUtilities.cs | 0 .../ConflictResolution}/PackageRank.cs | 0 .../ConflictResolution}/PlatformManifestReader.cs | 0 .../ConflictResolution}/ReferenceComparer.cs | 0 .../ConflictResolution}/RemoveDepsFileConflicts.cs | 0 .../Microsoft.NET.Build.Tasks.csproj | 5 +++++ 18 files changed, 18 insertions(+) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/ConflictItem.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/ConflictResolver.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/FileUtilities.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/FileUtilities.net45.cs (97%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/FileUtilities.netstandard.cs (98%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/HandlePackageFileConflicts.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/ILog.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/ItemUtilities.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/MSBuildLog.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/MSBuildUtilities.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/MetadataNames.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/NuGetUtilities.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/PackageRank.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/PlatformManifestReader.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/ReferenceComparer.cs (100%) rename src/Tasks/{Microsoft.Packaging.Tools/tasks => Microsoft.NET.Build.Tasks/ConflictResolution}/RemoveDepsFileConflicts.cs (100%) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 0a3cecd46953..002197732c6e 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -2,6 +2,10 @@ + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + 15.1.548 1.0.3 @@ -9,6 +13,7 @@ 4.3.0-beta1-2418 9.0.1 6.0.4 + 1.4.2 diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictItem.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictItem.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/ConflictResolver.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs similarity index 97% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs index 59b417d374dc..68e444651031 100644 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.net45.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if NET46 + using System; using System.Reflection; @@ -22,3 +24,5 @@ private static Version GetAssemblyVersion(string sourcePath) } } } + +#endif \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs similarity index 98% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs index c8368ca45d78..f2f3c9f455ca 100644 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/FileUtilities.netstandard.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if NETCOREAPP1_0 + using System; using System.IO; using System.Reflection.Metadata; @@ -40,3 +42,5 @@ private static Version GetAssemblyVersion(string sourcePath) } } } + +#endif \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/HandlePackageFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/HandlePackageFileConflicts.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ILog.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/ILog.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ItemUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/ItemUtilities.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildLog.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildLog.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/MSBuildUtilities.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/MetadataNames.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/MetadataNames.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/NuGetUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/NuGetUtilities.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/PackageRank.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/PackageRank.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/PlatformManifestReader.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/PlatformManifestReader.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/ReferenceComparer.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/ReferenceComparer.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/RemoveDepsFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs similarity index 100% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/RemoveDepsFileConflicts.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index ab39b40ec6a8..ffbf88eec94f 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -35,6 +35,10 @@ $(MsBuildPackagesVersion) Runtime + + + @@ -91,6 +95,7 @@ + From ab151125c20c26a5624ab6f7061184c93b60c2e9 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 20 Mar 2017 15:46:55 -0700 Subject: [PATCH 03/34] Update namespace for conflict resolution --- .../ConflictResolution/ConflictItem.cs | 2 +- .../ConflictResolution/ConflictResolver.cs | 2 +- .../ConflictResolution/FileUtilities.cs | 2 +- .../ConflictResolution/FileUtilities.net45.cs | 2 +- .../ConflictResolution/FileUtilities.netstandard.cs | 2 +- .../ConflictResolution/HandlePackageFileConflicts.cs | 2 +- src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs | 2 +- .../ConflictResolution/ItemUtilities.cs | 2 +- .../Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs | 2 +- .../ConflictResolution/MSBuildUtilities.cs | 2 +- .../ConflictResolution/MetadataNames.cs | 2 +- .../ConflictResolution/NuGetUtilities.cs | 2 +- .../Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs | 2 +- .../ConflictResolution/PlatformManifestReader.cs | 2 +- .../ConflictResolution/ReferenceComparer.cs | 2 +- .../ConflictResolution/RemoveDepsFileConflicts.cs | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs index 33428301497c..39d8b3b87ce2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs @@ -6,7 +6,7 @@ using System; using System.IO; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { internal enum ConflictItemType { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index 518111be13d7..e284447fe7fc 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -6,7 +6,7 @@ using System; using System.Collections.Generic; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { class ConflictResolver { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs index 6f374bf8f930..cb87f04f9358 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs @@ -7,7 +7,7 @@ using System.Diagnostics; using System.IO; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static partial class FileUtilities { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs index 68e444651031..9f69379f3641 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs @@ -7,7 +7,7 @@ using System; using System.Reflection; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static partial class FileUtilities { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs index f2f3c9f455ca..3329e12918ef 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs @@ -9,7 +9,7 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static partial class FileUtilities { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs index 45fda6a1ab5e..5f93d2d20351 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Linq; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { public partial class HandlePackageFileConflicts : Task { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs index 272fc9136171..26cbee804190 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs @@ -4,7 +4,7 @@ using Microsoft.Build.Framework; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { public enum LogImportance { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs index b95d2944b5f6..fa9658970e29 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs @@ -6,7 +6,7 @@ using System; using System.IO; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static partial class ItemUtilities { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs index da12b82b186b..703097385b35 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs @@ -6,7 +6,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { internal class MSBuildLog : ILog { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs index 2af2c2e27d9c..231873fe93f7 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { /// /// Internal utilties copied from microsoft/MSBuild repo. diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs index b205ed500d37..521c798aac75 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static class MetadataNames diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs index 463d36723644..d6bbebab6f68 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs @@ -6,7 +6,7 @@ using System; using System.IO; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static partial class NuGetUtilities { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs index f6446f558744..97ff5b7489e0 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { class PackageRank { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs index 126ed5b698f9..d97928ce0a3d 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.IO; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { static class PlatformManifestReader { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs index befd3d8ee44a..b1d097492a72 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { internal class ReferenceComparer : IEqualityComparer, IEqualityComparer { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs index bcb1cc61de51..ded713366c86 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs @@ -10,7 +10,7 @@ using System.IO; using System.Linq; -namespace Microsoft.DotNet.Build.Tasks +namespace Microsoft.NET.Build.Tasks.ConflictResolution { public partial class RemoveDepsFileConflicts : Task { From 3bdd8e0cfcf9b28fcb284a0df9053ea0bc339b7a Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 20 Mar 2017 15:52:49 -0700 Subject: [PATCH 04/34] Update license header from conflict resolution code to match other files --- .../ConflictResolution/ConflictItem.cs | 5 ++--- .../ConflictResolution/ConflictResolver.cs | 5 ++--- .../ConflictResolution/FileUtilities.cs | 5 ++--- .../ConflictResolution/FileUtilities.net45.cs | 5 ++--- .../ConflictResolution/FileUtilities.netstandard.cs | 5 ++--- .../ConflictResolution/HandlePackageFileConflicts.cs | 5 ++--- .../Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs | 5 ++--- .../ConflictResolution/ItemUtilities.cs | 5 ++--- .../ConflictResolution/MSBuildLog.cs | 5 ++--- .../ConflictResolution/MSBuildUtilities.cs | 6 +++--- .../ConflictResolution/MetadataNames.cs | 5 ++--- .../ConflictResolution/NuGetUtilities.cs | 5 ++--- .../ConflictResolution/PackageRank.cs | 5 ++--- .../ConflictResolution/PlatformManifestReader.cs | 5 ++--- .../ConflictResolution/ReferenceComparer.cs | 5 ++--- .../ConflictResolution/RemoveDepsFileConflicts.cs | 5 ++--- 16 files changed, 33 insertions(+), 48 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs index 39d8b3b87ce2..fef05f913331 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; using System; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index e284447fe7fc..26d46dea1d7e 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Utilities; using System; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs index cb87f04f9358..73bd82e0fe9c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs index 9f69379f3641..8419c4c36d83 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.net45.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET46 diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs index 3329e12918ef..22c16a588ba6 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/FileUtilities.netstandard.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETCOREAPP1_0 diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs index 5f93d2d20351..182374901946 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; using Microsoft.Build.Utilities; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs index 26cbee804190..2fc6b390e071 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ILog.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs index fa9658970e29..2967bdd5f9fd 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; using System; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs index 703097385b35..c8b588499829 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildLog.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using Microsoft.Build.Framework; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs index 231873fe93f7..bd71a059ead8 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MSBuildUtilities.cs @@ -1,6 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; namespace Microsoft.NET.Build.Tasks.ConflictResolution diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs index 521c798aac75..989fa009591d 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/MetadataNames.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.NET.Build.Tasks.ConflictResolution diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs index d6bbebab6f68..ba0edbf0d337 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; using System; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs index 97ff5b7489e0..99480a808044 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PackageRank.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs index d97928ce0a3d..2e3575e8e78c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Utilities; using System; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs index b1d097492a72..ba17e58b17d4 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections; using System.Collections.Generic; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs index ded713366c86..ced5282bc734 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs @@ -1,6 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; using Microsoft.Build.Utilities; From ddfd332bfc0de14b462696b8e7627748dbebd166 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Sun, 26 Mar 2017 16:06:44 -0700 Subject: [PATCH 05/34] Add conflict resolution .targets file to SDK --- .../Microsoft.NET.Build.Tasks.csproj | 3 +++ .../build/Microsoft.NET.Sdk.targets | 1 + .../build}/Microsoft.Packaging.Tools.targets | 23 ++++++++++++++----- 3 files changed, 21 insertions(+), 6 deletions(-) rename src/Tasks/{Microsoft.Packaging.Tools/tasks/targets => Microsoft.NET.Build.Tasks/build}/Microsoft.Packaging.Tools.targets (90%) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index ffbf88eec94f..0a691f331ade 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -167,6 +167,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets index 170ebe25fac1..dc257d598b6c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets @@ -421,6 +421,7 @@ Copyright (c) .NET Foundation. All rights reserved. + diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets similarity index 90% rename from src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets rename to src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets index beab07ddb0d6..f031485c42f1 100644 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets @@ -1,10 +1,21 @@ + + - - $(MSBuildThisFileDirectory) - $(MSBuildThisFileDirectory)desktop/ + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - + @@ -55,7 +66,7 @@ - + <_RemoveDepsFileConflictsSemaphore>$(IntermediateOutputPath)\RemoveDepsFileConflicts.semaphore - + From cd2362983d4c3dd468a404da4c6fcdaf53f85e93 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 27 Mar 2017 19:09:49 -0700 Subject: [PATCH 06/34] Add tests for conflict resolution scenarios --- .../GivenThatWeWantToTargetNetStandard2.cs | 83 +++++++++++++ ...enThatWeWantToPublishAHelloWorldProject.cs | 116 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs new file mode 100644 index 000000000000..1c1ce4a97ab4 --- /dev/null +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs @@ -0,0 +1,83 @@ +using System.IO; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using Xunit; +using static Microsoft.NET.TestFramework.Commands.MSBuildTest; +using System.Linq; +using FluentAssertions; +using System.Xml.Linq; +using System.Runtime.Versioning; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System; +using Microsoft.NET.TestFramework.ProjectConstruction; + +namespace Microsoft.NET.Build.Tests +{ + public class GivenThatWeWantToTargetNetStandard2 : SdkTest + { + [Fact] + public void It_builds_a_netstandard2_library_successfully() + { + TestProject project = new TestProject() + { + Name = "NetStandard2Library", + TargetFrameworks = "netstandard2.0", + IsSdkProject = true + }; + + var testAsset = _testAssetsManager.CreateTestProject(project) + .Restore(project.Name); + + string projectFolder = Path.Combine(testAsset.Path, project.Name); + + var buildCommand = new BuildCommand(Stage0MSBuild, projectFolder); + + buildCommand + .Execute() + .Should() + .Pass(); + + } + + [Fact] + public void It_resolves_assembly_conflicts() + { + TestProject project = new TestProject() + { + Name = "NetStandard2Library", + TargetFrameworks = "netstandard2.0", + IsSdkProject = true + }; + + + var testAsset = _testAssetsManager.CreateTestProject(project) + .WithProjectChanges(p => + { + var ns = p.Root.Name.Namespace; + + // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: + // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); + + var itemGroup = new XElement(ns + "ItemGroup"); + p.Root.Add(itemGroup); + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", "System.Xml.XDocument"), + new XAttribute("Version", "4.3.0"))); + + }) + .Restore(project.Name); + + string projectFolder = Path.Combine(testAsset.Path, project.Name); + + var buildCommand = new BuildCommand(Stage0MSBuild, projectFolder); + + buildCommand + .Execute() + .Should() + .Pass(); + + } + } +} diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index 7e410dec6b22..262519bd42be 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -11,6 +11,8 @@ using Microsoft.NET.TestFramework.ProjectConstruction; using Xunit; using static Microsoft.NET.TestFramework.Commands.MSBuildTest; +using System.Xml.Linq; +using System.Runtime.CompilerServices; namespace Microsoft.NET.Publish.Tests { @@ -161,6 +163,120 @@ public static void Main() .HaveStdOutContaining("Hello from a netcoreapp2.0.!"); } + [Fact] + public void Conflicts_are_resolved_when_publishing_a_portable_app() + { + Conflicts_are_resolved_when_publishing(false); + } + + [Fact] + public void Conflicts_are_resolved_when_publishing_a_self_contained_app() + { + Conflicts_are_resolved_when_publishing(true); + } + + void Conflicts_are_resolved_when_publishing(bool selfContained, [CallerMemberName] string callingMethod = "") + { + var targetFramework = "netcoreapp2.0"; + var rid = selfContained ? EnvironmentInfo.GetCompatibleRid(targetFramework) : null; + + TestProject testProject = new TestProject() + { + Name = selfContained ? "SelfContainedWithConflicts" : "PortableWithConflicts", + IsSdkProject = true, + TargetFrameworks = targetFramework, + RuntimeIdentifier = rid, + IsExe = true, + }; + + string outputMessage = $"Hello from {testProject.Name}!"; + + testProject.SourceFiles["Program.cs"] = @" +using System; +public static class Program +{ + public static void Main() + { + Console.WriteLine(""" + outputMessage + @"""); + } +} +"; + var testProjectInstance = _testAssetsManager.CreateTestProject(testProject, "SelfContainedPublishConflicts") + .WithProjectChanges(p => + { + + var ns = p.Root.Name.Namespace; + + // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: + // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); + + var itemGroup = new XElement(ns + "ItemGroup"); + p.Root.Add(itemGroup); + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", "System.Xml.XDocument"), + new XAttribute("Version", "4.3.0"))); + + }) + .Restore(testProject.Name); + + var publishCommand = new PublishCommand(Stage0MSBuild, Path.Combine(testProjectInstance.TestRoot, testProject.Name)); + var publishResult = publishCommand.Execute(); + + publishResult.Should().Pass(); + + var publishDirectory = publishCommand.GetOutputDirectory( + targetFramework: targetFramework, + runtimeIdentifier: rid ?? string.Empty); + + ICommand runCommand; + + if (selfContained) + { + var selfContainedExecutable = testProject.Name + Constants.ExeSuffix; + + string selfContainedExecutableFullPath = Path.Combine(publishDirectory.FullName, selfContainedExecutable); + + var libPrefix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "" : "lib"; + + publishDirectory.Should().HaveFiles(new[] { + selfContainedExecutable, + $"{testProject.Name}.dll", + $"{testProject.Name}.pdb", + $"{testProject.Name}.deps.json", + $"{testProject.Name}.runtimeconfig.json", + $"{libPrefix}coreclr{Constants.DynamicLibSuffix}", + $"{libPrefix}hostfxr{Constants.DynamicLibSuffix}", + $"{libPrefix}hostpolicy{Constants.DynamicLibSuffix}", + $"mscorlib.dll", + $"System.Private.CoreLib.dll", + }); + + runCommand = Command.Create(selfContainedExecutableFullPath, new string[] { }) + .EnsureExecutable(); + } + + else + { + publishDirectory.Should().OnlyHaveFiles(new[] { + $"{testProject.Name}.dll", + $"{testProject.Name}.pdb", + $"{testProject.Name}.deps.json", + $"{testProject.Name}.runtimeconfig.json" + }); + + runCommand = Command.Create(RepoInfo.DotNetHostPath, new[] { Path.Combine(publishDirectory.FullName, $"{testProject.Name}.dll") }); + } + + runCommand + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining(outputMessage); + + } + [Fact] public void A_deployment_project_can_reference_the_hello_world_project() { From 01576d296d150435dd9f73682c3a26d525fae337 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 28 Mar 2017 12:02:22 -0700 Subject: [PATCH 07/34] Add test for conflict resolution when targeting .NET Framework, update other conflict resolution tests to cover more packages --- .../GivenThatWeWantToBuildADesktopLibrary.cs | 55 +++++++++++++++++ ...nThatWeWantToBuildANetStandard2Library.cs} | 12 ++-- ...enThatWeWantToPublishAHelloWorldProject.cs | 9 ++- test/Microsoft.NET.TestFramework/TestAsset.cs | 61 +++++++++++++++++++ 4 files changed, 130 insertions(+), 7 deletions(-) rename test/Microsoft.NET.Build.Tests/{GivenThatWeWantToTargetNetStandard2.cs => GivenThatWeWantToBuildANetStandard2Library.cs} (85%) diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs index 4884af407315..147d79c79432 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs @@ -84,5 +84,60 @@ public void Method1() .Pass(); } + + [Fact] + public void It_resolves_assembly_conflicts_with_a_NETFramework_library() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + TestProject project = new TestProject() + { + Name = "NETFrameworkLibrary", + TargetFrameworks = "net462", + IsSdkProject = true + }; + + var testAsset = _testAssetsManager.CreateTestProject(project) + .WithProjectChanges(p => + { + var ns = p.Root.Name.Namespace; + + // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: + // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); + + var itemGroup = new XElement(ns + "ItemGroup"); + p.Root.Add(itemGroup); + + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", "NETStandard.Library"), + new XAttribute("Version", "$(BundledNETStandardPackageVersion)"))); + + foreach (var dependency in TestAsset.NetStandard1_3Dependencies) + { + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", dependency.Item1), + new XAttribute("Version", dependency.Item2))); + } + + }) + .Restore(project.Name); + + string projectFolder = Path.Combine(testAsset.Path, project.Name); + + var buildCommand = new BuildCommand(MSBuildTest.Stage0MSBuild, projectFolder); + + buildCommand + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .NotHaveStdOutContaining("warning") + .And + .NotHaveStdOutContaining("MSB3243"); + } } } diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs similarity index 85% rename from test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs rename to test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs index 1c1ce4a97ab4..0cc2aea7fca1 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetNetStandard2.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs @@ -15,7 +15,7 @@ namespace Microsoft.NET.Build.Tests { - public class GivenThatWeWantToTargetNetStandard2 : SdkTest + public class GivenThatWeWantToBuildANetStandard2Library : SdkTest { [Fact] public void It_builds_a_netstandard2_library_successfully() @@ -62,9 +62,13 @@ public void It_resolves_assembly_conflicts() var itemGroup = new XElement(ns + "ItemGroup"); p.Root.Add(itemGroup); - itemGroup.Add(new XElement(ns + "PackageReference", - new XAttribute("Include", "System.Xml.XDocument"), - new XAttribute("Version", "4.3.0"))); + + foreach (var dependency in TestAsset.NetStandard1_3Dependencies) + { + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", dependency.Item1), + new XAttribute("Version", dependency.Item2))); + } }) .Restore(project.Name); diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index 262519bd42be..7ad08561dd01 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -212,10 +212,13 @@ public static void Main() var itemGroup = new XElement(ns + "ItemGroup"); p.Root.Add(itemGroup); - itemGroup.Add(new XElement(ns + "PackageReference", - new XAttribute("Include", "System.Xml.XDocument"), - new XAttribute("Version", "4.3.0"))); + foreach (var dependency in TestAsset.NetStandard1_3Dependencies) + { + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", dependency.Item1), + new XAttribute("Version", dependency.Item2))); + } }) .Restore(testProject.Name); diff --git a/test/Microsoft.NET.TestFramework/TestAsset.cs b/test/Microsoft.NET.TestFramework/TestAsset.cs index a7d253194291..244996224882 100644 --- a/test/Microsoft.NET.TestFramework/TestAsset.cs +++ b/test/Microsoft.NET.TestFramework/TestAsset.cs @@ -175,5 +175,66 @@ private bool IsInBinOrObjFolder(string path) return path.Contains(binFolderWithTrailingSlash) || path.Contains(objFolderWithTrailingSlash); } + + public static IEnumerable> NetStandard1_3Dependencies + { + get + { + string netstandardDependenciesXml = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; + + XElement netStandardDependencies = XElement.Parse(netstandardDependenciesXml); + + foreach (var dependency in netStandardDependencies.Elements("dependency")) + { + yield return Tuple.Create(dependency.Attribute("id").Value, dependency.Attribute("version").Value); + } + } + } } } From 660bca11b5d61ac95324b667603d06a639aa73d4 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 30 Mar 2017 18:03:10 -0700 Subject: [PATCH 08/34] Create IConflictItem interface to make ConflictResolver more testable --- .../ConflictResolution/ConflictItem.cs | 14 +++++++++++++- .../ConflictResolution/ConflictResolver.cs | 18 ++++++++++++------ .../HandlePackageFileConflicts.cs | 6 +++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs index fef05f913331..8f00227aa9f2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs @@ -14,8 +14,20 @@ internal enum ConflictItemType Runtime, Platform } + + internal interface IConflictItem + { + Version AssemblyVersion { get; } + ConflictItemType ItemType { get; } + bool Exists { get; } + string FileName { get; } + Version FileVersion { get; } + string PackageId { get; } + string DisplayName { get; } + } + // Wraps an ITask item and adds lazy evaluated properties used by Conflict resolution. - internal class ConflictItem + internal class ConflictItem : IConflictItem { public ConflictItem(ITaskItem originalItem, ConflictItemType itemType) { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index 26d46dea1d7e..cd8be1b588a0 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -5,11 +5,17 @@ using System; using System.Collections.Generic; +// Notes on functionality and how to test it +// The conflict resolver finds conflicting items, and if there are any of them it reports the "losing" item via the foundConflict callback + + + + namespace Microsoft.NET.Build.Tasks.ConflictResolution { - class ConflictResolver + internal class ConflictResolver where TConflictItem : class, IConflictItem { - private Dictionary winningItemsByKey = new Dictionary(); + private Dictionary winningItemsByKey = new Dictionary(); private ILog log; private PackageRank packageRank; @@ -19,7 +25,7 @@ public ConflictResolver(PackageRank packageRank, ILog log) this.packageRank = packageRank; } - public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, Action foundConflict, bool commitWinner = true) + public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, Action foundConflict, bool commitWinner = true) { if (conflictItems == null) { @@ -35,7 +41,7 @@ public void ResolveConflicts(IEnumerable conflictItems, Func conflictItems, Func conflictItems, Func(packageRanks, log); compileConflictScope.ResolveConflicts(referenceItems, ci => ItemUtilities.GetReferenceFileName(ci.OriginalItem), HandleCompileConflict); // resolve conflicts that class in output - var runtimeConflictScope = new ConflictResolver(packageRanks, log); + var runtimeConflictScope = new ConflictResolver(packageRanks, log); runtimeConflictScope.ResolveConflicts(referenceItems, ci => ItemUtilities.GetReferenceTargetPath(ci.OriginalItem), @@ -73,7 +73,7 @@ public override bool Execute() // resolve conflicts with platform (eg: shared framework) items // we only commit the platform items since its not a conflict if other items share the same filename. - var platformConflictScope = new ConflictResolver(packageRanks, log); + var platformConflictScope = new ConflictResolver(packageRanks, log); var platformItems = PlatformManifests?.SelectMany(pm => PlatformManifestReader.LoadConflictItems(pm.ItemSpec, log)) ?? Enumerable.Empty(); platformConflictScope.ResolveConflicts(platformItems, pi => pi.FileName, pi => { }); From 9c7dae0c828e51a2ae58445db232b7f0d413c332 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 3 Apr 2017 10:59:17 -0700 Subject: [PATCH 09/34] Fix incorrect logic (probably copy/paste error) in conflict resolver item type logic --- .../ConflictResolution/ConflictResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index cd8be1b588a0..d79721e1d58e 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -179,7 +179,7 @@ private TConflictItem ResolveConflict(TConflictItem item1, TConflictItem item2) if (!isPlatform1 && isPlatform2) { log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because it is a platform item."); - return item1; + return item2; } log.LogMessage($"{conflictMessage}. Could not determine winner due to equal file and assembly versions."); From 5d4ecd81be7e292abb4f036108db688c0853dea2 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 3 Apr 2017 12:12:48 -0700 Subject: [PATCH 10/34] Add unit tests for conflict resolver --- .../GivenAConflictResolver.cs | 435 ++++++++++++++++++ ...Microsoft.NET.Build.Tasks.UnitTests.csproj | 1 + .../ConflictResolution/ConflictResolver.cs | 7 +- 3 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs new file mode 100644 index 000000000000..8cd4169c1ccb --- /dev/null +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs @@ -0,0 +1,435 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Text; +using FluentAssertions; +using Microsoft.Build.Framework; +using Microsoft.Extensions.DependencyModel; +using Xunit; +using Microsoft.NET.Build.Tasks.ConflictResolution; +using System.Linq; + +namespace Microsoft.NET.Build.Tasks.UnitTests +{ + public class GivenAConflictResolver + { + // - Different keys (same metadata) should not result in a conflict + [Fact] + public void ItemsWithDifferentKeysDontConflict() + { + var item1 = new MockConflictItem("System.Ben"); + var item2 = new MockConflictItem("System.Immo"); + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - One item doesn't resolve to a file that exists + [Fact] + public void WhenOnlyOneItemExistsAWinnerCannotBeDetermined() + { + var item1 = new MockConflictItem() { Exists = false }; + var item2 = new MockConflictItem() { Exists = true }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(item2); + } + + // - Neither item resolves to a file that exists + [Fact] + public void WhenNeitherItemExistsAWinnerCannotBeDetermined() + { + var item1 = new MockConflictItem() { Exists = false, AssemblyVersion = new Version("1.0.0.0") }; + var item2 = new MockConflictItem() { Exists = false, AssemblyVersion = new Version("2.0.0.0") }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(item2); + } + + + [Fact] + public void WhenAnItemDoesntExistButDoesNotConflictWithAnythingItIsNotReported() + { + var result = GetConflicts( + new MockConflictItem("System.Ben"), + new MockConflictItem("System.Immo") { Exists = false }, + new MockConflictItem("System.Dave") + ); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Neither has an assembly version + [Fact] + public void WhenItemsConflictAndDontHaveAssemblyVersionsTheFileVersionIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { AssemblyVersion = null, FileVersion = new Version("1.0.0.0") }; + var item2 = new MockConflictItem() { AssemblyVersion = null, FileVersion = new Version("3.0.0.0") }; + var item3 = new MockConflictItem() { AssemblyVersion = null, FileVersion = new Version("2.0.0.0") }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item1, item3); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Only one item has an assembly version + [Fact] + public void WhenItemsConflictAndOnlyOneHasAnAssemblyVersionAWinnerCannotBeDetermined() + { + var item1 = new MockConflictItem() { AssemblyVersion = new Version("1.0.0.0") }; + var item2 = new MockConflictItem() { AssemblyVersion = null }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(item2); + } + + // - Assembly versions match + [Fact] + public void WhenItemsConflictAndAssemblyVersionsMatchTheFileVersionIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { FileVersion = new Version("3.0.0.0") }; + var item2 = new MockConflictItem() { FileVersion = new Version("2.0.0.0") }; + var item3 = new MockConflictItem() { FileVersion = new Version("1.0.0.0") }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item2, item3); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Assembly versions differ + [Fact] + public void WhenItemsConflictTheAssemblyVersionIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { AssemblyVersion = new Version("1.0.0.0") }; + var item2 = new MockConflictItem() { AssemblyVersion = new Version("2.0.0.0") }; + var item3 = new MockConflictItem() { AssemblyVersion = new Version("3.0.0.0") }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item1, item2); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Neither has a file version + [Fact] + public void WhenItemsConflictAndDontHaveFileVersionsThePackageRankIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { FileVersion = null, PackageId = "Package3" }; + var item2 = new MockConflictItem() { FileVersion = null, PackageId = "Package2" }; + var item3 = new MockConflictItem() { FileVersion = null, PackageId = "Package1" }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item1, item2); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Only one item has a file version + [Fact] + public void WhenItemsConflictAndOnlyOneHasAFileVersionAWinnerCannotBeDetermined() + { + var item1 = new MockConflictItem() { FileVersion = null }; + var item2 = new MockConflictItem() { FileVersion = new Version("1.0.0.0") }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(item2); + } + + // - File versions match + [Fact] + public void WhenItemsConflictAndFileVersionsMatchThePackageRankIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { PackageId = "Package2" }; + var item2 = new MockConflictItem() { PackageId = "Package3" }; + var item3 = new MockConflictItem() { PackageId = "Package1" }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item2, item1); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - File versions differ + [Fact] + public void WhenItemsConflictTheFileVersionIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { FileVersion = new Version("2.0.0.0") }; + var item2 = new MockConflictItem() { FileVersion = new Version("1.0.0.0") }; + var item3 = new MockConflictItem() { FileVersion = new Version("3.0.0.0") }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item2, item1); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Neither item has a package rank + [Fact] + public void WhenItemsConflictAndDontHaveAPackageRankTheItemTypeIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { PackageId = "Unranked1", ItemType = ConflictItemType.Platform }; + var item2 = new MockConflictItem() { PackageId = "Unranked2", ItemType = ConflictItemType.Reference }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().Equal(item2); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Only one item has a package rank + [Fact] + public void WhenItemsConflictAndOnlyOneHasAPackageRankItWins() + { + var item1 = new MockConflictItem() { PackageId = "Unranked1" }; + var item2 = new MockConflictItem() { PackageId = "Ranked1" }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().Equal(item1); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Package ranks match + [Fact] + public void WhenItemsConflictAndPackageRanksMatchTheItemTypeIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { PackageId = "Package1", ItemType = ConflictItemType.Reference }; + var item2 = new MockConflictItem() { PackageId = "Package1", ItemType = ConflictItemType.Platform }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().Equal(item1); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + + // - Package ranks differ + [Fact] + public void WhenItemsConflictThePackageRankIsUsedToResolveTheConflict() + { + var item1 = new MockConflictItem() { PackageId = "Package1" }; + var item2 = new MockConflictItem() { PackageId = "Package2" }; + var item3 = new MockConflictItem() { PackageId = "Package3" }; + + var result = GetConflicts(item1, item2, item3); + + result.Conflicts.Should().Equal(item2, item3); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + // - Both are platform items + [Fact] + public void WhenItemsConflictAndBothArePlatformItemsTheConflictCannotBeResolved() + { + var item1 = new MockConflictItem() { ItemType = ConflictItemType.Platform }; + var item2 = new MockConflictItem() { ItemType = ConflictItemType.Platform }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(item2); + } + + // - Neither are platform items + [Fact] + public void WhenItemsConflictAndNeitherArePlatformItemsTheConflictCannotBeResolved() + { + var item1 = new MockConflictItem() { ItemType = ConflictItemType.Reference }; + var item2 = new MockConflictItem() { ItemType = ConflictItemType.CopyLocal }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(item2); + } + + // - One item is a platform item + [Fact] + public void WhenItemsConflictAPlatformItemWins() + { + var item1 = new MockConflictItem() { ItemType = ConflictItemType.Reference }; + var item2 = new MockConflictItem() { ItemType = ConflictItemType.Platform }; + + var result = GetConflicts(item1, item2); + + result.Conflicts.Should().Equal(item1); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + [Fact] + public void WhenCommitWinnerIsFalseOnlyTheFirstResolvedConflictIsReported() + { + var committedItem = new MockConflictItem() { AssemblyVersion = new Version("2.0.0.0") } ; + + var uncommittedItem1 = new MockConflictItem() { AssemblyVersion = new Version("3.0.0.0") }; + var uncommittedItem2 = new MockConflictItem() { AssemblyVersion = new Version("1.0.0.0") }; + var uncommittedItem3 = new MockConflictItem() { AssemblyVersion = new Version("2.0.0.0") }; + + var result = GetConflicts(new[] { committedItem }, uncommittedItem1, uncommittedItem2, uncommittedItem3); + + result.Conflicts.Should().Equal(committedItem); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + [Fact] + public void WhenCommitWinnerIsFalseAndThereIsNoWinnerEachUnresolvedConflictIsReported() + { + var committedItem = new MockConflictItem(); + + var uncommittedItem1 = new MockConflictItem(); + var uncommittedItem2 = new MockConflictItem(); + var uncommittedItem3 = new MockConflictItem(); + + var result = GetConflicts(new[] { committedItem }, uncommittedItem1, uncommittedItem2, uncommittedItem3); + + result.Conflicts.Should().BeEmpty(); + result.UnresolvedConflicts.Should().Equal(uncommittedItem1, uncommittedItem2, uncommittedItem3); + } + + [Fact] + public void WhenCommitWinnerIsFalseMultipleConflictsAreReportedIfTheCommittedItemWins() + { + var committedItem = new MockConflictItem() { AssemblyVersion = new Version("4.0.0.0") }; + + var uncommittedItem1 = new MockConflictItem() { AssemblyVersion = new Version("3.0.0.0") }; + var uncommittedItem2 = new MockConflictItem() { AssemblyVersion = new Version("1.0.0.0") }; + var uncommittedItem3 = new MockConflictItem() { AssemblyVersion = new Version("2.0.0.0") }; + + var result = GetConflicts(new[] { committedItem }, uncommittedItem1, uncommittedItem2, uncommittedItem3); + + result.Conflicts.Should().Equal(uncommittedItem1, uncommittedItem2, uncommittedItem3); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + [Fact] + public void WhenCommitWinnerIsFalseConflictsWithDifferentKeysAreReported() + { + var committedItem1 = new MockConflictItem("System.Ben") { AssemblyVersion = new Version("2.0.0.0") }; + var committedItem2 = new MockConflictItem("System.Immo") { AssemblyVersion = new Version("2.0.0.0") }; + + var uncommittedItem1 = new MockConflictItem("System.Ben") { AssemblyVersion = new Version("1.0.0.0") }; + var uncommittedItem2 = new MockConflictItem("System.Immo") { AssemblyVersion = new Version("3.0.0.0") }; + var uncommittedItem3 = new MockConflictItem("System.Dave") { AssemblyVersion = new Version("3.0.0.0") }; + var uncommittedItem4 = new MockConflictItem("System.Ben") { AssemblyVersion = new Version("3.0.0.0") }; + + var result = GetConflicts(new[] { committedItem1, committedItem2 }, uncommittedItem1, uncommittedItem2, uncommittedItem3, uncommittedItem4); + + result.Conflicts.Should().Equal(uncommittedItem1, committedItem2, committedItem1); + result.UnresolvedConflicts.Should().BeEmpty(); + } + + static ConflictResults GetConflicts(params MockConflictItem[] items) + { + return GetConflicts(items, Array.Empty()); + } + + static ConflictResults GetConflicts(MockConflictItem [] itemsToCommit, params MockConflictItem [] itemsNotToCommit) + { + ConflictResults ret = new ConflictResults(); + + void ConflictHandler(MockConflictItem item) + { + ret.Conflicts.Add(item); + } + + void UnresolvedConflictHandler(MockConflictItem item) + { + ret.UnresolvedConflicts.Add(item); + } + + string[] packagesForRank = itemsToCommit.Concat(itemsNotToCommit) + .Select(i => i.PackageId) + .Where(id => !id.StartsWith("Unranked", StringComparison.OrdinalIgnoreCase)) + .Distinct() + .OrderBy(id => id) + .ToArray(); + + var resolver = new ConflictResolver(new PackageRank(packagesForRank), new MockLog()); + + resolver.ResolveConflicts(itemsToCommit, GetItemKey, ConflictHandler, + unresolvedConflict: UnresolvedConflictHandler); + + resolver.ResolveConflicts(itemsNotToCommit, GetItemKey, ConflictHandler, + commitWinner: false, + unresolvedConflict: UnresolvedConflictHandler); + + return ret; + } + + static string GetItemKey(MockConflictItem item) + { + return item.Key; + } + + class ConflictResults + { + public List Conflicts { get; set; } = new List(); + public List UnresolvedConflicts { get; set; } = new List(); + } + + class MockConflictItem : IConflictItem + { + public MockConflictItem(string name = "System.Ben") + { + Key = name + ".dll"; + AssemblyVersion = new Version("1.0.0.0"); + ItemType = ConflictItemType.Reference; + Exists = true; + FileName = name + ".dll"; + FileVersion = new Version("1.0.0.0"); + PackageId = name; + DisplayName = name; + } + public string Key { get; set; } + + public Version AssemblyVersion { get; set; } + + public ConflictItemType ItemType { get; set; } + + public bool Exists { get; set; } + + public string FileName { get; set; } + + public Version FileVersion { get; set; } + + public string PackageId { get; set; } + + public string DisplayName { get; set; } + } + + class MockLog : ILog + { + public void LogError(string message, params object[] messageArgs) + { + } + + public void LogMessage(string message, params object[] messageArgs) + { + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + } + + public void LogWarning(string message, params object[] messageArgs) + { + } + } + + } +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj index e4d0c74c4f4c..e419ebeece9f 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Microsoft.NET.Build.Tasks.UnitTests.csproj @@ -42,6 +42,7 @@ + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index d79721e1d58e..069189e0b299 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -25,7 +25,8 @@ public ConflictResolver(PackageRank packageRank, ILog log) this.packageRank = packageRank; } - public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, Action foundConflict, bool commitWinner = true) + public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, Action foundConflict, bool commitWinner = true, + Action unresolvedConflict = null) { if (conflictItems == null) { @@ -52,6 +53,10 @@ public void ResolveConflicts(IEnumerable conflictItems, Func Date: Mon, 3 Apr 2017 17:27:45 -0700 Subject: [PATCH 11/34] Don't report a conflict between two items that don't resolve to a file on disk This avoids conflicts being reported in log files when the same FrameworkAssembly reference is declared in multiple NuGet packages --- .../ConflictResolution/ConflictResolver.cs | 6 +++ .../GivenThatWeWantToBuildADesktopExe.cs | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index 069189e0b299..0e94aa56d8a3 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -92,6 +92,12 @@ private TConflictItem ResolveConflict(TConflictItem item1, TConflictItem item2) var exists1 = item1.Exists; var exists2 = item2.Exists; + if (!exists1 && !exists2) + { + // If neither file exists, then don't report a conflict, as both items should be resolved (or not) to the same reference assembly + return null; + } + if (!exists1 || !exists2) { var fileMessage = !exists1 ? diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopExe.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopExe.cs index fe9f3731c4bb..973f43e9762c 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopExe.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopExe.cs @@ -308,6 +308,45 @@ public static void Main(string [] args) } + [Fact] + public void It_does_not_report_conflicts_if_the_same_framework_assembly_is_referenced_multiple_times() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + var testProject = new TestProject() + { + Name = "DuplicateFrameworkReferences", + TargetFrameworks = "net461", + IsSdkProject = true, + IsExe = true + }; + + var testAsset = _testAssetsManager.CreateTestProject(testProject, testProject.Name) + .WithProjectChanges(p => + { + var ns = p.Root.Name.Namespace; + + var itemGroup = new XElement(ns + "ItemGroup"); + p.Root.Add(itemGroup); + + itemGroup.Add(new XElement(ns + "Reference", new XAttribute("Include", "System"))); + }) + .Restore(testProject.Name); + + var buildCommand = new BuildCommand(Stage0MSBuild, Path.Combine(testAsset.TestRoot, testProject.Name)); + + buildCommand + .CaptureStdOut() + .Execute("/v:diag") + .Should() + .Pass() + .And + .NotHaveStdOutMatching("Encountered conflict", System.Text.RegularExpressions.RegexOptions.CultureInvariant | System.Text.RegularExpressions.RegexOptions.IgnoreCase); + } + [Fact] public void It_generates_binding_redirects_if_needed() { From e2debd165e5db719b1e802dba375b87778607edb Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 01:14:20 -0700 Subject: [PATCH 12/34] Make conflict resolution strings localizable --- .../ConflictResolution/ConflictResolver.cs | 96 +++++++++++++--- .../PlatformManifestReader.cs | 25 +++- .../Resources/Strings.Designer.cs | 108 ++++++++++++++++++ .../Resources/Strings.resx | 36 ++++++ 4 files changed, 243 insertions(+), 22 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index 0e94aa56d8a3..45656e2afb23 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -4,6 +4,7 @@ using Microsoft.Build.Utilities; using System; using System.Collections.Generic; +using System.Globalization; // Notes on functionality and how to test it // The conflict resolver finds conflicting items, and if there are any of them it reports the "losing" item via the foundConflict callback @@ -85,9 +86,14 @@ public void ResolveConflicts(IEnumerable conflictItems, Func assemblyVersion2) + { + winningDisplayName = item1.DisplayName; + winningVersion = assemblyVersion1; + losingVersion = assemblyVersion2; + } + else + { + winningDisplayName = item2.DisplayName; + winningVersion = assemblyVersion2; + losingVersion = assemblyVersion1; + } + + + string assemblyMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.ChoosingAssemblyVersion, + winningDisplayName, + winningVersion, + losingVersion); + + log.LogMessage(assemblyMessage); + if (assemblyVersion1 > assemblyVersion2) { - log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because AssemblyVersion {assemblyVersion1} is greater than {assemblyVersion2}."); return item1; } if (assemblyVersion2 > assemblyVersion1) { - log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because AssemblyVersion {assemblyVersion2} is greater than {assemblyVersion1}."); return item2; } } @@ -144,21 +172,44 @@ private TConflictItem ResolveConflict(TConflictItem item1, TConflictItem item2) if (fileVersion1 == null ^ fileVersion2 == null) { var nonVersion = fileVersion1 == null ? item1.DisplayName : item2.DisplayName; - log.LogMessage($"{conflictMessage}. Could not determine a winner because {nonVersion} has no file version."); + string fileVersionMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.CouldNotDetermineWinner_FileVersion, + nonVersion); return null; } if (fileVersion1 != fileVersion2) { + string winningDisplayName; + Version winningVersion; + Version losingVersion; + if (fileVersion1 > fileVersion2) + { + winningDisplayName = item1.DisplayName; + winningVersion = fileVersion1; + losingVersion = fileVersion2; + } + else + { + winningDisplayName = item2.DisplayName; + winningVersion = fileVersion2; + losingVersion = fileVersion1; + } + + + string fileVersionMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.ChoosingFileVersion, + winningDisplayName, + winningVersion, + losingVersion); + + log.LogMessage(fileVersionMessage); + if (fileVersion1 > fileVersion2) { - log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because file version {fileVersion1} is greater than {fileVersion2}."); return item1; } if (fileVersion2 > fileVersion1) { - log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because file version {fileVersion2} is greater than {fileVersion1}."); return item2; } } @@ -168,13 +219,16 @@ private TConflictItem ResolveConflict(TConflictItem item1, TConflictItem item2) if (packageRank1 < packageRank2) { - log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because package it comes from a package that is preferred."); + string packageRankMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.ChoosingPreferredPackage, + item1.DisplayName); + log.LogMessage(packageRankMessage); return item1; } if (packageRank2 < packageRank1) { - log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because package it comes from a package that is preferred."); + string packageRankMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.ChoosingPreferredPackage, + item2.DisplayName); return item2; } @@ -183,17 +237,23 @@ private TConflictItem ResolveConflict(TConflictItem item1, TConflictItem item2) if (isPlatform1 && !isPlatform2) { - log.LogMessage($"{conflictMessage}. Choosing {item1.DisplayName} because it is a platform item."); + string platformMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.ChoosingPlatformItem, + item1.DisplayName); + log.LogMessage(platformMessage); return item1; } if (!isPlatform1 && isPlatform2) { - log.LogMessage($"{conflictMessage}. Choosing {item2.DisplayName} because it is a platform item."); + string platformMessage = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.CurrentCulture, Strings.ChoosingPlatformItem, + item2.DisplayName); + log.LogMessage(platformMessage); return item2; } - log.LogMessage($"{conflictMessage}. Could not determine winner due to equal file and assembly versions."); + string message = conflictMessage + SENTENCE_SPACING + string.Format(CultureInfo.InvariantCulture, Strings.ConflictCouldNotDetermineWinner); + + log.LogMessage(message); return null; } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs index 2e3575e8e78c..2412fc09dcd6 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/PlatformManifestReader.cs @@ -4,6 +4,7 @@ using Microsoft.Build.Utilities; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; namespace Microsoft.NET.Build.Tasks.ConflictResolution @@ -20,7 +21,9 @@ public static IEnumerable LoadConflictItems(string manifestPath, I if (!File.Exists(manifestPath)) { - log.LogError($"Could not load PlatformManifest from {manifestPath} because it did not exist"); + string errorMessage = string.Format(CultureInfo.InvariantCulture, Strings.CouldNotLoadPlatformManifest, + manifestPath); + log.LogError(errorMessage); yield break; } @@ -40,7 +43,11 @@ public static IEnumerable LoadConflictItems(string manifestPath, I if (lineParts.Length != 4) { - log.LogError($"Error parsing PlatformManifest from {manifestPath} line {lineNumber}. Lines must have the format fileName|packageId|assemblyVersion|fileVersion"); + string errorMessage = string.Format(CultureInfo.InvariantCulture, Strings.ErrorParsingPlatformManifest, + manifestPath, + lineNumber, + "fileName|packageId|assemblyVersion|fileVersion"); + log.LogError(errorMessage); yield break; } @@ -53,12 +60,22 @@ public static IEnumerable LoadConflictItems(string manifestPath, I if (assemblyVersionString.Length != 0 && !Version.TryParse(assemblyVersionString, out assemblyVersion)) { - log.LogError($"Error parsing PlatformManfiest from {manifestPath} line {lineNumber}. AssemblyVersion {assemblyVersionString} was invalid."); + string errorMessage = string.Format(CultureInfo.InvariantCulture, Strings.ErrorParsingPlatformManifestInvalidValue, + manifestPath, + lineNumber, + "AssemblyVersion", + assemblyVersionString); + log.LogError(errorMessage); } if (fileVersionString.Length != 0 && !Version.TryParse(fileVersionString, out fileVersion)) { - log.LogError($"Error parsing PlatformManifest from {manifestPath} line {lineNumber}. FileVersion {fileVersionString} was invalid."); + string errorMessage = string.Format(CultureInfo.InvariantCulture, Strings.ErrorParsingPlatformManifestInvalidValue, + manifestPath, + lineNumber, + "FileVersion", + fileVersionString); + log.LogError(errorMessage); } yield return new ConflictItem(fileName, packageId, assemblyVersion, fileVersion); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs index 02d70728e889..4e1d62335091 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.Designer.cs @@ -142,6 +142,51 @@ internal static string CannotInferTargetFrameworkIdentiferAndVersion { } } + /// + /// Looks up a localized string similar to Choosing '{0}' because AssemblyVersion '{1}' is greater than '{2}'.. + /// + internal static string ChoosingAssemblyVersion { + get { + return ResourceManager.GetString("ChoosingAssemblyVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choosing '{0}' because file version '{1}' is greater than '{2}'.. + /// + internal static string ChoosingFileVersion { + get { + return ResourceManager.GetString("ChoosingFileVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choosing '{0}' because it is a platform item.. + /// + internal static string ChoosingPlatformItem { + get { + return ResourceManager.GetString("ChoosingPlatformItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choosing '{0}' because it comes from a package that is preferred.. + /// + internal static string ChoosingPreferredPackage { + get { + return ResourceManager.GetString("ChoosingPreferredPackage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not determine winner due to equal file and assembly versions.. + /// + internal static string ConflictCouldNotDetermineWinner { + get { + return ResourceManager.GetString("ConflictCouldNotDetermineWinner", resourceCulture); + } + } + /// /// Looks up a localized string similar to Content file '{0}' does not contain expected parent package information.. /// @@ -169,6 +214,42 @@ internal static string ContentPreproccessorParameterRequired { } } + /// + /// Looks up a localized string similar to Could not determine winner because '{0}' does not exist.. + /// + internal static string CouldNotDetermineWinner_DoesntExist { + get { + return ResourceManager.GetString("CouldNotDetermineWinner_DoesntExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not determine a winner because '{0}' has no file version.. + /// + internal static string CouldNotDetermineWinner_FileVersion { + get { + return ResourceManager.GetString("CouldNotDetermineWinner_FileVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not determine a winner because '{0}' is not an assembly.. + /// + internal static string CouldNotDetermineWinner_NotAnAssembly { + get { + return ResourceManager.GetString("CouldNotDetermineWinner_NotAnAssembly", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not load PlatformManifest from '{0}' because it did not exist.. + /// + internal static string CouldNotLoadPlatformManifest { + get { + return ResourceManager.GetString("CouldNotLoadPlatformManifest", resourceCulture); + } + } + /// /// Looks up a localized string similar to Framework not installed: {0} in {1}. /// @@ -232,6 +313,33 @@ internal static string DuplicatePreprocessorToken { } } + /// + /// Looks up a localized string similar to Encountered conflict between '{0}' and '{1}'.. + /// + internal static string EncounteredConflict { + get { + return ResourceManager.GetString("EncounteredConflict", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error parsing PlatformManifest from '{0}' line {1}. Lines must have the format {2}.. + /// + internal static string ErrorParsingPlatformManifest { + get { + return ResourceManager.GetString("ErrorParsingPlatformManifest", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error parsing PlatformManifest from '{0}' line {1}. {2} '{3}' was invalid.. + /// + internal static string ErrorParsingPlatformManifestInvalidValue { + get { + return ResourceManager.GetString("ErrorParsingPlatformManifestInvalidValue", resourceCulture); + } + } + /// /// Looks up a localized string similar to Errors occured when emitting satellite assembly '{0}'.. /// diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx index d5cdc7d6beb4..3947c5b0f3c5 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Resources/Strings.resx @@ -258,4 +258,40 @@ It is not supported to build or publish a self-contained application without specifying a RuntimeIdentifier. Please either specify a RuntimeIdentifier or set SelfContained to false. + + Choosing '{0}' because AssemblyVersion '{1}' is greater than '{2}'. + + + Choosing '{0}' because file version '{1}' is greater than '{2}'. + + + Choosing '{0}' because it is a platform item. + + + Choosing '{0}' because it comes from a package that is preferred. + + + Could not determine winner due to equal file and assembly versions. + + + Could not determine winner because '{0}' does not exist. + + + Could not determine a winner because '{0}' has no file version. + + + Could not determine a winner because '{0}' is not an assembly. + + + Encountered conflict between '{0}' and '{1}'. + + + Could not load PlatformManifest from '{0}' because it did not exist. + + + Error parsing PlatformManifest from '{0}' line {1}. Lines must have the format {2}. + + + Error parsing PlatformManifest from '{0}' line {1}. {2} '{3}' was invalid. + \ No newline at end of file From 0157d3f2fdd24534e265fbb9dd735a8d784b6e3b Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 08:15:36 -0700 Subject: [PATCH 13/34] Fix test failures: Don't resolve publish assemblies for a RID, and don't do it if assets file doesn't exist --- .../build/Microsoft.Packaging.Tools.targets | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets index f031485c42f1..693a619fb40e 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets @@ -27,9 +27,9 @@ Copyright (c) .NET Foundation. All rights reserved. RunResolvePublishAssemblies GenerateBuildDependencyFile GeneratePublishDependencyFile - - _GetLockFileAssemblies + _GetLockFileAssemblies <_supportsTrimming>true @@ -49,15 +49,23 @@ Copyright (c) .NET Foundation. All rights reserved. + + _DefaultMicrosoftNETPlatformLibrary" + Condition=" '$(DesignTimeBuild)' != 'true' Or Exists('$(ProjectAssetsFile)')"> + We need to find all the files that will be loaded from deps for conflict resolution. + Note that the RuntimeIdentifier is not passed to this task, as we want to process the + rid-less assets which will be passed to the compiler. + --> From 0937875765c275bad98585aec970a8a8c4f24c72 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 08:38:38 -0700 Subject: [PATCH 14/34] Skip test asset path length check on non-Windows --- .../GivenThatWeWantToPublishAHelloWorldProject.cs | 2 +- test/Microsoft.NET.TestFramework/SdkTest.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index 7ad08561dd01..d7c1f6c80419 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -201,7 +201,7 @@ public static void Main() } } "; - var testProjectInstance = _testAssetsManager.CreateTestProject(testProject, "SelfContainedPublishConflicts") + var testProjectInstance = _testAssetsManager.CreateTestProject(testProject, testProject.Name) .WithProjectChanges(p => { diff --git a/test/Microsoft.NET.TestFramework/SdkTest.cs b/test/Microsoft.NET.TestFramework/SdkTest.cs index 867ff42326a8..d4a56a444a4e 100644 --- a/test/Microsoft.NET.TestFramework/SdkTest.cs +++ b/test/Microsoft.NET.TestFramework/SdkTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; namespace Microsoft.NET.TestFramework @@ -22,8 +23,8 @@ public void Dispose() { // Skip path length validation if running on full framework MSBuild. We do the path length validation // to avoid getting path to long errors when copying the test drop in our build infrastructure. However, - // those builds are only built with .NET Core MSBuild. - if (!UsingFullFrameworkMSBuild) + // those builds are only built with .NET Core MSBuild running on Windows + if (!UsingFullFrameworkMSBuild && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _testAssetsManager.ValidateDestinationDirectories(); } From 09704cad406309b3acf845b7f2b112cb36b6f1a7 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 15:01:56 -0700 Subject: [PATCH 15/34] Add test for conflict resolution for RID specific shared framework app --- ...enThatWeWantToPublishAHelloWorldProject.cs | 34 +++++++++++++++---- test/Microsoft.NET.TestFramework/TestAsset.cs | 2 +- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index d7c1f6c80419..d044752285bb 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -13,6 +13,7 @@ using static Microsoft.NET.TestFramework.Commands.MSBuildTest; using System.Xml.Linq; using System.Runtime.CompilerServices; +using System; namespace Microsoft.NET.Publish.Tests { @@ -166,23 +167,36 @@ public static void Main() [Fact] public void Conflicts_are_resolved_when_publishing_a_portable_app() { - Conflicts_are_resolved_when_publishing(false); + Conflicts_are_resolved_when_publishing(selfContained: false, ridSpecific: false); } [Fact] public void Conflicts_are_resolved_when_publishing_a_self_contained_app() { - Conflicts_are_resolved_when_publishing(true); + Conflicts_are_resolved_when_publishing(selfContained: true, ridSpecific: true); } - void Conflicts_are_resolved_when_publishing(bool selfContained, [CallerMemberName] string callingMethod = "") + [Fact] + public void Conflicts_are_resolved_when_publishing_a_rid_specific_shared_framework_app() { + // Test needs to be disabled until SDK updates to CLI 2.0.0-preview1-005722 or higher + //Conflicts_are_resolved_when_publishing(selfContained: false, ridSpecific: true); + } + + void Conflicts_are_resolved_when_publishing(bool selfContained, bool ridSpecific, [CallerMemberName] string callingMethod = "") + { + if (selfContained && !ridSpecific) + { + throw new ArgumentException("Self-contained apps must be rid specific"); + } + var targetFramework = "netcoreapp2.0"; - var rid = selfContained ? EnvironmentInfo.GetCompatibleRid(targetFramework) : null; + var rid = ridSpecific ? EnvironmentInfo.GetCompatibleRid(targetFramework) : null; TestProject testProject = new TestProject() { - Name = selfContained ? "SelfContainedWithConflicts" : "PortableWithConflicts", + Name = selfContained ? "SelfContainedWithConflicts" : + (ridSpecific ? "RidSpecificSharedFrameworkAppWithConflicts" : "PortableWithConflicts"), IsSdkProject = true, TargetFrameworks = targetFramework, RuntimeIdentifier = rid, @@ -219,6 +233,15 @@ public static void Main() new XAttribute("Include", dependency.Item1), new XAttribute("Version", dependency.Item2))); } + + if (!selfContained && ridSpecific) + { + var propertyGroup = new XElement(ns + "PropertyGroup"); + p.Root.Add(propertyGroup); + + propertyGroup.Add(new XElement(ns + "SelfContained", + "false")); + } }) .Restore(testProject.Name); @@ -257,7 +280,6 @@ public static void Main() runCommand = Command.Create(selfContainedExecutableFullPath, new string[] { }) .EnsureExecutable(); } - else { publishDirectory.Should().OnlyHaveFiles(new[] { diff --git a/test/Microsoft.NET.TestFramework/TestAsset.cs b/test/Microsoft.NET.TestFramework/TestAsset.cs index 244996224882..a5d6658620a0 100644 --- a/test/Microsoft.NET.TestFramework/TestAsset.cs +++ b/test/Microsoft.NET.TestFramework/TestAsset.cs @@ -182,7 +182,7 @@ public static IEnumerable> NetStandard1_3Dependencies { string netstandardDependenciesXml = @" - + From 2a446ab838f898e2eb285d1db04c69c0b62a383f Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 15:39:12 -0700 Subject: [PATCH 16/34] Simplify conflict resolution targets since we don't need to support down-level versions of NuGet --- .../build/Microsoft.Packaging.Tools.targets | 48 +++++-------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets index 693a619fb40e..d34a4dfa5caa 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets @@ -15,39 +15,6 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - - - - ResolvePackageDependenciesForBuild - RunResolvePublishAssemblies - GenerateBuildDependencyFile - GeneratePublishDependencyFile - - _GetLockFileAssemblies - - <_supportsTrimming>true - - - - - - ResolveNuGetPackageAssets - - - - - - $(ResolveAssemblyReferencesDependsOn);HandlePackageFileConflicts - HandlePackageFileConflicts;$(PrepareResourcesDependsOn) - - - + _GetLockFileAssemblies + + + $(IntermediateOutputPath)\RemoveDepsFileConflicts.semaphore - @@ -112,7 +86,7 @@ Copyright (c) .NET Foundation. All rights reserved. - + @@ -128,7 +102,7 @@ Copyright (c) .NET Foundation. All rights reserved. <_RemovePublishDepsFileConflictsSemaphore>$(IntermediateOutputPath)\RemovePublishDepsFileConflicts.semaphore - From 6d9159f4d5a7ffc30b96690d39ad75fba98f9950 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 16:14:07 -0700 Subject: [PATCH 17/34] Pass conflicts to remove to deps file generation task instead of reading and rewriting it afterwards --- .../RemoveDepsFileConflicts.cs | 156 ------------------ .../GenerateDepsFile.cs | 125 ++++++++++++++ .../build/Microsoft.NET.Publish.targets | 5 +- .../build/Microsoft.NET.Sdk.targets | 3 +- .../build/Microsoft.Packaging.Tools.targets | 26 --- 5 files changed, 131 insertions(+), 184 deletions(-) delete mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs deleted file mode 100644 index ced5282bc734..000000000000 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/RemoveDepsFileConflicts.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Microsoft.Extensions.DependencyModel; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Microsoft.NET.Build.Tasks.ConflictResolution -{ - public partial class RemoveDepsFileConflicts : Task - { - Dictionary> compileConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); - Dictionary> runtimeConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); - - [Required] - public string DepsFilePath { get; set; } - - [Required] - public ITaskItem[] Conflicts { get; set; } - - public override bool Execute() - { - LoadConflicts(); - - DependencyContext sourceDeps; - using (var sourceStream = File.Open(DepsFilePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) - { - sourceDeps = new DependencyContextJsonReader().Read(sourceStream); - } - - DependencyContext trimmedDeps = TrimConflicts(sourceDeps); - - var writer = new DependencyContextWriter(); - using (var fileStream = File.Create(DepsFilePath)) - { - writer.Write(trimmedDeps, fileStream); - } - - return !Log.HasLoggedErrors; - } - - private void LoadConflicts() - { - foreach (var conflict in Conflicts) - { - string packageId, packageSubPath; - NuGetUtilities.GetPackageParts(conflict.ItemSpec, out packageId, out packageSubPath); - - if (String.IsNullOrEmpty(packageId) || String.IsNullOrEmpty(packageSubPath)) - { - continue; - } - - var itemType = conflict.GetMetadata(nameof(ConflictItemType)); - var conflictPackages = (itemType == nameof(ConflictItemType.Reference)) ? compileConflicts : runtimeConflicts; - - HashSet conflictFiles; - if (!conflictPackages.TryGetValue(packageId, out conflictFiles)) - { - conflictPackages[packageId] = conflictFiles = new HashSet(StringComparer.OrdinalIgnoreCase); - } - - conflictFiles.Add(packageSubPath); - } - } - - private DependencyContext TrimConflicts(DependencyContext sourceDeps) - { - return new DependencyContext(sourceDeps.Target, - sourceDeps.CompilationOptions, - TrimCompilationLibraries(sourceDeps.CompileLibraries), - TrimRuntimeLibraries(sourceDeps.RuntimeLibraries), - sourceDeps.RuntimeGraph); - } - - private IEnumerable TrimRuntimeLibraries(IReadOnlyList runtimeLibraries) - { - foreach(var runtimeLibrary in runtimeLibraries) - { - HashSet conflictFiles; - if (runtimeConflicts.TryGetValue(runtimeLibrary.Name, out conflictFiles)) - { - yield return new RuntimeLibrary(runtimeLibrary.Type, - runtimeLibrary.Name, - runtimeLibrary.Version, - runtimeLibrary.Hash, - TrimAssetGroups(runtimeLibrary.RuntimeAssemblyGroups, conflictFiles).ToArray(), - TrimAssetGroups(runtimeLibrary.NativeLibraryGroups, conflictFiles).ToArray(), - TrimResourceAssemblies(runtimeLibrary.ResourceAssemblies, conflictFiles), - runtimeLibrary.Dependencies, - runtimeLibrary.Serviceable); - } - else - { - yield return runtimeLibrary; - } - } - } - - private IEnumerable TrimAssetGroups(IEnumerable assetGroups, ISet conflicts) - { - foreach (var assetGroup in assetGroups) - { - yield return new RuntimeAssetGroup(assetGroup.Runtime, TrimAssemblies(assetGroup.AssetPaths, conflicts)); - } - } - - private IEnumerable TrimResourceAssemblies(IEnumerable resourceAssemblies, ISet conflicts) - { - foreach(var resourceAssembly in resourceAssemblies) - { - if (!conflicts.Contains(resourceAssembly.Path)) - { - yield return resourceAssembly; - } - } - } - - private IEnumerable TrimCompilationLibraries(IReadOnlyList compileLibraries) - { - foreach (var compileLibrary in compileLibraries) - { - HashSet conflictFiles; - if (compileConflicts.TryGetValue(compileLibrary.Name, out conflictFiles)) - { - yield return new CompilationLibrary(compileLibrary.Type, - compileLibrary.Name, - compileLibrary.Version, - compileLibrary.Hash, - TrimAssemblies(compileLibrary.Assemblies, conflictFiles), - compileLibrary.Dependencies, - compileLibrary.Serviceable); - } - else - { - yield return compileLibrary; - } - } - } - - private IEnumerable TrimAssemblies(IEnumerable assemblies, ISet conflicts) - { - foreach(var assembly in assemblies) - { - if (!conflicts.Contains(assembly)) - { - yield return assembly; - } - } - } - } -} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs index 2bf4a5189544..aa5ee25fbb1a 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs @@ -8,6 +8,8 @@ using Microsoft.Extensions.DependencyModel; using Newtonsoft.Json; using NuGet.ProjectModel; +using System; +using System.Linq; namespace Microsoft.NET.Build.Tasks { @@ -50,6 +52,9 @@ public class GenerateDepsFile : TaskBase [Required] public ITaskItem[] ReferenceSatellitePaths { get; set; } + [Required] + public ITaskItem[] Conflicts { get; set; } + public ITaskItem CompilerOptions { get; set; } public ITaskItem[] PrivateAssetsPackageReferences { get; set; } @@ -64,8 +69,13 @@ public ITaskItem[] FilesWritten get { return _filesWritten.ToArray(); } } + Dictionary> compileConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); + Dictionary> runtimeConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); + protected override void ExecuteCore() { + LoadConflicts(); + LockFile lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath); CompilationOptions compilationOptions = CompilationOptionsConverter.ConvertFrom(CompilerOptions); @@ -103,6 +113,11 @@ protected override void ExecuteCore() .WithReferenceAssembliesPath(FrameworkReferenceResolver.GetDefaultReferenceAssembliesPath()) .Build(); + if (compileConflicts.Any() || runtimeConflicts.Any()) + { + dependencyContext = TrimConflicts(dependencyContext); + } + var writer = new DependencyContextWriter(); using (var fileStream = File.Create(DepsFilePath)) { @@ -111,5 +126,115 @@ protected override void ExecuteCore() _filesWritten.Add(new TaskItem(DepsFilePath)); } + + private void LoadConflicts() + { + foreach (var conflict in Conflicts) + { + string packageId, packageSubPath; + ConflictResolution.NuGetUtilities.GetPackageParts(conflict.ItemSpec, out packageId, out packageSubPath); + + if (String.IsNullOrEmpty(packageId) || String.IsNullOrEmpty(packageSubPath)) + { + continue; + } + + var itemType = conflict.GetMetadata(nameof(ConflictResolution.ConflictItemType)); + var conflictPackages = (itemType == nameof(ConflictResolution.ConflictItemType.Reference)) ? compileConflicts : runtimeConflicts; + + HashSet conflictFiles; + if (!conflictPackages.TryGetValue(packageId, out conflictFiles)) + { + conflictPackages[packageId] = conflictFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + } + + conflictFiles.Add(packageSubPath); + } + } + + private DependencyContext TrimConflicts(DependencyContext sourceDeps) + { + return new DependencyContext(sourceDeps.Target, + sourceDeps.CompilationOptions, + TrimCompilationLibraries(sourceDeps.CompileLibraries), + TrimRuntimeLibraries(sourceDeps.RuntimeLibraries), + sourceDeps.RuntimeGraph); + } + + private IEnumerable TrimRuntimeLibraries(IReadOnlyList runtimeLibraries) + { + foreach (var runtimeLibrary in runtimeLibraries) + { + HashSet conflictFiles; + if (runtimeConflicts.TryGetValue(runtimeLibrary.Name, out conflictFiles)) + { + yield return new RuntimeLibrary(runtimeLibrary.Type, + runtimeLibrary.Name, + runtimeLibrary.Version, + runtimeLibrary.Hash, + TrimAssetGroups(runtimeLibrary.RuntimeAssemblyGroups, conflictFiles).ToArray(), + TrimAssetGroups(runtimeLibrary.NativeLibraryGroups, conflictFiles).ToArray(), + TrimResourceAssemblies(runtimeLibrary.ResourceAssemblies, conflictFiles), + runtimeLibrary.Dependencies, + runtimeLibrary.Serviceable); + } + else + { + yield return runtimeLibrary; + } + } + } + + private IEnumerable TrimAssetGroups(IEnumerable assetGroups, ISet conflicts) + { + foreach (var assetGroup in assetGroups) + { + yield return new RuntimeAssetGroup(assetGroup.Runtime, TrimAssemblies(assetGroup.AssetPaths, conflicts)); + } + } + + private IEnumerable TrimResourceAssemblies(IEnumerable resourceAssemblies, ISet conflicts) + { + foreach (var resourceAssembly in resourceAssemblies) + { + if (!conflicts.Contains(resourceAssembly.Path)) + { + yield return resourceAssembly; + } + } + } + + private IEnumerable TrimCompilationLibraries(IReadOnlyList compileLibraries) + { + foreach (var compileLibrary in compileLibraries) + { + HashSet conflictFiles; + if (compileConflicts.TryGetValue(compileLibrary.Name, out conflictFiles)) + { + yield return new CompilationLibrary(compileLibrary.Type, + compileLibrary.Name, + compileLibrary.Version, + compileLibrary.Hash, + TrimAssemblies(compileLibrary.Assemblies, conflictFiles), + compileLibrary.Dependencies, + compileLibrary.Serviceable); + } + else + { + yield return compileLibrary; + } + } + } + + private IEnumerable TrimAssemblies(IEnumerable assemblies, ISet conflicts) + { + foreach (var assembly in assemblies) + { + if (!conflicts.Contains(assembly)) + { + yield return assembly; + } + } + } } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets index 1273d6b698d7..e916d20f56b5 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets @@ -443,7 +443,9 @@ Copyright (c) .NET Foundation. All rights reserved. --> @@ -462,6 +464,7 @@ Copyright (c) .NET Foundation. All rights reserved. ReferenceSatellitePaths="@(ReferenceSatellitePaths)" RuntimeIdentifier="$(RuntimeIdentifier)" PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" + Conflicts="@(_ConflictPackageFiles);@(_PublishConflictPackageFiles)" CompilerOptions="@(DependencyFileCompilerOptions)" PrivateAssetsPackageReferences="@(PrivateAssetsPackageReference)" IsSelfContained="$(SelfContained)" /> diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets index dc257d598b6c..728b45b22df5 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets @@ -80,7 +80,7 @@ Copyright (c) .NET Foundation. All rights reserved. --> diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets index d34a4dfa5caa..6cc33d04b1b8 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets @@ -72,20 +72,6 @@ Copyright (c) .NET Foundation. All rights reserved. - - <_RemoveDepsFileConflictsSemaphore>$(IntermediateOutputPath)\RemoveDepsFileConflicts.semaphore - - - - - - - - - - - - <_RemovePublishDepsFileConflictsSemaphore>$(IntermediateOutputPath)\RemovePublishDepsFileConflicts.semaphore - - - - - - - - - \ No newline at end of file From 6e73517bf8111926d28807b24763ccba043a5805 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 16:20:11 -0700 Subject: [PATCH 18/34] Rename Conflicts property of GenerateDepsFile task to FilesToSkip --- .../GenerateDepsFile.cs | 60 +++++++++---------- .../build/Microsoft.NET.Publish.targets | 2 +- .../build/Microsoft.NET.Sdk.targets | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs index aa5ee25fbb1a..276fb451281d 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs @@ -53,7 +53,7 @@ public class GenerateDepsFile : TaskBase public ITaskItem[] ReferenceSatellitePaths { get; set; } [Required] - public ITaskItem[] Conflicts { get; set; } + public ITaskItem[] FilesToSkip { get; set; } public ITaskItem CompilerOptions { get; set; } @@ -69,12 +69,12 @@ public ITaskItem[] FilesWritten get { return _filesWritten.ToArray(); } } - Dictionary> compileConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); - Dictionary> runtimeConflicts = new Dictionary>(StringComparer.OrdinalIgnoreCase); + Dictionary> compileFilesToSkip = new Dictionary>(StringComparer.OrdinalIgnoreCase); + Dictionary> runtimeFilesToSkip = new Dictionary>(StringComparer.OrdinalIgnoreCase); protected override void ExecuteCore() { - LoadConflicts(); + LoadFilesToSkip(); LockFile lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath); CompilationOptions compilationOptions = CompilationOptionsConverter.ConvertFrom(CompilerOptions); @@ -113,9 +113,9 @@ protected override void ExecuteCore() .WithReferenceAssembliesPath(FrameworkReferenceResolver.GetDefaultReferenceAssembliesPath()) .Build(); - if (compileConflicts.Any() || runtimeConflicts.Any()) + if (compileFilesToSkip.Any() || runtimeFilesToSkip.Any()) { - dependencyContext = TrimConflicts(dependencyContext); + dependencyContext = TrimFilesToSkip(dependencyContext); } var writer = new DependencyContextWriter(); @@ -127,32 +127,32 @@ protected override void ExecuteCore() } - private void LoadConflicts() + private void LoadFilesToSkip() { - foreach (var conflict in Conflicts) + foreach (var fileToSkip in FilesToSkip) { string packageId, packageSubPath; - ConflictResolution.NuGetUtilities.GetPackageParts(conflict.ItemSpec, out packageId, out packageSubPath); + ConflictResolution.NuGetUtilities.GetPackageParts(fileToSkip.ItemSpec, out packageId, out packageSubPath); if (String.IsNullOrEmpty(packageId) || String.IsNullOrEmpty(packageSubPath)) { continue; } - var itemType = conflict.GetMetadata(nameof(ConflictResolution.ConflictItemType)); - var conflictPackages = (itemType == nameof(ConflictResolution.ConflictItemType.Reference)) ? compileConflicts : runtimeConflicts; + var itemType = fileToSkip.GetMetadata(nameof(ConflictResolution.ConflictItemType)); + var packagesWithFilesToSkip = (itemType == nameof(ConflictResolution.ConflictItemType.Reference)) ? compileFilesToSkip : runtimeFilesToSkip; - HashSet conflictFiles; - if (!conflictPackages.TryGetValue(packageId, out conflictFiles)) + HashSet filesToSkipForPackage; + if (!packagesWithFilesToSkip.TryGetValue(packageId, out filesToSkipForPackage)) { - conflictPackages[packageId] = conflictFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + packagesWithFilesToSkip[packageId] = filesToSkipForPackage = new HashSet(StringComparer.OrdinalIgnoreCase); } - conflictFiles.Add(packageSubPath); + filesToSkipForPackage.Add(packageSubPath); } } - private DependencyContext TrimConflicts(DependencyContext sourceDeps) + private DependencyContext TrimFilesToSkip(DependencyContext sourceDeps) { return new DependencyContext(sourceDeps.Target, sourceDeps.CompilationOptions, @@ -165,16 +165,16 @@ private IEnumerable TrimRuntimeLibraries(IReadOnlyList conflictFiles; - if (runtimeConflicts.TryGetValue(runtimeLibrary.Name, out conflictFiles)) + HashSet filesToSkip; + if (runtimeFilesToSkip.TryGetValue(runtimeLibrary.Name, out filesToSkip)) { yield return new RuntimeLibrary(runtimeLibrary.Type, runtimeLibrary.Name, runtimeLibrary.Version, runtimeLibrary.Hash, - TrimAssetGroups(runtimeLibrary.RuntimeAssemblyGroups, conflictFiles).ToArray(), - TrimAssetGroups(runtimeLibrary.NativeLibraryGroups, conflictFiles).ToArray(), - TrimResourceAssemblies(runtimeLibrary.ResourceAssemblies, conflictFiles), + TrimAssetGroups(runtimeLibrary.RuntimeAssemblyGroups, filesToSkip).ToArray(), + TrimAssetGroups(runtimeLibrary.NativeLibraryGroups, filesToSkip).ToArray(), + TrimResourceAssemblies(runtimeLibrary.ResourceAssemblies, filesToSkip), runtimeLibrary.Dependencies, runtimeLibrary.Serviceable); } @@ -185,19 +185,19 @@ private IEnumerable TrimRuntimeLibraries(IReadOnlyList TrimAssetGroups(IEnumerable assetGroups, ISet conflicts) + private IEnumerable TrimAssetGroups(IEnumerable assetGroups, ISet filesToTrim) { foreach (var assetGroup in assetGroups) { - yield return new RuntimeAssetGroup(assetGroup.Runtime, TrimAssemblies(assetGroup.AssetPaths, conflicts)); + yield return new RuntimeAssetGroup(assetGroup.Runtime, TrimAssemblies(assetGroup.AssetPaths, filesToTrim)); } } - private IEnumerable TrimResourceAssemblies(IEnumerable resourceAssemblies, ISet conflicts) + private IEnumerable TrimResourceAssemblies(IEnumerable resourceAssemblies, ISet filesToTrim) { foreach (var resourceAssembly in resourceAssemblies) { - if (!conflicts.Contains(resourceAssembly.Path)) + if (!filesToTrim.Contains(resourceAssembly.Path)) { yield return resourceAssembly; } @@ -208,14 +208,14 @@ private IEnumerable TrimCompilationLibraries(IReadOnlyList conflictFiles; - if (compileConflicts.TryGetValue(compileLibrary.Name, out conflictFiles)) + HashSet filesToSkip; + if (compileFilesToSkip.TryGetValue(compileLibrary.Name, out filesToSkip)) { yield return new CompilationLibrary(compileLibrary.Type, compileLibrary.Name, compileLibrary.Version, compileLibrary.Hash, - TrimAssemblies(compileLibrary.Assemblies, conflictFiles), + TrimAssemblies(compileLibrary.Assemblies, filesToSkip), compileLibrary.Dependencies, compileLibrary.Serviceable); } @@ -226,11 +226,11 @@ private IEnumerable TrimCompilationLibraries(IReadOnlyList TrimAssemblies(IEnumerable assemblies, ISet conflicts) + private IEnumerable TrimAssemblies(IEnumerable assemblies, ISet filesToTrim) { foreach (var assembly in assemblies) { - if (!conflicts.Contains(assembly)) + if (!filesToTrim.Contains(assembly)) { yield return assembly; } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets index e916d20f56b5..57319a405c22 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets @@ -464,7 +464,7 @@ Copyright (c) .NET Foundation. All rights reserved. ReferenceSatellitePaths="@(ReferenceSatellitePaths)" RuntimeIdentifier="$(RuntimeIdentifier)" PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" - Conflicts="@(_ConflictPackageFiles);@(_PublishConflictPackageFiles)" + FilesToSkip="@(_ConflictPackageFiles);@(_PublishConflictPackageFiles)" CompilerOptions="@(DependencyFileCompilerOptions)" PrivateAssetsPackageReferences="@(PrivateAssetsPackageReference)" IsSelfContained="$(SelfContained)" /> diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets index 728b45b22df5..6787cb2d55ec 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets @@ -102,7 +102,7 @@ Copyright (c) .NET Foundation. All rights reserved. ReferenceSatellitePaths="@(ReferenceSatellitePaths)" RuntimeIdentifier="$(RuntimeIdentifier)" PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" - Conflicts="@(_ConflictPackageFiles)" + FilesToSkip="@(_ConflictPackageFiles)" CompilerOptions="@(DependencyFileCompilerOptions)" IsSelfContained="$(SelfContained)"> From 9ee2bfcdde7893da981acff03430a3571364d932 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 16:27:49 -0700 Subject: [PATCH 19/34] Rename conflict resolution targets file --- .../Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj | 2 +- ...g.Tools.targets => Microsoft.NET.ConflictResolution.targets} | 2 +- .../Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/Tasks/Microsoft.NET.Build.Tasks/build/{Microsoft.Packaging.Tools.targets => Microsoft.NET.ConflictResolution.targets} (99%) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index 0a691f331ade..5360d073a788 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -167,7 +167,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets similarity index 99% rename from src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets rename to src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets index 6cc33d04b1b8..07ca9a54e907 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.Packaging.Tools.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets @@ -1,6 +1,6 @@ - 2.0.0-beta-001783-00 2.1.0 4.19.2 4.19.0 diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs index c7f390a72b96..a5a29ee88c95 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs @@ -59,7 +59,6 @@ public static string GetMessage() Name = "Referencer", IsSdkProject = true, TargetFrameworks = referencerTarget, - RuntimeFrameworkVersion = RepoInfo.NetCoreApp20Version, // Need to use a self-contained app for now because we don't use a CLI that has a "2.0" shared framework RuntimeIdentifier = EnvironmentInfo.GetCompatibleRid(referencerTarget), IsExe = true, diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index d044752285bb..41e28d09a77d 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -110,7 +110,6 @@ public void Publish_standalone_post_netcoreapp2_app_and_it_should_run() Name = "Hello", IsSdkProject = true, TargetFrameworks = targetFramework, - RuntimeFrameworkVersion = RepoInfo.NetCoreApp20Version, RuntimeIdentifier = rid, IsExe = true, }; @@ -179,8 +178,7 @@ public void Conflicts_are_resolved_when_publishing_a_self_contained_app() [Fact] public void Conflicts_are_resolved_when_publishing_a_rid_specific_shared_framework_app() { - // Test needs to be disabled until SDK updates to CLI 2.0.0-preview1-005722 or higher - //Conflicts_are_resolved_when_publishing(selfContained: false, ridSpecific: true); + Conflicts_are_resolved_when_publishing(selfContained: false, ridSpecific: true); } void Conflicts_are_resolved_when_publishing(bool selfContained, bool ridSpecific, [CallerMemberName] string callingMethod = "") @@ -196,7 +194,7 @@ void Conflicts_are_resolved_when_publishing(bool selfContained, bool ridSpecific TestProject testProject = new TestProject() { Name = selfContained ? "SelfContainedWithConflicts" : - (ridSpecific ? "RidSpecificSharedFrameworkAppWithConflicts" : "PortableWithConflicts"), + (ridSpecific ? "RidSpecificSharedConflicts" : "PortableWithConflicts"), IsSdkProject = true, TargetFrameworks = targetFramework, RuntimeIdentifier = rid, diff --git a/test/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs b/test/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs index cb49146232d9..eebe90d7f954 100644 --- a/test/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs +++ b/test/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs @@ -142,12 +142,6 @@ internal void Create(TestAsset targetTestAsset, string testProjectsSourceFolder) { propertyGroup.Add(new XElement(ns + "TargetFramework", this.TargetFrameworks)); } - // Workaround for .NET Core 2.0 - // - if (this.TargetFrameworks.Contains("netcoreapp2.0") && this.RuntimeFrameworkVersion == null) - { - this.RuntimeFrameworkVersion = RepoInfo.NetCoreApp20Version; - } if (!string.IsNullOrEmpty(this.RuntimeFrameworkVersion)) { diff --git a/test/Microsoft.NET.TestFramework/RepoInfo.cs b/test/Microsoft.NET.TestFramework/RepoInfo.cs index aaa1732e8ac8..c66df5cade78 100644 --- a/test/Microsoft.NET.TestFramework/RepoInfo.cs +++ b/test/Microsoft.NET.TestFramework/RepoInfo.cs @@ -86,28 +86,6 @@ public static string DotNetHostPath } } - public static string NetCoreApp20Version { get; } = ReadNetCoreApp20Version(); - - private static string ReadNetCoreApp20Version() - { - var dependencyVersionsPath = Path.Combine(RepoRoot, "build", "DependencyVersions.props"); - var root = XDocument.Load(dependencyVersionsPath).Root; - var ns = root.Name.Namespace; - - var version = root - .Elements(ns + "PropertyGroup") - .Elements(ns + "MicrosoftNETCoreApp20Version") - .FirstOrDefault() - ?.Value; - - if (string.IsNullOrEmpty(version)) - { - throw new InvalidOperationException($"Could not find a property named 'MicrosoftNETCoreApp20Version' in {dependencyVersionsPath}"); - } - - return version; - } - private static string FindConfigurationInBasePath() { // assumes tests are always executed from the "bin/$Configuration/Tests" directory From 49695bf8fdaa5d71fb77d100ffda9b81a23dad1f Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 16:52:05 -0700 Subject: [PATCH 21/34] Delete trimming and other files that were copied over from netstandard repo --- .../Microsoft.NET.ConflictResolution.targets | 2 - .../docs/trimming.md | 148 -------- .../pkg/Microsoft.Packaging.Tools.pkgproj | 20 -- .../Microsoft.Packaging.Tools.Tasks.builds | 17 - .../Microsoft.Packaging.Tools.Tasks.csproj | 57 --- .../tasks/Trimming/FileNode.cs | 162 --------- .../tasks/Trimming/IIsIncluded.cs | 11 - .../BuildErrorException.cs | 33 -- .../LockFileCache.cs | 62 ---- .../tasks/Trimming/NuGetPackageNode.cs | 68 ---- .../tasks/Trimming/TrimFiles.cs | 324 ------------------ .../tasks/Trimming/Trimmable.cs | 48 --- .../tasks/project.json | 30 -- ...Microsoft.Packaging.Tools.Trimming.targets | 80 ----- 14 files changed, 1062 deletions(-) delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/project.json delete mode 100644 src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets index 07ca9a54e907..da685b799db4 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets @@ -84,6 +84,4 @@ Copyright (c) .NET Foundation. All rights reserved. - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md b/src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md deleted file mode 100644 index 916458d70d8a..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/docs/trimming.md +++ /dev/null @@ -1,148 +0,0 @@ -#Dependency Trimming - -This package provides build infrastructure for trimming the output of an application. - -It determines what is used by the application by examining static dependencies of the application binary as well as any directly referenced packages. For any file that is unused it will be removed from the set of files copied to the output and publish folders and removed from the application's dependency file(`deps.json`) in the case of a .NET Core application. - -Applications which rely on dynamic dependencies, for example using reflection or runtime compilation like ASP.NET MVC, can specify their dynamic dependencies by referencing packages that contain those dependencies or specifying dependent files as *[roots](#roots)*. - -## How to use -First install the `Microsoft.Packaging.Tools` package in your application. - -You must use *Visual Studio 2017* or later, or *.NET Core command-line (CLI) tools 1.0* or later. - -### From the commandline -Specify `/p:TrimUnusedDependencies=true` when building the project with either `dotnet` or `msbuild`. - -Examples: -``` -dotnet build /p:TrimUnusedDependencies=true -dotnet publish /p:TrimUnusedDependencies=true -msbuild /p:TrimUnusedDependencies=true -msbuild /t:Publish /p:TrimUnusedDependencies=true -``` - -**Important:** Specify TrimUnusedDependencies for both *build* and *publish*, otherwise *build* will produce an application that is not trimmed and debugging will run against an untrimmed application that may hide any problems introduced by trimming, like missing dynamic dependencies. - -### From the IDE or committing the change to your project - -In your project (*.csproj* file) make the following change. - -```xml - - true - -``` - -### Additional options -`@(TrimFilesRootFiles)` - Additional *root* files to consider part of the application. See [roots](#roots). -`@(TrimFilesRootPackages)` - Additional *root* packages to consider part of the application. See [roots](#roots). -`@(TrimmableFiles)` - Files which should be trimmed from the application. See [trimmable](#trimmable). -`@(TrimmablePackages)` - Packages which should be trimmed from the application. See [trimmable](#trimmable). -`$(TrimFilesPreferNativeImages)` - Prefer a file with the `.ni.dll` extension over a file with the `.dll` extension. `.ni.dll` files are native images and significantly larger than a managed assembly but will load faster since they don't need to be JIT compiled. Default is `false`. -`$(RootPackageReference)` - Set to `false` to indicate that `PackageReferences` should not be considered as *[roots](roots)*. Default is `true`. -`$(TreatMetaPackagesAsTrimmable)` - When set to `true` indicates that meta-packages (packages without any file assets) should be treated as *[trimmable](#trimmable)*. Default is `true`. - -**Examples:** -- Specify TrimFilesRootFiles to include file `System.IO.Pipes.dll`. - -```xml - - - -``` - -- Specify TrimmablePackages to indicate that the `NuGet.Client` package should be considered trimmable and only the files in its closure that are actually used should be included. - -```xml - - - -``` - -- Specify TrimFilesPreferNativeImages to prefer faster and larger native images if they exist. - -```xml - - true - -``` - -- Specify RootPackageReference to prefer avoid *rooting* packages directly reference by the project. - -```xml - - false - -``` - -## How it works -The trimming task examines all of the binaries and packages that make up your project and constructs a graph of the two that is related. We start by identifying roots that are included in the application then we traverse the relationships between those to determine if other files or packages should be included in the app. - -### Roots -By default the application is a *root*, as well as all `PackageReference`s from the project file. - -The direct packages references may be excluded from the set of *roots* by specifying the property `RootPackageReference=false`. - -Additional file *roots* may be specified using the `TrimFilesRootFiles` item. -Additional package *roots* may be specified using the `TrimFilesRootPackages` item. - -### Trimmable -Files or packages may be treated as *trimmable*. Essentially this means that when the file or package is encountered while examining dependencies, that file or package will not be included nor will its dependencies unless otherwise referenced. - -If a file is *trimmable* this means that the file will not be included in the application. This takes precedence over all other indirect or direct references, including *roots*. - -If a package is *trimmable* this means that a package's files will not be included in the application unless those files are directly referenced by another file or as a root. - -Additional *trimmable* files may be specified using the `TrimmableFiles` item. -Additional *trimmable* packages may be specified using the `TrimmablePackages` item. - -### File relationships -Managed assemblies are related to other managed assemblies by assembly references in the compiled assembly. Managed assemblies are related to native libraries by DllImports, P-Invokes, to those libraries. - -#### Adding file relationships explicitly -Not all relationships can be discovered statically. A file may define relationships to other files by placing a text file next to it, with the `.dependencies` extension and list other files that it depends on. - -For example: -Suppose `foo.dll` depends on `somelibrary.dll` but that dependency is dynamic. The developer of `foo.dll` can specify this dependency by placing a file foo.dll.dependencies next to foo.dll where the content of that file is a single line: `somelibrary.dll`. - -### Package relationships -Packages are related to other packages by dependencies. Files are related to packages if they are contained in a package. - -Package relationships are established by the dependencies of a package. In this way if a file (a.dll) has a dynamic dependency on another file (b.dll) which is contained in a package (B), that file may be included in a package (A) with a dependency on the other package (b). - -## How to identify and fix missing dynamic dependencies -The best way to identify dynamic dependencies is to run your application with trimming enabled and without. If it fails only with trimming enabled then the cause of the failure is likely trimming. - -A missing assembly may cause the application to fail with an exception like the following: - -``` -Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly -'AssemblyName, Culture=culture, PublicKeyToken=0123456789abcdef' or one of its dependencies. -The system cannot find the file specified. -``` - -To fix this you can *root* the assembly `'AssemblyName, Culture=culture, PublicKeyToken=0123456789abcdef'` by adding the following to your project file. - -```xml - - - -``` - -A missing native library may cause the application to fail with an exception like the following: - -``` -Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'native.dll': -The specified module could not be found. (Exception from HRESULT: 0x8007007E) -``` - -To fix this you can *root* the native library `'native.dll'` by adding the following to your project file. - -```xml - - - -``` - -**Note:** Just because you see these exceptions doesn't necessarily mean trimming is the root cause. If you don't see the exception when running the application with trimming disabled then trimming is the likely cause. If you see the exception when running the application with trimming disabled then the cause could be a missing pre-requisite or an undeclared dependency from some package. \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj b/src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj deleted file mode 100644 index 27b4d6947d8d..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/pkg/Microsoft.Packaging.Tools.pkgproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - 1.0.0 - true - true - - - - - - - build - - - - - diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds b/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds deleted file mode 100644 index 26422204cda2..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.builds +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - true - - - - - net451 - - - - - diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj b/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj deleted file mode 100644 index 3913a41b8eb3..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Microsoft.Packaging.Tools.Tasks.csproj +++ /dev/null @@ -1,57 +0,0 @@ - - - - - Library - .NETStandard,Version=v1.5 - .NETFramework,Version=v4.5.1 - false - {65E58605-AE96-46C2-8C6C-F28A5EB9B565} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs deleted file mode 100644 index bafbd55bc185..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/FileNode.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; - -namespace Microsoft.DotNet.Build.Tasks -{ - internal class FileNode : IIsIncluded - { - IEnumerable _dependencies; - - internal const string NuGetPackageIdMetadata = "NuGetPackageId"; - internal const string NuGetPackageVersionMetadata = "NuGetPackageVersion"; - internal const string AdditionalDependenciesFileSuffix = ".dependencies"; - - public FileNode(ITaskItem fileItem, IDictionary allPackages) - { - Name = fileItem.GetMetadata("Filename") + fileItem.GetMetadata("Extension"); - OriginalItem = fileItem; - PackageId = fileItem.GetMetadata(NuGetPackageIdMetadata); - SourceFile = fileItem.GetMetadata("FullPath"); - - if (string.IsNullOrEmpty(PackageId)) - { - PackageId = NuGetUtilities.GetPackageIdFromSourcePath(SourceFile); - } - - if (!string.IsNullOrEmpty(PackageId)) - { - NuGetPackageNode package; - - if (!allPackages.TryGetValue(PackageId, out package)) - { - // some file came from a package that wasn't found in the lock file - } - else - { - Package = package; - Package.Files.Add(this); - } - } - } - - public bool IsIncluded { get; set; } - public string Name { get; } - public ITaskItem OriginalItem { get; } - public string PackageId { get; } - public string SourceFile { get; } - public NuGetPackageNode Package { get; } - public IEnumerable Dependencies { get { return _dependencies; } } - - public void PopulateDependencies(Dictionary allFiles, bool preferNativeImage, ILog log) - { - List dependencies = new List(); - - try - { - using (var peReader = new PEReader(new FileStream(SourceFile, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))) - { - if (peReader.HasMetadata) - { - var reader = peReader.GetMetadataReader(); - foreach (var handle in reader.AssemblyReferences) - { - var reference = reader.GetAssemblyReference(handle); - var referenceName = reader.GetString(reference.Name); - - var referenceCandidates = preferNativeImage ? - new[] { referenceName + ".ni.dll", referenceName + ".dll" } : - new[] { referenceName + ".dll", referenceName + ".ni.dll" }; - - FileNode referencedFile = null; - foreach (var referenceCandidate in referenceCandidates) - { - if (allFiles.TryGetValue(referenceCandidate, out referencedFile)) - { - break; - } - } - - if (referencedFile != null) - { - dependencies.Add(referencedFile); - } - else - { - // static dependency that wasn't satisfied, this can happen if folks use - // lightup code to guard the static dependency. - // this can also happen when referencing a package that isn't implemented - // on this platform but don't fail the build here - log.LogMessage(LogImportance.Low, $"Could not locate assembly dependency {referenceName} of {SourceFile}."); - } - } - - for (int i = 1, count = reader.GetTableRowCount(TableIndex.ModuleRef); i <= count; i++) - { - var moduleRef = reader.GetModuleReference(MetadataTokens.ModuleReferenceHandle(i)); - var moduleName = reader.GetString(moduleRef.Name); - - var moduleRefCandidates = new[] { moduleName, moduleName + ".dll", moduleName + ".so", moduleName + ".dylib" }; - - FileNode referencedNativeFile = null; - foreach (var moduleRefCandidate in moduleRefCandidates) - { - if (allFiles.TryGetValue(moduleRefCandidate, out referencedNativeFile)) - { - break; - } - } - - if (referencedNativeFile != null) - { - dependencies.Add(referencedNativeFile); - } - else - { - // DLLImport that wasn't satisfied - } - } - } - } - } - catch(BadImageFormatException) - { - // not a PE - } - - // allow for components to specify their dependencies themselves, by placing a file next to their source file. - var additionalDependenciesFile = SourceFile + AdditionalDependenciesFileSuffix; - - if (File.Exists(additionalDependenciesFile)) - { - foreach(var additionalDependency in File.ReadAllLines(additionalDependenciesFile)) - { - if (additionalDependency.Length == 0 || additionalDependency[0] == '#') - { - continue; - } - - FileNode additionalDependencyFile; - if (allFiles.TryGetValue(additionalDependency, out additionalDependencyFile)) - { - dependencies.Add(additionalDependencyFile); - } - else - { - log.LogMessage(LogImportance.Low, $"Could not locate explicit dependency {additionalDependency} of {SourceFile} specified in {additionalDependenciesFile}."); - } - } - } - - _dependencies = dependencies; - } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs deleted file mode 100644 index 5643648805ec..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/IIsIncluded.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.DotNet.Build.Tasks -{ - interface IIsIncluded - { - bool IsIncluded { get; set; } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs deleted file mode 100644 index 3dd0adb5ff01..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/BuildErrorException.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Globalization; - -namespace Microsoft.NET.Build.Tasks -{ - /// - /// Represents an error that is neither avoidable in all cases nor indicative of a bug in this library. - /// It will be logged as a plain build error without the exception type or stack. - /// - internal class BuildErrorException : Exception - { - public BuildErrorException() - { - } - - public BuildErrorException(string message) : base(message) - { - } - - public BuildErrorException(string message, Exception innerException) : base(message, innerException) - { - } - - public BuildErrorException(string format, params string[] args) - : this(string.Format(CultureInfo.CurrentCulture, format, args)) - { - } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs deleted file mode 100644 index 60a5e93adfc8..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Microsoft.NET.Build.Tasks/LockFileCache.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; -using Microsoft.Build.Framework; -using NuGet.Common; -using NuGet.ProjectModel; - -namespace Microsoft.NET.Build.Tasks -{ - internal class LockFileCache - { - private IBuildEngine4 _buildEngine; - - public LockFileCache(IBuildEngine4 buildEngine) - { - _buildEngine = buildEngine; - } - - public LockFile GetLockFile(string path) - { - if (!Path.IsPathRooted(path)) - { - throw new BuildErrorException("Assets file path '{0}' is not rooted. Only full paths are supported.", path); - } - - string lockFileKey = GetTaskObjectKey(path); - - LockFile result; - object existingLockFileTaskObject = _buildEngine.GetRegisteredTaskObject(lockFileKey, RegisteredTaskObjectLifetime.Build); - if (existingLockFileTaskObject == null) - { - result = LoadLockFile(path); - - _buildEngine.RegisterTaskObject(lockFileKey, result, RegisteredTaskObjectLifetime.Build, true); - } - else - { - result = (LockFile)existingLockFileTaskObject; - } - - return result; - } - - private static string GetTaskObjectKey(string lockFilePath) - { - return $"{nameof(LockFileCache)}:{lockFilePath}"; - } - - private LockFile LoadLockFile(string path) - { - if (!File.Exists(path)) - { - throw new BuildErrorException("Assets file '{0}' not found. Run a NuGet package restore to generate this file.", path); - } - - // TODO - https://github.com/dotnet/sdk/issues/18 adapt task logger to Nuget Logger - return LockFileUtilities.GetLockFile(path, NullLogger.Instance); - } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs deleted file mode 100644 index 452558830711..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/NuGetPackageNode.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; - -namespace Microsoft.DotNet.Build.Tasks -{ - /// - /// Metedata and information for a package listed in the lock file. - /// - internal sealed class NuGetPackageNode : IIsIncluded - { - private const string NuGetPackageDependencies = "dependencies"; - private List _dependencies = new List(); - private IEnumerable _dependencyIds; - - public NuGetPackageNode(string id, string version, IEnumerable dependencyIds) - { - Id = id; - IsRuntimePackage = id.StartsWith("runtime."); - Version = version; - _dependencyIds = dependencyIds; - } - - public string Id { get; } - private bool IsRuntimePackage { get; } - public bool IsIncluded { get; set; } - public string Version { get; } - public bool IsProject { get; } - public bool IsMetaPackage { get { return Files.Count == 0; } } - public IEnumerable Dependencies { get { return _dependencies; } } - public IList Files { get; } = new List(); - - public void PopulateDependencies(Dictionary allPackages, ILog log) - { - foreach(var dependencyId in _dependencyIds) - { - NuGetPackageNode dependencyNode; - if (!allPackages.TryGetValue(dependencyId, out dependencyNode)) - { - // package declared a dependency but NuGet was missing the dependent package - // in the lock file. This indicates a broken restore, but don't fail trimming - log.LogMessage(LogImportance.Low, $"Could not locate dependency {dependencyId} of package {Id}."); - } - else - { - _dependencies.Add(dependencyNode); - - // Runtime packages may be brought in by a file-based dependency, - // but runtime packages may be missing the dependencies needed since those are - // often declared by the idenity package since it is in the compile graph - // and capable of bringing in other runtime-split packages. - - // Map back up to the identity package so that we can root it and its dependencies. - // This creates an artificial cycle, but our graph walk doesn't care about cycles. - if (dependencyNode.IsRuntimePackage) - { - dependencyNode._dependencies.Add(this); - } - } - } - - _dependencyIds = null; - } - - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs deleted file mode 100644 index 0dd557ec0fd2..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/TrimFiles.cs +++ /dev/null @@ -1,324 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Microsoft.NET.Build.Tasks; -using NuGet.Frameworks; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - public partial class TrimFiles : Task - { - /// - /// Files to consider as a roots for trimming. These files and their dependency closure will be preserved. - /// Typically the project's output assembly. - /// - [Required] - public ITaskItem[] RootFiles { get; set; } - - /// - /// Packages to consider as roots for trimming. These packages and their dependency closure will be preserved. - /// Identity either package ID or packageID/version. - /// - [Required] - public ITaskItem[] RootPackages { get; set; } - - /// - /// Optional additional packages which can be trimmed. Default is common meta-packages. - /// Identity is package ID. - /// - public ITaskItem[] TrimmablePackages { get; set; } - - /// - /// Optional additional files which can be trimmed. Default is none. - /// - public ITaskItem[] TrimmableFiles { get; set; } - - /// - /// project.assets.json file for this project to be used for finding package dependencies. - /// - [Required] - public string AssetsFilePath { get; set; } - - /// - /// Target framework to use when determining package dependencies. - /// - [Required] - public string TargetFramework { get; set; } - - /// - /// RuntimeIdentifier to use when determining package dependencies. - /// - public string RuntimeIdentifier { get; set; } - - /// - /// Resolved runtime items to consider for trimming. - /// Should have metadata (where appropriate): - /// NuGetPackageId - /// NuGetPackageVersion - /// - [Required] - public ITaskItem[] RuntimeItems { get; set; } - - /// - /// Resolved runtime items to consider for trimming. - /// Should have metadata (where appropriate): - /// NuGetPackageId - /// NuGetPackageVersion - /// - public ITaskItem[] OtherRuntimeItems { get; set; } - - /// - /// True to prefer a native image ('.ni.dll') over non-native image when walking references. - /// - public bool PreferNativeImages { get; set; } - - /// - /// True to treat packages with only package references as trimmable. - /// - public bool TreatMetaPackagesAsTrimmable { get; set; } - - /// - /// A subset of ReferenceCopyLocalPaths after trimming has been done. - /// - [Output] - public ITaskItem[] RuntimeItemsAfterTrimming { get; set; } - - [Output] - public ITaskItem[] TrimmedItems { get; set; } - - private ILog log; - private Trimmable trimmable; - - public override bool Execute() - { - log = new MSBuildLog(Log); - - // Build the package graph - var packages = GetPackagesFromAssetsFile(); - - // Build file graph - var files = GetFiles(packages); - - trimmable = new Trimmable(TrimmablePackages?.Select(i => i.ItemSpec), - TrimmableFiles?.Select(i => GetFileNameFromItem(i))); - - Queue packageRoots = GetPackageRoots(packages, trimmable); - Queue fileRoots = GetFileRoots(files, trimmable); - - while (packageRoots.Count > 0 || fileRoots.Count > 0) - { - while (fileRoots.Count > 0) - { - var fileNode = fileRoots.Dequeue(); - - foreach(var file in fileNode.Dependencies.Where(f => !trimmable.IsFileTrimmable(f.Name))) - { - IncludeNode(fileRoots, file); - } - - if (fileNode.Package != null && !IsPackageTrimmable(fileNode.Package)) - { - IncludeNode(packageRoots, fileNode.Package); - } - } - - while (packageRoots.Count > 0) - { - var packageNode = packageRoots.Dequeue(); - - foreach(var dependency in packageNode.Dependencies.Where(d => !IsPackageTrimmable(d))) - { - IncludeNode(packageRoots, dependency); - } - - foreach(var file in packageNode.Files.Where(f => !trimmable.IsFileTrimmable(f.Name))) - { - IncludeNode(fileRoots, file); - } - } - } - - var excludedItems = files.Values.Where(f => !f.IsIncluded).Select(f => f.OriginalItem); - TrimmedItems = excludedItems.ToArray(); - RuntimeItemsAfterTrimming = RuntimeItems.Except(excludedItems).ToArray(); - - LogResults(files.Values); - - return !Log.HasLoggedErrors; - } - - private void LogResults(IEnumerable results) - { - int numIncluded = 0, numExcluded = 0; - long sizeIncluded = 0, sizeExcluded = 0; - foreach (var file in results) - { - if (file.IsIncluded) - { - numIncluded++; - sizeIncluded += GetFileSize(file); - } - else - { - numExcluded++; - sizeExcluded += GetFileSize(file); - } - } - - Log.LogMessage(MessageImportance.High, $"Trimmed {numExcluded} out of {numExcluded + numIncluded} files for a savings of {(double)sizeExcluded / (1024*1024):0.##} MB"); - Log.LogMessage(MessageImportance.High, $"Final app size is {(double)sizeIncluded / (1024 * 1024):0.##} MB"); - } - - private long GetFileSize(FileNode file) - { - return new FileInfo(file.SourceFile).Length; - } - - Queue GetPackageRoots(IDictionary packages, Trimmable trimmable) - { - var packageRootQueue = new Queue(); - - if (RootPackages != null) - { - var rootPackageIds = RootPackages.Select(i => GetPackageIdFromItemSpec(i.ItemSpec)); - - foreach (var rootPackageId in rootPackageIds) - { - NuGetPackageNode rootPackage; - - if (!packages.TryGetValue(rootPackageId, out rootPackage)) - { - throw new Exception($"Root package {rootPackageId} was specified but was not found in {AssetsFilePath}"); - } - - if (!IsPackageTrimmable(rootPackage)) - { - IncludeNode(packageRootQueue, rootPackage); - } - } - } - - return packageRootQueue; - } - - private static string GetPackageIdFromItemSpec(string itemSpec) - { - var separatorIndex = itemSpec.IndexOf('/'); - - return separatorIndex > 0 ? itemSpec.Substring(0, separatorIndex) : itemSpec; - } - - Queue GetFileRoots(IDictionary files, Trimmable trimmable) - { - var fileRootQueue = new Queue(); - - var trimmedRootFilenames = RootFiles.Select(i => GetFileNameFromItem(i)).Where(f => !trimmable.IsFileTrimmable(f)); - - foreach(var rootFilename in trimmedRootFilenames) - { - FileNode rootFile; - if (files.TryGetValue(rootFilename, out rootFile)) - { - IncludeNode(fileRootQueue, rootFile); - } - else - { - Log.LogMessage($"Root file {rootFilename} was specified but was not found in the file index."); - } - } - - return fileRootQueue; - } - internal IDictionary GetPackagesFromAssetsFile() - { - var lockFile = new LockFileCache(BuildEngine4).GetLockFile(AssetsFilePath); - var lockFileTarget = lockFile.GetTarget(NuGetFramework.Parse(TargetFramework), RuntimeIdentifier); - - if (lockFileTarget == null) - { - var targetString = string.IsNullOrEmpty(RuntimeIdentifier) ? TargetFramework : $"{TargetFramework}/{RuntimeIdentifier}"; - - throw new Exception($"Missing target section {targetString} from assets file {AssetsFilePath}. Ensure you have restored this project previously."); - } - - Dictionary packages = new Dictionary(lockFileTarget.Libraries.Count, StringComparer.OrdinalIgnoreCase); - - foreach (var library in lockFileTarget.Libraries) - { - var dependencyIds = library.Dependencies.Select(d => d.Id); - - packages.Add(library.Name, new NuGetPackageNode(library.Name, library.Version.ToString(), dependencyIds)); - } - - // Connect the graph - foreach (var package in packages.Values) - { - package.PopulateDependencies(packages, log); - } - - return packages; - } - - internal IDictionary GetFiles(IDictionary packages) - { - var files = new Dictionary(RuntimeItems.Length, StringComparer.OrdinalIgnoreCase); - IEnumerable runtimeItems = RuntimeItems; - - if (OtherRuntimeItems != null) - { - runtimeItems = runtimeItems.Concat(OtherRuntimeItems); - } - - foreach(var runtimeItem in runtimeItems) - { - var fileNode = new FileNode(runtimeItem, packages); - files.Add(fileNode.Name, fileNode); - } - - // root files are likely not in the RuntimeItems - foreach (var rootFile in RootFiles) - { - var fileNode = new FileNode(rootFile, packages); - if (!files.ContainsKey(fileNode.Name) && File.Exists(fileNode.SourceFile)) - { - files.Add(fileNode.Name, fileNode); - } - } - - // connect the graph - foreach(var file in files.Values) - { - file.PopulateDependencies(files, PreferNativeImages, log); - } - - return files; - } - - private bool IsPackageTrimmable(NuGetPackageNode package) - { - return trimmable.IsPackageTrimmable(package.Id) || - (TreatMetaPackagesAsTrimmable && package.IsMetaPackage); - } - - private static void IncludeNode(Queue queue, T node) where T : IIsIncluded - { - if (!node.IsIncluded) - { - node.IsIncluded = true; - queue.Enqueue(node); - } - } - - private static string GetFileNameFromItem(ITaskItem item) - { - return item.GetMetadata("FileName") + item.GetMetadata("Extension"); - } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs b/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs deleted file mode 100644 index 189e1701336a..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/Trimming/Trimmable.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.DotNet.Build.Tasks -{ - class Trimmable - { - private static readonly string[] defaultPackages = { "Microsoft.NETCore.App", "NETStandard.Library", "Microsoft.NETCore.UniversalWindowsPlatform"}; - - private HashSet _trimmablePackages; - private HashSet _trimmableFiles; - public Trimmable(IEnumerable additionalPackages, IEnumerable trimmableFiles) - { - _trimmablePackages = new HashSet(defaultPackages, StringComparer.OrdinalIgnoreCase); - _trimmableFiles = new HashSet(StringComparer.OrdinalIgnoreCase); - - if (additionalPackages != null) - { - foreach(var additionalPackage in additionalPackages) - { - _trimmablePackages.Add(additionalPackage); - } - } - - if (trimmableFiles != null) - { - foreach(var trimmableFile in trimmableFiles) - { - _trimmableFiles.Add(trimmableFile); - } - } - } - - public bool IsPackageTrimmable(string packageId) - { - return _trimmablePackages.Contains(packageId); - } - - public bool IsFileTrimmable(string fileName) - { - return _trimmableFiles.Contains(fileName); - } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/project.json b/src/Tasks/Microsoft.Packaging.Tools/tasks/project.json deleted file mode 100644 index 3869cf8c060d..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/project.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "dependencies": { - "Microsoft.DotNet.PlatformAbstractions": "1.0.1-beta-000933", - "Microsoft.Extensions.DependencyModel": "1.0.1-beta-000933", - "NuGet.ProjectModel": "4.0.0-rtm-2241", - "NETStandard.Library": "1.6.0", - "System.Reflection.Metadata": "1.3.0" - }, - "frameworks": { - "netstandard1.5": { - "dependencies": { - "Microsoft.Build": "0.1.0-preview-00022", - "Microsoft.Build.Framework": "0.1.0-preview-00022", - "Microsoft.Build.Tasks.Core": "0.1.0-preview-00022", - "Microsoft.Build.Utilities.Core": "0.1.0-preview-00022", - "Microsoft.Tpl.Dataflow": { - "version": "4.5.24", - "exclude": "all" - }, - "System.Diagnostics.FileVersionInfo": "4.0.0" - }, - "imports": [ "dnxcore50", "portable-net45+win8+wpa81" ] - }, - "net451": { - "dependencies": { - "Microsoft.TargetingPack.NETFramework.v4.5.1": "1.0.1" - } - } - } -} diff --git a/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets b/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets deleted file mode 100644 index 5375d642312d..000000000000 --- a/src/Tasks/Microsoft.Packaging.Tools/tasks/targets/Microsoft.Packaging.Tools.Trimming.targets +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - true - - - - - - - - - - - - - - - - - - - - - - - - <_trimOtherRuntimeItems Include="@(_LockFileAssemblies)" Exclude="@(_ConflictPackageFiles)" /> - - - - - - - - - - - - - - - - - - - - - - - - - - From 8a5e3dfe783a78d7be494baf68afd5bb0ccc05f2 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 4 Apr 2017 22:28:53 -0700 Subject: [PATCH 22/34] Disable tests which depend on bundled versions of .NET Core / .NET Standard when running on full framework --- .../GivenThatWeWantToReferenceAnAssembly.cs | 7 +++++++ ...GivenThatWeWantToVerifyNuGetReferenceCompat.cs | 15 ++++++++++++--- ...venThatWeWantToVerifyProjectReferenceCompat.cs | 14 +++++++++++--- .../GivenThatWeWantToPublishAHelloWorldProject.cs | 14 ++++++++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs index a5a29ee88c95..3ac6eba67371 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToReferenceAnAssembly.cs @@ -26,6 +26,13 @@ public void ItRunsAppsDirectlyReferencingAssemblies( string referencerTarget, string dependencyTarget) { + if (UsingFullFrameworkMSBuild) + { + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + string identifier = referencerTarget.ToString() + "_" + dependencyTarget.ToString(); TestProject dependencyProject = new TestProject() diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyNuGetReferenceCompat.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyNuGetReferenceCompat.cs index 7ddbb5c3f5ab..621d226d87e6 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyNuGetReferenceCompat.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyNuGetReferenceCompat.cs @@ -82,9 +82,7 @@ public class GivenThatWeWantToVerifyNuGetReferenceCompat : SdkTest, IClassFixtur [InlineData("netcoreapp1.0", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netcoreapp1.0", true, true)] [InlineData("netcoreapp1.1", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netcoreapp1.0 netcoreapp1.1", true, true)] [InlineData("netcoreapp2.0", "PartM1", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)] - // Fullframework NuGet versioning on Jenkins infrastructure issue - // https://github.com/dotnet/sdk/issues/1041 - //[InlineData("netcoreapp2.0", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)] + [InlineData("netcoreapp2.0", "Full", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)] // OptIn matrix throws an exception for each permutation // https://github.com/dotnet/sdk/issues/1025 @@ -94,6 +92,17 @@ public class GivenThatWeWantToVerifyNuGetReferenceCompat : SdkTest, IClassFixtur public void Nuget_reference_compat(string referencerTarget, string testDescription, string rawDependencyTargets, bool restoreSucceeds, bool buildSucceeds) { + if (UsingFullFrameworkMSBuild && + (referencerTarget == "netcoreapp2.0" || referencerTarget == "netstandard2.0")) + { + // Fullframework NuGet versioning on Jenkins infrastructure issue + // https://github.com/dotnet/sdk/issues/1041 + + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + string referencerDirectoryNamePostfix = "_" + referencerTarget + "_" + testDescription; TestProject referencerProject = GetTestProject(ConstantStringValues.ReferencerDirectoryName, referencerTarget, true); diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyProjectReferenceCompat.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyProjectReferenceCompat.cs index 3b91f6125768..99e302e5977b 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyProjectReferenceCompat.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToVerifyProjectReferenceCompat.cs @@ -38,13 +38,21 @@ public class GivenThatWeWantToVerifyProjectReferenceCompat : SdkTest [InlineData("netcoreapp1.0", "FullMatrix", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netcoreapp1.0", true, true)] [InlineData("netcoreapp1.1", "FullMatrix", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netcoreapp1.0 netcoreapp1.1", true, true)] [InlineData("netcoreapp2.0", "PartialM1", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)] - // Fullframework NuGet versioning on Jenkins infrastructure issue - // https://github.com/dotnet/sdk/issues/1041 - //[InlineData("netcoreapp2.0", "FullMatrix", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)] + [InlineData("netcoreapp2.0", "FullMatrix", "netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netcoreapp1.0 netcoreapp1.1 netcoreapp2.0", true, true)] public void Project_reference_compat(string referencerTarget, string testIDPostFix, string rawDependencyTargets, bool restoreSucceeds, bool buildSucceeds) { + if (UsingFullFrameworkMSBuild && referencerTarget == "netcoreapp2.0") + { + // Fullframework NuGet versioning on Jenkins infrastructure issue + // https://github.com/dotnet/sdk/issues/1041 + + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + string identifier = "_TestID_" + referencerTarget + "_" + testIDPostFix; TestProject referencerProject = GetTestProject("Referencer", referencerTarget, true); diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index 41e28d09a77d..e2fdbf1b4146 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -101,6 +101,13 @@ public void It_publishes_self_contained_apps_to_the_publish_folder_and_the_app_s [Fact] public void Publish_standalone_post_netcoreapp2_app_and_it_should_run() { + if (UsingFullFrameworkMSBuild) + { + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + var targetFramework = "netcoreapp2.0"; var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); @@ -183,6 +190,13 @@ public void Conflicts_are_resolved_when_publishing_a_rid_specific_shared_framewo void Conflicts_are_resolved_when_publishing(bool selfContained, bool ridSpecific, [CallerMemberName] string callingMethod = "") { + if (UsingFullFrameworkMSBuild) + { + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + if (selfContained && !ridSpecific) { throw new ArgumentException("Self-contained apps must be rid specific"); From 5bc37350f8923d0394c97f6cef6c5c5067b393b7 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 6 Apr 2017 14:30:49 -0700 Subject: [PATCH 23/34] Consolidate NuGetUtils and NuGetUtilities classes --- .../ConflictResolution/ConflictItem.cs | 2 +- .../ConflictResolution/NuGetUtilities.cs | 56 ------------------- .../GenerateDepsFile.cs | 2 +- .../Microsoft.NET.Build.Tasks/NuGetUtils.cs | 44 +++++++++++++++ 4 files changed, 46 insertions(+), 58 deletions(-) delete mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs index 8f00227aa9f2..70fec4f6e88b 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs @@ -157,7 +157,7 @@ public string PackageId if (packageId.Length == 0) { - packageId = NuGetUtilities.GetPackageIdFromSourcePath(SourcePath) ?? String.Empty; + packageId = NuGetUtils.GetPackageIdFromSourcePath(SourcePath) ?? String.Empty; } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs deleted file mode 100644 index ba0edbf0d337..000000000000 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/NuGetUtilities.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Build.Framework; -using System; -using System.IO; - -namespace Microsoft.NET.Build.Tasks.ConflictResolution -{ - static partial class NuGetUtilities - { - /// - /// Gets PackageId from sourcePath. - /// - /// - /// - public static string GetPackageIdFromSourcePath(string sourcePath) - { - string packageId, unused; - GetPackageParts(sourcePath, out packageId, out unused); - return packageId; - } - - /// - /// Gets PackageId and package subpath from source path - /// - /// full path to package file - /// package ID - /// subpath of asset within package - public static void GetPackageParts(string fullPath, out string packageId, out string packageSubPath) - { - packageId = null; - packageSubPath = null; - try - { - // this method is just a temporary heuristic until we get metadata added to items created by the .NETCore SDK - for (var dir = Directory.GetParent(fullPath); dir != null; dir = dir.Parent) - { - var nuspecs = dir.GetFiles("*.nuspec"); - - if (nuspecs.Length > 0) - { - packageId = Path.GetFileNameWithoutExtension(nuspecs[0].Name); - packageSubPath = fullPath.Substring(dir.FullName.Length + 1).Replace('\\', '/'); - break; - } - } - } - catch (Exception) - { } - - return; - - } - } -} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs index 276fb451281d..971f5f109890 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs @@ -132,7 +132,7 @@ private void LoadFilesToSkip() foreach (var fileToSkip in FilesToSkip) { string packageId, packageSubPath; - ConflictResolution.NuGetUtilities.GetPackageParts(fileToSkip.ItemSpec, out packageId, out packageSubPath); + NuGetUtils.GetPackageParts(fileToSkip.ItemSpec, out packageId, out packageSubPath); if (String.IsNullOrEmpty(packageId) || String.IsNullOrEmpty(packageSubPath)) { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs b/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs index 6c05fb88b463..06d0f02ea2e2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs @@ -37,5 +37,49 @@ public static NuGetFramework ParseFrameworkName(string frameworkName) { return frameworkName == null ? null : NuGetFramework.Parse(frameworkName); } + + /// + /// Gets PackageId from sourcePath. + /// + /// + /// + public static string GetPackageIdFromSourcePath(string sourcePath) + { + string packageId, unused; + GetPackageParts(sourcePath, out packageId, out unused); + return packageId; + } + + /// + /// Gets PackageId and package subpath from source path + /// + /// full path to package file + /// package ID + /// subpath of asset within package + public static void GetPackageParts(string fullPath, out string packageId, out string packageSubPath) + { + packageId = null; + packageSubPath = null; + try + { + // this method is just a temporary heuristic until we get metadata added to items created by the .NETCore SDK + for (var dir = Directory.GetParent(fullPath); dir != null; dir = dir.Parent) + { + var nuspecs = dir.GetFiles("*.nuspec"); + + if (nuspecs.Length > 0) + { + packageId = Path.GetFileNameWithoutExtension(nuspecs[0].Name); + packageSubPath = fullPath.Substring(dir.FullName.Length + 1).Replace('\\', '/'); + break; + } + } + } + catch (Exception) + { } + + return; + + } } } From b3712b5e3af1eaa01716603d89abf8ccdabfec49 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 6 Apr 2017 14:55:23 -0700 Subject: [PATCH 24/34] Consolidate ITaskItemExtensions and ItemUtilities --- .../ITaskItemExtensions.cs | 32 ------------------- .../{ConflictResolution => }/ItemUtilities.cs | 28 ++++++++++++++-- .../Microsoft.NET.Build.Tasks.csproj | 2 +- 3 files changed, 26 insertions(+), 36 deletions(-) delete mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/ITaskItemExtensions.cs rename src/Tasks/Microsoft.NET.Build.Tasks/{ConflictResolution => }/ItemUtilities.cs (84%) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ITaskItemExtensions.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ITaskItemExtensions.cs deleted file mode 100644 index 7c86fee2d33d..000000000000 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ITaskItemExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using Microsoft.Build.Framework; - -namespace Microsoft.NET.Build.Tasks -{ - internal static class ITaskItemExtensions - { - public static bool? GetBooleanMetadata(this ITaskItem item, string metadataName) - { - bool? result = null; - - string value = item.GetMetadata(metadataName); - bool parsedResult; - if (bool.TryParse(value, out parsedResult)) - { - result = parsedResult; - } - - return result; - } - - public static bool HasMetadataValue(this ITaskItem item, string name, string expectedValue) - { - string value = item.GetMetadata(name); - - return string.Equals(value, expectedValue, StringComparison.OrdinalIgnoreCase); - } - } -} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs similarity index 84% rename from src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs index 2967bdd5f9fd..85d78b519a38 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ItemUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs @@ -1,14 +1,36 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Build.Framework; using System; +using Microsoft.Build.Framework; +using Microsoft.NET.Build.Tasks.ConflictResolution; using System.IO; -namespace Microsoft.NET.Build.Tasks.ConflictResolution +namespace Microsoft.NET.Build.Tasks { - static partial class ItemUtilities + internal static class ItemUtilities { + public static bool? GetBooleanMetadata(this ITaskItem item, string metadataName) + { + bool? result = null; + + string value = item.GetMetadata(metadataName); + bool parsedResult; + if (bool.TryParse(value, out parsedResult)) + { + result = parsedResult; + } + + return result; + } + + public static bool HasMetadataValue(this ITaskItem item, string name, string expectedValue) + { + string value = item.GetMetadata(name); + + return string.Equals(value, expectedValue, StringComparison.OrdinalIgnoreCase); + } + /// /// Get's the filename to use for identifying reference conflicts /// diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index 5360d073a788..fb24202fea21 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -66,7 +66,7 @@ - + From ea4431a1df64b11b0069b08685184896183e00fd Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 6 Apr 2017 21:39:29 -0700 Subject: [PATCH 25/34] Apply code review feedback --- .../GivenAConflictResolver.cs | 18 ---- .../ConflictResolution/ConflictItem.cs | 90 +++++++++---------- .../ConflictResolution/ConflictResolver.cs | 10 +-- .../HandlePackageFileConflicts.cs | 12 ++- .../ConflictResolution/ReferenceComparer.cs | 34 ------- .../GenerateDepsFile.cs | 8 +- .../Microsoft.NET.Build.Tasks.csproj | 5 +- .../Microsoft.NET.Build.Tasks/NuGetUtils.cs | 3 +- .../Microsoft.NET.ConflictResolution.targets | 3 +- 9 files changed, 63 insertions(+), 120 deletions(-) delete mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs index 8cd4169c1ccb..364c05e37655 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAConflictResolver.cs @@ -15,7 +15,6 @@ namespace Microsoft.NET.Build.Tasks.UnitTests { public class GivenAConflictResolver { - // - Different keys (same metadata) should not result in a conflict [Fact] public void ItemsWithDifferentKeysDontConflict() { @@ -28,7 +27,6 @@ public void ItemsWithDifferentKeysDontConflict() result.UnresolvedConflicts.Should().BeEmpty(); } - // - One item doesn't resolve to a file that exists [Fact] public void WhenOnlyOneItemExistsAWinnerCannotBeDetermined() { @@ -41,7 +39,6 @@ public void WhenOnlyOneItemExistsAWinnerCannotBeDetermined() result.UnresolvedConflicts.Should().Equal(item2); } - // - Neither item resolves to a file that exists [Fact] public void WhenNeitherItemExistsAWinnerCannotBeDetermined() { @@ -68,7 +65,6 @@ public void WhenAnItemDoesntExistButDoesNotConflictWithAnythingItIsNotReported() result.UnresolvedConflicts.Should().BeEmpty(); } - // - Neither has an assembly version [Fact] public void WhenItemsConflictAndDontHaveAssemblyVersionsTheFileVersionIsUsedToResolveTheConflict() { @@ -82,7 +78,6 @@ public void WhenItemsConflictAndDontHaveAssemblyVersionsTheFileVersionIsUsedToRe result.UnresolvedConflicts.Should().BeEmpty(); } - // - Only one item has an assembly version [Fact] public void WhenItemsConflictAndOnlyOneHasAnAssemblyVersionAWinnerCannotBeDetermined() { @@ -95,7 +90,6 @@ public void WhenItemsConflictAndOnlyOneHasAnAssemblyVersionAWinnerCannotBeDeterm result.UnresolvedConflicts.Should().Equal(item2); } - // - Assembly versions match [Fact] public void WhenItemsConflictAndAssemblyVersionsMatchTheFileVersionIsUsedToResolveTheConflict() { @@ -109,7 +103,6 @@ public void WhenItemsConflictAndAssemblyVersionsMatchTheFileVersionIsUsedToResol result.UnresolvedConflicts.Should().BeEmpty(); } - // - Assembly versions differ [Fact] public void WhenItemsConflictTheAssemblyVersionIsUsedToResolveTheConflict() { @@ -123,7 +116,6 @@ public void WhenItemsConflictTheAssemblyVersionIsUsedToResolveTheConflict() result.UnresolvedConflicts.Should().BeEmpty(); } - // - Neither has a file version [Fact] public void WhenItemsConflictAndDontHaveFileVersionsThePackageRankIsUsedToResolveTheConflict() { @@ -137,7 +129,6 @@ public void WhenItemsConflictAndDontHaveFileVersionsThePackageRankIsUsedToResolv result.UnresolvedConflicts.Should().BeEmpty(); } - // - Only one item has a file version [Fact] public void WhenItemsConflictAndOnlyOneHasAFileVersionAWinnerCannotBeDetermined() { @@ -150,7 +141,6 @@ public void WhenItemsConflictAndOnlyOneHasAFileVersionAWinnerCannotBeDetermined( result.UnresolvedConflicts.Should().Equal(item2); } - // - File versions match [Fact] public void WhenItemsConflictAndFileVersionsMatchThePackageRankIsUsedToResolveTheConflict() { @@ -164,7 +154,6 @@ public void WhenItemsConflictAndFileVersionsMatchThePackageRankIsUsedToResolveTh result.UnresolvedConflicts.Should().BeEmpty(); } - // - File versions differ [Fact] public void WhenItemsConflictTheFileVersionIsUsedToResolveTheConflict() { @@ -178,7 +167,6 @@ public void WhenItemsConflictTheFileVersionIsUsedToResolveTheConflict() result.UnresolvedConflicts.Should().BeEmpty(); } - // - Neither item has a package rank [Fact] public void WhenItemsConflictAndDontHaveAPackageRankTheItemTypeIsUsedToResolveTheConflict() { @@ -191,7 +179,6 @@ public void WhenItemsConflictAndDontHaveAPackageRankTheItemTypeIsUsedToResolveTh result.UnresolvedConflicts.Should().BeEmpty(); } - // - Only one item has a package rank [Fact] public void WhenItemsConflictAndOnlyOneHasAPackageRankItWins() { @@ -204,7 +191,6 @@ public void WhenItemsConflictAndOnlyOneHasAPackageRankItWins() result.UnresolvedConflicts.Should().BeEmpty(); } - // - Package ranks match [Fact] public void WhenItemsConflictAndPackageRanksMatchTheItemTypeIsUsedToResolveTheConflict() { @@ -218,7 +204,6 @@ public void WhenItemsConflictAndPackageRanksMatchTheItemTypeIsUsedToResolveTheCo } - // - Package ranks differ [Fact] public void WhenItemsConflictThePackageRankIsUsedToResolveTheConflict() { @@ -232,7 +217,6 @@ public void WhenItemsConflictThePackageRankIsUsedToResolveTheConflict() result.UnresolvedConflicts.Should().BeEmpty(); } - // - Both are platform items [Fact] public void WhenItemsConflictAndBothArePlatformItemsTheConflictCannotBeResolved() { @@ -245,7 +229,6 @@ public void WhenItemsConflictAndBothArePlatformItemsTheConflictCannotBeResolved( result.UnresolvedConflicts.Should().Equal(item2); } - // - Neither are platform items [Fact] public void WhenItemsConflictAndNeitherArePlatformItemsTheConflictCannotBeResolved() { @@ -258,7 +241,6 @@ public void WhenItemsConflictAndNeitherArePlatformItemsTheConflictCannotBeResolv result.UnresolvedConflicts.Should().Equal(item2); } - // - One item is a platform item [Fact] public void WhenItemsConflictAPlatformItemWins() { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs index 70fec4f6e88b..0484c7aaa411 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictItem.cs @@ -46,153 +46,153 @@ public ConflictItem(string fileName, string packageId, Version assemblyVersion, FileVersion = fileVersion; } - private bool hasAssemblyVersion; - private Version assemblyVersion; + private bool _hasAssemblyVersion; + private Version _assemblyVersion; public Version AssemblyVersion { get { - if (!hasAssemblyVersion) + if (!_hasAssemblyVersion) { - assemblyVersion = null; + _assemblyVersion = null; var assemblyVersionString = OriginalItem?.GetMetadata(nameof(AssemblyVersion)) ?? String.Empty; if (assemblyVersionString.Length != 0) { - Version.TryParse(assemblyVersionString, out assemblyVersion); + Version.TryParse(assemblyVersionString, out _assemblyVersion); } else { - assemblyVersion = FileUtilities.TryGetAssemblyVersion(SourcePath); + _assemblyVersion = FileUtilities.TryGetAssemblyVersion(SourcePath); } // assemblyVersion may be null but don't try to recalculate it - hasAssemblyVersion = true; + _hasAssemblyVersion = true; } - return assemblyVersion; + return _assemblyVersion; } private set { - assemblyVersion = value; - hasAssemblyVersion = true; + _assemblyVersion = value; + _hasAssemblyVersion = true; } } public ConflictItemType ItemType { get; } - private bool? exists; + private bool? _exists; public bool Exists { get { - if (exists == null) + if (_exists == null) { - exists = ItemType == ConflictItemType.Platform || File.Exists(SourcePath); + _exists = ItemType == ConflictItemType.Platform || File.Exists(SourcePath); } - return exists.Value; + return _exists.Value; } } - private string fileName; + private string _fileName; public string FileName { get { - if (fileName == null) + if (_fileName == null) { - fileName = OriginalItem == null ? String.Empty : OriginalItem.GetMetadata(MetadataNames.FileName) + OriginalItem.GetMetadata(MetadataNames.Extension); + _fileName = OriginalItem == null ? String.Empty : OriginalItem.GetMetadata(MetadataNames.FileName) + OriginalItem.GetMetadata(MetadataNames.Extension); } - return fileName; + return _fileName; } - private set { fileName = value; } + private set { _fileName = value; } } - private bool hasFileVersion; - private Version fileVersion; + private bool _hasFileVersion; + private Version _fileVersion; public Version FileVersion { get { - if (!hasFileVersion) + if (!_hasFileVersion) { - fileVersion = null; + _fileVersion = null; var fileVersionString = OriginalItem?.GetMetadata(nameof(FileVersion)) ?? String.Empty; if (fileVersionString.Length != 0) { - Version.TryParse(fileVersionString, out fileVersion); + Version.TryParse(fileVersionString, out _fileVersion); } else { - fileVersion = FileUtilities.GetFileVersion(SourcePath); + _fileVersion = FileUtilities.GetFileVersion(SourcePath); } // fileVersion may be null but don't try to recalculate it - hasFileVersion = true; + _hasFileVersion = true; } - return fileVersion; + return _fileVersion; } private set { - fileVersion = value; - hasFileVersion = true; + _fileVersion = value; + _hasFileVersion = true; } } public ITaskItem OriginalItem { get; } - private string packageId; + private string _packageId; public string PackageId { get { - if (packageId == null) + if (_packageId == null) { - packageId = OriginalItem?.GetMetadata(MetadataNames.NuGetPackageId) ?? String.Empty; + _packageId = OriginalItem?.GetMetadata(MetadataNames.NuGetPackageId) ?? String.Empty; - if (packageId.Length == 0) + if (_packageId.Length == 0) { - packageId = NuGetUtils.GetPackageIdFromSourcePath(SourcePath) ?? String.Empty; + _packageId = NuGetUtils.GetPackageIdFromSourcePath(SourcePath) ?? String.Empty; } } - return packageId.Length == 0 ? null : packageId; + return _packageId.Length == 0 ? null : _packageId; } - private set { packageId = value; } + private set { _packageId = value; } } - private string sourcePath; + private string _sourcePath; public string SourcePath { get { - if (sourcePath == null) + if (_sourcePath == null) { - sourcePath = ItemUtilities.GetSourcePath(OriginalItem) ?? String.Empty; + _sourcePath = ItemUtilities.GetSourcePath(OriginalItem) ?? String.Empty; } - return sourcePath.Length == 0 ? null : sourcePath; + return _sourcePath.Length == 0 ? null : _sourcePath; } - private set { sourcePath = value; } + private set { _sourcePath = value; } } - public string displayName; + private string _displayName; public string DisplayName { get { - if (displayName == null) + if (_displayName == null) { var itemSpec = OriginalItem == null ? FileName : OriginalItem.ItemSpec; - displayName = $"{ItemType}:{itemSpec}"; + _displayName = $"{ItemType}:{itemSpec}"; } - return displayName; + return _displayName; } } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs index 45656e2afb23..0fedc03c3079 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ConflictResolver.cs @@ -6,14 +6,9 @@ using System.Collections.Generic; using System.Globalization; -// Notes on functionality and how to test it -// The conflict resolver finds conflicting items, and if there are any of them it reports the "losing" item via the foundConflict callback - - - - namespace Microsoft.NET.Build.Tasks.ConflictResolution { + // The conflict resolver finds conflicting items, and if there are any of them it reports the "losing" item via the foundConflict callback internal class ConflictResolver where TConflictItem : class, IConflictItem { private Dictionary winningItemsByKey = new Dictionary(); @@ -26,7 +21,8 @@ public ConflictResolver(PackageRank packageRank, ILog log) this.packageRank = packageRank; } - public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, Action foundConflict, bool commitWinner = true, + public void ResolveConflicts(IEnumerable conflictItems, Func getItemKey, + Action foundConflict, bool commitWinner = true, Action unresolvedConflict = null) { if (conflictItems == null) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs index 9e3c01e54cb3..96c30fcdb26b 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs @@ -9,11 +9,11 @@ namespace Microsoft.NET.Build.Tasks.ConflictResolution { - public partial class HandlePackageFileConflicts : Task + public class HandlePackageFileConflicts : TaskBase { - HashSet referenceConflicts = new HashSet(); - HashSet copyLocalConflicts = new HashSet(); - HashSet allConflicts = new HashSet(); + private HashSet referenceConflicts = new HashSet(); + private HashSet copyLocalConflicts = new HashSet(); + private HashSet allConflicts = new HashSet(); public ITaskItem[] References { get; set; } @@ -37,7 +37,7 @@ public partial class HandlePackageFileConflicts : Task [Output] public ITaskItem[] Conflicts { get; set; } - public override bool Execute() + protected override void ExecuteCore() { var log = new MSBuildLog(Log); var packageRanks = new PackageRank(PreferredPackages); @@ -93,8 +93,6 @@ public override bool Execute() ReferencesWithoutConflicts = RemoveConflicts(References, referenceConflicts); ReferenceCopyLocalPathsWithoutConflicts = RemoveConflicts(ReferenceCopyLocalPaths, copyLocalConflicts); Conflicts = CreateConflictTaskItems(allConflicts); - - return !Log.HasLoggedErrors; } private ITaskItem[] CreateConflictTaskItems(ICollection conflicts) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs deleted file mode 100644 index ba17e58b17d4..000000000000 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ReferenceComparer.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Microsoft.NET.Build.Tasks.ConflictResolution -{ - internal class ReferenceComparer : IEqualityComparer, IEqualityComparer - { - public static ReferenceComparer Instance { get; } = new ReferenceComparer(); - - public bool Equals(T x, T y) - { - return ReferenceEquals(x, y); - } - - bool IEqualityComparer.Equals(object x, object y) - { - return ReferenceEquals(x, y); - } - - public int GetHashCode(T obj) - { - return RuntimeHelpers.GetHashCode(obj); - } - - public int GetHashCode(object obj) - { - return RuntimeHelpers.GetHashCode(obj); - } - } -} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs index 971f5f109890..eb513ee03cf3 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs @@ -1,14 +1,14 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Generic; -using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.Extensions.DependencyModel; using Newtonsoft.Json; using NuGet.ProjectModel; using System; +using System.Collections.Generic; +using System.IO; using System.Linq; namespace Microsoft.NET.Build.Tasks @@ -69,8 +69,8 @@ public ITaskItem[] FilesWritten get { return _filesWritten.ToArray(); } } - Dictionary> compileFilesToSkip = new Dictionary>(StringComparer.OrdinalIgnoreCase); - Dictionary> runtimeFilesToSkip = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private Dictionary> compileFilesToSkip = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private Dictionary> runtimeFilesToSkip = new Dictionary>(StringComparer.OrdinalIgnoreCase); protected override void ExecuteCore() { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index fb24202fea21..09567e20866c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -35,10 +35,9 @@ $(MsBuildPackagesVersion) Runtime - + - + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs b/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs index 06d0f02ea2e2..e96e7bd91bff 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/NuGetUtils.cs @@ -62,7 +62,8 @@ public static void GetPackageParts(string fullPath, out string packageId, out st packageSubPath = null; try { - // this method is just a temporary heuristic until we get metadata added to items created by the .NETCore SDK + // this method is just a temporary heuristic until we flow the NuGet metadata through the right items + // https://github.com/dotnet/sdk/issues/1091 for (var dir = Directory.GetParent(fullPath); dir != null; dir = dir.Parent) { var nuspecs = dir.GetFiles("*.nuspec"); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets index da685b799db4..be1cf8fd2cbc 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets @@ -34,7 +34,8 @@ Copyright (c) .NET Foundation. All rights reserved. TargetFramework="$(TargetFrameworkMoniker)" RuntimeIdentifier="" PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" - PrivateAssetsPackageReferences="@(PrivateAssetsPackageReference)"> + PrivateAssetsPackageReferences="@(PrivateAssetsPackageReference)" + IsSelfContained="$(SelfContained)"> From d525ece695b4164880e73dc8bebf5deaa3c465ad Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 6 Apr 2017 22:43:37 -0700 Subject: [PATCH 26/34] Disable conflict resolution logic from Microsoft.Packaging.Tools package --- .../Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props index 0447bc652afc..da7c497eb1ba 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props @@ -13,6 +13,9 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + true From 4b04b4ebc28cc09147faa4979935c37fc863e346 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Fri, 7 Apr 2017 12:48:33 -0700 Subject: [PATCH 27/34] Add tests for deps.json conflict removal --- .../GivenThatWeWantToBuildANetCoreApp.cs | 56 ++++++++++++ ...enThatWeWantToPublishAHelloWorldProject.cs | 20 +++++ .../Assertions/DependencyContextAssertions.cs | 87 +++++++++++++++++++ .../Assertions/DependencyContextExtensions.cs | 15 ++++ 4 files changed, 178 insertions(+) create mode 100644 test/Microsoft.NET.TestFramework/Assertions/DependencyContextAssertions.cs create mode 100644 test/Microsoft.NET.TestFramework/Assertions/DependencyContextExtensions.cs diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs index 1cb8f43b69b7..2e249f4fc630 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Microsoft.Extensions.DependencyModel; using Microsoft.NET.TestFramework; using Microsoft.NET.TestFramework.Assertions; using Microsoft.NET.TestFramework.Commands; @@ -97,5 +98,60 @@ public void It_restores_only_ridless_tfm() targetDefs.Count.Should().Be(1); targetDefs.Should().Contain(".NETCoreApp,Version=v1.1"); } + + [Fact] + public void It_trims_conflicts_from_the_deps_file() + { + TestProject project = new TestProject() + { + Name = "NetCore2App", + TargetFrameworks = "netcoreapp2.0", + IsExe = true, + IsSdkProject = true + }; + + var testAsset = _testAssetsManager.CreateTestProject(project) + .WithProjectChanges(p => + { + var ns = p.Root.Name.Namespace; + + // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: + // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); + + var itemGroup = new XElement(ns + "ItemGroup"); + p.Root.Add(itemGroup); + + foreach (var dependency in TestAsset.NetStandard1_3Dependencies) + { + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", dependency.Item1), + new XAttribute("Version", dependency.Item2))); + } + + }) + .Restore(project.Name); + + string projectFolder = Path.Combine(testAsset.Path, project.Name); + + var buildCommand = new BuildCommand(Stage0MSBuild, projectFolder); + + buildCommand + .Execute() + .Should() + .Pass(); + + string outputFolder = buildCommand.GetOutputDirectory(project.TargetFrameworks).FullName; + + using (var depsJsonFileStream = File.OpenRead(Path.Combine(outputFolder, $"{project.Name}.deps.json"))) + { + var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream); + dependencyContext.Should() + .OnlyHaveRuntimeAssemblies("", project.Name) + .And + .HaveNoDuplicateRuntimeAssemblies("") + .And + .HaveNoDuplicateNativeAssets(""); ; + } + } } } diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index e2fdbf1b4146..3e7e59ac783a 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -14,6 +14,7 @@ using System.Xml.Linq; using System.Runtime.CompilerServices; using System; +using Microsoft.Extensions.DependencyModel; namespace Microsoft.NET.Publish.Tests { @@ -266,6 +267,17 @@ public static void Main() targetFramework: targetFramework, runtimeIdentifier: rid ?? string.Empty); + DependencyContext dependencyContext; + using (var depsJsonFileStream = File.OpenRead(Path.Combine(publishDirectory.FullName, $"{testProject.Name}.deps.json"))) + { + dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream); + } + + dependencyContext.Should() + .HaveNoDuplicateRuntimeAssemblies(rid ?? "") + .And + .HaveNoDuplicateNativeAssets(rid ?? ""); + ICommand runCommand; if (selfContained) @@ -289,6 +301,11 @@ public static void Main() $"System.Private.CoreLib.dll", }); + dependencyContext.Should() + .OnlyHaveRuntimeAssembliesWhichAreInFolder(rid, publishDirectory.FullName) + .And + .OnlyHaveNativeAssembliesWhichAreInFolder(rid, publishDirectory.FullName, testProject.Name); + runCommand = Command.Create(selfContainedExecutableFullPath, new string[] { }) .EnsureExecutable(); } @@ -301,6 +318,9 @@ public static void Main() $"{testProject.Name}.runtimeconfig.json" }); + dependencyContext.Should() + .OnlyHaveRuntimeAssemblies(rid ?? "", testProject.Name); + runCommand = Command.Create(RepoInfo.DotNetHostPath, new[] { Path.Combine(publishDirectory.FullName, $"{testProject.Name}.dll") }); } diff --git a/test/Microsoft.NET.TestFramework/Assertions/DependencyContextAssertions.cs b/test/Microsoft.NET.TestFramework/Assertions/DependencyContextAssertions.cs new file mode 100644 index 000000000000..4b5820b77b64 --- /dev/null +++ b/test/Microsoft.NET.TestFramework/Assertions/DependencyContextAssertions.cs @@ -0,0 +1,87 @@ +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.Extensions.DependencyModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Microsoft.NET.TestFramework.Assertions +{ + public class DependencyContextAssertions + { + private DependencyContext _dependencyContext; + + public DependencyContextAssertions(DependencyContext dependencyContext) + { + _dependencyContext = dependencyContext; + } + + public AndConstraint HaveNoDuplicateRuntimeAssemblies(string runtimeIdentifier) + { + var assemblyNames = _dependencyContext.GetRuntimeAssemblyNames(runtimeIdentifier); + + var duplicateAssemblies = assemblyNames.GroupBy(n => n.Name).Where(g => g.Count() > 1); + duplicateAssemblies.Select(g => g.Key).Should().BeEmpty(); + + return new AndConstraint(this); + } + + public AndConstraint HaveNoDuplicateNativeAssets(string runtimeIdentifier) + { + var nativeAssets = _dependencyContext.GetRuntimeNativeAssets(runtimeIdentifier); + var nativeFilenames = nativeAssets.Select(n => Path.GetFileName(n)); + var duplicateNativeAssets = nativeFilenames.GroupBy(n => n).Where(g => g.Count() > 1); + duplicateNativeAssets.Select(g => g.Key).Should().BeEmpty(); + + return new AndConstraint(this); + } + + public AndConstraint OnlyHaveRuntimeAssemblies(string runtimeIdentifier, params string[] runtimeAssemblyNames) + { + var assemblyNames = _dependencyContext.GetRuntimeAssemblyNames(runtimeIdentifier); + + assemblyNames.Select(n => n.Name) + .Should() + .BeEquivalentTo(runtimeAssemblyNames); + + return new AndConstraint(this); + } + + public AndConstraint OnlyHaveRuntimeAssembliesWhichAreInFolder(string runtimeIdentifier, string folder) + { + var assemblyNames = _dependencyContext.GetRuntimeAssemblyNames(runtimeIdentifier); + + var assemblyFiles = assemblyNames.Select(an => Path.Combine(folder, an.Name + ".dll")); + + var missingFiles = assemblyFiles.Where(f => !File.Exists(f)); + + missingFiles.Should().BeEmpty(); + + return new AndConstraint(this); + } + + public AndConstraint OnlyHaveNativeAssembliesWhichAreInFolder(string runtimeIdentifier, string folder, string appName) + { + var nativeAssets = _dependencyContext.GetRuntimeNativeAssets(runtimeIdentifier); + var nativeAssetsWithPath = nativeAssets.Select(f => + { + // apphost gets renamed to the name of the app in self-contained publish + if (Path.GetFileNameWithoutExtension(f) == "apphost") + { + return Path.Combine(folder, appName + Constants.ExeSuffix); + } + else + { + return Path.Combine(folder, Path.GetFileName(f)); + } + }); + var missingNativeAssets = nativeAssetsWithPath.Where(f => !File.Exists(f)); + missingNativeAssets.Should().BeEmpty(); + + return new AndConstraint(this); + } + } +} diff --git a/test/Microsoft.NET.TestFramework/Assertions/DependencyContextExtensions.cs b/test/Microsoft.NET.TestFramework/Assertions/DependencyContextExtensions.cs new file mode 100644 index 000000000000..ad0c5a1e74d2 --- /dev/null +++ b/test/Microsoft.NET.TestFramework/Assertions/DependencyContextExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.NET.TestFramework.Assertions +{ + public static class DependencyContextExtensions + { + public static DependencyContextAssertions Should(this DependencyContext dependencyContext) + { + return new DependencyContextAssertions(dependencyContext); + } + } +} From 5e4ca3a944331e9f0b845742fd550c44e9bd5430 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Fri, 7 Apr 2017 14:25:57 -0700 Subject: [PATCH 28/34] Disable test on full framework until #1077 is fixed --- .../GivenThatWeWantToBuildANetCoreApp.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs index 2e249f4fc630..d181902715bf 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs @@ -102,6 +102,13 @@ public void It_restores_only_ridless_tfm() [Fact] public void It_trims_conflicts_from_the_deps_file() { + if (UsingFullFrameworkMSBuild) + { + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + TestProject project = new TestProject() { Name = "NetCore2App", From d43b9a72e9eacef78b0b589c299a4bb3a22228a9 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Sat, 8 Apr 2017 22:38:56 -0700 Subject: [PATCH 29/34] Remove out of date comments about how to disable conflict resolution --- .../GivenThatWeWantToBuildADesktopLibrary.cs | 3 --- .../GivenThatWeWantToBuildANetCoreApp.cs | 3 --- .../GivenThatWeWantToBuildANetStandard2Library.cs | 3 --- .../GivenThatWeWantToPublishAHelloWorldProject.cs | 3 --- 4 files changed, 12 deletions(-) diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs index dc0242dd48e6..15eb47480b41 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildADesktopLibrary.cs @@ -131,9 +131,6 @@ public void It_resolves_assembly_conflicts_with_a_NETFramework_library() { var ns = p.Root.Name.Namespace; - // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: - // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); - var itemGroup = new XElement(ns + "ItemGroup"); p.Root.Add(itemGroup); diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs index d181902715bf..8d38e0da6372 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreApp.cs @@ -122,9 +122,6 @@ public void It_trims_conflicts_from_the_deps_file() { var ns = p.Root.Name.Namespace; - // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: - // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); - var itemGroup = new XElement(ns + "ItemGroup"); p.Root.Add(itemGroup); diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs index 0cc2aea7fca1..f735c783bc3e 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetStandard2Library.cs @@ -57,9 +57,6 @@ public void It_resolves_assembly_conflicts() { var ns = p.Root.Name.Namespace; - // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: - // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); - var itemGroup = new XElement(ns + "ItemGroup"); p.Root.Add(itemGroup); diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index 3e7e59ac783a..04054b8e958c 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -234,9 +234,6 @@ public static void Main() var ns = p.Root.Name.Namespace; - // Note: if you want to see how this fails when conflicts are not resolved, set the DisableHandlePackageFileConflicts property to true, like this: - // p.Root.Element(ns + "PropertyGroup").Add(new XElement(ns + "DisableHandlePackageFileConflicts", "True")); - var itemGroup = new XElement(ns + "ItemGroup"); p.Root.Add(itemGroup); From 1ab0fb70949c2d8d7950ee1b4c1a00aba1a29144 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 10 Apr 2017 00:41:13 -0700 Subject: [PATCH 30/34] Add tests for running app from output folder, fix issues with conflict resolution when RID is specified --- .../Microsoft.NET.ConflictResolution.targets | 38 ++----- .../GivenThatWeWantToBuildANetCoreApp.cs | 99 +++++++++++++++++++ .../TestAssetsManager.cs | 11 ++- 3 files changed, 118 insertions(+), 30 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets index be1cf8fd2cbc..9b5708109f03 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets @@ -16,41 +16,21 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - - + + <_LockFileAssemblies Include="@(NativeCopyLocalItems)" /> + <_LockFileAssemblies Include="@(RuntimeCopyLocalItems)" /> + <_LockFileAssemblies Include="@(ResourceCopyLocalItems)" /> + - - - - _GetLockFileAssemblies - - + + { + if (includeConflicts) + { + var ns = p.Root.Name.Namespace; + + var itemGroup = new XElement(ns + "ItemGroup"); + p.Root.Add(itemGroup); + + foreach (var dependency in TestAsset.NetStandard1_3Dependencies) + { + itemGroup.Add(new XElement(ns + "PackageReference", + new XAttribute("Include", dependency.Item1), + new XAttribute("Version", dependency.Item2))); + } + } + }) + .Restore(project.Name); + + string projectFolder = Path.Combine(testAsset.Path, project.Name); + + var buildCommand = new BuildCommand(Stage0MSBuild, projectFolder); + + buildCommand + .Execute() + .Should() + .Pass(); + + string outputFolder = buildCommand.GetOutputDirectory(project.TargetFrameworks, runtimeIdentifier: runtimeIdentifier ?? "").FullName; + + Command.Create(RepoInfo.DotNetHostPath, new[] { Path.Combine(outputFolder, project.Name + ".dll") }) + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining(outputMessage); + + } + [Fact] public void It_trims_conflicts_from_the_deps_file() { diff --git a/test/Microsoft.NET.TestFramework/TestAssetsManager.cs b/test/Microsoft.NET.TestFramework/TestAssetsManager.cs index 3cf4e452b26f..9dbf3c813e28 100644 --- a/test/Microsoft.NET.TestFramework/TestAssetsManager.cs +++ b/test/Microsoft.NET.TestFramework/TestAssetsManager.cs @@ -111,7 +111,16 @@ private string GetTestDestinationDirectoryPath( #else string baseDirectory = AppContext.BaseDirectory; #endif - string ret = Path.Combine(baseDirectory, callingMethod + identifier, testProjectName); + string ret; + if (testProjectName == callingMethod) + { + // If testProjectName and callingMethod are the same, don't duplicate it in the test path + ret = Path.Combine(baseDirectory, callingMethod + identifier); + } + else + { + ret = Path.Combine(baseDirectory, callingMethod + identifier, testProjectName); + } TestDestinationDirectories.Add(ret); From 09c45a6f50cee8bb496a73ff440648e4561c7e75 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 10 Apr 2017 17:42:22 -0700 Subject: [PATCH 31/34] Make conflict resolution targets private --- .../Microsoft.NET.ConflictResolution.targets | 24 ++++++++----------- .../build/Microsoft.NET.Publish.targets | 4 ++-- .../build/Microsoft.NET.Sdk.targets | 2 +- ...rosoft.PackageDependencyResolution.targets | 1 + 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets index 9b5708109f03..2977c55178c4 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets @@ -16,21 +16,17 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - <_LockFileAssemblies Include="@(NativeCopyLocalItems)" /> - <_LockFileAssemblies Include="@(RuntimeCopyLocalItems)" /> - <_LockFileAssemblies Include="@(ResourceCopyLocalItems)" /> - - - - + + + + + <_LockFileAssemblies Include="@(AllCopyLocalItems->WithMetadataValue('Type', 'assembly'))" /> + + - + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets index fe7e697935bd..c1317fd66d38 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets @@ -447,8 +447,8 @@ Copyright (c) .NET Foundation. All rights reserved. diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets index c2c7e5f85146..c584692ef710 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets @@ -80,7 +80,7 @@ Copyright (c) .NET Foundation. All rights reserved. --> Date: Mon, 10 Apr 2017 23:06:21 -0700 Subject: [PATCH 32/34] Don't use Path metadata for destination path in conflict resolution The AllCopyLocalItems has the Path metadata set to the path of the file within the package, not the target path it will be copied to --- src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs index 85d78b519a38..d87a534dca1a 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ItemUtilities.cs @@ -113,7 +113,7 @@ public static string GetSourcePath(ITaskItem item) return sourcePath; } - static readonly string[] s_targetPathMetadata = new[] { MetadataNames.TargetPath, MetadataNames.DestinationSubPath, MetadataNames.Path }; + static readonly string[] s_targetPathMetadata = new[] { MetadataNames.TargetPath, MetadataNames.DestinationSubPath }; public static string GetTargetPath(ITaskItem item) { // first use TargetPath, DestinationSubPath, then Path, then fallback to filename+extension alone From da7ad2e336678e061645c7b586511da4efd4c618 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Mon, 10 Apr 2017 23:08:30 -0700 Subject: [PATCH 33/34] Rename HandlePackageFileConflicts task to ResolvePackageFileConflicts to avoid conflicts with the version from Microsoft.Packaging.Tools --- ...leConflicts.cs => ResolvePackageFileConflicts.cs} | 2 +- .../build/Microsoft.NET.ConflictResolution.targets | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/{HandlePackageFileConflicts.cs => ResolvePackageFileConflicts.cs} (99%) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ResolvePackageFileConflicts.cs similarity index 99% rename from src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs rename to src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ResolvePackageFileConflicts.cs index 96c30fcdb26b..abe3dfb9c47c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/HandlePackageFileConflicts.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ConflictResolution/ResolvePackageFileConflicts.cs @@ -9,7 +9,7 @@ namespace Microsoft.NET.Build.Tasks.ConflictResolution { - public class HandlePackageFileConflicts : TaskBase + public class ResolvePackageFileConflicts : TaskBase { private HashSet referenceConflicts = new HashSet(); private HashSet copyLocalConflicts = new HashSet(); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets index 2977c55178c4..2ab644f75673 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.ConflictResolution.targets @@ -16,9 +16,9 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - + - +