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; }
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); } }
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); }
/* 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); }
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(); }
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); }
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); } }
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(); }
/* 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); }
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); }