private bool IsSupported(NuGetFramework inboxFx, NuGetAssetResolver resolver) { var compileAssets = resolver.ResolveCompileAssets(inboxFx); // We assume that packages will only support inbox frameworks with lib/tfm assets and not runtime specific assets. // This effectively means we'll never reduce dependencies if a package happens to support an inbox framework with // a RID asset, but that is OK because RID assets can only be used by nuget3 + project.json // and we don't care about reducing dependencies for project.json because indirect dependencies are hidden. var runtimeAssets = resolver.ResolveRuntimeAssets(inboxFx, null); foreach (var compileAsset in compileAssets.Where(c => !NuGetAssetResolver.IsPlaceholder(c))) { string fileName = Path.GetFileName(compileAsset); if (!runtimeAssets.Any(r => Path.GetFileName(r).Equals(fileName, StringComparison.OrdinalIgnoreCase))) { // ref with no matching lib return(false); } } // Either all compile assets had matching runtime assets, or all were placeholders, make sure we have at // least one runtime asset to cover the placeholder case return(runtimeAssets.Any()); }
private void ExamineAssets(string assetType, string package, string target, IEnumerable <string> runtimeItems, out bool hasRealAsset, out bool hasPlaceHolder) { hasPlaceHolder = false; hasRealAsset = false; StringBuilder assetLog = new StringBuilder($"{assetType} assets for {ContractName} on {target}: "); if (runtimeItems != null && runtimeItems.Any()) { foreach (var runtimeItem in runtimeItems) { assetLog.AppendLine(); assetLog.Append($" {runtimeItem}"); if (!hasRealAsset && NuGetAssetResolver.IsPlaceholder(runtimeItem)) { hasPlaceHolder = true; } else { hasRealAsset = true; hasPlaceHolder = false; } } } else { assetLog.AppendLine(); assetLog.Append(" <none>"); } Log.LogMessage(LogImportance.Low, assetLog.ToString()); }
private IEnumerable <string> GetRuntimeAssetFoldersForPromotion(ContentItemGroup runtimeAssets, NuGetFramework targetFramework, string targetFrameworkName) { if (runtimeAssets == null || runtimeAssets.Items.Count == 0) { return(Enumerable.Empty <string>()); } if (runtimeAssets.Items.All(ci => NuGetAssetResolver.IsPlaceholder(ci.Path))) { return(Enumerable.Empty <string>()); } if (targetFrameworkName == null) { targetFrameworkName = targetFramework.GetShortFolderName(); } var resolvedFramework = runtimeAssets.Properties["tfm"] as NuGetFramework; if (targetFramework.Equals(resolvedFramework)) { Log.LogMessage(LogImportance.Low, $"Not promoting explicit implementation for {targetFrameworkName}"); return(Enumerable.Empty <string>()); } return(NuGetAssetResolver.GetPackageTargetDirectories(runtimeAssets)); }
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); }
private IEnumerable <string> GetObscuredAssetFolders(ContentItemGroup assets, ContentItemGroup obscuredAssets, NuGetFramework targetFramework, string targetFrameworkName, string expectedAssetFolder, string ignoredAssetFolder = null) { if (assets == null || assets.Items.Count == 0) { return(Enumerable.Empty <string>()); } if (assets.Items.Any(ci => !NuGetAssetResolver.IsPlaceholder(ci.Path))) { return(Enumerable.Empty <string>()); } if (targetFrameworkName == null) { targetFrameworkName = targetFramework.GetShortFolderName(); } var resolvedFramework = assets.Properties["tfm"] as NuGetFramework; if (targetFramework.Equals(resolvedFramework)) { Log.LogMessage(LogImportance.Low, $"Not overriding explicit placeholder for {targetFrameworkName}"); return(Enumerable.Empty <string>()); } var obscuredAssetPaths = NuGetAssetResolver.GetPackageTargetDirectories(obscuredAssets); if (ignoredAssetFolder != null) { string ignoredFolder = ignoredAssetFolder + '/'; obscuredAssetPaths = obscuredAssetPaths.Where(i => - 1 == i.IndexOf(ignoredFolder, StringComparison.OrdinalIgnoreCase)); } if (expectedAssetFolder != null) { var unexpectedAssetPaths = obscuredAssetPaths.Where(ri => !ri.StartsWith(expectedAssetFolder, StringComparison.OrdinalIgnoreCase)); foreach (var unexpectedAssetPath in unexpectedAssetPaths) { Log.LogWarning($"Unexpected targetPath {unexpectedAssetPath}. Expected only {expectedAssetFolder}."); } // filter after we've warned obscuredAssetPaths = obscuredAssetPaths.Except(unexpectedAssetPaths); } if (!obscuredAssetPaths.Any()) { // it's acceptable to have no override, this is the case for packages which // carry implementation in a runtime-specific package Log.LogMessage(LogImportance.Low, $"No {expectedAssetFolder} assets could be found to override inbox placeholder for {targetFrameworkName}."); } return(obscuredAssetPaths); }
public PackageItem(ITaskItem item) { OriginalItem = item; SourcePath = item.GetMetadata("FullPath"); SourceProject = GetMetadata("MSBuildSourceProjectFile"); string value = GetMetadata("TargetFramework"); if (!String.IsNullOrWhiteSpace(value)) { TargetFramework = NuGetFramework.Parse(value); } TargetPath = item.GetMetadata(nameof(TargetPath)); AdditionalProperties = GetMetadata(nameof(AdditionalProperties)); UndefineProperties = GetMetadata(nameof(UndefineProperties)); HarvestedFrom = GetMetadata(nameof(HarvestedFrom)); Package = GetMetadata("PackageId"); PackageVersion = GetMetadata("PackageVersion"); IsDll = Path.GetExtension(SourcePath).Equals(".dll", StringComparison.OrdinalIgnoreCase); IsPlaceholder = NuGetAssetResolver.IsPlaceholder(SourcePath); IsRef = TargetPath.StartsWith("ref/", StringComparison.OrdinalIgnoreCase); // determine if we need to append filename to TargetPath // see https://docs.nuget.org/create/nuspec-reference#specifying-files-to-include-in-the-package // SourcePath specifies file and target specifies file - do nothing // SourcePath specifies file and Target specifies directory - copy filename // SourcePath specifies wildcard files - copy wildcard // SourcePath specifies recursive wildcard - do not allow, recursive directory may impact asset selection // we don't want to attempt to expand the wildcard since the build may not yet be complete. if (SourcePath.Contains("**")) { throw new ArgumentException($"Recursive wildcards \"**\" are not permitted in source paths for packages: {SourcePath}. Recursive directory may impact asset selection and we don't want to attempt to expand the wildcard since the build may not yet be complete."); } string sourceFile = Path.GetFileName(SourcePath); if (!Path.GetExtension(TargetPath).Equals(Path.GetExtension(sourceFile), StringComparison.OrdinalIgnoreCase) || sourceFile.Contains("*")) { TargetPath = Path.Combine(TargetPath, sourceFile); } // standardize to / TargetPath = TargetPath.Replace('\\', '/'); int dirLength = TargetPath.LastIndexOf('/'); TargetDirectory = (dirLength > 0) ? TargetPath.Substring(0, dirLength) : String.Empty; }
private void UpdateFromPackage(PackageIndex index, string packagePath, bool filter = false) { string id; NuGetVersion version; IEnumerable <Version> assemblyVersions; using (var reader = new PackageArchiveReader(packagePath)) { var identity = reader.GetIdentity(); id = identity.Id; version = identity.Version; if (filter && !ShouldInclude(id)) { return; } var refFiles = reader.GetFiles("ref").Where(r => !NuGetAssetResolver.IsPlaceholder(r)); if (!refFiles.Any()) { refFiles = reader.GetFiles("lib"); } assemblyVersions = refFiles.Select(refFile => { using (var refStream = reader.GetStream(refFile)) using (var memStream = new MemoryStream()) { refStream.CopyTo(memStream); memStream.Seek(0, SeekOrigin.Begin); return(VersionUtility.GetAssemblyVersion(memStream)); } }).ToArray(); } UpdateFromValues(index, id, version, assemblyVersions); }
private void ValidateSupport() { var runtimeFxSuppression = GetSuppressionValues(Suppression.PermitRuntimeTargetMonikerMismatch) ?? new HashSet <string>(); ValidationReport report = null; if (ValidationReport != null) { report = CreateValidationReport(); } // validate support for each TxM:RID foreach (var validateFramework in _frameworks.Values) { NuGetFramework fx = validateFramework.Framework; Version supportedVersion = validateFramework.SupportedVersion; var compileAssetPaths = _resolver.ResolveCompileAssets(fx, PackageId); bool hasCompileAsset, hasCompilePlaceHolder; NuGetAssetResolver.ExamineAssets(Log, "Compile", ContractName, fx.ToString(), compileAssetPaths, out hasCompileAsset, out hasCompilePlaceHolder); if (report != null && validateFramework.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 = compileAssetPaths.Where(c => !NuGetAssetResolver.IsPlaceholder(c)).Select(c => GetPackageAssetFromTargetPath(c)).ToArray() }; report.Targets.Add(fx.ToString(), reportTarget); } // resolve/test for each RID associated with this framework. foreach (string runtimeId in validateFramework.RuntimeIds) { string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}"; var runtimeAssetPaths = _resolver.ResolveRuntimeAssets(fx, runtimeId); 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 (report != null) { var reportTarget = new Target() { Framework = fx.ToString(), RuntimeID = runtimeId, CompileAssets = compileAssetPaths.Where(c => !NuGetAssetResolver.IsPlaceholder(c)).Select(c => GetPackageAssetFromTargetPath(c)).ToArray(), RuntimeAssets = runtimeAssetPaths.Where(r => !NuGetAssetResolver.IsPlaceholder(r)).Select(r => GetPackageAssetFromTargetPath(r)).ToArray() }; report.Targets.Add(target, reportTarget); } 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 = compileAssetPaths.Where(IsDll); if (referenceAssemblies.Count() > 1) { Log.LogError($"{ContractName} should only contain a single compile asset for {target}."); } foreach (var referenceAssembly in referenceAssemblies) { referenceAssemblyVersion = _targetPathToPackageItem[referenceAssembly].Version; if (!VersionUtility.IsCompatibleApiVersion(supportedVersion, referenceAssemblyVersion)) { Log.LogError($"{ContractName} should support API version {supportedVersion} on {target} but {referenceAssembly} 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 = runtimeAssetPaths.Where(IsDll); Dictionary <string, string> implementationFiles = new Dictionary <string, string>(); foreach (var implementationAssembly in implementationAssemblies) { var packageItem = _targetPathToPackageItem[implementationAssembly]; Version implementationVersion = packageItem.Version; if (!VersionUtility.IsCompatibleApiVersion(supportedVersion, implementationVersion)) { Log.LogError($"{ContractName} should support API version {supportedVersion} on {target} but {implementationAssembly} 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.Equals(implementationVersion)) { Log.LogError($"{ContractName} has a higher runtime version ({implementationVersion}) than compile version ({referenceAssemblyVersion}) on .NET Desktop framework {target}. This will break bindingRedirects."); } string fileName = Path.GetFileName(implementationAssembly); if (implementationFiles.ContainsKey(fileName)) { Log.LogError($"{ContractName} includes both {implementationAssembly} and {implementationFiles[fileName]} an on {target} which have the same name and will clash when both packages are used."); } else { implementationFiles[fileName] = implementationAssembly; } if (packageItem.TargetFramework != 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 = _targetPathToPackageItem.Values.Where(i => i.TargetFramework == fx && // exact framework // Same file Path.GetFileName(i.TargetPath).Equals(fileName, StringComparison.OrdinalIgnoreCase) && // Is implementation (i.TargetPath.StartsWith("lib") || i.TargetPath.StartsWith("runtimes")) && // is not the same source file as was already selected i.SourcePath != packageItem.SourcePath); if (matchingFxAssets.Any()) { Log.LogError($"When targeting {target} {ContractName} will use {implementationAssembly} which targets {packageItem.TargetFramework.GetShortFolderName()} but {String.Join(";", matchingFxAssets.Select(i => i.TargetPath))} targets {fx.GetShortFolderName()} specifically."); } } } } } } } } // Set output items AllSupportedFrameworks = _frameworks.Values.Where(fx => fx.SupportedVersion != null).Select(fx => fx.ToItem()).OrderBy(i => i.ItemSpec).ToArray(); if (!String.IsNullOrEmpty(ValidationReport)) { report.Save(ValidationReport); } }
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 var dependencyGroups = Dependencies.GroupBy(d => d.GetMetadata("TargetFramework")).Select(dg => new { Framework = NuGetFramework.Parse(dg.Key), Dependencies = dg.ToArray() }); List <ITaskItem> addedDependencies = new List <ITaskItem>(); // Exclude any non-portable frameworks that already have specific dependency groups. var frameworksToExclude = dependencyGroups.Select(dg => dg.Framework).Where(fx => !FrameworkUtilities.IsGenerationMoniker(fx)); // 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 (var portableDependencyGroup in dependencyGroups.Where(dg => FrameworkUtilities.IsGenerationMoniker(dg.Framework))) { // Determine inbox frameworks for this generation that don't already have explicit groups HashSet <NuGetFramework> inboxFrameworksList = new HashSet <NuGetFramework>( Frameworks.GetAlllInboxFrameworks(FrameworkListsPath) .Where(fx => !fx.IsPCL) .Where(fx => Generations.DetermineGenerationForFramework(fx, UseNetPlatform) >= portableDependencyGroup.Framework.Version && !frameworksToExclude.Any(exFx => exFx.Framework == fx.Framework && exFx.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)); // 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))); // Create dependency items for each inbox framework. foreach (var framework in inboxFrameworksList) { bool addedDependencyToFramework = false; foreach (ITaskItem dependency in portableDependencyGroup.Dependencies) { string version = GetVersion(dependency); if (!Frameworks.IsInbox(FrameworkListsPath, framework, dependency.ItemSpec, version)) { addedDependencyToFramework = true; AddDependency(addedDependencies, new TaskItem(dependency), framework, portableDependencyGroup.Framework); } } if (!addedDependencyToFramework) { AddDependency(addedDependencies, new TaskItem("_._"), framework, portableDependencyGroup.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))); }
/* 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); }
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 ValidateIndex() { if (SkipIndexCheck) { return; } if (PackageIndexes == null || PackageIndexes.Length == 0) { return; } PackageIndex.Current.Merge(PackageIndexes.Select(pi => pi.GetMetadata("FullPath"))); PackageInfo info; if (!PackageIndex.Current.Packages.TryGetValue(PackageId, out info)) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} is missing an entry for package {PackageId}. Please run /t:UpdatePackageIndex on this project to commit an update."); return; } var thisPackageFiles = _validateFiles[PackageId]; var refFiles = thisPackageFiles.Where(f => f.TargetPath.StartsWith("ref/", StringComparison.OrdinalIgnoreCase)).Where(r => !NuGetAssetResolver.IsPlaceholder(r.TargetPath)); if (!refFiles.Any()) { refFiles = thisPackageFiles.Where(f => f.TargetPath.StartsWith("lib/", StringComparison.OrdinalIgnoreCase)); } var thisPackageVersion = VersionUtility.As3PartVersion(NuGetVersion.Parse(PackageVersion).Version); var refFileVersions = new HashSet <Version>(refFiles.Where(r => r.Version != null).Select(r => VersionUtility.As4PartVersion(r.Version))); foreach (var refFileVersion in refFileVersions) { Version refPackageVersion; // determine if we're missing a mapping for this package if (!info.AssemblyVersionInPackageVersion.TryGetValue(refFileVersion, out refPackageVersion)) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} is missing an assembly version entry for {refFileVersion} for package {PackageId}. Please run /t:UpdatePackageIndex on this project to commit an update."); } else { // determine if we have a mapping for an unstable package and that unstable package is not this one if (!info.StableVersions.Contains(refPackageVersion) && refPackageVersion != thisPackageVersion) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} indicates that assembly version {refFileVersion} is contained in non-stable package version {refPackageVersion} which differs from this package version {thisPackageVersion}."); } } } var orphanedAssemblyVersions = info.AssemblyVersionInPackageVersion .Where(pair => pair.Value == thisPackageVersion && !refFileVersions.Contains(pair.Key)) .Select(pair => pair.Key); if (orphanedAssemblyVersions.Any()) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} is has an assembly version entry(s) for {String.Join(", ", orphanedAssemblyVersions)} which are no longer in package {PackageId}. Please run /t:UpdatePackageIndex on this project to commit an update."); } }
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); }
/// <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 (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); }
/// <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(); // find the best framework foreach (var compileFx in compileFxs) { CompileAssets = _resolver.ResolveCompileAssets(compileFx) .Where(ca => !NuGetAssetResolver.IsPlaceholder(ca)) .Select(ca => PackageItemAsResolvedAsset(_targetPathToPackageItem[ca])) .ToArray(); if (CompileAssets.Any()) { Log.LogMessage($"Resolved compile assets from {compileFx.ToString()}: {String.Join(";", CompileAssets.Select(c => c.ItemSpec))}"); break; } } if (!CompileAssets.Any()) { Log.LogError($"Could not locate compile assets for any of the frameworks {String.Join(";", compileFxs.Select(fx => fx.ToString()))}"); } foreach (var runtimeFx in runtimeFxs) { RuntimeAssets = _resolver.ResolveRuntimeAssets(runtimeFx, TargetRuntime) .Where(ra => !NuGetAssetResolver.IsPlaceholder(ra)) .Select(ra => PackageItemAsResolvedAsset(_targetPathToPackageItem[ra])) .ToArray(); if (RuntimeAssets.Any()) { Log.LogMessage($"Resolved runtime assets from {runtimeFx.ToString()}: {String.Join(";", RuntimeAssets.Select(r => r.ItemSpec))}"); break; } } if (!RuntimeAssets.Any()) { Log.LogError($"Could not locate runtime assets for any of the frameworks {String.Join(";", runtimeFxs.Select(fx => fx.ToString()))}"); } return(!Log.HasLoggedErrors); }