diff --git a/src/Microsoft.DotNet.ProjectModel/ErrorCodes.NuGet.cs b/src/Microsoft.DotNet.ProjectModel/ErrorCodes.NuGet.cs index 11a95a80e..b251211cc 100644 --- a/src/Microsoft.DotNet.ProjectModel/ErrorCodes.NuGet.cs +++ b/src/Microsoft.DotNet.ProjectModel/ErrorCodes.NuGet.cs @@ -20,7 +20,7 @@ namespace Microsoft.Extensions.ProjectModel // Invalid A section. The target B refers to a single file, but the pattern C produces multiple files. To mark the target as a directory, suffix it with '/'. public static readonly string NU1005 = nameof(NU1005); - // A. Please run \"dnu restore\" to generate a new lock file. + // A. Please run \"dotnet restore\" to generate a new lock file. public static readonly string NU1006 = nameof(NU1006); // Dependency specified was A but ended up with B. @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.ProjectModel // A is an unsupported framework. public static readonly string NU1008 = nameof(NU1008); - // The expected lock file doesn't exist. Please run \"dnu restore\" to generate a new lock file. + // The expected lock file doesn't exist. Please run \"dotnet restore\" to generate a new lock file. public static readonly string NU1009 = nameof(NU1009); // The dependency type was changed @@ -37,5 +37,8 @@ namespace Microsoft.Extensions.ProjectModel // The dependency target '{0}' is unsupported. public static readonly string NU1011 = nameof(NU1011); + + // Dependency conflict. + public static readonly string NU1012 = nameof(NU1012); } } diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFile.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFile.cs index ebf45594e..38268f804 100644 --- a/src/Microsoft.DotNet.ProjectModel/Graph/LockFile.cs +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFile.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using Microsoft.Extensions.ProjectModel.Utilities; using NuGet.Versioning; namespace Microsoft.Extensions.ProjectModel.Graph @@ -85,66 +86,7 @@ namespace Microsoft.Extensions.ProjectModel.Graph name = $"fx/{name}"; } - return $"{name} {RenderVersion(arg.VersionRange)}"; - } - - private string RenderVersion(VersionRange range) - { - if (range == null) - { - return null; - } - - if (range.MinVersion == range.MaxVersion && - (range.Float == null || range.Float.FloatBehavior == NuGetVersionFloatBehavior.None)) - { - return range.MinVersion.ToNormalizedString(); - } - var sb = new StringBuilder(); - sb.Append(">= "); - switch (range?.Float?.FloatBehavior) - { - case null: - case NuGetVersionFloatBehavior.None: - sb.Append(range.MinVersion.ToNormalizedString()); - break; - case NuGetVersionFloatBehavior.Prerelease: - // Work around nuget bug: https://github.com/NuGet/Home/issues/1598 - // sb.AppendFormat("{0}-*", range.MinVersion); - sb.Append($"{range.MinVersion.Version.Major}.{range.MinVersion.Version.Minor}.{range.MinVersion.Version.Build}"); - if (string.IsNullOrEmpty(range.MinVersion.Release) || - string.Equals("-", range.MinVersion.Release)) - { - sb.Append($"-*"); - } - else - { - sb.Append($"-{range.MinVersion.Release}*"); - } - break; - case NuGetVersionFloatBehavior.Revision: - sb.Append($"{range.MinVersion.Version.Major}.{range.MinVersion.Version.Minor}.{range.MinVersion.Version.Build}.*"); - break; - case NuGetVersionFloatBehavior.Patch: - sb.Append($"{range.MinVersion.Version.Major}.{range.MinVersion.Version.Minor}.*"); - break; - case NuGetVersionFloatBehavior.Minor: - sb.AppendFormat($"{range.MinVersion.Version.Major}.*"); - break; - case NuGetVersionFloatBehavior.Major: - sb.AppendFormat("*"); - break; - default: - break; - } - - if (range.MaxVersion != null) - { - sb.Append(range.IsMaxInclusive ? " <= " : " < "); - sb.Append(range.MaxVersion); - } - - return sb.ToString(); + return $"{name} {VersionUtility.RenderVersion(arg.VersionRange)}"; } } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/LibraryManager.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/LibraryManager.cs index 5e3b529b7..5a7287802 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/LibraryManager.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/LibraryManager.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.ProjectModel.Graph; +using Microsoft.Extensions.ProjectModel.Utilities; +using NuGet.Versioning; namespace Microsoft.Extensions.ProjectModel.Resolution { @@ -36,6 +38,9 @@ namespace Microsoft.Extensions.ProjectModel.Resolution messages.AddRange(_diagnostics); } + var dependencies = new Dictionary>(); + var topLevel = new List(); + foreach (var library in GetLibraries()) { if (!library.Resolved) @@ -62,6 +67,19 @@ namespace Microsoft.Extensions.ProjectModel.Resolution } else { + // Store dependency -> library for later + // J.N -> [(R1, P1), (R2, P2)] + foreach (var dependency in library.Dependencies) + { + List items; + if (!dependencies.TryGetValue(dependency.Name, out items)) + { + items = new List(); + dependencies[dependency.Name] = items; + } + items.Add(new DependencyItem(dependency, library)); + } + foreach (var range in library.RequestedRanges) { // Skip libraries that aren't specified in a project.json @@ -77,13 +95,14 @@ namespace Microsoft.Extensions.ProjectModel.Resolution continue; } - if (range.VersionRange == null) { // TODO: Show errors/warnings for things without versions continue; } + topLevel.Add(library); + // If we ended up with a declared version that isn't what was asked for directly // then report a warning // Case 1: Non floating version and the minimum doesn't match what was specified @@ -93,13 +112,48 @@ namespace Microsoft.Extensions.ProjectModel.Resolution (range.VersionRange.IsFloating && !range.VersionRange.Float.Satisfies(library.Identity.Version))) { - var message = $"Dependency specified was {range} but ended up with {library.Identity}."; + var message = $"Dependency specified was {FormatLibraryRange(range)} but ended up with {library.Identity}."; - AddDiagnostics(messages, - library, - message, - DiagnosticMessageSeverity.Warning, - ErrorCodes.NU1007); + messages.Add( + new DiagnosticMessage( + ErrorCodes.NU1007, + message, + range.SourceFilePath, + DiagnosticMessageSeverity.Warning, + range.SourceLine, + range.SourceColumn, + library)); + } + } + } + } + + // Version conflicts + foreach (var library in topLevel) + { + List items; + if (dependencies.TryGetValue(library.Identity.Name, out items)) + { + foreach (var item in items) + { + var versionRange = item.Dependency.VersionRange; + + if (versionRange == null || item.Dependency.Target != LibraryType.Package) + { + continue; + } + + if (library.Identity.Version.IsPrerelease && !versionRange.IncludePrerelease) + { + versionRange = VersionRange.SetIncludePrerelease(versionRange, includePrerelease: true); + } + + if (item.Library != library && !versionRange.Satisfies(library.Identity.Version)) + { + var errorCode = ErrorCodes.NU1012; + var message = $"Dependency conflict. {item.Library.Identity} expected {FormatLibraryRange(item.Dependency)} but got {library.Identity.Version}"; + + AddDiagnostics(messages, item.Library, message, DiagnosticMessageSeverity.Warning, errorCode); } } } @@ -115,7 +169,7 @@ namespace Microsoft.Extensions.ProjectModel.Resolution return range.Name; } - return range.Name + " " + range.VersionRange; + return range.Name + " " + VersionUtility.RenderVersion(range.VersionRange); } private void AddDiagnostics(List messages, @@ -164,5 +218,17 @@ namespace Microsoft.Extensions.ProjectModel.Resolution } } } + + private struct DependencyItem + { + public LibraryRange Dependency { get; private set; } + public LibraryDescription Library { get; private set; } + + public DependencyItem(LibraryRange dependency, LibraryDescription library) + { + Dependency = dependency; + Library = library; + } + } } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs b/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs index 1e0df2b7c..ea427e329 100644 --- a/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs +++ b/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.Loader; +using System.Text; using NuGet.Versioning; namespace Microsoft.Extensions.ProjectModel.Utilities @@ -10,5 +11,63 @@ namespace Microsoft.Extensions.ProjectModel.Utilities { return new NuGetVersion(AssemblyLoadContext.GetAssemblyName(path).Version); } + public static string RenderVersion(VersionRange range) + { + if (range == null) + { + return null; + } + + if (range.MinVersion == range.MaxVersion && + (range.Float == null || range.Float.FloatBehavior == NuGetVersionFloatBehavior.None)) + { + return range.MinVersion.ToNormalizedString(); + } + var sb = new StringBuilder(); + sb.Append(">= "); + switch (range?.Float?.FloatBehavior) + { + case null: + case NuGetVersionFloatBehavior.None: + sb.Append(range.MinVersion.ToNormalizedString()); + break; + case NuGetVersionFloatBehavior.Prerelease: + // Work around nuget bug: https://github.com/NuGet/Home/issues/1598 + // sb.AppendFormat("{0}-*", range.MinVersion); + sb.Append($"{range.MinVersion.Version.Major}.{range.MinVersion.Version.Minor}.{range.MinVersion.Version.Build}"); + if (string.IsNullOrEmpty(range.MinVersion.Release) || + string.Equals("-", range.MinVersion.Release)) + { + sb.Append($"-*"); + } + else + { + sb.Append($"-{range.MinVersion.Release}*"); + } + break; + case NuGetVersionFloatBehavior.Revision: + sb.Append($"{range.MinVersion.Version.Major}.{range.MinVersion.Version.Minor}.{range.MinVersion.Version.Build}.*"); + break; + case NuGetVersionFloatBehavior.Patch: + sb.Append($"{range.MinVersion.Version.Major}.{range.MinVersion.Version.Minor}.*"); + break; + case NuGetVersionFloatBehavior.Minor: + sb.AppendFormat($"{range.MinVersion.Version.Major}.*"); + break; + case NuGetVersionFloatBehavior.Major: + sb.AppendFormat("*"); + break; + default: + break; + } + + if (range.MaxVersion != null) + { + sb.Append(range.IsMaxInclusive ? " <= " : " < "); + sb.Append(range.MaxVersion); + } + + return sb.ToString(); + } } } diff --git a/src/Microsoft.DotNet.ProjectModel/project.json b/src/Microsoft.DotNet.ProjectModel/project.json index b64c8bfc6..9912361ef 100644 --- a/src/Microsoft.DotNet.ProjectModel/project.json +++ b/src/Microsoft.DotNet.ProjectModel/project.json @@ -13,7 +13,7 @@ "NuGet.Packaging": "3.2.0", - "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-*", + "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15791", "Microsoft.Extensions.JsonParser.Sources": { "type": "build", "version": "1.0.0-*"