/// <summary> /// Gets package metadata from a the provided <see cref="NuspecReader"/> instance. /// </summary> /// <param name="nuspecReader">The </param> public static PackageMetadata FromNuspecReader(NuspecReader nuspecReader) { return(new PackageMetadata( nuspecReader.GetMetadata().ToDictionary(kvp => kvp.Key, kvp => kvp.Value), nuspecReader.GetDependencyGroups(true), nuspecReader.GetFrameworkReferenceGroups(), nuspecReader.GetPackageTypes(), nuspecReader.GetMinClientVersion() )); }
/// <summary> /// Gets package metadata from a the provided <see cref="NuspecReader"/> instance. /// </summary> /// <param name="nuspecReader">The <see cref="NuspecReader"/> instance used to read the <see cref="PackageMetadata"/></param> /// <param name="strict"> /// Whether or not to be strict when reading the <see cref="NuspecReader"/>. This should be <code>true</code> /// on initial ingestion but false when a package that has already been accepted is being processed.</param> /// <exception cref="PackagingException"> /// We default to use a strict version-check on dependency groups. /// When an invalid dependency version range is detected, a <see cref="PackagingException"/> will be thrown. /// </exception> public static PackageMetadata FromNuspecReader(NuspecReader nuspecReader, bool strict) { var strictNuspecReader = new StrictNuspecReader(nuspecReader.Xml); var metadataLookup = strictNuspecReader.GetMetadataLookup(); if (strict) { var duplicates = metadataLookup .Where(g => g.Count() > 1) .Select(g => g.Key) .ToList(); if (duplicates.Any()) { throw new PackagingException(string.Format( CoreStrings.Manifest_DuplicateMetadataElements, string.Join("', '", duplicates))); } } // Reject invalid metadata element names. Today this only rejects element names that collide with // properties generated downstream. var metadataKeys = new HashSet <string>(metadataLookup.Select(g => g.Key)); metadataKeys.IntersectWith(RestrictedMetadataElements); if (metadataKeys.Any()) { throw new PackagingException(string.Format( CoreStrings.Manifest_InvalidMetadataElements, string.Join("', '", metadataKeys.OrderBy(x => x)))); } // Reject non-boolean values for boolean metadata. foreach (var booleanName in BooleanMetadataElements) { foreach (var unparsedBoolean in metadataLookup[booleanName]) { if (!bool.TryParse(unparsedBoolean, out var parsedBoolean)) { throw new PackagingException(string.Format( CoreStrings.Manifest_InvalidBooleanMetadata, booleanName)); } } } return(new PackageMetadata( nuspecReader.GetMetadata().ToDictionary(kvp => kvp.Key, kvp => kvp.Value), nuspecReader.GetDependencyGroups(useStrictVersionCheck: strict), nuspecReader.GetFrameworkReferenceGroups(), nuspecReader.GetPackageTypes(), nuspecReader.GetMinClientVersion(), nuspecReader.GetRepositoryMetadata(), nuspecReader.GetLicenseMetadata())); }
private static void EnsurePackageCompatibility( NuGetProject nuGetProject, PackageIdentity packageIdentity, NuspecReader nuspecReader) { // Validate that the current version of NuGet satisfies the minVersion attribute specified in the .nuspec MinClientVersionUtility.VerifyMinClientVersion(nuspecReader); // Validate the package type. There must be zero package types or exactly one package // type that is one of the recognized package types. var packageTypes = nuspecReader.GetPackageTypes(); var identityString = $"{packageIdentity.Id} {packageIdentity.Version.ToNormalizedString()}"; if (packageTypes.Count > 1) { throw new PackagingException(string.Format( CultureInfo.CurrentCulture, Strings.MultiplePackageTypesNotSupported, identityString)); } else if (packageTypes.Count == 1) { var packageType = packageTypes[0]; var packageTypeString = packageType.Name; if (packageType.Version != PackageType.EmptyVersion) { packageTypeString += " " + packageType.Version; } var projectName = NuGetProject.GetUniqueNameOrName(nuGetProject); if (packageType == PackageType.Legacy || // Added for "quirks mode", but not yet fully implemented. packageType == PackageType.Dependency) // A package explicitly stated as a dependency. { // These types are always acceptable. } else if (nuGetProject is ProjectKNuGetProjectBase && packageType == PackageType.DotnetCliTool) { // ProjectKNuGetProjectBase projects are .NET Core (both "dotnet" and "dnx"). // .NET CLI tools are support for "dotnet" projects. The projects eventually // call into INuGetPackageManager, which is not implemented by NuGet. This code // will make the decision of how to install the .NET CLI tool package. } else { throw new PackagingException(string.Format( CultureInfo.CurrentCulture, Strings.UnsupportedPackageType, identityString, packageTypeString, projectName)); } } }
private static void EnsurePackageCompatibility( NuGetProject nuGetProject, PackageIdentity packageIdentity, NuspecReader nuspecReader) { // Validate that the current version of NuGet satisfies the minVersion attribute specified in the .nuspec MinClientVersionUtility.VerifyMinClientVersion(nuspecReader); // Validate the package type. There must be zero package types or exactly one package // type that is one of the recognized package types. var packageTypes = nuspecReader.GetPackageTypes(); var identityString = $"{packageIdentity.Id} {packageIdentity.Version.ToNormalizedString()}"; if (packageTypes.Count > 1) { throw new PackagingException(string.Format( CultureInfo.CurrentCulture, Strings.MultiplePackageTypesNotSupported, identityString)); } else if (packageTypes.Count == 1) { var packageType = packageTypes[0]; var packageTypeString = packageType.Name; if (packageType.Version != PackageType.EmptyVersion) { packageTypeString += " " + packageType.Version; } var projectName = NuGetProject.GetUniqueNameOrName(nuGetProject); if (packageType == PackageType.Legacy || // Added for "quirks mode", but not yet fully implemented. packageType == PackageType.Dependency) // A package explicitly stated as a dependency. { // These types are always acceptable. } else { throw new PackagingException(string.Format( CultureInfo.CurrentCulture, Strings.UnsupportedPackageType, identityString, packageTypeString, projectName)); } } }
private static List <PackageType> GetPackageTypes(NuspecReader nuspec) { var packageTypes = nuspec .GetPackageTypes() .Select(t => new PackageType { Name = t.Name, Version = t.Version.ToString() }) .ToList(); // Default to the standard "dependency" package type if no types were found. if (packageTypes.Count == 0) { packageTypes.Add(new PackageType { Name = NuGetPackageType.Dependency.Name, Version = NuGetPackageType.Dependency.Version.ToString(), }); } return(packageTypes); }
/// <summary> /// This method combines the logic used in restore operations to make a determination about the TFM supported by the package. /// We have curated a set of compatibility requirements for our needs in NuGet.org. The client logic can be found here: /// https://github.com/NuGet/NuGet.Client/blob/63255047fe7052cc33b763356ff995d9166f719e/src/NuGet.Core/NuGet.Commands/RestoreCommand/CompatibilityChecker.cs#L252-L294 /// https://github.com/NuGet/NuGet.Client/blob/63255047fe7052cc33b763356ff995d9166f719e/src/NuGet.Core/NuGet.Commands/RestoreCommand/CompatibilityChecker.cs#L439-L442 /// ...and our combination of these elements is below. /// The logic is essentially this: /// - Determine whether we're looking at a tools package. In this case we will use tools "pattern sets" (collections of file patterns /// defined in <see cref="ManagedCodeConventions" />) to assess which frameworks are targeted by the package. /// - If this isn't a tools package, we look for build-time, runtime, content and resource file patterns /// For added details on the various cases, see unit tests targeting this method. /// </summary> public virtual IEnumerable <NuGetFramework> GetSupportedFrameworks(NuspecReader nuspecReader, IList <string> packageFiles) { var supportedTFMs = Enumerable.Empty <NuGetFramework>(); if (packageFiles != null && packageFiles.Any() && nuspecReader != null) { // Setup content items for analysis var items = new ContentItemCollection(); items.Load(packageFiles); var runtimeGraph = new RuntimeGraph(); var conventions = new ManagedCodeConventions(runtimeGraph); // Let's test for tools packages first--they're a special case var groups = Enumerable.Empty <ContentItemGroup>(); var packageTypes = nuspecReader.GetPackageTypes(); if (packageTypes.Count == 1 && (packageTypes[0] == PackageType.DotnetTool || packageTypes[0] == PackageType.DotnetCliTool)) { // Only a package that is a tool package (and nothing else) will be matched against tools pattern set groups = items.FindItemGroups(conventions.Patterns.ToolsAssemblies); } else { // Gather together a list of pattern sets indicating the kinds of packages we wish to evaluate var patterns = new[] { conventions.Patterns.CompileRefAssemblies, conventions.Patterns.CompileLibAssemblies, conventions.Patterns.RuntimeAssemblies, conventions.Patterns.ContentFiles, conventions.Patterns.ResourceAssemblies, }; // Add MSBuild to this list, but we need to ensure we have package assets before they make the cut. // A series of files in the right places won't matter if there's no {id}.props|targets. var msbuildPatterns = new[] { conventions.Patterns.MSBuildFiles, conventions.Patterns.MSBuildMultiTargetingFiles, }; // We'll create a set of "groups" --these are content items which satisfy file pattern sets var standardGroups = patterns .SelectMany(p => items.FindItemGroups(p)); // Filter out MSBuild assets that don't match the package ID and append to groups we already have var packageId = nuspecReader.GetId(); var msbuildGroups = msbuildPatterns .SelectMany(p => items.FindItemGroups(p)) .Where(g => HasBuildItemsForPackageId(g.Items, packageId)); groups = standardGroups.Concat(msbuildGroups); } // Now that we have a collection of groups which have made it through the pattern set filter, let's transform them into TFMs supportedTFMs = groups .SelectMany(p => p.Properties) .Where(pair => pair.Key == ManagedCodeConventions.PropertyNames.TargetFrameworkMoniker) .Select(pair => pair.Value) .Cast <NuGetFramework>() .Distinct(); } return(supportedTFMs); }