Exemplo n.º 1
0
        private async Task VerifyDotnetToolCompatibilityChecks(CompatibilityData compatibilityData, GraphItem <RemoteResolveResult> node, RestoreTargetGraph graph, List <CompatibilityIssue> issues)
        {
            var containsDotnetToolPackageType = compatibilityData.TargetLibrary.PackageType.Contains(PackageType.DotnetTool);

            if (compatibilityData.TargetLibrary.PackageType.Count != 1 && containsDotnetToolPackageType)
            {
                var issue = CompatibilityIssue.ToolsPackageWithExtraPackageTypes(
                    new PackageIdentity(node.Key.Name, node.Key.Version));

                issues.Add(issue);
                await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1204, issue, graph));
            }

            if (containsDotnetToolPackageType &&
                !(HasCompatibleToolsAssets(compatibilityData.TargetLibrary) || !compatibilityData.Files.Any(p => p.StartsWith("tools/", StringComparison.OrdinalIgnoreCase))))
            {
                var available = GetAvailableFrameworkRuntimePairs(compatibilityData, graph);
                var issue     = CompatibilityIssue.IncompatibleToolsPackage(
                    new PackageIdentity(node.Key.Name, node.Key.Version),
                    graph.Framework,
                    graph.RuntimeIdentifier,
                    available);

                issues.Add(issue);
                await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1202, issue, graph));
            }

            if (ProjectStyle.DotnetToolReference == compatibilityData.PackageSpec.RestoreMetadata?.ProjectStyle)
            {
                // If the package is not autoreferenced or a tool package
                if (!containsDotnetToolPackageType && compatibilityData.PackageSpec.GetAllPackageDependencies().Where(e => !e.AutoReferenced).Any(e => e.Name.Equals(compatibilityData.TargetLibrary.Name, StringComparison.OrdinalIgnoreCase)))
                {
                    var issue = CompatibilityIssue.IncompatiblePackageWithDotnetTool(new PackageIdentity(node.Key.Name, node.Key.Version));
                    issues.Add(issue);
                    await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1212, issue, graph));
                }
            }
            else
            {
                if (containsDotnetToolPackageType)
                {
                    var issue = CompatibilityIssue.IncompatiblePackageWithDotnetTool(new PackageIdentity(node.Key.Name, node.Key.Version));
                    issues.Add(issue);
                    await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1212, issue, graph));
                }
            }
        }
Exemplo n.º 2
0
        internal async Task <CompatibilityCheckResult> CheckAsync(
            RestoreTargetGraph graph,
            Dictionary <string, LibraryIncludeFlags> includeFlags,
            PackageSpec packageSpec)
        {
            // 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>();

            if (packageSpec.RestoreMetadata?.ProjectStyle == ProjectStyle.DotnetToolReference)
            {
                // Autoreferenced packages are allowed. Currently they're using Microsoft.NET.Platforms as an auto-ref package
                if (packageSpec.GetAllPackageDependencies().Where(e => !e.AutoReferenced).Count() != 1)
                {
                    // Create issue
                    var issue = CompatibilityIssue.IncompatibleProjectType(
                        new PackageIdentity(packageSpec.Name, packageSpec.Version));

                    issues.Add(issue);
                    await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1211, issue, graph));
                }
            }

            // Verify framework assets also as part of runtime assets validation.
            foreach (var node in graph.Flattened)
            {
                await _log.LogAsync(LogLevel.Debug, 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 || !IsProjectFrameworkCompatible(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);
                        await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1201, issue, graph));
                    }

                    // 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, packageSpec);
                if (compatibilityData == null)
                {
                    continue;
                }

                if (!IsPackageCompatible(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);
                    await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1202, issue, graph));
                }

                await VerifyDotnetToolCompatibilityChecks(compatibilityData, node, graph, issues);

                // 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);
                    await _log.LogAsync(GetErrorMessage(NuGetLogCode.NU1203, issue, graph));
                }
            }

            return(new CompatibilityCheckResult(graph, issues));
        }
Exemplo n.º 3
0
 /// <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));
 }