Example #1
0
        public override bool Execute()
        {
            _packageItems = Files.Select(f => new PackageItem(f));
            var packageDlls = _packageItems.Where(pi => Path.GetExtension(pi.SourcePath).Equals(".dll", StringComparison.OrdinalIgnoreCase));
            var packagePaths = _packageItems.Select(pi => pi.TargetPath);
            var packagePathsWithoutPlaceHolders = packagePaths.Where(pi => !NuGetAssetResolver.IsPlaceholder(pi));

            if (!String.IsNullOrEmpty(RuntimeJson) && !File.Exists(RuntimeJson))
            {
                Log.LogError("Could not load runtime file: {0}", RuntimeJson);
                RuntimeJson = null;
            }

            NuGetAssetResolver resolver = new NuGetAssetResolver(RuntimeJson, packagePaths);
            NuGetAssetResolver obscuredResolver = new NuGetAssetResolver(RuntimeJson, packagePathsWithoutPlaceHolders);

            List<ITaskItem> newItems = new List<ITaskItem>();

            // determine if an inbox placeholder obscures an OOB implementation.
            foreach (var oobFx in OOBFrameworks)
            {
                NuGetFramework targetFramework = NuGetFramework.Parse(oobFx);

                // first see if any dlls are explicitly marked for this framework.
                IEnumerable<string> obscuredCompileFolders = packageDlls.Where(pi => pi.OriginalItem.GetMetadata("EnsureOOBFrameworkRef") == oobFx).Select(pi => pi.TargetDirectory);

                if (!obscuredCompileFolders.Any())
                {
                    // no dlls were marked, resolve without placeholders to determine what to promote.
                    var compileItems = resolver.GetCompileItems(targetFramework);
                    var obscuredCompileItems = obscuredResolver.GetCompileItems(targetFramework);
                    obscuredCompileFolders = GetObscuredAssetFolders(compileItems, obscuredCompileItems, targetFramework, targetFrameworkName: oobFx, expectedAssetFolder: "ref", ignoredAssetFolder: "lib");
                }

                var promotedCompileItems = ExpandAssetFoldersToItems(obscuredCompileFolders, targetAssetFolder: "ref", targetFrameworkName: oobFx);
                newItems.AddRange(promotedCompileItems);

                // don't use 'any' in paths due to https://github.com/NuGet/Home/issues/1676
                string targetLibFolder = !String.IsNullOrEmpty(RuntimeId) && RuntimeId != "any" ? $"runtimes/{RuntimeId}/lib" : "lib";
                IEnumerable<string> obscuredRuntimeFolders = packageDlls.Where(pi => pi.OriginalItem.GetMetadata("EnsureOOBFrameworkLib") == oobFx).Select(pi => pi.TargetDirectory);

                if (!obscuredRuntimeFolders.Any())
                {
                    var runtimeItems = resolver.GetRuntimeItems(targetFramework, RuntimeId);
                    var obscuredRuntimeItems = obscuredResolver.GetRuntimeItems(targetFramework, RuntimeId);
                    obscuredRuntimeFolders = GetObscuredAssetFolders(runtimeItems, obscuredRuntimeItems, targetFramework, targetFrameworkName: oobFx, expectedAssetFolder: targetLibFolder);
                }

                var promotedRuntimeItems = ExpandAssetFoldersToItems(obscuredRuntimeFolders, targetLibFolder, targetFrameworkName: oobFx);
                newItems.AddRange(promotedRuntimeItems);
            }

            AdditionalFiles = newItems.ToArray();

            return !Log.HasLoggedErrors;
        }
Example #2
0
        public override bool Execute()
        {
            _packageItems = Files.Select(f => new PackageItem(f));
            var packageDlls = _packageItems.Where(pi => Path.GetExtension(pi.SourcePath).Equals(".dll", StringComparison.OrdinalIgnoreCase));
            var packagePaths = _packageItems.Select(pi => pi.TargetPath);
            var packagePathsWithoutPlaceHolders = packagePaths.Where(pi => !NuGetAssetResolver.IsPlaceholder(pi));

            if (!String.IsNullOrEmpty(RuntimeJson) && !File.Exists(RuntimeJson))
            {
                Log.LogError("Could not load runtime file: {0}", RuntimeJson);
                RuntimeJson = null;
            }

            NuGetAssetResolver resolver = new NuGetAssetResolver(RuntimeJson, packagePaths);
            NuGetAssetResolver obscuredResolver = new NuGetAssetResolver(RuntimeJson, packagePathsWithoutPlaceHolders);

            List<ITaskItem> newItems = new List<ITaskItem>();

            // determine if an inbox placeholder obscures an OOB implementation.
            foreach (var oobFramework in OOBFrameworks)
            {
                var oobFx = oobFramework.ItemSpec;
                NuGetFramework targetFramework = NuGetFramework.Parse(oobFx);

                // first see if any dlls are explicitly marked for this framework.
                IEnumerable<string> obscuredCompileFolders = packageDlls.Where(pi => pi.OriginalItem.GetMetadata("EnsureOOBFrameworkRef") == oobFx).Select(pi => pi.TargetDirectory);

                if (!obscuredCompileFolders.Any())
                {
                    // no dlls were marked, resolve without placeholders to determine what to promote.
                    var compileItems = resolver.GetCompileItems(targetFramework);
                    var obscuredCompileItems = obscuredResolver.GetCompileItems(targetFramework);
                    obscuredCompileFolders = GetObscuredAssetFolders(compileItems, obscuredCompileItems, targetFramework, targetFrameworkName: oobFx, expectedAssetFolder: "ref", ignoredAssetFolder: "lib");
                }

                var promotedCompileItems = ExpandAssetFoldersToItems(obscuredCompileFolders, targetAssetFolder: "ref", targetFrameworkName: oobFx);
                newItems.AddRange(promotedCompileItems);

                // don't use 'any' in paths due to https://github.com/NuGet/Home/issues/1676
                string targetLibFolder = !String.IsNullOrEmpty(RuntimeId) && RuntimeId != "any" ? $"runtimes/{RuntimeId}/lib" : "lib";
                IEnumerable<string> obscuredRuntimeFolders = packageDlls.Where(pi => pi.OriginalItem.GetMetadata("EnsureOOBFrameworkLib") == oobFx).Select(pi => pi.TargetDirectory);

                if (!obscuredRuntimeFolders.Any())
                {
                    var runtimeItems = resolver.GetRuntimeItems(targetFramework, RuntimeId);
                    var obscuredRuntimeItems = obscuredResolver.GetRuntimeItems(targetFramework, RuntimeId);
                    obscuredRuntimeFolders = GetObscuredAssetFolders(runtimeItems, obscuredRuntimeItems, targetFramework, targetFrameworkName: oobFx, expectedAssetFolder: targetLibFolder);
                }

                var promotedRuntimeItems = ExpandAssetFoldersToItems(obscuredRuntimeFolders, targetLibFolder, targetFrameworkName: oobFx);

                // If we promoted compile assets but couldn't find any runtime assets to promote we could 
                // be missing dependencies since a dependency group will be created for the compile assets
                // that may differ from the runtime assets.
                if (promotedCompileItems.Any() && !promotedRuntimeItems.Any())
                {
                    string oobFxRid = oobFramework.GetMetadata("RuntimeId") ?? RuntimeId;

                    // find the actual implementation that will be used.
                    var runtimeItems = resolver.GetRuntimeItems(targetFramework, oobFxRid);
                    var promotedRuntimeFolders = GetRuntimeAssetFoldersForPromotion(runtimeItems, targetFramework, oobFx);

                    // use null here to indicate that this should not actually go into the package but only be used for 
                    // dependency harvesting
                    promotedRuntimeItems = ExpandAssetFoldersToItems(promotedRuntimeFolders, "$none$", targetFrameworkName: oobFx);
                }

                newItems.AddRange(promotedRuntimeItems);
            }

            AdditionalFiles = newItems.ToArray();

            return !Log.HasLoggedErrors;
        }
        private void LoadFiles()
        {
            var packageItems = new Dictionary<string, List<PackageItem>>();
            foreach (var file in Files)
            {
                try
                {
                    var packageItem = new PackageItem(file);

                    if (!packageItem.TargetPath.StartsWith("runtimes") &&  !packageItem.IsDll && !packageItem.IsPlaceholder)
                    {
                        continue;
                    }

                    if (String.IsNullOrWhiteSpace(packageItem.TargetPath))
                    {
                        Log.LogError($"{packageItem.TargetPath} is missing TargetPath metadata");
                    }

                    string packageId = packageItem.Package ?? PackageId;

                    if (!packageItems.ContainsKey(packageId))
                    {
                        packageItems[packageId] = new List<PackageItem>();
                    }
                    packageItems[packageId].Add(packageItem);
                }
                catch (Exception ex)
                {
                    Log.LogError($"Could not parse File {file.ItemSpec}. {ex}");
                    // skip it.
                }
            }

            // build a map to translate back to source file from resolved asset
            // we use package-specific paths since we're resolving a set of packages.
            _targetPathToPackageItem = new Dictionary<string, PackageItem>();
            _unusedTargetPaths = new HashSet<string>();
            foreach (var packageSpecificItems in packageItems)
            {
                foreach (PackageItem packageItem in packageSpecificItems.Value)
                {
                    string packageSpecificTargetPath = AggregateNuGetAssetResolver.AsPackageSpecificTargetPath(packageSpecificItems.Key, packageItem.TargetPath);

                    if (_targetPathToPackageItem.ContainsKey(packageSpecificTargetPath))
                    {
                        Log.LogError($"Files {_targetPathToPackageItem[packageSpecificTargetPath].SourcePath} and {packageItem.SourcePath} have the same TargetPath {packageSpecificTargetPath}.");
                    }
                    _targetPathToPackageItem[packageSpecificTargetPath] = packageItem;
                    _unusedTargetPaths.Add(packageSpecificTargetPath);
                }
            }

            _resolver = new AggregateNuGetAssetResolver(RuntimeFile);
            foreach (string packageId in packageItems.Keys)
            {
                _resolver.AddPackageItems(packageId, packageItems[packageId].Select(f => f.TargetPath));
            }

            // create a resolver that can be used to determine the API version for inbox assemblies
            // since inbox assemblies are represented with placeholders we can remove the placeholders
            // and use the netstandard reference assembly to determine the API version
            if (packageItems.Any() && packageItems.ContainsKey(PackageId))
            {
                var filesWithoutPlaceholders = packageItems[PackageId]
                    .Select(pf => pf.TargetPath)
                    .Where(f => !NuGetAssetResolver.IsPlaceholder(f));

                _resolverWithoutPlaceholders = new NuGetAssetResolver(RuntimeFile, filesWithoutPlaceholders);
            }
        }
Example #4
0
        private void HarvestSupportedFrameworks()
        {
            List<ITaskItem> supportedFrameworks = new List<ITaskItem>();

            AggregateNuGetAssetResolver resolver = new AggregateNuGetAssetResolver(RuntimeFile);
            string packagePath = Path.Combine(PackagesFolder, PackageId, PackageVersion);

            // add the primary package
            resolver.AddPackageItems(PackageId, GetPackageItems(packagePath));

            if (RuntimePackages != null)
            {
                // add any split runtime packages
                foreach (var runtimePackage in RuntimePackages)
                {
                    var runtimePackageId = runtimePackage.ItemSpec;
                    var runtimePackageVersion = runtimePackage.GetMetadata("Version");

                    resolver.AddPackageItems(runtimePackageId, GetPackageItems(PackagesFolder, runtimePackageId, runtimePackageVersion));
                }
            }

            // create a resolver that can be used to determine the API version for inbox assemblies
            // since inbox assemblies are represented with placeholders we can remove the placeholders
            // and use the netstandard reference assembly to determine the API version
            var filesWithoutPlaceholders = GetPackageItems(packagePath)
                .Where(f => !NuGetAssetResolver.IsPlaceholder(f));
            NuGetAssetResolver resolverWithoutPlaceholders = new NuGetAssetResolver(RuntimeFile, filesWithoutPlaceholders);

            string package = $"{PackageId}/{PackageVersion}";

            foreach (var framework in Frameworks)
            {
                var runtimeIds = framework.GetMetadata("RuntimeIDs")?.Split(';');

                NuGetFramework fx;
                try
                {
                    fx = FrameworkUtilities.ParseNormalized(framework.ItemSpec);
                }
                catch (Exception ex)
                {
                    Log.LogError($"Could not parse Framework {framework.ItemSpec}. {ex}");
                    continue;
                }

                if (fx.Equals(NuGetFramework.UnsupportedFramework))
                {
                    Log.LogError($"Did not recognize {framework.ItemSpec} as valid Framework.");
                    continue;
                }

                var compileAssets = resolver.ResolveCompileAssets(fx, PackageId);

                bool hasCompileAsset, hasCompilePlaceHolder;
                NuGetAssetResolver.ExamineAssets(Log, "Compile", package, fx.ToString(), compileAssets, out hasCompileAsset, out hasCompilePlaceHolder);

                // start by making sure it has some asset available for compile
                var isSupported = hasCompileAsset || hasCompilePlaceHolder;

                if (!isSupported)
                {
                    Log.LogMessage(LogImportance.Low, $"Skipping {fx} because it is not supported.");
                    continue;
                }

                foreach (var runtimeId in runtimeIds)
                {
                    string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}";

                    var runtimeAssets = resolver.ResolveRuntimeAssets(fx, runtimeId);

                    bool hasRuntimeAsset, hasRuntimePlaceHolder;
                    NuGetAssetResolver.ExamineAssets(Log, "Runtime", package, target, runtimeAssets, out hasRuntimeAsset, out hasRuntimePlaceHolder);

                    isSupported &= hasCompileAsset == hasRuntimeAsset;
                    isSupported &= hasCompilePlaceHolder == hasRuntimePlaceHolder;

                    if (!isSupported)
                    {
                        Log.LogMessage(LogImportance.Low, $"Skipping {fx} because it is not supported on {target}.");
                        break;
                    }
                }

                if (isSupported)
                {
                    var supportedFramework = new TaskItem(framework.ItemSpec);
                    supportedFramework.SetMetadata("HarvestedFromPackage", package);

                    // set version

                    // first try the resolved compile asset for this package
                    var refAssm = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r))?.Substring(PackageId.Length + 1);

                    if (refAssm == null)
                    {
                        // if we didn't have a compile asset it means this framework is supported inbox with a placeholder
                        // resolve the assets without placeholders to pick up the netstandard reference assembly.
                        compileAssets = resolverWithoutPlaceholders.ResolveCompileAssets(fx);
                        refAssm = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r));
                    }

                    string version = "unknown";
                    if (refAssm != null)
                    {
                        version = VersionUtility.GetAssemblyVersion(Path.Combine(packagePath, refAssm))?.ToString() ?? version;
                    }

                    supportedFramework.SetMetadata("Version", version);

                    Log.LogMessage(LogImportance.Low, $"Validating version {version} for {supportedFramework.ItemSpec} because it was supported by {PackageId}/{PackageVersion}.");

                    supportedFrameworks.Add(supportedFramework);
                }
            }

            SupportedFrameworks = supportedFrameworks.ToArray();
        }
        /// <summary>
        /// Generates a table in markdown that lists the API version supported by
        /// various packages at all levels of NETStandard.
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            if (PackageAssets == null || PackageAssets.Length == 0)
            {
                Log.LogError("PackageAssets argument must be specified");
                return(false);
            }

            if (TargetMonikers == null || TargetMonikers.Length == 0)
            {
                Log.LogError("TargetMoniker argument must be specified");
                return(false);
            }

            NuGetFramework[] compileFxs = TargetMonikers.Select(fx => NuGetFramework.Parse(fx)).ToArray();
            NuGetFramework[] runtimeFxs = compileFxs;

            if (RuntimeTargetMonikers != null && RuntimeTargetMonikers.Length > 0)
            {
                runtimeFxs = RuntimeTargetMonikers.Select(fx => NuGetFramework.Parse(fx)).ToArray();
            }

            LoadFiles();

            var buildProjects = new List <BuildProject>();

            CompileAssets = null;
            // find the best framework
            foreach (var compileFx in compileFxs)
            {
                var compileAssets = _resolver.ResolveCompileAssets(compileFx);

                if (compileAssets.Any())
                {
                    var compileItems = compileAssets.Where(ca => !NuGetAssetResolver.IsPlaceholder(ca))
                                       .Select(ca => _targetPathToPackageItem[ca]);

                    buildProjects.AddRange(compileItems.Select(ci => BuildProjectFromPackageItem(ci)).Where(bp => bp != null));

                    CompileAssets = compileItems.Select(ci => PackageItemAsResolvedAsset(ci)).ToArray();

                    Log.LogMessage($"Resolved compile assets from {compileFx.ToString()}: {String.Join(";", CompileAssets.Select(c => c.ItemSpec))}");
                    break;
                }
            }

            if (CompileAssets == null)
            {
                Log.LogError($"Could not locate compile assets for any of the frameworks {String.Join(";", compileFxs.Select(fx => fx.ToString()))}");
            }

            RuntimeAssets = null;
            foreach (var runtimeFx in runtimeFxs)
            {
                var runtimeAssets = _resolver.ResolveRuntimeAssets(runtimeFx, TargetRuntime);

                if (runtimeAssets.Any())
                {
                    var runtimeItems = runtimeAssets.Where(ra => !NuGetAssetResolver.IsPlaceholder(ra))
                                       .Select(ra => _targetPathToPackageItem[ra]);

                    buildProjects.AddRange(runtimeItems.Select(ri => BuildProjectFromPackageItem(ri)).Where(bp => bp != null));

                    RuntimeAssets = runtimeItems.SelectMany(ri => PackageItemAndSymbolsAsResolvedAsset(ri)).ToArray();

                    Log.LogMessage($"Resolved runtime assets from {runtimeFx.ToString()}: {String.Join(";", RuntimeAssets.Select(r => r.ItemSpec))}");
                    break;
                }
            }

            if (!string.IsNullOrEmpty(TargetRuntime) && RuntimeAssets == null)
            {
                Log.LogError($"Could not locate runtime assets for any of the frameworks {String.Join(";", runtimeFxs.Select(fx => fx.ToString()))}");
            }

            BuildProjects = buildProjects.Distinct().Select(bp => bp.ToItem()).ToArray();

            return(!Log.HasLoggedErrors);
        }
Example #6
0
        /* Given a set of available frameworks ("InboxOnTargetFrameworks"), and a list of desired frameworks,
         * reduce the set of frameworks to the minimum set of frameworks which is compatible (preferring inbox frameworks. */
        public override bool Execute()
        {
            if (null == Dependencies)
            {
                Log.LogError("Dependencies argument must be specified");
                return(false);
            }
            if (PackageIndexes == null && PackageIndexes.Length == 0)
            {
                Log.LogError("PackageIndexes argument must be specified");
                return(false);
            }

            var index = PackageIndex.Load(PackageIndexes.Select(pi => pi.GetMetadata("FullPath")));

            // Retrieve the list of dependency group TFM's
            var dependencyGroups = Dependencies
                                   .Select(dependencyItem => new TaskItemPackageDependency(dependencyItem))
                                   .GroupBy(dependency => dependency.TargetFramework)
                                   .Select(dependencyGrouping => new TaskItemPackageDependencyGroup(dependencyGrouping.Key, dependencyGrouping))
                                   .ToArray();

            // Prepare a resolver for evaluating if candidate frameworks are actually supported by the package
            PackageItem[]      packageItems = Files.Select(f => new PackageItem(f)).ToArray();
            var                packagePaths = packageItems.Select(pi => pi.TargetPath);
            NuGetAssetResolver resolver     = new NuGetAssetResolver(null, packagePaths);

            // Determine all inbox frameworks which are supported by this package
            var supportedInboxFrameworks = index.GetAlllInboxFrameworks().Where(fx => IsSupported(fx, resolver));

            var newDependencyGroups = new Queue <TaskItemPackageDependencyGroup>();

            // For each inbox framework determine its best compatible dependency group and create an explicit group, trimming out any inbox dependencies
            foreach (var supportedInboxFramework in supportedInboxFrameworks)
            {
                var nearestDependencyGroup = dependencyGroups.GetNearest(supportedInboxFramework);

                // We found a compatible dependency group that is not the same as this framework
                if (nearestDependencyGroup != null && nearestDependencyGroup.TargetFramework != supportedInboxFramework)
                {
                    // remove all dependencies which are inbox on supportedInboxFramework
                    var filteredDependencies = nearestDependencyGroup.Packages.Where(d => !index.IsInbox(d.Id, supportedInboxFramework, d.AssemblyVersion)).ToArray();

                    newDependencyGroups.Enqueue(new TaskItemPackageDependencyGroup(supportedInboxFramework, filteredDependencies));
                }
            }

            // Remove any redundant groups from the added set (EG: net45 and net46 with the same set of dependencies)
            int groupsToCheck = newDependencyGroups.Count;

            for (int i = 0; i < groupsToCheck; i++)
            {
                // to determine if this is a redundant group, we dequeue so that it won't be considered in the following check for nearest group.
                var group = newDependencyGroups.Dequeue();

                // of the remaining groups, find the most compatible one
                var nearestGroup = newDependencyGroups.Concat(dependencyGroups).GetNearest(group.TargetFramework);

                // either we found no compatible group,
                // or the closest compatible group has different dependencies,
                // or the closest compatible group is portable and this is not (Portable profiles have different framework precedence, https://github.com/NuGet/Home/issues/6483),
                // keep it in the set of additions
                if (nearestGroup == null ||
                    !group.Packages.SetEquals(nearestGroup.Packages) ||
                    FrameworkUtilities.IsPortableMoniker(group.TargetFramework) != FrameworkUtilities.IsPortableMoniker(nearestGroup.TargetFramework))
                {
                    // not redundant, keep it in the queue
                    newDependencyGroups.Enqueue(group);
                }
            }

            // Build the items representing added dependency groups.
            List <ITaskItem> trimmedDependencies = new List <ITaskItem>();

            foreach (var newDependencyGroup in newDependencyGroups)
            {
                if (newDependencyGroup.Packages.Count == 0)
                {
                    // no dependencies (all inbox), use a placeholder dependency.
                    var item = new TaskItem(PlaceHolderDependency);
                    item.SetMetadata("TargetFramework", newDependencyGroup.TargetFramework.GetShortFolderName());
                    trimmedDependencies.Add(item);
                }
                else
                {
                    foreach (var dependency in newDependencyGroup.Packages)
                    {
                        var item = new TaskItem(dependency.Item);
                        // emit CopiedFromTargetFramework to aide in debugging.
                        item.SetMetadata("CopiedFromTargetFramework", item.GetMetadata("TargetFramework"));
                        item.SetMetadata("TargetFramework", newDependencyGroup.TargetFramework.GetShortFolderName());
                        trimmedDependencies.Add(item);
                    }
                }
            }
            TrimmedDependencies = trimmedDependencies.ToArray();
            return(!Log.HasLoggedErrors);
        }
Example #7
0
        private void ValidateSupport()
        {
            var runtimeFxSuppression = GetSuppressionValues(Suppression.PermitRuntimeTargetMonikerMismatch) ?? new HashSet <string>();

            // validate support for each TxM:RID
            foreach (var validateFramework in _frameworks.Values)
            {
                NuGetFramework fx = validateFramework.Framework;
                Version        supportedVersion = validateFramework.SupportedVersion;

                Target compileTarget;
                if (!_report.Targets.TryGetValue(fx.ToString(), out compileTarget))
                {
                    Log.LogError($"Missing target {fx.ToString()} from validation report {ReportFile}");
                    continue;
                }

                var  compileAssetPaths = compileTarget.CompileAssets.Select(ca => ca.PackagePath);
                bool hasCompileAsset, hasCompilePlaceHolder;
                NuGetAssetResolver.ExamineAssets(Log, "Compile", ContractName, fx.ToString(), compileAssetPaths, out hasCompileAsset, out hasCompilePlaceHolder);

                // resolve/test for each RID associated with this framework.
                foreach (string runtimeId in validateFramework.RuntimeIds)
                {
                    string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}";

                    Target runtimeTarget;
                    if (!_report.Targets.TryGetValue(target, out runtimeTarget))
                    {
                        Log.LogError($"Missing target {target} from validation report {ReportFile}");
                        continue;
                    }

                    var runtimeAssetPaths = runtimeTarget.RuntimeAssets.Select(ra => ra.PackagePath);

                    bool hasRuntimeAsset, hasRuntimePlaceHolder;
                    NuGetAssetResolver.ExamineAssets(Log, "Runtime", ContractName, target, runtimeAssetPaths, out hasRuntimeAsset, out hasRuntimePlaceHolder);

                    if (null == supportedVersion)
                    {
                        // Contract should not be supported on this platform.
                        bool permitImplementation = HasSuppression(Suppression.PermitImplementation, target);

                        if (hasCompileAsset && (hasRuntimeAsset & !permitImplementation))
                        {
                            Log.LogError($"{ContractName} should not be supported on {target} but has both compile and runtime assets.");
                        }
                        else if (hasRuntimeAsset & !permitImplementation)
                        {
                            Log.LogError($"{ContractName} should not be supported on {target} but has runtime assets.");
                        }

                        if (hasRuntimePlaceHolder && hasCompilePlaceHolder)
                        {
                            Log.LogError($"{ContractName} should not be supported on {target} but has placeholders for both compile and runtime which will permit the package to install.");
                        }
                    }
                    else
                    {
                        if (validateFramework.IsInbox)
                        {
                            if (!hasCompileAsset && !hasCompilePlaceHolder)
                            {
                                Log.LogError($"Framework {fx} should support {ContractName} inbox but was missing a placeholder for compile-time.  You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project.");
                            }
                            else if (hasCompileAsset)
                            {
                                Log.LogError($"Framework {fx} should support {ContractName} inbox but contained a reference assemblies: {String.Join(", ", compileAssetPaths)}.  You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project.");
                            }

                            if (!hasRuntimeAsset && !hasRuntimePlaceHolder)
                            {
                                Log.LogError($"Framework {fx} should support {ContractName} inbox but was missing a placeholder for run-time.  You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project.");
                            }
                            else if (hasRuntimeAsset)
                            {
                                Log.LogError($"Framework {fx} should support {ContractName} inbox but contained a implementation assemblies: {String.Join(", ", runtimeAssetPaths)}.  You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project.");
                            }
                        }
                        else
                        {
                            Version referenceAssemblyVersion = null;
                            if (!hasCompileAsset)
                            {
                                Log.LogError($"{ContractName} should be supported on {target} but has no compile assets.");
                            }
                            else
                            {
                                var referenceAssemblies = compileTarget.CompileAssets.Where(ca => IsDll(ca.PackagePath));

                                if (referenceAssemblies.Count() > 1)
                                {
                                    Log.LogError($"{ContractName} should only contain a single compile asset for {target}.");
                                }

                                foreach (var referenceAssembly in referenceAssemblies)
                                {
                                    referenceAssemblyVersion = referenceAssembly.Version;

                                    if (!VersionUtility.IsCompatibleApiVersion(supportedVersion, referenceAssemblyVersion))
                                    {
                                        Log.LogError($"{ContractName} should support API version {supportedVersion} on {target} but {referenceAssembly.LocalPath} was found to support {referenceAssemblyVersion?.ToString() ?? "<unknown version>"}.");
                                    }
                                }
                            }

                            if (!hasRuntimeAsset && !FrameworkUtilities.IsGenerationMoniker(validateFramework.Framework))
                            {
                                Log.LogError($"{ContractName} should be supported on {target} but has no runtime assets.");
                            }
                            else
                            {
                                var implementationAssemblies = runtimeTarget.RuntimeAssets.Where(ra => IsDll(ra.PackagePath));

                                Dictionary <string, PackageAsset> implementationFiles = new Dictionary <string, PackageAsset>();
                                foreach (var implementationAssembly in implementationAssemblies)
                                {
                                    Version implementationVersion = implementationAssembly.Version;

                                    if (!VersionUtility.IsCompatibleApiVersion(supportedVersion, implementationVersion))
                                    {
                                        Log.LogError($"{ContractName} should support API version {supportedVersion} on {target} but {implementationAssembly.LocalPath} was found to support {implementationVersion?.ToString() ?? "<unknown version>"}.");
                                    }

                                    // Previously we only permitted compatible mismatch if Suppression.PermitHigherCompatibleImplementationVersion was specified
                                    // this is a permitted thing on every framework but desktop (which requires exact match to ensure bindingRedirects exist)
                                    // Now make this the default, we'll check desktop, where it matters, more strictly
                                    if (referenceAssemblyVersion != null &&
                                        !VersionUtility.IsCompatibleApiVersion(referenceAssemblyVersion, implementationVersion))
                                    {
                                        Log.LogError($"{ContractName} has mismatched compile ({referenceAssemblyVersion}) and runtime ({implementationVersion}) versions on {target}.");
                                    }

                                    if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.Net &&
                                        referenceAssemblyVersion != null &&
                                        !referenceAssemblyVersion.Equals(implementationVersion))
                                    {
                                        Log.LogError($"{ContractName} has a higher runtime version ({implementationVersion}) than compile version ({referenceAssemblyVersion}) on .NET Desktop framework {target}.  This will break bindingRedirects.  If the live reference was replaced with a harvested reference you may need to set <Preserve>true</Preserve> on your reference assembly ProjectReference.");
                                    }

                                    string fileName = Path.GetFileName(implementationAssembly.PackagePath);

                                    if (implementationFiles.ContainsKey(fileName))
                                    {
                                        Log.LogError($"{ContractName} includes both {implementationAssembly.LocalPath} and {implementationFiles[fileName].LocalPath} an on {target} which have the same name and will clash when both packages are used.");
                                    }
                                    else
                                    {
                                        implementationFiles[fileName] = implementationAssembly;
                                    }

                                    if (!implementationAssembly.Equals(fx) && !runtimeFxSuppression.Contains(fx.ToString()))
                                    {
                                        // the selected asset wasn't an exact framework match, let's see if we have an exact match in any other runtime asset.
                                        var matchingFxAssets = _report.UnusedAssets.Where(i => i.TargetFramework == fx && // exact framework
                                                                                                                          // Same file
                                                                                          Path.GetFileName(i.PackagePath).Equals(fileName, StringComparison.OrdinalIgnoreCase) &&
                                                                                                                          // Is implementation
                                                                                          (i.PackagePath.StartsWith("lib") || i.PackagePath.StartsWith("runtimes")) &&
                                                                                                                          // is not the same source file as was already selected
                                                                                          i.LocalPath != implementationAssembly.LocalPath);

                                        if (matchingFxAssets.Any())
                                        {
                                            Log.LogError($"When targeting {target} {ContractName} will use {implementationAssembly.LocalPath} which targets {implementationAssembly.TargetFramework.GetShortFolderName()}  but {String.Join(";", matchingFxAssets.Select(i => i.PackagePath))} targets {fx.GetShortFolderName()} specifically.");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Set output items
            AllSupportedFrameworks = _frameworks.Values.Where(fx => fx.SupportedVersion != null).Select(fx => fx.ToItem()).OrderBy(i => i.ItemSpec).ToArray();
        }
Example #8
0
        public override bool Execute()
        {
            LoadFiles();
            LoadFrameworks();

            var report = new PackageReport()
            {
                Id                  = PackageId,
                Version             = PackageVersion,
                SupportedFrameworks = new Dictionary <string, string>()
            };

            string package = $"{PackageId}/{PackageVersion}";

            foreach (var framework in _frameworks.OrderBy(f => f.Key.ToString()))
            {
                var fx         = framework.Key;
                var runtimeIds = framework.Value;

                var compileAssets = _resolver.ResolveCompileAssets(fx, PackageId);

                bool hasCompileAsset, hasCompilePlaceHolder;
                NuGetAssetResolver.ExamineAssets(Log, "Compile", package, fx.ToString(), compileAssets, out hasCompileAsset, out hasCompilePlaceHolder);
                MarkUsed(compileAssets);

                // start by making sure it has some asset available for compile
                var isSupported = hasCompileAsset || hasCompilePlaceHolder;

                if (runtimeIds.All(rid => !String.IsNullOrEmpty(rid)))
                {
                    // Add Framework only (compile) target if all RIDs are non-empty.
                    // This acts as a compile target for a framework that requires a RID for runtime.
                    var reportTarget = new Target()
                    {
                        Framework     = fx.ToString(),
                        RuntimeID     = null,
                        CompileAssets = compileAssets.Select(c => GetPackageAssetFromTargetPath(c)).ToArray()
                    };
                    report.Targets.Add(fx.ToString(), reportTarget);
                }

                foreach (var runtimeId in runtimeIds)
                {
                    string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}";

                    var runtimeAssets = _resolver.ResolveRuntimeAssets(fx, runtimeId);

                    bool hasRuntimeAsset, hasRuntimePlaceHolder;
                    NuGetAssetResolver.ExamineAssets(Log, "Runtime", package, target, runtimeAssets, out hasRuntimeAsset, out hasRuntimePlaceHolder);
                    MarkUsed(runtimeAssets);

                    if (!FrameworkUtilities.IsGenerationMoniker(fx) && !fx.IsPCL)
                    {
                        // only look at runtime assets for runnable frameworks.
                        isSupported &= (hasCompileAsset && hasRuntimeAsset) ||           // matching assets
                                       (hasCompilePlaceHolder && hasRuntimeAsset) ||     // private runtime
                                       (hasCompilePlaceHolder && hasRuntimePlaceHolder); // placeholders
                    }

                    var nativeAssets = _resolver.ResolveNativeAssets(fx, runtimeId);
                    MarkUsed(nativeAssets);

                    var reportTarget = new Target()
                    {
                        Framework     = fx.ToString(),
                        RuntimeID     = runtimeId,
                        CompileAssets = compileAssets.Select(c => GetPackageAssetFromTargetPath(c)).ToArray(),
                        RuntimeAssets = runtimeAssets.Select(r => GetPackageAssetFromTargetPath(r)).ToArray(),
                        NativeAssets  = nativeAssets.Select(n => GetPackageAssetFromTargetPath(n)).ToArray()
                    };
                    report.Targets[target] = reportTarget;
                }

                if (isSupported)
                {
                    // Find version
                    // first try the resolved compile asset for this package
                    var refAssm = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r))?.Substring(PackageId.Length + 1);

                    if (refAssm == null)
                    {
                        // if we didn't have a compile asset it means this framework is supported inbox with a placeholder
                        // resolve the assets without placeholders to pick up the netstandard reference assembly.
                        compileAssets = _resolverWithoutPlaceholders.ResolveCompileAssets(fx);
                        refAssm       = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r));
                    }

                    var version = "unknown";
                    if (refAssm != null)
                    {
                        version = _targetPathToPackageItem[AggregateNuGetAssetResolver.AsPackageSpecificTargetPath(PackageId, refAssm)].Version?.ToString() ?? version;
                    }

                    report.SupportedFrameworks.Add(fx.ToString(), version);
                }
            }

            report.UnusedAssets = _unusedTargetPaths.Select(tp => GetPackageAssetFromTargetPath(tp)).ToArray();

            report.Save(ReportFile);

            return(!Log.HasLoggedErrors);
        }
Example #9
0
        private void LoadFiles()
        {
            var packageItems = new Dictionary <string, List <PackageItem> >();

            foreach (var file in Files)
            {
                try
                {
                    var packageItem = new PackageItem(file);

                    if (!packageItem.TargetPath.StartsWith("runtimes") && !packageItem.IsDll && !packageItem.IsPlaceholder)
                    {
                        continue;
                    }

                    if (String.IsNullOrWhiteSpace(packageItem.TargetPath))
                    {
                        Log.LogError($"{packageItem.TargetPath} is missing TargetPath metadata");
                    }

                    string packageId = packageItem.Package ?? PackageId;

                    if (!packageItems.ContainsKey(packageId))
                    {
                        packageItems[packageId] = new List <PackageItem>();
                    }
                    packageItems[packageId].Add(packageItem);
                }
                catch (Exception ex)
                {
                    Log.LogError($"Could not parse File {file.ItemSpec}. {ex}");
                    // skip it.
                }
            }

            // build a map to translate back to source file from resolved asset
            // we use package-specific paths since we're resolving a set of packages.
            _targetPathToPackageItem = new Dictionary <string, PackageItem>();
            _unusedTargetPaths       = new HashSet <string>();
            foreach (var packageSpecificItems in packageItems)
            {
                foreach (PackageItem packageItem in packageSpecificItems.Value)
                {
                    string packageSpecificTargetPath = AggregateNuGetAssetResolver.AsPackageSpecificTargetPath(packageSpecificItems.Key, packageItem.TargetPath);

                    if (_targetPathToPackageItem.ContainsKey(packageSpecificTargetPath))
                    {
                        Log.LogError($"Files {_targetPathToPackageItem[packageSpecificTargetPath].SourcePath} and {packageItem.SourcePath} have the same TargetPath {packageSpecificTargetPath}.");
                    }
                    _targetPathToPackageItem[packageSpecificTargetPath] = packageItem;
                    _unusedTargetPaths.Add(packageSpecificTargetPath);
                }
            }

            _resolver = new AggregateNuGetAssetResolver(RuntimeFile);
            foreach (string packageId in packageItems.Keys)
            {
                _resolver.AddPackageItems(packageId, packageItems[packageId].Select(f => f.TargetPath));
            }

            // create a resolver that can be used to determine the API version for inbox assemblies
            // since inbox assemblies are represented with placeholders we can remove the placeholders
            // and use the netstandard reference assembly to determine the API version
            if (packageItems.Any() && packageItems.ContainsKey(PackageId))
            {
                var filesWithoutPlaceholders = packageItems[PackageId]
                                               .Select(pf => pf.TargetPath)
                                               .Where(f => !NuGetAssetResolver.IsPlaceholder(f));

                _resolverWithoutPlaceholders = new NuGetAssetResolver(RuntimeFile, filesWithoutPlaceholders);
            }
        }
Example #10
0
        private void HarvestSupportedFrameworks()
        {
            List <ITaskItem> supportedFrameworks = new List <ITaskItem>();

            AggregateNuGetAssetResolver resolver = new AggregateNuGetAssetResolver(RuntimeFile);
            string packagePath = Path.Combine(PackagesFolder, PackageId, PackageVersion);

            // add the primary package
            resolver.AddPackageItems(PackageId, GetPackageItems(packagePath));

            if (RuntimePackages != null)
            {
                // add any split runtime packages
                foreach (var runtimePackage in RuntimePackages)
                {
                    var runtimePackageId      = runtimePackage.ItemSpec;
                    var runtimePackageVersion = runtimePackage.GetMetadata("Version");

                    resolver.AddPackageItems(runtimePackageId, GetPackageItems(PackagesFolder, runtimePackageId, runtimePackageVersion));
                }
            }

            // create a resolver that can be used to determine the API version for inbox assemblies
            // since inbox assemblies are represented with placeholders we can remove the placeholders
            // and use the netstandard reference assembly to determine the API version
            var filesWithoutPlaceholders = GetPackageItems(packagePath)
                                           .Where(f => !NuGetAssetResolver.IsPlaceholder(f));
            NuGetAssetResolver resolverWithoutPlaceholders = new NuGetAssetResolver(RuntimeFile, filesWithoutPlaceholders);

            string package = $"{PackageId}/{PackageVersion}";

            foreach (var framework in Frameworks)
            {
                var runtimeIds = framework.GetMetadata("RuntimeIDs")?.Split(';');

                NuGetFramework fx;
                try
                {
                    fx = FrameworkUtilities.ParseNormalized(framework.ItemSpec);
                }
                catch (Exception ex)
                {
                    Log.LogError($"Could not parse Framework {framework.ItemSpec}. {ex}");
                    continue;
                }

                if (fx.Equals(NuGetFramework.UnsupportedFramework))
                {
                    Log.LogError($"Did not recognize {framework.ItemSpec} as valid Framework.");
                    continue;
                }

                var compileAssets = resolver.ResolveCompileAssets(fx, PackageId);

                bool hasCompileAsset, hasCompilePlaceHolder;
                NuGetAssetResolver.ExamineAssets(Log, "Compile", package, fx.ToString(), compileAssets, out hasCompileAsset, out hasCompilePlaceHolder);

                // start by making sure it has some asset available for compile
                var isSupported = hasCompileAsset || hasCompilePlaceHolder;

                if (!isSupported)
                {
                    Log.LogMessage(LogImportance.Low, $"Skipping {fx} because it is not supported.");
                    continue;
                }

                foreach (var runtimeId in runtimeIds)
                {
                    string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}";

                    var runtimeAssets = resolver.ResolveRuntimeAssets(fx, runtimeId);

                    bool hasRuntimeAsset, hasRuntimePlaceHolder;
                    NuGetAssetResolver.ExamineAssets(Log, "Runtime", package, target, runtimeAssets, out hasRuntimeAsset, out hasRuntimePlaceHolder);

                    isSupported &= hasCompileAsset == hasRuntimeAsset;
                    isSupported &= hasCompilePlaceHolder == hasRuntimePlaceHolder;

                    if (!isSupported)
                    {
                        Log.LogMessage(LogImportance.Low, $"Skipping {fx} because it is not supported on {target}.");
                        break;
                    }
                }

                if (isSupported)
                {
                    var supportedFramework = new TaskItem(framework.ItemSpec);
                    supportedFramework.SetMetadata("HarvestedFromPackage", package);

                    // set version

                    // first try the resolved compile asset for this package
                    var refAssm = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r))?.Substring(PackageId.Length + 1);

                    if (refAssm == null)
                    {
                        // if we didn't have a compile asset it means this framework is supported inbox with a placeholder
                        // resolve the assets without placeholders to pick up the netstandard reference assembly.
                        compileAssets = resolverWithoutPlaceholders.ResolveCompileAssets(fx);
                        refAssm       = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r));
                    }

                    string version = "unknown";
                    if (refAssm != null)
                    {
                        version = VersionUtility.GetAssemblyVersion(Path.Combine(packagePath, refAssm))?.ToString() ?? version;
                    }

                    supportedFramework.SetMetadata("Version", version);

                    Log.LogMessage($"Validating version {version} for {supportedFramework.ItemSpec} because it was supported by {PackageId}/{PackageVersion}.");

                    supportedFrameworks.Add(supportedFramework);
                }
            }

            SupportedFrameworks = supportedFrameworks.ToArray();
        }
Example #11
0
        /* Given a set of available frameworks ("InboxOnTargetFrameworks"), and a list of desired frameworks,
         * reduce the set of frameworks to the minimum set of frameworks which is compatible (preferring inbox frameworks. */
        public override bool Execute()
        {
            if (null == Dependencies)
            {
                Log.LogError("Dependencies argument must be specified");
                return(false);
            }
            if (null == FrameworkListsPath)
            {
                Log.LogError("FrameworkListsPath argument must be specified");
                return(false);
            }

            // Retrieve the list of generation dependency group TFM's
            Dictionary <string, IEnumerable <ITaskItem> > portableDependencyGroups = new Dictionary <string, IEnumerable <ITaskItem> >();

            foreach (ITaskItem dependency in Dependencies)
            {
                string framework = dependency.GetMetadata("TargetFramework");
                if (framework != null && FrameworkUtilities.IsGenerationMoniker(framework) && !portableDependencyGroups.ContainsKey(framework))
                {
                    portableDependencyGroups.Add(framework, Dependencies.Where(d => d.GetMetadata("TargetFramework") == framework));
                }
            }

            List <ITaskItem> addedDependencies     = new List <ITaskItem>();
            List <string>    placeHolderFrameworks = new List <string>();

            var frameworksToExclude = TrimFrameworks?.Select(fx => NuGetFramework.Parse(fx))?.ToArray() ?? new NuGetFramework[0];

            // Prepare a resolver for evaluating if candidate frameworks are actually supported by the package
            PackageItem[] packageItems = Files.Select(f => new PackageItem(f)).ToArray();
            var           packagePaths = packageItems.Select(pi => pi.TargetPath);
            var           targetFrameworksWithPlaceHolders = packageItems.Where(pi => NuGetAssetResolver.IsPlaceholder(pi.TargetPath)).Select(pi => pi.TargetFramework);

            NuGetAssetResolver resolver = new NuGetAssetResolver(null, packagePaths);

            foreach (string portableDependency in portableDependencyGroups.Keys)
            {
                NuGetFramework portableDependencyFramework = NuGetFramework.Parse(portableDependency);

                // Determine inbox frameworks for this generations dependencies as a whole
                HashSet <NuGetFramework> inboxFrameworksList = new HashSet <NuGetFramework>();

                foreach (NuGetFramework inboxFramework in Frameworks.GetAlllInboxFrameworks(FrameworkListsPath))
                {
                    if (Generations.DetermineGenerationForFramework(inboxFramework, UseNetPlatform) >= portableDependencyFramework.Version &&
                        !frameworksToExclude.Contains(inboxFramework))
                    {
                        inboxFrameworksList.Add(inboxFramework);
                    }
                }

                // Only add the lowest version for a particular inbox framework.  EG if both net45 and net46 are supported by this generation,
                //        only add net45
                inboxFrameworksList.RemoveWhere(fx => inboxFrameworksList.Any(otherFx => (otherFx.Framework.Equals(fx.Framework)) && (otherFx.Version < fx.Version)));

                // Check for assets which have a ref, but not a lib asset. If we have any of these, then they are actually not supported frameworks
                // and we should not include them.
                inboxFrameworksList.RemoveWhere(inboxFx => !IsSupported(inboxFx, resolver));

                // Remove the frameworks which have placeholders.
                inboxFrameworksList.RemoveWhere(fx => targetFrameworksWithPlaceHolders.Any(tfx => tfx != null && fx.DotNetFrameworkName == tfx.DotNetFrameworkName));

                // Create dependency items for each inbox framework.
                foreach (string framework in inboxFrameworksList.Select(fx => fx.GetShortFolderName()))
                {
                    bool addedDependencyToFramework = false;
                    foreach (ITaskItem dependency in portableDependencyGroups[portableDependency])
                    {
                        // If we don't have the AssemblyVersion metadata (4 part version string), fall back and use Version (3 part version string)
                        string version = dependency.GetMetadata("AssemblyVersion");
                        if (string.IsNullOrEmpty(version))
                        {
                            version = dependency.GetMetadata("Version");

                            int prereleaseIndex = version.IndexOf('-');
                            if (prereleaseIndex != -1)
                            {
                                version = version.Substring(0, prereleaseIndex);
                            }
                        }
                        if (!Frameworks.IsInbox(FrameworkListsPath, framework, dependency.ItemSpec, version))
                        {
                            addedDependencyToFramework = true;
                            TaskItem dependencyItem = new TaskItem(dependency);
                            dependencyItem.SetMetadata("TargetFramework", framework);
                            // "Generation" is not required metadata, we just include it because it can be useful for debugging.
                            dependencyItem.SetMetadata("Generation", portableDependency);
                            addedDependencies.Add(dependencyItem);
                        }
                    }
                    if (!addedDependencyToFramework)
                    {
                        TaskItem dependencyItem = new TaskItem("_._");
                        dependencyItem.SetMetadata("TargetFramework", framework);
                        // "Generation" is not required metadata, we just include it because it can be useful for debugging.
                        dependencyItem.SetMetadata("Generation", portableDependency);
                        addedDependencies.Add(dependencyItem);
                        placeHolderFrameworks.Add(framework);
                    }
                }
            }

            // Collapse frameworks
            // For any dependency with a targetframework, if there is another target framework which is compatible and older, remove this dependency.

            // Get all Dependencies which are not in a portable dependency group so that we can collapse the frameworks.  If we include
            // the portable frameworks, then we'll end up collapsing to those.
            List <NuGetFramework> allDependencyGroups = new List <NuGetFramework>();

            allDependencyGroups.AddRange(Dependencies.Select(d => NuGetFramework.Parse(d.GetMetadata("TargetFramework"))).Where(a => !allDependencyGroups.Contains(a) &&
                                                                                                                                !FrameworkUtilities.IsGenerationMoniker(a) &&
                                                                                                                                !FrameworkUtilities.IsPortableMoniker(a)));
            allDependencyGroups.AddRange(addedDependencies.Select(d => NuGetFramework.Parse(d.GetMetadata("TargetFramework"))).Where(a => !allDependencyGroups.Contains(a) &&
                                                                                                                                     !FrameworkUtilities.IsGenerationMoniker(a) &&
                                                                                                                                     !FrameworkUtilities.IsPortableMoniker(a)));

            List <NuGetFramework> collapsedDependencyGroups = FrameworkUtilities.ReduceDownwards(allDependencyGroups).ToList <NuGetFramework>();

            // Get the list of dependency groups that we collapsed down so that we can add them back if they contained different dependencies than what is present in the collapsed group.

            /* TODO: Look into NuGet's sorting algorithm, they may have a bug (fixed in this line). They were not including version in the sort.
             *       See ReduceCore in https://github.com/NuGet/NuGet.Client/blob/23ea68b91a439fcfd7f94bcd01bcdee2e8adae92/src/NuGet.Core/NuGet.Frameworks/FrameworkReducer.cs */
            IEnumerable <NuGetFramework> removedDependencyGroups = allDependencyGroups.Where(d => !collapsedDependencyGroups.Contains(d))?.OrderBy(f => f.Framework, StringComparer.OrdinalIgnoreCase).ThenBy(f => f.Version);

            foreach (NuGetFramework removedDependencyGroup in removedDependencyGroups)
            {
                // always recalculate collapsedDependencyGroups in case we added an item in a previous iteration.  Dependency groups are sorted, so this should be additive and we shouldn't need to restart the collapse / add back cycle
                var nearest = FrameworkUtilities.GetNearest(removedDependencyGroup, collapsedDependencyGroups.ToArray());

                // gather the dependencies for this dependency group and the calculated "nearest" dependency group
                var nearestDependencies = addedDependencies.Where(d => nearest.Equals(NuGetFramework.Parse(d.GetMetadata("TargetFramework")))).OrderBy(f => f.ToString());
                var currentDependencies = addedDependencies.Where(d => removedDependencyGroup.Equals(NuGetFramework.Parse(d.GetMetadata("TargetFramework")))).OrderBy(f => f.ToString());

                // The nearest dependency group's dependencies are different than this dependency group's dependencies
                if (nearestDependencies.Count() != currentDependencies.Count())
                {
                    // ignore if dependency is a placeholder
                    if (currentDependencies.Count() > 0)
                    {
                        if (!NuGetAssetResolver.IsPlaceholder(currentDependencies.First().ToString()))
                        {
                            collapsedDependencyGroups.Add(removedDependencyGroup);
                        }
                    }
                    else
                    {
                        collapsedDependencyGroups.Add(removedDependencyGroup);
                    }
                }
                // identical dependency count between current and nearest, and the count is > 0
                else if (nearestDependencies.Count() > 0)
                {
                    if (!currentDependencies.SequenceEqual(nearestDependencies, new DependencyITaskItemComparer()))
                    {
                        collapsedDependencyGroups.Add(removedDependencyGroup);
                    }
                }
            }


            List <ITaskItem> collapsedDependencies = new List <ITaskItem>();

            foreach (ITaskItem dependency in addedDependencies)
            {
                if (collapsedDependencyGroups.Contains(NuGetFramework.Parse(dependency.GetMetadata("TargetFramework"))))
                {
                    collapsedDependencies.Add(dependency);
                }
            }
            TrimmedDependencies = collapsedDependencies.ToArray();

            return(!Log.HasLoggedErrors);
        }
Example #12
0
 private IEnumerable <ITaskItem> CreateLayoutFiles(IEnumerable <PackageAsset> assets, string subFolder, string assetType)
 {
     return(assets.Where(a => !NuGetAssetResolver.IsPlaceholder(a.LocalPath))
            .SelectMany(a => CreateLayoutFile(a.LocalPath, subFolder, assetType)));
 }
        public override bool Execute()
        {
            _packageItems = Files.Select(f => new PackageItem(f));
            var packageDlls  = _packageItems.Where(pi => Path.GetExtension(pi.SourcePath).Equals(".dll", StringComparison.OrdinalIgnoreCase));
            var packagePaths = _packageItems.Select(pi => pi.TargetPath);
            var packagePathsWithoutPlaceHolders = packagePaths.Where(pi => !NuGetAssetResolver.IsPlaceholder(pi));

            if (!String.IsNullOrEmpty(RuntimeJson) && !File.Exists(RuntimeJson))
            {
                Log.LogError("Could not load runtime file: {0}", RuntimeJson);
                RuntimeJson = null;
            }

            NuGetAssetResolver resolver         = new NuGetAssetResolver(RuntimeJson, packagePaths);
            NuGetAssetResolver obscuredResolver = new NuGetAssetResolver(RuntimeJson, packagePathsWithoutPlaceHolders);

            List <ITaskItem> newItems = new List <ITaskItem>();

            // determine if an inbox placeholder obscures an OOB implementation.
            foreach (var oobFramework in OOBFrameworks)
            {
                var            oobFx           = oobFramework.ItemSpec;
                NuGetFramework targetFramework = NuGetFramework.Parse(oobFx);

                // first see if any dlls are explicitly marked for this framework.
                IEnumerable <string> obscuredCompileFolders = packageDlls.Where(pi => pi.OriginalItem.GetMetadata("EnsureOOBFrameworkRef") == oobFx).Select(pi => pi.TargetDirectory);

                if (!obscuredCompileFolders.Any())
                {
                    // no dlls were marked, resolve without placeholders to determine what to promote.
                    var compileItems         = resolver.GetCompileItems(targetFramework);
                    var obscuredCompileItems = obscuredResolver.GetCompileItems(targetFramework);
                    obscuredCompileFolders = GetObscuredAssetFolders(compileItems, obscuredCompileItems, targetFramework, targetFrameworkName: oobFx, expectedAssetFolder: "ref", ignoredAssetFolder: "lib");
                }

                var promotedCompileItems = ExpandAssetFoldersToItems(obscuredCompileFolders, targetAssetFolder: "ref", targetFrameworkName: oobFx);
                newItems.AddRange(promotedCompileItems);

                // don't use 'any' in paths due to https://github.com/NuGet/Home/issues/1676
                string targetLibFolder = !String.IsNullOrEmpty(RuntimeId) && RuntimeId != "any" ? $"runtimes/{RuntimeId}/lib" : "lib";
                IEnumerable <string> obscuredRuntimeFolders = packageDlls.Where(pi => pi.OriginalItem.GetMetadata("EnsureOOBFrameworkLib") == oobFx).Select(pi => pi.TargetDirectory);

                if (!obscuredRuntimeFolders.Any())
                {
                    var runtimeItems         = resolver.GetRuntimeItems(targetFramework, RuntimeId);
                    var obscuredRuntimeItems = obscuredResolver.GetRuntimeItems(targetFramework, RuntimeId);
                    obscuredRuntimeFolders = GetObscuredAssetFolders(runtimeItems, obscuredRuntimeItems, targetFramework, targetFrameworkName: oobFx, expectedAssetFolder: targetLibFolder);
                }

                var promotedRuntimeItems = ExpandAssetFoldersToItems(obscuredRuntimeFolders, targetLibFolder, targetFrameworkName: oobFx);

                // If we promoted compile assets but couldn't find any runtime assets to promote we could
                // be missing dependencies since a dependency group will be created for the compile assets
                // that may differ from the runtime assets.
                if (promotedCompileItems.Any() && !promotedRuntimeItems.Any())
                {
                    string oobFxRid = oobFramework.GetMetadata("RuntimeId") ?? RuntimeId;

                    // find the actual implementation that will be used.
                    var runtimeItems           = resolver.GetRuntimeItems(targetFramework, oobFxRid);
                    var promotedRuntimeFolders = GetRuntimeAssetFoldersForPromotion(runtimeItems, targetFramework, oobFx);

                    // use null here to indicate that this should not actually go into the package but only be used for
                    // dependency harvesting
                    promotedRuntimeItems = ExpandAssetFoldersToItems(promotedRuntimeFolders, "$none$", targetFrameworkName: oobFx);
                }

                newItems.AddRange(promotedRuntimeItems);
            }

            AdditionalFiles = newItems.ToArray();

            return(!Log.HasLoggedErrors);
        }