2015-10-13 14:31:29 -07:00
|
|
|
// Copyright (c) .NET Foundation. All rights reserved.
|
|
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
|
|
|
|
using System.Collections.Generic;
|
2015-10-20 01:43:37 -07:00
|
|
|
using System.Linq;
|
|
|
|
using Microsoft.Extensions.ProjectModel.Graph;
|
2015-11-08 09:52:28 -08:00
|
|
|
using Microsoft.Extensions.ProjectModel.Utilities;
|
|
|
|
using NuGet.Versioning;
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
namespace Microsoft.Extensions.ProjectModel.Resolution
|
|
|
|
{
|
|
|
|
public class LibraryManager
|
|
|
|
{
|
2015-10-17 07:34:04 -07:00
|
|
|
private readonly IList<LibraryDescription> _libraries;
|
|
|
|
private readonly IList<DiagnosticMessage> _diagnostics;
|
2015-11-08 07:38:42 -08:00
|
|
|
private readonly string _projectPath;
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-20 01:43:37 -07:00
|
|
|
public LibraryManager(IList<LibraryDescription> libraries,
|
2015-11-08 07:38:42 -08:00
|
|
|
IList<DiagnosticMessage> diagnostics,
|
|
|
|
string projectPath)
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
|
|
|
_libraries = libraries;
|
2015-10-17 07:34:04 -07:00
|
|
|
_diagnostics = diagnostics;
|
2015-11-08 07:38:42 -08:00
|
|
|
_projectPath = projectPath;
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
|
2015-10-17 07:34:04 -07:00
|
|
|
public IList<LibraryDescription> GetLibraries()
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
2015-10-17 07:34:04 -07:00
|
|
|
return _libraries;
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public IList<DiagnosticMessage> GetAllDiagnostics()
|
|
|
|
{
|
|
|
|
var messages = new List<DiagnosticMessage>();
|
|
|
|
|
|
|
|
if (_diagnostics != null)
|
|
|
|
{
|
|
|
|
messages.AddRange(_diagnostics);
|
|
|
|
}
|
|
|
|
|
2015-11-08 09:52:28 -08:00
|
|
|
var dependencies = new Dictionary<string, List<DependencyItem>>();
|
2015-11-08 10:22:09 -08:00
|
|
|
var topLevel = new List<LibraryItem>();
|
2015-11-08 09:52:28 -08:00
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
foreach (var library in GetLibraries())
|
|
|
|
{
|
|
|
|
if (!library.Resolved)
|
|
|
|
{
|
|
|
|
string message;
|
|
|
|
string errorCode;
|
|
|
|
if (library.Compatible)
|
|
|
|
{
|
2015-10-20 01:43:37 -07:00
|
|
|
foreach (var range in library.RequestedRanges)
|
|
|
|
{
|
|
|
|
errorCode = ErrorCodes.NU1001;
|
2015-10-21 00:26:57 -07:00
|
|
|
message = $"The dependency {FormatLibraryRange(range)} could not be resolved.";
|
2015-10-20 01:43:37 -07:00
|
|
|
|
2015-10-21 03:53:07 -07:00
|
|
|
AddDiagnostics(messages, library, message, DiagnosticMessageSeverity.Error, errorCode);
|
2015-10-20 01:43:37 -07:00
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
errorCode = ErrorCodes.NU1002;
|
2015-10-20 01:43:37 -07:00
|
|
|
message = $"The dependency {library.Identity} does not support framework {library.Framework}.";
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-21 03:53:07 -07:00
|
|
|
AddDiagnostics(messages, library, message, DiagnosticMessageSeverity.Error, errorCode);
|
2015-10-20 01:43:37 -07:00
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-08 09:52:28 -08:00
|
|
|
// Store dependency -> library for later
|
|
|
|
// J.N -> [(R1, P1), (R2, P2)]
|
|
|
|
foreach (var dependency in library.Dependencies)
|
|
|
|
{
|
|
|
|
List<DependencyItem> items;
|
|
|
|
if (!dependencies.TryGetValue(dependency.Name, out items))
|
|
|
|
{
|
|
|
|
items = new List<DependencyItem>();
|
|
|
|
dependencies[dependency.Name] = items;
|
|
|
|
}
|
|
|
|
items.Add(new DependencyItem(dependency, library));
|
|
|
|
}
|
|
|
|
|
2015-10-20 01:43:37 -07:00
|
|
|
foreach (var range in library.RequestedRanges)
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
2015-10-20 01:43:37 -07:00
|
|
|
// Skip libraries that aren't specified in a project.json
|
2015-11-08 07:38:42 -08:00
|
|
|
// Only report problems for this project
|
2015-10-20 01:43:37 -07:00
|
|
|
if (string.IsNullOrEmpty(range.SourceFilePath))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-11-08 07:38:42 -08:00
|
|
|
// We only care about things requested in this project
|
|
|
|
if (!string.Equals(_projectPath, range.SourceFilePath))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-10-20 01:43:37 -07:00
|
|
|
if (range.VersionRange == null)
|
|
|
|
{
|
|
|
|
// TODO: Show errors/warnings for things without versions
|
|
|
|
continue;
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-11-08 10:22:09 -08:00
|
|
|
topLevel.Add(new LibraryItem(range, library));
|
2015-11-08 09:52:28 -08:00
|
|
|
|
2015-10-20 01:43:37 -07:00
|
|
|
// 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
|
|
|
|
// Case 2: Floating version that fell outside of the range
|
|
|
|
if ((!range.VersionRange.IsFloating &&
|
|
|
|
range.VersionRange.MinVersion != library.Identity.Version) ||
|
|
|
|
(range.VersionRange.IsFloating &&
|
|
|
|
!range.VersionRange.Float.Satisfies(library.Identity.Version)))
|
|
|
|
{
|
2015-11-08 09:52:28 -08:00
|
|
|
var message = $"Dependency specified was {FormatLibraryRange(range)} but ended up with {library.Identity}.";
|
|
|
|
|
|
|
|
messages.Add(
|
|
|
|
new DiagnosticMessage(
|
|
|
|
ErrorCodes.NU1007,
|
|
|
|
message,
|
|
|
|
range.SourceFilePath,
|
|
|
|
DiagnosticMessageSeverity.Warning,
|
|
|
|
range.SourceLine,
|
|
|
|
range.SourceColumn,
|
|
|
|
library));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Version conflicts
|
2015-11-08 10:22:09 -08:00
|
|
|
foreach (var libraryItem in topLevel)
|
2015-11-08 09:52:28 -08:00
|
|
|
{
|
2015-11-08 10:22:09 -08:00
|
|
|
var library = libraryItem.Library;
|
|
|
|
|
|
|
|
if (library.Identity.Type != LibraryType.Package)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-11-08 09:52:28 -08:00
|
|
|
List<DependencyItem> items;
|
|
|
|
if (dependencies.TryGetValue(library.Identity.Name, out items))
|
|
|
|
{
|
|
|
|
foreach (var item in items)
|
|
|
|
{
|
|
|
|
var versionRange = item.Dependency.VersionRange;
|
2015-10-20 01:43:37 -07:00
|
|
|
|
2015-11-08 10:22:09 -08:00
|
|
|
if (versionRange == null)
|
2015-11-08 09:52:28 -08:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (library.Identity.Version.IsPrerelease && !versionRange.IncludePrerelease)
|
|
|
|
{
|
|
|
|
versionRange = VersionRange.SetIncludePrerelease(versionRange, includePrerelease: true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item.Library != library && !versionRange.Satisfies(library.Identity.Version))
|
|
|
|
{
|
|
|
|
var message = $"Dependency conflict. {item.Library.Identity} expected {FormatLibraryRange(item.Dependency)} but got {library.Identity.Version}";
|
|
|
|
|
2015-11-08 10:22:09 -08:00
|
|
|
messages.Add(
|
|
|
|
new DiagnosticMessage(
|
|
|
|
ErrorCodes.NU1012,
|
|
|
|
message,
|
|
|
|
libraryItem.RequestedRange.SourceFilePath,
|
|
|
|
DiagnosticMessageSeverity.Warning,
|
|
|
|
libraryItem.RequestedRange.SourceLine,
|
|
|
|
libraryItem.RequestedRange.SourceColumn,
|
|
|
|
library));
|
2015-10-20 01:43:37 -07:00
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return messages;
|
|
|
|
}
|
2015-10-20 01:43:37 -07:00
|
|
|
|
2015-10-21 00:26:57 -07:00
|
|
|
private static string FormatLibraryRange(LibraryRange range)
|
|
|
|
{
|
|
|
|
if (range.VersionRange == null)
|
|
|
|
{
|
|
|
|
return range.Name;
|
|
|
|
}
|
|
|
|
|
2015-11-08 09:52:28 -08:00
|
|
|
return range.Name + " " + VersionUtility.RenderVersion(range.VersionRange);
|
2015-10-21 00:26:57 -07:00
|
|
|
}
|
|
|
|
|
2015-10-21 03:53:07 -07:00
|
|
|
private void AddDiagnostics(List<DiagnosticMessage> messages,
|
|
|
|
LibraryDescription library,
|
|
|
|
string message,
|
|
|
|
DiagnosticMessageSeverity severity,
|
|
|
|
string errorCode)
|
2015-10-20 01:43:37 -07:00
|
|
|
{
|
|
|
|
// A (in project.json) -> B (unresolved) (not in project.json)
|
|
|
|
foreach (var source in GetRangesWithSourceLocations(library).Distinct())
|
|
|
|
{
|
2015-11-08 07:38:42 -08:00
|
|
|
// We only care about things requested in this project
|
|
|
|
if (!string.Equals(_projectPath, source.SourceFilePath))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-10-20 01:43:37 -07:00
|
|
|
messages.Add(
|
|
|
|
new DiagnosticMessage(
|
|
|
|
errorCode,
|
|
|
|
message,
|
|
|
|
source.SourceFilePath,
|
2015-10-21 03:53:07 -07:00
|
|
|
severity,
|
2015-10-20 01:43:37 -07:00
|
|
|
source.SourceLine,
|
|
|
|
source.SourceColumn,
|
|
|
|
library));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerable<LibraryRange> GetRangesWithSourceLocations(LibraryDescription library)
|
2015-11-08 07:39:23 -08:00
|
|
|
{
|
2015-10-20 01:43:37 -07:00
|
|
|
foreach (var range in library.RequestedRanges)
|
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(range.SourceFilePath))
|
|
|
|
{
|
|
|
|
yield return range;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var parent in library.Parents)
|
|
|
|
{
|
|
|
|
foreach (var relevantPath in GetRangesWithSourceLocations(parent))
|
|
|
|
{
|
|
|
|
yield return relevantPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-08 09:52:28 -08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-11-08 10:22:09 -08:00
|
|
|
|
|
|
|
private struct LibraryItem
|
|
|
|
{
|
|
|
|
public LibraryRange RequestedRange { get; private set; }
|
|
|
|
public LibraryDescription Library { get; private set; }
|
|
|
|
|
|
|
|
public LibraryItem(LibraryRange requestedRange, LibraryDescription library)
|
|
|
|
{
|
|
|
|
RequestedRange = requestedRange;
|
|
|
|
Library = library;
|
|
|
|
}
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
}
|