// Gets the runtime graph specified in the path. // returns null if an error is hit. A valid runtime graph otherwise. private RuntimeGraph GetRuntimeGraph(string runtimeGraphPath) { if (File.Exists(runtimeGraphPath)) { try { using (var stream = File.OpenRead(runtimeGraphPath)) { var runtimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(stream); return(runtimeGraph); } } catch (Exception e) { _logger.Log( RestoreLogMessage.CreateError( NuGetLogCode.NU1007, string.Format(CultureInfo.CurrentCulture, Strings.Error_ProjectRuntimeJsonIsUnreadable, runtimeGraphPath, e.Message))); } } else { _logger.Log( RestoreLogMessage.CreateError( NuGetLogCode.NU1007, string.Format(CultureInfo.CurrentCulture, Strings.Error_ProjectRuntimeJsonNotFound, runtimeGraphPath))); } return(null); }
public void GetMessagesForProject_WithMultipleMessages_SelectedMessagesForCorrectProject() { // Arrange var project1 = @"z:\src\solution\project1\project1.csproj"; var project2 = @"z:\src\solution\project2\project2.csproj"; var project1Error = RestoreLogMessage.CreateError(NuGetLogCode.NU1001, "project1 error"); project1Error.ProjectPath = project1; var project2Error = RestoreLogMessage.CreateError(NuGetLogCode.NU1002, "project2 error"); project2Error.ProjectPath = project2; var solutionMessages = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(project1Error), AssetsLogMessage.Create(project2Error) }; // Act IReadOnlyList <IAssetsLogMessage> actual = DependencyGraphSpecRequestProvider.GetMessagesForProject(solutionMessages, project1); // Assert var actualError = Assert.Single(actual); Assert.Equal(NuGetLogCode.NU1001, actualError.Code); }
public static RestoreLogMessage GetInvalidFallbackCombinationMessage(string path) { var error = RestoreLogMessage.CreateError(NuGetLogCode.NU1003, Strings.Error_InvalidATF); error.ProjectPath = path; error.FilePath = path; return(error); }
/// <summary> /// Logs an error and returns false if any cycles exist. /// </summary> private static async Task<bool> ValidateCyclesAsync(IEnumerable<RestoreTargetGraph> graphs, ILogger logger) { foreach (var graph in graphs) { foreach (var cycle in graph.AnalyzeResult.Cycles) { var text = Strings.Log_CycleDetected + $" {Environment.NewLine} {cycle.GetPath()}."; await logger.LogAsync(RestoreLogMessage.CreateError(NuGetLogCode.NU1606, text, cycle.Key?.Name, graph.TargetGraphName)); return false; } } return true; }
public void RestoreLogMessage_TestCreateError(NuGetLogCode code, string message, string libraryId, string[] targetGraphs) { // Arrange & Act var logMessage = RestoreLogMessage.CreateError(code, message, libraryId, targetGraphs); // Assert Assert.Equal(LogLevel.Error, logMessage.Level); Assert.Equal(code, logMessage.Code); Assert.Equal(message, logMessage.Message); Assert.Equal(0, logMessage.StartLineNumber); Assert.Equal(0, logMessage.EndLineNumber); Assert.Equal(0, logMessage.StartColumnNumber); Assert.Equal(0, logMessage.EndColumnNumber); targetGraphs.SequenceEqual(logMessage.TargetGraphs); }
private async Task LogErrorAsync(ILogger logger, string id, FatalProtocolException e) { if (!_ignoreWarning) { // Sometimes, there's a better root cause for a source failures we log that instead of NU1301. // We only do this for errors, and not warnings. var unwrappedLogMessage = UnwrapToLogMessage(e); if (unwrappedLogMessage != null) { await logger.LogAsync(unwrappedLogMessage); } else { await logger.LogAsync(RestoreLogMessage.CreateError(NuGetLogCode.NU1301, e.Message, id)); } }
private async Task <bool> ResolutionSucceeded(IEnumerable <RestoreTargetGraph> graphs, IList <DownloadDependencyResolutionResult> downloadDependencyResults, RemoteWalkContext context, CancellationToken token) { var graphSuccess = true; foreach (var graph in graphs) { if (graph.Conflicts.Any()) { graphSuccess = false; foreach (var conflict in graph.Conflicts) { var graphName = DiagnosticUtility.FormatGraphName(graph); var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_ResolverConflict, conflict.Name, string.Join(", ", conflict.Requests), graphName); _logger.Log(RestoreLogMessage.CreateError(NuGetLogCode.NU1106, message, conflict.Name, graph.TargetGraphName)); } } if (graph.Unresolved.Count > 0) { graphSuccess = false; } } if (!graphSuccess) { // Log message for any unresolved dependencies await UnresolvedMessages.LogAsync(graphs, context, context.Logger, token); } var ddSuccess = downloadDependencyResults.All(e => e.Unresolved.Count == 0); if (!ddSuccess) { await UnresolvedMessages.LogAsync(downloadDependencyResults, context.RemoteLibraryProviders, context.CacheContext, context.Logger, token); } return(graphSuccess && ddSuccess); }
/// <summary> /// Logs an error and returns false if any conflicts exist. /// </summary> private static async Task<bool> ValidateConflictsAsync(IEnumerable<RestoreTargetGraph> graphs, ILogger logger) { foreach (var graph in graphs) { foreach (var versionConflict in graph.AnalyzeResult.VersionConflicts) { var message = string.Format( CultureInfo.CurrentCulture, Strings.Log_VersionConflict, versionConflict.Selected.Key.Name) + $" {Environment.NewLine} {versionConflict.Selected.GetPath()} {Environment.NewLine} {versionConflict.Conflicting.GetPath()}."; await logger.LogAsync(RestoreLogMessage.CreateError(NuGetLogCode.NU1607, message, versionConflict.Selected.Key.Name, graph.TargetGraphName)); return false; } } return true; }
private async Task <bool> ResolutionSucceeded(IEnumerable <RestoreTargetGraph> graphs, RemoteWalkContext context, CancellationToken token) { var success = true; foreach (var graph in graphs) { if (graph.Conflicts.Any()) { success = false; foreach (var conflict in graph.Conflicts) { var graphName = DiagnosticUtility.FormatGraphName(graph); var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_ResolverConflict, conflict.Name, string.Join(", ", conflict.Requests), graphName); _logger.Log(RestoreLogMessage.CreateError(NuGetLogCode.NU1106, message)); } } if (graph.Unresolved.Count > 0) { success = false; } } if (!success) { // Log message for any unresolved dependencies await UnresolvedMessages.LogAsync(graphs, context, context.Logger, token); } return(success); }
private async Task <IEnumerable <RestoreTargetGraph> > ExecuteRestoreAsync( NuGetv3LocalRepository userPackageFolder, IReadOnlyList <NuGetv3LocalRepository> fallbackPackageFolders, RemoteWalkContext context, CancellationToken token) { if (_request.Project.TargetFrameworks.Count == 0) { var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_ProjectDoesNotSpecifyTargetFrameworks, _request.Project.Name, _request.Project.FilePath); await _logger.LogAsync(RestoreLogMessage.CreateError(NuGetLogCode.NU1001, message)); _success = false; return(Enumerable.Empty <RestoreTargetGraph>()); } _logger.LogMinimal(string.Format(CultureInfo.CurrentCulture, Strings.Log_RestoringPackages, _request.Project.FilePath)); // Get external project references // If the top level project already exists, update the package spec provided // with the RestoreRequest spec. var updatedExternalProjects = GetProjectReferences(context); // Determine if the targets and props files should be written out. context.IsMsBuildBased = _request.ProjectStyle != ProjectStyle.DotnetCliTool; // Load repositories // the external project provider is specific to the current restore project context.ProjectLibraryProviders.Add( new PackageSpecReferenceDependencyProvider(updatedExternalProjects, _logger)); var remoteWalker = new RemoteDependencyWalker(context); var projectRange = new LibraryRange() { Name = _request.Project.Name, VersionRange = new VersionRange(_request.Project.Version), TypeConstraint = LibraryDependencyTarget.Project | LibraryDependencyTarget.ExternalProject }; // Resolve dependency graphs var allInstalledPackages = new HashSet <LibraryIdentity>(); var allGraphs = new List <RestoreTargetGraph>(); var runtimeIds = RequestRuntimeUtility.GetRestoreRuntimes(_request); var projectFrameworkRuntimePairs = CreateFrameworkRuntimePairs(_request.Project, runtimeIds); var hasSupports = _request.Project.RuntimeGraph.Supports.Count > 0; var projectRestoreRequest = new ProjectRestoreRequest( _request, _request.Project, _request.ExistingLockFile, _runtimeGraphCache, _runtimeGraphCacheByPackage, _logger); var projectRestoreCommand = new ProjectRestoreCommand(projectRestoreRequest); var result = await projectRestoreCommand.TryRestore( projectRange, projectFrameworkRuntimePairs, allInstalledPackages, userPackageFolder, fallbackPackageFolders, remoteWalker, context, forceRuntimeGraphCreation : hasSupports, token : token); var success = result.Item1; allGraphs.AddRange(result.Item2); _success = success; // Calculate compatibility profiles to check by merging those defined in the project with any from the command line foreach (var profile in _request.Project.RuntimeGraph.Supports) { var runtimes = result.Item3; CompatibilityProfile compatProfile; if (profile.Value.RestoreContexts.Any()) { // Just use the contexts from the project definition compatProfile = profile.Value; } else if (!runtimes.Supports.TryGetValue(profile.Value.Name, out compatProfile)) { // No definition of this profile found, so just continue to the next one var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_UnknownCompatibilityProfile, profile.Key); await _logger.LogAsync(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1502, message)); continue; } foreach (var pair in compatProfile.RestoreContexts) { _logger.LogDebug($" {profile.Value.Name} -> +{pair}"); _request.CompatibilityProfiles.Add(pair); } } // Walk additional runtime graphs for supports checks if (_success && _request.CompatibilityProfiles.Any()) { var compatibilityResult = await projectRestoreCommand.TryRestore( projectRange, _request.CompatibilityProfiles, allInstalledPackages, userPackageFolder, fallbackPackageFolders, remoteWalker, context, forceRuntimeGraphCreation : true, token : token); _success = compatibilityResult.Item1; // TryRestore may contain graphs that are already in allGraphs if the // supports section contains the same TxM as the project framework. var currentGraphs = new HashSet <KeyValuePair <NuGetFramework, string> >( allGraphs.Select(graph => new KeyValuePair <NuGetFramework, string>( graph.Framework, graph.RuntimeIdentifier)) ); foreach (var graph in compatibilityResult.Item2) { var key = new KeyValuePair <NuGetFramework, string>( graph.Framework, graph.RuntimeIdentifier); if (currentGraphs.Add(key)) { allGraphs.Add(graph); } } } return(allGraphs); }
/// <summary> /// Create a specific error message for the unresolved dependency. /// </summary> public static async Task <RestoreLogMessage> GetMessageAsync(string targetGraphName, LibraryRange unresolved, IList <IRemoteDependencyProvider> remoteLibraryProviders, SourceCacheContext sourceCacheContext, ILogger logger, CancellationToken token) { // Default to using the generic unresolved error code, this will be overridden later. var code = NuGetLogCode.NU1100; var message = string.Empty; if (unresolved.TypeConstraintAllows(LibraryDependencyTarget.ExternalProject) && !unresolved.TypeConstraintAllows(LibraryDependencyTarget.Package)) { // Project // Check if the name is a path and if it exists. All project paths should have been normalized and converted to full paths before this. if (unresolved.Name.IndexOf(Path.DirectorySeparatorChar) > -1 && File.Exists(unresolved.Name)) { // File exists but the dg spec did not contain the spec code = NuGetLogCode.NU1105; message = string.Format(CultureInfo.CurrentCulture, Strings.Error_UnableToFindProjectInfo, unresolved.Name); } else { // Generic missing project error code = NuGetLogCode.NU1104; message = string.Format(CultureInfo.CurrentCulture, Strings.Error_ProjectDoesNotExist, unresolved.Name); } } else if (unresolved.TypeConstraintAllows(LibraryDependencyTarget.Package) && remoteLibraryProviders.Count > 0) { // Package var range = unresolved.VersionRange ?? VersionRange.All; var sourceInfo = await GetSourceInfosForIdAsync(unresolved.Name, range, remoteLibraryProviders, sourceCacheContext, logger, token); var allVersions = new SortedSet <NuGetVersion>(sourceInfo.SelectMany(e => e.Value)); if (allVersions.Count == 0) { // No versions found code = NuGetLogCode.NU1101; var sourceList = string.Join(", ", sourceInfo.Select(e => e.Key.Name) .OrderBy(e => e, StringComparer.OrdinalIgnoreCase)); message = string.Format(CultureInfo.CurrentCulture, Strings.Error_NoPackageVersionsExist, unresolved.Name, sourceList); } else { // At least one version found var firstLine = string.Empty; var rangeString = range.ToNonSnapshotRange().PrettyPrint(); if (!IsPrereleaseAllowed(range) && HasPrereleaseVersionsOnly(range, allVersions)) { code = NuGetLogCode.NU1103; firstLine = string.Format(CultureInfo.CurrentCulture, Strings.Error_NoStablePackageVersionsExist, unresolved.Name, rangeString); } else { code = NuGetLogCode.NU1102; firstLine = string.Format(CultureInfo.CurrentCulture, Strings.Error_NoPackageVersionsExistInRange, unresolved.Name, rangeString); } var lines = new List <string>() { firstLine }; lines.AddRange(sourceInfo.Select(e => FormatSourceInfo(e, range))); message = DiagnosticUtility.GetMultiLineMessage(lines); } } else { // Unknown or non-specific. // Also shown when no sources exist. message = string.Format(CultureInfo.CurrentCulture, Strings.Log_UnresolvedDependency, unresolved.ToString(), targetGraphName); // Set again for clarity code = NuGetLogCode.NU1100; } return(RestoreLogMessage.CreateError(code, message, unresolved.Name, targetGraphName)); }
/// <summary> /// Create an error message for the given issue. /// </summary> private static RestoreLogMessage GetErrorMessage(NuGetLogCode logCode, CompatibilityIssue issue, RestoreTargetGraph graph) { return(RestoreLogMessage.CreateError(logCode, issue.Format(), issue.Package.Id, graph.TargetGraphName)); }
/// <summary> /// This is where the nominate calls for the IVs1 and IVS3 APIs combine. The reason for this method is to avoid duplication and potential issues /// The issue with this method is that it has some weird custom logging to ensure backward compatibility. It's on the implementer to ensure these calls are correct. /// <param name="projectUniqueName">projectUniqueName</param> /// <param name="projectRestoreInfo">projectRestoreInfo. Can be null</param> /// <param name="projectRestoreInfo2">proectRestoreInfo2. Can be null</param> /// <param name="token"></param> /// <remarks>Exactly one of projectRestoreInfos has to null.</remarks> /// <returns>The task that scheduled restore</returns> private Task <bool> NominateProjectAsync(string projectUniqueName, IVsProjectRestoreInfo projectRestoreInfo, IVsProjectRestoreInfo2 projectRestoreInfo2, CancellationToken token) { if (string.IsNullOrEmpty(projectUniqueName)) { throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(projectUniqueName)); } if (projectRestoreInfo == null && projectRestoreInfo2 == null) { throw new ArgumentNullException(nameof(projectRestoreInfo)); } if (projectRestoreInfo != null && projectRestoreInfo2 != null) { throw new ArgumentException($"Internal error: Both {nameof(projectRestoreInfo)} and {nameof(projectRestoreInfo2)} cannot have values. Please file an issue at NuGet/Home if you see this exception."); } if (projectRestoreInfo != null) { if (projectRestoreInfo.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } else { if (projectRestoreInfo2.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } try { _logger.LogInformation( $"The nominate API is called for '{projectUniqueName}'."); var projectNames = ProjectNames.FromFullProjectPath(projectUniqueName); DependencyGraphSpec dgSpec; IReadOnlyList <IAssetsLogMessage> nominationErrors = null; try { dgSpec = ToDependencyGraphSpec(projectNames, projectRestoreInfo, projectRestoreInfo2); } catch (Exception e) { var restoreLogMessage = RestoreLogMessage.CreateError(NuGetLogCode.NU1105, string.Format(Resources.NU1105, projectNames.ShortName, e.Message)); restoreLogMessage.LibraryId = projectUniqueName; nominationErrors = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(restoreLogMessage) }; var projectDirectory = Path.GetDirectoryName(projectUniqueName); string projectIntermediatePath = projectRestoreInfo == null ? projectRestoreInfo2.BaseIntermediatePath : projectRestoreInfo.BaseIntermediatePath; var dgSpecOutputPath = GetProjectOutputPath(projectDirectory, projectIntermediatePath); dgSpec = CreateMinimalDependencyGraphSpec(projectUniqueName, dgSpecOutputPath); } _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec, nominationErrors); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); } catch (OperationCanceledException) { throw; } catch (Exception e) { _logger.LogError(e.ToString()); TelemetryUtility.EmitException(nameof(VsSolutionRestoreService), nameof(NominateProjectAsync), e); return(Task.FromResult(false)); } }
public static IReadOnlyList <IRestoreLogMessage> ValidatePackagesConfigLockFiles( string projectFile, string packagesConfigFile, string projectName, string nuGetLockFilePath, string restorePackagesWithLockFile, NuGetFramework projectTfm, string packagesFolderPath, bool restoreLockedMode, CancellationToken token) { var lockFilePath = GetPackagesLockFilePath(Path.GetDirectoryName(packagesConfigFile), nuGetLockFilePath, projectName); var lockFileExists = File.Exists(lockFilePath); var lockFileOptIn = MSBuildStringUtility.GetBooleanOrNull(restorePackagesWithLockFile); var useLockFile = lockFileOptIn == true || lockFileExists; if (lockFileOptIn == false && lockFileExists) { var message = string.Format(CultureInfo.CurrentCulture, Strings.Error_InvalidLockFileInput, lockFilePath); var errors = new List <IRestoreLogMessage>(); var log = RestoreLogMessage.CreateError(NuGetLogCode.NU1005, message, packagesConfigFile); log.ProjectPath = projectFile ?? packagesConfigFile; errors.Add(log); return(errors); } if (useLockFile) { PackagesLockFile projectLockFileEquivalent = PackagesConfigLockFileUtility.FromPackagesConfigFile(packagesConfigFile, projectTfm, packagesFolderPath, token); if (!lockFileExists) { PackagesLockFileFormat.Write(lockFilePath, projectLockFileEquivalent); return(null); } else { PackagesLockFile lockFile = PackagesLockFileFormat.Read(lockFilePath); PackagesLockFileUtilities.LockFileValidityWithMatchedResults comparisonResult = PackagesLockFileUtilities.IsLockFileStillValid(projectLockFileEquivalent, lockFile); if (comparisonResult.IsValid) { // check sha hashes bool allContentHashesMatch = comparisonResult.MatchedDependencies.All(pair => pair.Key.ContentHash == pair.Value.ContentHash); if (allContentHashesMatch) { return(null); } else { var errors = new List <IRestoreLogMessage>(); foreach (var difference in comparisonResult.MatchedDependencies.Where(kvp => kvp.Key.ContentHash != kvp.Value.ContentHash)) { var message = string.Format(CultureInfo.CurrentCulture, Strings.Error_PackageValidationFailed, difference.Key.Id + "." + difference.Key.ResolvedVersion); var log = RestoreLogMessage.CreateError(NuGetLogCode.NU1403, message, packagesConfigFile); log.ProjectPath = projectFile ?? packagesConfigFile; errors.Add(log); } return(errors); } } else { if (restoreLockedMode) { var errors = new List <IRestoreLogMessage>(); var log = RestoreLogMessage.CreateError(NuGetLogCode.NU1004, Strings.Error_RestoreInLockedModePackagesConfig, packagesConfigFile); log.ProjectPath = projectFile ?? packagesConfigFile; errors.Add(log); return(errors); } else { PackagesLockFileFormat.Write(lockFilePath, projectLockFileEquivalent); return(null); } } } } else { return(null); } }
internal CompatibilityCheckResult Check( RestoreTargetGraph graph, Dictionary <string, LibraryIncludeFlags> includeFlags) { // The Compatibility Check is designed to alert the user to cases where packages are not behaving as they would // expect, due to compatibility issues. // // During this check, we scan all packages for a given restore graph and check the following conditions // (using an example TxM 'foo' and an example Runtime ID 'bar'): // // * If any package provides a "ref/foo/Thingy.dll", there MUST be a matching "lib/foo/Thingy.dll" or // "runtimes/bar/lib/foo/Thingy.dll" provided by a package in the graph. // * All packages that contain Managed Assemblies must provide assemblies for 'foo'. If a package // contains any of 'ref/' folders, 'lib/' folders, or framework assemblies, it must provide at least // one of those for the 'foo' framework. Otherwise, the package is intending to provide managed assemblies // but it does not support the target platform. If a package contains only 'content/', 'build/', 'tools/' or // other NuGet convention folders, it is exempt from this check. Thus, content-only packages are always considered // compatible, regardless of if they actually provide useful content. // // It is up to callers to invoke the compatibility check on the graphs they wish to check, but the general behavior in // the restore command is to invoke a compatibility check for each of: // // * The Targets (TxMs) defined in the project.json, with no Runtimes // * All combinations of TxMs and Runtimes defined in the project.json // * Additional (TxMs, Runtime) pairs defined by the "supports" mechanism in project.json var runtimeAssemblies = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var compileAssemblies = new Dictionary <string, LibraryIdentity>(StringComparer.OrdinalIgnoreCase); var issues = new List <CompatibilityIssue>(); // Verify framework assets also as part of runtime assets validation. foreach (var node in graph.Flattened) { _log.LogDebug(string.Format(CultureInfo.CurrentCulture, Strings.Log_CheckingPackageCompatibility, node.Key.Name, node.Key.Version, graph.Name)); // Check project compatibility if (node.Key.Type == LibraryType.Project) { // Get the full library var localMatch = node.Data?.Match as LocalMatch; if (localMatch == null || !IsProjectCompatible(localMatch.LocalLibrary)) { var available = new List <NuGetFramework>(); // If the project info is available find all available frameworks if (localMatch?.LocalLibrary != null) { available = GetProjectFrameworks(localMatch.LocalLibrary); } // Create issue var issue = CompatibilityIssue.IncompatibleProject( new PackageIdentity(node.Key.Name, node.Key.Version), graph.Framework, graph.RuntimeIdentifier, available); issues.Add(issue); _log.Log(RestoreLogMessage.CreateError(NuGetLogCode.NU1201, issue.Format())); } // Skip further checks on projects continue; } // Find the include/exclude flags for this package LibraryIncludeFlags packageIncludeFlags; if (!includeFlags.TryGetValue(node.Key.Name, out packageIncludeFlags)) { packageIncludeFlags = LibraryIncludeFlags.All; } // If the package has compile and runtime assets excluded the compatibility check // is not needed. Packages with no ref or lib entries are considered // compatible in IsCompatible. if ((packageIncludeFlags & (LibraryIncludeFlags.Compile | LibraryIncludeFlags.Runtime)) == LibraryIncludeFlags.None) { continue; } var compatibilityData = GetCompatibilityData(graph, node.Key); if (compatibilityData == null) { continue; } if (!IsCompatible(compatibilityData)) { var available = GetPackageFrameworks(compatibilityData, graph); var issue = CompatibilityIssue.IncompatiblePackage( new PackageIdentity(node.Key.Name, node.Key.Version), graph.Framework, graph.RuntimeIdentifier, available); issues.Add(issue); _log.Log(RestoreLogMessage.CreateError(NuGetLogCode.NU1202, issue.Format())); } // Check for matching ref/libs if we're checking a runtime-specific graph var targetLibrary = compatibilityData.TargetLibrary; if (_validateRuntimeAssets && !string.IsNullOrEmpty(graph.RuntimeIdentifier)) { // Skip runtime checks for packages that have runtime references excluded, // this allows compile only packages that do not have runtimes for the // graph RID to be used. if ((packageIncludeFlags & LibraryIncludeFlags.Runtime) == LibraryIncludeFlags.Runtime) { // Scan the package for ref assemblies foreach (var compile in targetLibrary.CompileTimeAssemblies .Where(p => Path.GetExtension(p.Path) .Equals(".dll", StringComparison.OrdinalIgnoreCase))) { var name = Path.GetFileNameWithoutExtension(compile.Path); // If we haven't already started tracking this compile-time assembly, AND there isn't already a runtime-loadable version if (!compileAssemblies.ContainsKey(name) && !runtimeAssemblies.Contains(name)) { // Track this assembly as potentially compile-time-only compileAssemblies.Add(name, node.Key); } } // Match up runtime assemblies foreach (var runtime in targetLibrary.RuntimeAssemblies .Where(p => Path.GetExtension(p.Path) .Equals(".dll", StringComparison.OrdinalIgnoreCase))) { var name = Path.GetFileNameWithoutExtension(runtime.Path); // If there was a compile-time-only assembly under this name... if (compileAssemblies.ContainsKey(name)) { // Remove it, we've found a matching runtime ref compileAssemblies.Remove(name); } // Track this assembly as having a runtime assembly runtimeAssemblies.Add(name); // Fix for NuGet/Home#752 - Consider ".ni.dll" (native image/ngen) files matches for ref/ assemblies if (name.EndsWith(".ni", StringComparison.OrdinalIgnoreCase)) { var withoutNi = name.Substring(0, name.Length - 3); if (compileAssemblies.ContainsKey(withoutNi)) { compileAssemblies.Remove(withoutNi); } runtimeAssemblies.Add(withoutNi); } } } } } // Generate errors for un-matched reference assemblies, if we're checking a runtime-specific graph if (_validateRuntimeAssets && !string.IsNullOrEmpty(graph.RuntimeIdentifier)) { foreach (var compile in compileAssemblies) { var issue = CompatibilityIssue.ReferenceAssemblyNotImplemented( compile.Key, new PackageIdentity(compile.Value.Name, compile.Value.Version), graph.Framework, graph.RuntimeIdentifier); issues.Add(issue); _log.Log(RestoreLogMessage.CreateError(NuGetLogCode.NU1203, issue.Format())); } } return(new CompatibilityCheckResult(graph, issues)); }