public static Generations Load(string generationsPath, bool useNetPlatform) { Generations result; string cachePath = $"{generationsPath}:{useNetPlatform}"; if (s_generationsCache.TryGetValue(cachePath, out result)) { return(result); } result = new Generations(useNetPlatform); using (var fileStream = File.OpenRead(generationsPath)) using (var streamReader = new StreamReader(fileStream)) using (var jsonReader = new JsonTextReader(streamReader)) { var json = JToken.Load(jsonReader); foreach (var generation in EachProperty(json["generations"]).Select(ReadGeneration)) { if (useNetPlatform) { // net platform, dotnet, is 4.1 ahead of netstandard. EG: netstandard1.0 = dotnet5.1, and so on. generation.Version = new Version(generation.Version.Major + 4, generation.Version.Minor + 1, 0, 0); } result._generations.Add(generation); } } result._generations.Sort((g1, g2) => g1.Version.CompareTo(g2.Version)); return(result); }
public static Generations Load(string generationsPath, bool useNetPlatform) { Generations result; string cachePath = $"{generationsPath}:{useNetPlatform}"; if (s_generationsCache.TryGetValue(cachePath, out result)) return result; result = new Generations(useNetPlatform); using (var fileStream = File.OpenRead(generationsPath)) using (var streamReader = new StreamReader(fileStream)) using (var jsonReader = new JsonTextReader(streamReader)) { var json = JToken.Load(jsonReader); foreach (var generation in EachProperty(json["generations"]).Select(ReadGeneration)) { if (useNetPlatform) { // net platform, dotnet, is 4.1 ahead of netstandard. EG: netstandard1.0 = dotnet5.1, and so on. generation.Version = new Version(generation.Version.Major + 4, generation.Version.Minor + 1, 0, 0); } result._generations.Add(generation); } } result._generations.Sort((g1, g2) => g1.Version.CompareTo(g2.Version)); return result; }
public static Generations Load(string generationsPath) { Generations result; if (s_generationsCache.TryGetValue(generationsPath, out result)) return result; result = new Generations(); using (var fileStream = File.OpenRead(generationsPath)) using (var streamReader = new StreamReader(fileStream)) using (var jsonReader = new JsonTextReader(streamReader)) { var json = JToken.Load(jsonReader); foreach (var generation in EachProperty(json["generations"]).Select(ReadGeneration)) { result._generations.Add(generation); } } result._generations.Sort((g1, g2) => g1.Version.CompareTo(g2.Version)); return result; }
/* 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 void LoadSupport() { _frameworks = new Dictionary <NuGetFramework, ValidationFramework>(); // determine which TxM:RIDs should be considered for support based on Frameworks item foreach (var framework in Frameworks) { 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; } string runtimeIdList = framework.GetMetadata("RuntimeIDs"); if (_frameworks.ContainsKey(fx)) { Log.LogError($"Framework {fx} has been listed in Frameworks more than once."); continue; } _frameworks[fx] = new ValidationFramework(fx); if (!String.IsNullOrWhiteSpace(runtimeIdList)) { _frameworks[fx].RuntimeIds = runtimeIdList.Split(';'); } } // keep a list of explicitly listed supported frameworks so that we can check for conflicts. HashSet <NuGetFramework> explicitlySupportedFrameworks = new HashSet <NuGetFramework>(); // determine what version should be supported based on SupportedFramework items foreach (var supportedFramework in SupportedFrameworks) { NuGetFramework fx; string fxString = supportedFramework.ItemSpec; bool isExclusiveVersion = fxString.Length > 1 && fxString[0] == '[' && fxString[fxString.Length - 1] == ']'; if (isExclusiveVersion) { fxString = fxString.Substring(1, fxString.Length - 2); } try { fx = FrameworkUtilities.ParseNormalized(fxString); } catch (Exception ex) { Log.LogError($"Could not parse TargetFramework {fxString} as a SupportedFramework. {ex}"); continue; } if (fx.Equals(NuGetFramework.UnsupportedFramework)) { Log.LogError($"Did not recognize TargetFramework {fxString} as a SupportedFramework."); continue; } Version supportedVersion; string version = supportedFramework.GetMetadata("Version"); try { supportedVersion = Version.Parse(version); } catch (Exception ex) { Log.LogError($"Could not parse Version {version} on SupportedFramework item {supportedFramework.ItemSpec}. {ex}"); continue; } ValidationFramework validationFramework = null; if (!_frameworks.TryGetValue(fx, out validationFramework)) { Log.LogError($"SupportedFramework {fx} was specified but is not part of the Framework list to use for validation."); continue; } if (explicitlySupportedFrameworks.Contains(fx)) { if (validationFramework.SupportedVersion != supportedVersion) { Log.LogError($"Framework {fx} has been listed in SupportedFrameworks more than once with different versions {validationFramework.SupportedVersion} and {supportedVersion}. Framework should only be listed once with the expected API version for that platform."); } continue; } explicitlySupportedFrameworks.Add(fx); validationFramework.SupportedVersion = supportedVersion; if (!isExclusiveVersion) { // find all frameworks of higher version, sorted by version ascending var higherFrameworks = _frameworks.Values.Where(vf => vf.Framework.Framework == fx.Framework && vf.Framework.Version > fx.Version).OrderBy(vf => vf.Framework.Version); foreach (var higherFramework in higherFrameworks) { if (higherFramework.SupportedVersion != null && higherFramework.SupportedVersion > supportedVersion) { // found an higher framework version a higher API version, stop applying this supported version break; } higherFramework.SupportedVersion = supportedVersion; } } } // determine which Frameworks should support inbox _frameworkSet = FrameworkSet.Load(FrameworkListsPath); foreach (IEnumerable <Framework> inboxFxGroup in _frameworkSet.Frameworks.Values) { foreach (Framework inboxFx in inboxFxGroup) { // get currently supported version to see if we have OOB'ed it Version inboxVersion = null; inboxFx.Assemblies.TryGetValue(ContractName, out inboxVersion); if (inboxVersion != null) { NuGetFramework fx = FrameworkUtilities.ParseNormalized(inboxFx.ShortName); ValidationFramework validationFramework = null; if (_frameworks.TryGetValue(fx, out validationFramework)) { Version supportedVersion = validationFramework.SupportedVersion; if (supportedVersion != null && (supportedVersion.Major > inboxVersion.Major || (supportedVersion.Major == inboxVersion.Major && supportedVersion.Minor > inboxVersion.Minor))) { // Higher major.minor Log.LogMessage(LogImportance.Low, $"Framework {fx} supported {ContractName} as inbox but the current supported version {supportedVersion} is higher in major.minor than inbox version {inboxVersion}. Assuming out of box."); continue; } else if (supportedVersion != null && supportedVersion < inboxVersion) { // Lower version Log.LogError($"Framework {fx} supports {ContractName} as inbox but the current supported version {supportedVersion} is lower than the inbox version {inboxVersion}"); } // equal major.minor, build.revision difference is permitted, prefer the version listed by ContractSupport item } if (validationFramework == null) { // we may not be explicitly validating for this framework so add it to validate inbox assets. _frameworks[fx] = validationFramework = new ValidationFramework(fx) { SupportedVersion = inboxVersion }; } validationFramework.IsInbox = true; } } } // for every framework we know about, also validate it's generation to ensure it can // be targeted by PCL. Even if a package only supports a single framework we still // want to include a portable reference assembly. This allows 3rd parties to add // their own implementation via a lineup/runtime.json. // only consider frameworks that support the contract at a specific version var portableFrameworks = _frameworks.Values.Where(fx => fx.SupportedVersion != null && fx.SupportedVersion != s_maxVersion).ToArray(); var genVersionSuppression = GetSuppressionValues(Suppression.PermitPortableVersionMismatch) ?? new HashSet <string>(); Dictionary <NuGetFramework, ValidationFramework> generationsToValidate = new Dictionary <NuGetFramework, ValidationFramework>(); foreach (var framework in portableFrameworks) { NuGetFramework generation = new NuGetFramework(_generationIdentifier, Generations.DetermineGenerationForFramework(framework.Framework, UseNetPlatform)); Log.LogMessage(LogImportance.Low, $"Validating {generation} for {ContractName}, {framework.SupportedVersion} since it is supported by {framework.Framework}"); ValidationFramework existingGeneration = null; if (generationsToValidate.TryGetValue(generation, out existingGeneration)) { if (!VersionUtility.IsCompatibleApiVersion(framework.SupportedVersion, existingGeneration.SupportedVersion) && !genVersionSuppression.Contains(framework.Framework.ToString())) { Log.LogError($"Framework {framework.Framework} supports {ContractName} at {framework.SupportedVersion} which is lower than {existingGeneration.SupportedVersion} supported by generation {generation.GetShortFolderName()}"); } } else { generationsToValidate.Add(generation, new ValidationFramework(generation) { SupportedVersion = framework.SupportedVersion }); } } foreach (var generation in generationsToValidate) { _frameworks.Add(generation.Key, generation.Value); } // for every generation supported explcitly in implementation, ensure // it can be targeted by PCL. foreach (var packageGroup in _resolver.GetAllRuntimeItems()) { var allGenerationalImplementations = packageGroup.Value .Where(contentGroup => FrameworkUtilities.IsGenerationMoniker(contentGroup.Properties[PropertyNames.TargetFrameworkMoniker] as NuGetFramework)) .SelectMany(contentGroup => contentGroup.Items.Select(item => _targetPathToPackageItem[AggregateNuGetAssetResolver.AsPackageSpecificTargetPath(packageGroup.Key, item.Path)])); foreach (var generationalImplementation in allGenerationalImplementations) { NuGetFramework generation = generationalImplementation.TargetFramework; if (_frameworks.ContainsKey(generation)) { continue; } Version supportedVersion = generationalImplementation.Version; Log.LogMessage(LogImportance.Low, $"Validating {generation} for {ContractName}, {supportedVersion} since it is supported by {generationalImplementation.TargetPath}"); _frameworks.Add(generation, new ValidationFramework(generation) { SupportedVersion = supportedVersion }); } } }
private void LoadSupport() { _frameworks = new Dictionary <NuGetFramework, ValidationFramework>(); // determine which TxM:RIDs should be considered for support based on Frameworks item foreach (var framework in Frameworks) { 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; } string runtimeIdList = framework.GetMetadata("RuntimeIDs"); if (_frameworks.ContainsKey(fx)) { Log.LogError($"Framework {fx} has been listed in Frameworks more than once."); continue; } _frameworks[fx] = new ValidationFramework(fx); if (!String.IsNullOrWhiteSpace(runtimeIdList)) { _frameworks[fx].RuntimeIds = runtimeIdList.Split(';'); } } // keep a list of explicitly listed supported frameworks so that we can check for conflicts. HashSet <NuGetFramework> explicitlySupportedFrameworks = new HashSet <NuGetFramework>(); // determine what version should be supported based on SupportedFramework items foreach (var supportedFramework in SupportedFrameworks) { NuGetFramework fx; string fxString = supportedFramework.ItemSpec; bool isExclusiveVersion = fxString.Length > 1 && fxString[0] == '[' && fxString[fxString.Length - 1] == ']'; if (isExclusiveVersion) { fxString = fxString.Substring(1, fxString.Length - 2); } try { fx = FrameworkUtilities.ParseNormalized(fxString); } catch (Exception ex) { Log.LogError($"Could not parse TargetFramework {fxString} as a SupportedFramework. {ex}"); continue; } if (fx.Equals(NuGetFramework.UnsupportedFramework)) { Log.LogError($"Did not recognize TargetFramework {fxString} as a SupportedFramework."); continue; } Version supportedVersion; string version = supportedFramework.GetMetadata("Version"); try { supportedVersion = Version.Parse(version); } catch (Exception ex) { Log.LogError($"Could not parse Version {version} on SupportedFramework item {supportedFramework.ItemSpec}. {ex}"); continue; } ValidationFramework validationFramework = null; if (!_frameworks.TryGetValue(fx, out validationFramework)) { Log.LogError($"SupportedFramework {fx} was specified but is not part of the Framework list to use for validation."); continue; } if (explicitlySupportedFrameworks.Contains(fx)) { if (supportedVersion <= validationFramework.SupportedVersion) { // if we've already picked up a higher/equal version, prefer it continue; } } else { explicitlySupportedFrameworks.Add(fx); } validationFramework.SupportedVersion = supportedVersion; if (!isExclusiveVersion) { // find all frameworks of higher version, sorted by version ascending IEnumerable <ValidationFramework> higherFrameworks = _frameworks.Values.Where(vf => vf.Framework.Framework == fx.Framework && vf.Framework.Version > fx.Version).OrderBy(vf => vf.Framework.Version); // netcore50 is the last `netcore` framework, after that we use `uap` if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.NetCore) { var uapFrameworks = _frameworks.Values.Where(vf => vf.Framework.Framework == FrameworkConstants.FrameworkIdentifiers.UAP).OrderBy(vf => vf.Framework.Version); higherFrameworks = higherFrameworks.Concat(uapFrameworks); } foreach (var higherFramework in higherFrameworks) { if (higherFramework.SupportedVersion != null && higherFramework.SupportedVersion > supportedVersion) { // found an higher framework version a higher API version, stop applying this supported version break; } higherFramework.SupportedVersion = supportedVersion; } } } // determine which Frameworks should support inbox _frameworkSet = FrameworkSet.Load(FrameworkListsPath); foreach (IEnumerable <Framework> inboxFxGroup in _frameworkSet.Frameworks.Values) { foreach (Framework inboxFx in inboxFxGroup) { // get currently supported version to see if we have OOB'ed it Version inboxVersion = null; inboxFx.Assemblies.TryGetValue(ContractName, out inboxVersion); if (inboxVersion != null) { NuGetFramework fx = FrameworkUtilities.ParseNormalized(inboxFx.ShortName); ValidationFramework validationFramework = null; if (_frameworks.TryGetValue(fx, out validationFramework)) { Version supportedVersion = validationFramework.SupportedVersion; if (supportedVersion != null && (supportedVersion.Major > inboxVersion.Major || (supportedVersion.Major == inboxVersion.Major && supportedVersion.Minor > inboxVersion.Minor))) { // Higher major.minor Log.LogMessage(LogImportance.Low, $"Framework {fx} supported {ContractName} as inbox but the current supported version {supportedVersion} is higher in major.minor than inbox version {inboxVersion}. Assuming out of box."); continue; } else if (supportedVersion != null && supportedVersion < inboxVersion && inboxVersion != s_maxVersion) { // Lower version Log.LogError($"Framework {fx} supports {ContractName} as inbox but the current supported version {supportedVersion} is lower than the inbox version {inboxVersion}"); } // equal major.minor, build.revision difference is permitted, prefer the version listed by ContractSupport item } if (validationFramework == null) { // we may not be explicitly validating for this framework so add it to validate inbox assets. _frameworks[fx] = validationFramework = new ValidationFramework(fx) { SupportedVersion = inboxVersion }; } validationFramework.IsInbox = true; } } } // for every framework we know about, also infer it's netstandard version to ensure it can // be targeted by PCL. Even if a package only supports a single framework we still // want to include a portable reference assembly. This allows 3rd parties to add // their own implementation via a lineup/runtime.json. // only consider frameworks that support the contract at a specific version var inferFrameworks = _frameworks.Values.Where(fx => fx.SupportedVersion != null && fx.SupportedVersion != s_maxVersion).ToArray(); var genVersionSuppression = GetSuppressionValues(Suppression.PermitPortableVersionMismatch) ?? new HashSet <string>(); var inferNETStandardSuppression = GetSuppressionValues(Suppression.SuppressNETStandardInference) ?? new HashSet <string>(); Dictionary <NuGetFramework, ValidationFramework> generationsToValidate = new Dictionary <NuGetFramework, ValidationFramework>(); foreach (var inferFramework in inferFrameworks) { var inferFrameworkMoniker = inferFramework.Framework.ToString(); if (inferNETStandardSuppression.Contains(inferFrameworkMoniker)) { continue; } NuGetFramework generation = new NuGetFramework(_generationIdentifier, Generations.DetermineGenerationForFramework(inferFramework.Framework, UseNetPlatform)); Log.LogMessage(LogImportance.Low, $"Validating {generation} for {ContractName}, {inferFramework.SupportedVersion} since it is supported by {inferFrameworkMoniker}"); ValidationFramework existingGeneration = null; if (generationsToValidate.TryGetValue(generation, out existingGeneration)) { // the netstandard version should be the minimum version supported by all platforms that support that netstandard version. if (inferFramework.SupportedVersion < existingGeneration.SupportedVersion) { Log.LogMessage($"Framework {inferFramework.Framework} supports {ContractName} at {inferFramework.SupportedVersion} which is lower than {existingGeneration.SupportedVersion} supported by generation {generation.GetShortFolderName()}. Lowering the version supported by {generation.GetShortFolderName()}."); existingGeneration.SupportedVersion = inferFramework.SupportedVersion; } } else { generationsToValidate.Add(generation, new ValidationFramework(generation) { SupportedVersion = inferFramework.SupportedVersion }); } } foreach (var generation in generationsToValidate) { _frameworks.Add(generation.Key, generation.Value); } }
public override bool Execute() { if (String.IsNullOrEmpty(PackageTargetFramework)) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework is not defined"); return(true); } NuGetFramework fx = null; try { fx = NuGetFramework.Parse(PackageTargetFramework); } catch (Exception ex) { Log.LogError($"Could not parse PackageTargetFramework {PackageTargetFramework}. {ex}"); return(false); } Version assemblyVersion = null; if (!Version.TryParse(AssemblyVersion, out assemblyVersion)) { Log.LogError($"Could not parse AssemblyVersion {AssemblyVersion}."); return(false); } if (UseNetPlatform) { if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.NetStandard) { Log.LogError($"Validating for legacy 'dotnet' moniker but package targets new 'netstandard' moniker."); return(false); } else if (fx.Framework != FrameworkConstants.FrameworkIdentifiers.NetPlatform) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework {fx} is not {FrameworkConstants.FrameworkIdentifiers.NetPlatform}"); return(true); } } else { if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.NetPlatform) { if (fx.Version > new Version(5, 0)) { NuGetFramework netstandardFx = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.NetStandard, new Version(fx.Version.Major - 4, fx.Version.Minor - 1)); Log.LogError($"{fx.GetShortFolderName()} is no longer supported please update to {netstandardFx.GetShortFolderName()}. Validating as {netstandardFx.GetShortFolderName()}."); // update to netstandard so that we can get the right number of errors fx = netstandardFx; } else { Log.LogError($"Invalid framework version {fx.GetShortFolderName()} please update to appropriate netstandard version."); // update to nestandard so that we'll do the actual calculation fx = FrameworkConstants.CommonFrameworks.NetStandard; } } if (fx.Framework != FrameworkConstants.FrameworkIdentifiers.NetStandard) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework {fx} is not {FrameworkConstants.FrameworkIdentifiers.NetStandard}"); return(true); } } _generations = Generations.Load(GenerationDefinitionsFile, UseNetPlatform); Dictionary <string, string> candidateRefs = CandidateReferences.ToDictionary(r => r.GetMetadata("FileName"), r => r.GetMetadata("FullPath")); Version idealGeneration = _generations.DetermineGenerationFromSeeds(AssemblyName, assemblyVersion, Log) ?? new Version(0, 0, 0, 0); if (idealGeneration > fx.Version) { Log.LogError($"Assembly {AssemblyName}, Version={assemblyVersion} is generation {idealGeneration} based on the seed data in {GenerationDefinitionsFile} which is greater than project generation {fx.Version}."); } HashSet <string> ignoredRefs = null; if (IgnoredReferences != null) { ignoredRefs = new HashSet <string>(IgnoredReferences.Select(ir => ir.ItemSpec), StringComparer.OrdinalIgnoreCase); } Version defaultGeneration = UseNetPlatform ? FrameworkConstants.CommonFrameworks.DotNet.Version : FrameworkConstants.CommonFrameworks.NetStandard.Version; foreach (var reference in DirectReferences) { string path = reference.GetMetadata("FullPath"); // workaround issue where portable targeting pack design time facades // include dangling refs and refs to higher versions of contracts than // exist in the targeting pack. if (path.IndexOf(".NETPortable", StringComparison.OrdinalIgnoreCase) != -1 && designTimeFacades.Contains(Path.GetFileNameWithoutExtension(path))) { continue; } if (ignoredRefs != null && ignoredRefs.Contains(Path.GetFileNameWithoutExtension(path))) { continue; } if (!File.Exists(path)) { Log.LogError($"Reference {path} does not exist."); continue; } var dependencyGeneration = _generations.DetermineGenerationFromFile(path, Log, candidateRefs: candidateRefs, ignoredRefs: ignoredRefs) ?? defaultGeneration; if (dependencyGeneration > fx.Version) { Log.LogError($"Dependency {path} is generation {dependencyGeneration} which is greater than project generation {fx.Version}."); } if (dependencyGeneration > idealGeneration) { idealGeneration = dependencyGeneration; } } if (fx.Version > idealGeneration) { Log.LogMessage(LogImportance.Low, $"Generation {fx.Version} is higher than the ideal miniumum {idealGeneration}."); } return(!Log.HasLoggedErrors); }
public override bool Execute() { if (String.IsNullOrEmpty(PackageTargetFramework)) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework is not defined"); return true; } NuGetFramework fx = null; try { fx = NuGetFramework.Parse(PackageTargetFramework); } catch(Exception ex) { Log.LogError($"Could not parse PackageTargetFramework {PackageTargetFramework}. {ex}"); return false; } Version assemblyVersion = null; if (!Version.TryParse(AssemblyVersion, out assemblyVersion)) { Log.LogError($"Could not parse AssemblyVersion {AssemblyVersion}."); return false; } if (fx.Framework != FrameworkConstants.FrameworkIdentifiers.NetPlatform) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework {fx} is not {FrameworkConstants.FrameworkIdentifiers.NetPlatform}"); return true; } _generations = Generations.Load(GenerationDefinitionsFile); Dictionary<string, string> candidateRefs = CandidateReferences.ToDictionary(r => r.GetMetadata("FileName"), r => r.GetMetadata("FullPath")); Version idealGeneration = _generations.DetermineGenerationFromSeeds(AssemblyName, assemblyVersion, Log) ?? new Version(0, 0, 0, 0); if (idealGeneration > fx.Version) { Log.LogError($"Assembly {AssemblyName}, Version={assemblyVersion} is generation {idealGeneration} based on the seed data in {GenerationDefinitionsFile} which is greater than project generation {fx.Version}."); } HashSet<string> ignoredRefs = null; if (IgnoredReferences != null) { ignoredRefs = new HashSet<string>(IgnoredReferences.Select(ir => ir.ItemSpec), StringComparer.OrdinalIgnoreCase); } foreach (var reference in DirectReferences) { string path = reference.GetMetadata("FullPath"); // workaround issue where portable targeting pack design time facades // include dangling refs and refs to higher versions of contracts than // exist in the targeting pack. if (path.IndexOf(".NETPortable", StringComparison.OrdinalIgnoreCase) != -1 && designTimeFacades.Contains(Path.GetFileNameWithoutExtension(path))) { continue; } if (ignoredRefs != null && ignoredRefs.Contains(Path.GetFileNameWithoutExtension(path))) { continue; } if (!File.Exists(path)) { Log.LogError($"Reference {path} does not exist."); continue; } var dependencyGeneration = _generations.DetermineGenerationFromFile(path, Log, candidateRefs: candidateRefs, ignoredRefs: ignoredRefs) ?? FrameworkConstants.CommonFrameworks.DotNet.Version; if (dependencyGeneration > fx.Version) { Log.LogError($"Dependency {path} is generation {dependencyGeneration} which is greater than project generation {fx.Version}."); } if (dependencyGeneration > idealGeneration) { idealGeneration = dependencyGeneration; } } if (fx.Version > idealGeneration) { Log.LogMessage(LogImportance.Low, $"Generation {fx.Version} is higher than the ideal miniumum {idealGeneration}."); } 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 (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() { if (String.IsNullOrEmpty(PackageTargetFramework)) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework is not defined"); return true; } NuGetFramework fx = null; try { fx = NuGetFramework.Parse(PackageTargetFramework); } catch(Exception ex) { Log.LogError($"Could not parse PackageTargetFramework {PackageTargetFramework}. {ex}"); return false; } Version assemblyVersion = null; if (!Version.TryParse(AssemblyVersion, out assemblyVersion)) { Log.LogError($"Could not parse AssemblyVersion {AssemblyVersion}."); return false; } if (UseNetPlatform) { if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.NetStandard) { Log.LogError($"Validating for legacy 'dotnet' moniker but package targets new 'netstandard' moniker."); return false; } else if (fx.Framework != FrameworkConstants.FrameworkIdentifiers.NetPlatform) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework {fx} is not {FrameworkConstants.FrameworkIdentifiers.NetPlatform}"); return true; } } else { if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.NetPlatform) { if (fx.Version > new Version(5, 0)) { NuGetFramework netstandardFx = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.NetStandard, new Version(fx.Version.Major - 4, fx.Version.Minor - 1)); Log.LogError($"{fx.GetShortFolderName()} is no longer supported please update to {netstandardFx.GetShortFolderName()}. Validating as {netstandardFx.GetShortFolderName()}."); // update to netstandard so that we can get the right number of errors fx = netstandardFx; } else { Log.LogError($"Invalid framework version {fx.GetShortFolderName()} please update to appropriate netstandard version."); // update to nestandard so that we'll do the actual calculation fx = FrameworkConstants.CommonFrameworks.NetStandard; } } if (fx.Framework != FrameworkConstants.FrameworkIdentifiers.NetStandard) { Log.LogMessage(LogImportance.Low, $"Skipping validation since PackageTargetFramework {fx} is not {FrameworkConstants.FrameworkIdentifiers.NetStandard}"); return true; } } _generations = Generations.Load(GenerationDefinitionsFile, UseNetPlatform); Dictionary<string, string> candidateRefs = CandidateReferences.ToDictionary(r => r.GetMetadata("FileName"), r => r.GetMetadata("FullPath")); Version idealGeneration = _generations.DetermineGenerationFromSeeds(AssemblyName, assemblyVersion, Log) ?? new Version(0, 0, 0, 0); if (idealGeneration > fx.Version) { Log.LogError($"Assembly {AssemblyName}, Version={assemblyVersion} is generation {idealGeneration} based on the seed data in {GenerationDefinitionsFile} which is greater than project generation {fx.Version}."); } HashSet<string> ignoredRefs = null; if (IgnoredReferences != null) { ignoredRefs = new HashSet<string>(IgnoredReferences.Select(ir => ir.ItemSpec), StringComparer.OrdinalIgnoreCase); } Version defaultGeneration = UseNetPlatform ? FrameworkConstants.CommonFrameworks.DotNet.Version : FrameworkConstants.CommonFrameworks.NetStandard.Version; foreach (var reference in DirectReferences) { string path = reference.GetMetadata("FullPath"); // workaround issue where portable targeting pack design time facades // include dangling refs and refs to higher versions of contracts than // exist in the targeting pack. if (path.IndexOf(".NETPortable", StringComparison.OrdinalIgnoreCase) != -1 && designTimeFacades.Contains(Path.GetFileNameWithoutExtension(path))) { continue; } if (ignoredRefs != null && ignoredRefs.Contains(Path.GetFileNameWithoutExtension(path))) { continue; } if (!File.Exists(path)) { Log.LogError($"Reference {path} does not exist."); continue; } var dependencyGeneration = _generations.DetermineGenerationFromFile(path, Log, candidateRefs: candidateRefs, ignoredRefs: ignoredRefs) ?? defaultGeneration; if (dependencyGeneration > fx.Version) { Log.LogError($"Dependency {path} is generation {dependencyGeneration} which is greater than project generation {fx.Version}."); } if (dependencyGeneration > idealGeneration) { idealGeneration = dependencyGeneration; } } if (fx.Version > idealGeneration) { Log.LogMessage(LogImportance.Low, $"Generation {fx.Version} is higher than the ideal miniumum {idealGeneration}."); } return !Log.HasLoggedErrors; }