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 UpdateFromPackage(PackageIndex index, string packagePath, bool filter = false) { string id; NuGetVersion version; IEnumerable <Version> assemblyVersions; IEnumerable <string> dllNames; using (var reader = new PackageArchiveReader(packagePath)) { var identity = reader.GetIdentity(); id = identity.Id; version = identity.Version; if (filter && !ShouldInclude(id)) { return; } var dlls = reader.GetFiles().Where(f => Path.GetExtension(f).Equals(".dll", StringComparison.OrdinalIgnoreCase)); assemblyVersions = dlls.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(); dllNames = dlls.Select(f => Path.GetFileNameWithoutExtension(f)).Distinct().ToArray(); } UpdateFromValues(index, id, version, assemblyVersions, dllNames); }
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(); }
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 ValidateSupport() { // 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; ExamineAssets("Compile", ContractName, fx.ToString(), compileAssetPaths, out hasCompileAsset, out hasCompilePlaceHolder); // resolve/test for each RID associated with this framework. foreach (string runtimeId in validateFramework.RuntimeIds) { string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}"; var runtimeAssetPaths = _resolver.ResolveRuntimeAssets(fx, runtimeId); bool hasRuntimeAsset, hasRuntimePlaceHolder; ExamineAssets("Runtime", ContractName, target, runtimeAssetPaths, out hasRuntimeAsset, out hasRuntimePlaceHolder); if (null == supportedVersion) { // Contract should not be supported on this platform. bool permitImplementation = HasSuppression(Suppression.PermitImplementation, target); if (hasCompileAsset && (hasRuntimeAsset & !permitImplementation)) { Log.LogError($"{ContractName} should not be supported on {target} but has both compile and runtime assets."); } else if (hasRuntimeAsset & !permitImplementation) { Log.LogError($"{ContractName} should not be supported on {target} but has runtime assets."); } if (hasRuntimePlaceHolder && hasCompilePlaceHolder) { Log.LogError($"{ContractName} should not be supported on {target} but has placeholders for both compile and runtime which will permit the package to install."); } } else { if (validateFramework.IsInbox) { if (!hasCompileAsset && !hasCompilePlaceHolder) { Log.LogError($"Framework {fx} should support {ContractName} inbox but was missing a placeholder for compile-time. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } else if (hasCompileAsset) { Log.LogError($"Framework {fx} should support {ContractName} inbox but contained a reference assemblies: {String.Join(", ", compileAssetPaths)}. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } if (!hasRuntimeAsset && !hasRuntimePlaceHolder) { Log.LogError($"Framework {fx} should support {ContractName} inbox but was missing a placeholder for run-time. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } else if (hasRuntimeAsset) { Log.LogError($"Framework {fx} should support {ContractName} inbox but contained a implementation assemblies: {String.Join(", ", runtimeAssetPaths)}. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } } else { Version referenceAssemblyVersion = null; if (!hasCompileAsset) { Log.LogError($"{ContractName} should be supported on {target} but has no compile assets."); } else { var referenceAssemblies = 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) { Version implementationVersion = _targetPathToPackageItem[implementationAssembly].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>"}."); } if (referenceAssemblyVersion != null && HasSuppression(Suppression.PermitHigherCompatibleImplementationVersion) ? !VersionUtility.IsCompatibleApiVersion(referenceAssemblyVersion, implementationVersion) : (implementationVersion != referenceAssemblyVersion)) { Log.LogError($"{ContractName} has mismatched compile ({referenceAssemblyVersion}) and runtime ({implementationVersion}) versions on {target}."); } 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; } } } } } } } // Set output items AllSupportedFrameworks = _frameworks.Values.Where(fx => fx.SupportedVersion != null).Select(fx => fx.ToItem()).OrderBy(i => i.ItemSpec).ToArray(); if (!String.IsNullOrEmpty(ValidationReport)) { WriteValidationReport(ValidationReport, _frameworks.Values); } }
public void GetLastStablePackagesFromIndex() { var index = PackageIndex.Load(PackageIndexes.Select(pi => pi.GetMetadata("FullPath"))); List <ITaskItem> lastStablePackages = new List <ITaskItem>(); foreach (var latestPackage in LatestPackages) { var packageId = latestPackage.ItemSpec; var versionString = latestPackage.GetMetadata("Version"); NuGetVersion nuGetVersion = null; if (versionString == null || !NuGetVersion.TryParse(versionString, out nuGetVersion)) { Log.LogMessage($"Could not parse version {versionString} for LatestPackage {packageId}, will use latest stable."); } var latestVersion = nuGetVersion?.Version; PackageInfo info; if (index.Packages.TryGetValue(packageId, out info)) { var candidateVersions = (latestVersion == null) ? info.StableVersions : info.StableVersions.Where(sv => VersionUtility.As4PartVersion(sv) < latestVersion); if (candidateVersions.Any()) { lastStablePackages.Add(CreateItem(latestPackage, candidateVersions.Max())); } } } LastStablePackages = lastStablePackages.ToArray(); }
public static Version GetLatestPatchStableVersionForRelease(this IEnumerable <Version> versions, int eraMajorVersion, int eraMinorVersion) { return(versions.Where(v => VersionUtility.As2PartVersion(v) == new Version(eraMajorVersion, eraMinorVersion)) .OrderByDescending(v => v) .FirstOrDefault()); }
public void HarvestFilesFromPackage() { string pathToPackage = Path.Combine(PackagesFolder, PackageId, PackageVersion); if (!Directory.Exists(pathToPackage)) { Log.LogError($"Cannot harvest from package {PackageId}/{PackageVersion} because {pathToPackage} does not exist."); return; } var livePackageFiles = Files.NullAsEmpty() .Where(f => IsIncludedExtension(f.GetMetadata("Extension"))) .Select(f => new PackageItem(f)) .ToDictionary(p => p.TargetPath, StringComparer.OrdinalIgnoreCase); var harvestedFiles = new List <ITaskItem>(); var removeFiles = new List <ITaskItem>(); // make sure we preserve refs that match desktop assemblies var liveDesktopDlls = livePackageFiles.Values.Where(pi => pi.IsDll && pi.TargetFramework?.Framework == FrameworkConstants.FrameworkIdentifiers.Net); var desktopRefVersions = liveDesktopDlls.Where(d => d.IsRef && d.Version != null).Select(d => d.Version); var desktopLibVersions = liveDesktopDlls.Where(d => !d.IsRef && d.Version != null).Select(d => d.Version); // find destkop assemblies with no matching lib. var preserveRefVersion = new HashSet <Version>(desktopLibVersions); preserveRefVersion.ExceptWith(desktopRefVersions); foreach (var extension in s_includedExtensions) { foreach (var packageFile in Directory.EnumerateFiles(pathToPackage, $"*{extension}", SearchOption.AllDirectories)) { string packagePath = packageFile.Substring(pathToPackage.Length + 1).Replace('\\', '/'); // determine if we should include this file from the harvested package // exclude if its specifically set for exclusion if (ShouldExclude(packagePath)) { Log.LogMessage(LogImportance.Low, $"Excluding package path {packagePath}."); continue; } var assemblyVersion = extension == s_dll?VersionUtility.GetAssemblyVersion(packageFile) : null; PackageItem liveFile = null; // determine if the harvested file clashes with a live built file // we'll prefer the harvested reference assembly so long as it's the same API // version and not required to match implementation 1:1 as is the case for desktop if (livePackageFiles.TryGetValue(packagePath, out liveFile)) { // Not a dll, not a ref, or not a versioned assembly: prefer live built file. if (extension != s_dll || !liveFile.IsRef || assemblyVersion == null || liveFile.Version == null) { Log.LogMessage(LogImportance.Low, $"Preferring live build of package path {packagePath} over the asset from last stable package."); continue; } // preserve desktop references to ensure bindingRedirects will work. if (liveFile.TargetFramework.Framework == FrameworkConstants.FrameworkIdentifiers.Net) { Log.LogMessage(LogImportance.Low, $"Preferring live build of package path {packagePath} over the asset from last stable package for desktop framework."); continue; } // as above but handle the case where a netstandard ref may be used for a desktop impl. if (preserveRefVersion.Contains(liveFile.Version)) { Log.LogMessage(LogImportance.Low, $"Preferring live build of package path {packagePath} over the asset from last stable package for desktop framework."); continue; } // preserve references with a different major.minor version if (assemblyVersion.Major != liveFile.Version.Major || assemblyVersion.Minor != liveFile.Version.Minor) { Log.LogMessage(LogImportance.Low, $"Preferring live build of reference {packagePath} over the asset from last stable package since the live build is a different API version."); continue; } // preserve references that specifically set the preserve metadata. bool preserve = false; bool.TryParse(liveFile.OriginalItem.GetMetadata("Preserve"), out preserve); if (preserve) { Log.LogMessage(LogImportance.Low, $"Preferring live build of reference {packagePath} over the asset from last stable package since Preserve was set to true."); continue; } // replace the live file with the harvested one, removing both the live file and PDB from the // file list. Log.LogMessage($"Using reference {packagePath} from last stable package {PackageId}/{PackageVersion} rather than the built reference {liveFile.SourcePath} since it is the same API version. Set <Preserve>true</Preserve> on {liveFile.SourceProject} if you'd like to avoid this.."); removeFiles.Add(liveFile.OriginalItem); PackageItem livePdbFile; if (livePackageFiles.TryGetValue(Path.ChangeExtension(packagePath, ".pdb"), out livePdbFile)) { removeFiles.Add(livePdbFile.OriginalItem); } } else { Log.LogMessage(LogImportance.Low, $"Including {packagePath} from last stable package {PackageId}/{PackageVersion}."); } var item = new TaskItem(packageFile); if (liveFile?.OriginalItem != null) { // preserve all the meta-data from the live file that was replaced. liveFile.OriginalItem.CopyMetadataTo(item); } else { var targetPath = Path.GetDirectoryName(packagePath).Replace('\\', '/'); item.SetMetadata("TargetPath", targetPath); string targetFramework = GetTargetFrameworkFromPackagePath(targetPath); item.SetMetadata("TargetFramework", targetFramework); // only harvest for non-portable frameworks, matches logic in packaging.targets. bool harvestDependencies = !targetFramework.StartsWith("portable-"); item.SetMetadata("HarvestDependencies", harvestDependencies.ToString()); item.SetMetadata("IsReferenceAsset", IsReferencePackagePath(targetPath).ToString()); } if (assemblyVersion != null) { // overwrite whatever metadata may have been copied from the live file. item.SetMetadata("AssemblyVersion", assemblyVersion.ToString()); } harvestedFiles.Add(item); } } HarvestedFiles = harvestedFiles.ToArray(); if (Files != null) { UpdatedFiles = Files.Except(removeFiles).ToArray(); } }
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)); if (!refFiles.Any()) { refFiles = thisPackageFiles.Where(f => f.TargetPath.StartsWith("lib/", StringComparison.OrdinalIgnoreCase)); } foreach (var refFileVersion in refFiles.Where(r => r.Version != null).Select(r => VersionUtility.As4PartVersion(r.Version)).Distinct()) { if (!info.AssemblyVersionInPackageVersion.ContainsKey(refFileVersion)) { 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."); } } }
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); } }
public void HarvestFilesFromPackage() { string pathToPackage = _packageFolders[PackageId]; var livePackageItems = Files.NullAsEmpty() .Where(f => IsIncludedExtension(f.GetMetadata("Extension"))) .Select(f => new PackageItem(f)); var livePackageFiles = new Dictionary <string, PackageItem>(StringComparer.OrdinalIgnoreCase); foreach (var livePackageItem in livePackageItems) { PackageItem existingitem; if (livePackageFiles.TryGetValue(livePackageItem.TargetPath, out existingitem)) { Log.LogError($"Package contains two files with same targetpath: {livePackageItem.TargetPath}, items:{livePackageItem.SourcePath}, {existingitem.SourcePath}."); } else { livePackageFiles.Add(livePackageItem.TargetPath, livePackageItem); } } var harvestedFiles = new List <ITaskItem>(); var removeFiles = new List <ITaskItem>(); // make sure we preserve refs that match desktop assemblies var liveDesktopDlls = livePackageFiles.Values.Where(pi => pi.IsDll && pi.TargetFramework?.Framework == FrameworkConstants.FrameworkIdentifiers.Net); var desktopRefVersions = liveDesktopDlls.Where(d => d.IsRef && d.Version != null).Select(d => d.Version); var desktopLibVersions = liveDesktopDlls.Where(d => !d.IsRef && d.Version != null).Select(d => d.Version); // find desktop assemblies with no matching lib. var preserveRefVersion = new HashSet <Version>(desktopLibVersions); preserveRefVersion.ExceptWith(desktopRefVersions); foreach (var extension in s_includedExtensions) { foreach (var packageFile in Directory.EnumerateFiles(pathToPackage, $"*{extension}", SearchOption.AllDirectories)) { string harvestPackagePath = packageFile.Substring(pathToPackage.Length + 1).Replace('\\', '/'); // determine if we should include this file from the harvested package // exclude if its specifically set for exclusion if (ShouldExclude(harvestPackagePath)) { Log.LogMessage(LogImportance.Low, $"Excluding package path {harvestPackagePath} because it is specifically excluded."); continue; } ITaskItem includeItem = null; if (!IncludeAllPaths && !ShouldInclude(harvestPackagePath, out includeItem)) { Log.LogMessage(LogImportance.Low, $"Excluding package path {harvestPackagePath} because it is not included in {nameof(PathsToInclude)}."); continue; } // allow for the harvested item to be moved var remappedTargetPath = includeItem?.GetMetadata("TargetPath"); if (!String.IsNullOrEmpty(remappedTargetPath)) { harvestPackagePath = remappedTargetPath + '/' + Path.GetFileName(packageFile); } List <string> targetPaths = new List <string>() { harvestPackagePath }; var additionalTargetPaths = includeItem?.GetMetadata("AdditionalTargetPath"); if (!String.IsNullOrEmpty(additionalTargetPaths)) { foreach (var additionalTargetPath in additionalTargetPaths.Split(';')) { if (!String.IsNullOrEmpty(additionalTargetPath)) { targetPaths.Add(additionalTargetPath + '/' + Path.GetFileName(packageFile)); } } } var assemblyVersion = extension == s_dll?VersionUtility.GetAssemblyVersion(packageFile) : null; PackageItem liveFile = null; foreach (var livePackagePath in targetPaths) { // determine if the harvested file clashes with a live built file // we'll prefer the harvested reference assembly so long as it's the same API // version and not required to match implementation 1:1 as is the case for desktop if (livePackageFiles.TryGetValue(livePackagePath, out liveFile)) { // Not a dll, or not a versioned assembly: prefer live built file. if (extension != s_dll || assemblyVersion == null || liveFile.Version == null) { // we don't consider this an error even for explicitly included files Log.LogMessage(LogImportance.Low, $"Preferring live build of package path {livePackagePath} over the asset from last stable package because the file is not versioned."); continue; } // not a ref if (!liveFile.IsRef) { LogSkipIncludedFile(livePackagePath, " because it is a newer implementation."); continue; } // preserve desktop references to ensure bindingRedirects will work. if (liveFile.TargetFramework.Framework == FrameworkConstants.FrameworkIdentifiers.Net) { LogSkipIncludedFile(livePackagePath, " because it is desktop reference."); continue; } // as above but handle the case where a netstandard ref may be used for a desktop impl. if (preserveRefVersion.Contains(liveFile.Version)) { LogSkipIncludedFile(livePackagePath, " because it will be applicable for desktop projects."); continue; } // preserve references with a different major.minor version if (assemblyVersion.Major != liveFile.Version.Major || assemblyVersion.Minor != liveFile.Version.Minor) { LogSkipIncludedFile(livePackagePath, $" because it is a different API version ( {liveFile.Version.Major}.{liveFile.Version.Minor} vs {assemblyVersion.Major}.{assemblyVersion.Minor}."); continue; } // preserve references that specifically set the preserve metadata. bool preserve = false; bool.TryParse(liveFile.OriginalItem.GetMetadata("Preserve"), out preserve); if (preserve) { LogSkipIncludedFile(livePackagePath, " because it set metadata Preserve=true."); continue; } // replace the live file with the harvested one, removing both the live file and PDB from the // file list. Log.LogMessage($"Using reference {livePackagePath} from last stable package {PackageId}/{PackageVersion} rather than the built reference {liveFile.SourcePath} since it is the same API version. Set <Preserve>true</Preserve> on {liveFile.SourceProject} if you'd like to avoid this.."); removeFiles.Add(liveFile.OriginalItem); PackageItem livePdbFile; if (livePackageFiles.TryGetValue(Path.ChangeExtension(livePackagePath, ".pdb"), out livePdbFile)) { removeFiles.Add(livePdbFile.OriginalItem); } } else { Log.LogMessage(LogImportance.Low, $"Including {livePackagePath} from last stable package {PackageId}/{PackageVersion}."); } var item = new TaskItem(packageFile); if (liveFile?.OriginalItem != null) { // preserve all the meta-data from the live file that was replaced. liveFile.OriginalItem.CopyMetadataTo(item); } else { if (includeItem != null) { includeItem.CopyMetadataTo(item); } var targetPath = Path.GetDirectoryName(livePackagePath).Replace('\\', '/'); item.SetMetadata("TargetPath", targetPath); string targetFramework = GetTargetFrameworkFromPackagePath(targetPath); item.SetMetadata("TargetFramework", targetFramework); // only harvest for non-portable frameworks, matches logic in packaging.targets. bool harvestDependencies = !targetFramework.StartsWith("portable-"); item.SetMetadata("HarvestDependencies", harvestDependencies.ToString()); item.SetMetadata("IsReferenceAsset", IsReferencePackagePath(targetPath).ToString()); } if (assemblyVersion != null) { // overwrite whatever metadata may have been copied from the live file. item.SetMetadata("AssemblyVersion", assemblyVersion.ToString()); } item.SetMetadata("HarvestedFrom", $"{PackageId}/{PackageVersion}/{harvestPackagePath}"); harvestedFiles.Add(item); } } } HarvestedFiles = harvestedFiles.ToArray(); if (_pathsNotIncluded != null) { foreach (var pathNotIncluded in _pathsNotIncluded) { Log.LogError($"Path '{pathNotIncluded}' was specified in {nameof(PathsToInclude)} but was not found in the package {PackageId}/{PackageVersion}."); } } if (Files != null) { UpdatedFiles = Files.Except(removeFiles).ToArray(); } }
private void UpdateFromValues(PackageIndex index, string id, NuGetVersion version, IEnumerable <Version> assemblyVersions, IEnumerable <string> dllNames) { PackageInfo info = GetOrCreatePackageInfo(index, id); var packageVersion = VersionUtility.As3PartVersion(version.Version); // if we have a stable version, add it to the stable versions list if (!version.IsPrerelease) { info.StableVersions.Add(packageVersion); } var assmVersions = new HashSet <Version>(assemblyVersions.NullAsEmpty().Where(v => v != null)); // add any new assembly versions info.AddAssemblyVersionsInPackage(assmVersions, packageVersion); // try to find an identity package to also add a mapping in the case this is a runtime package if (id.StartsWith("runtime.")) { foreach (var dllName in dllNames) { PackageInfo identityInfo; if (index.Packages.TryGetValue(dllName, out identityInfo)) { identityInfo.AddAssemblyVersionsInPackage(assmVersions, packageVersion); } } } // remove any assembly mappings which claim to be in this package version, but aren't in the assemblyList var orphanedAssemblyVersions = info.AssemblyVersionInPackageVersion .Where(pair => pair.Value == packageVersion && !assmVersions.Contains(pair.Key)) .Select(pair => pair.Key); if (orphanedAssemblyVersions.Any()) { // make sure these aren't coming from a runtime package. var runtimeAssemblyVersions = index.Packages .Where(p => p.Key.StartsWith("runtime.") && p.Key.EndsWith(id)) .SelectMany(p => p.Value.AssemblyVersionInPackageVersion) .Where(pair => pair.Value == packageVersion) .Select(pair => pair.Key); orphanedAssemblyVersions = orphanedAssemblyVersions.Except(runtimeAssemblyVersions); } foreach (var orphanedAssemblyVersion in orphanedAssemblyVersions.ToArray()) { info.AssemblyVersionInPackageVersion.Remove(orphanedAssemblyVersion); } // if no assemblies are present in this package nor were ever present if (assmVersions.Count == 0 && info.AssemblyVersionInPackageVersion.Count == 0) { // if in the native module map if (index.ModulesToPackages.Values.Any(p => p.Equals(id))) { // ensure the baseline is set info.BaselineVersion = packageVersion; } } }
public Version DetermineGenerationFromFile(string assemblyPath, ILog log, Version expectedVersion = null, IDictionary <string, string> candidateRefs = null, ICollection <string> ignoredRefs = null) { Version maxGeneration = null; if (_generationCache.TryGetValue(assemblyPath, out maxGeneration)) { log.LogMessage(LogImportance.Low, $"Generation of {assemblyPath} is {_generationIdentifier}{maxGeneration} from cache."); return(maxGeneration); } string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); if (ignoredRefs != null && ignoredRefs.Contains(assemblyName)) { return(null); } using (PEReader peReader = new PEReader(new FileStream(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))) { MetadataReader reader = peReader.GetMetadataReader(); AssemblyDefinition assemblyDef = reader.GetAssemblyDefinition(); assemblyName = reader.GetString(assemblyDef.Name); if (ignoredRefs != null && ignoredRefs.Contains(assemblyName)) { return(null); } // break a circular dependency int cycleIndex = _cycleStack.IndexOf(assemblyName); if (cycleIndex != -1) { log.LogError($"Cycle detected {string.Join(" > ", _cycleStack.Skip(cycleIndex).ToArray())} > {assemblyName}"); return(null); } _cycleStack.Add(assemblyName); if (expectedVersion != null && !VersionUtility.IsCompatibleApiVersion(expectedVersion, assemblyDef.Version)) { log.LogError($"Expected version {expectedVersion} for referenced assembly {assemblyPath} but found {assemblyDef.Version}"); } // first determine if the identity itself has a generation. maxGeneration = DetermineGenerationFromSeeds(assemblyName, assemblyDef.Version, log); foreach (var handle in reader.AssemblyReferences) { AssemblyReference reference = reader.GetAssemblyReference(handle); string referenceName = reader.GetString(reference.Name); if (ignoredRefs != null && ignoredRefs.Contains(referenceName)) { continue; } // indirect dependency: prefer the seed value if it exists since we only care about // reference assembly generation for indirect dependencies Version assemblyGeneration = DetermineGenerationFromSeeds(referenceName, reference.Version, log); if (assemblyGeneration == null) { string contractPath = null; if (candidateRefs != null && candidateRefs.TryGetValue(referenceName, out contractPath) && File.Exists(contractPath)) { // traverse the indirect dependencies recursively. assemblyGeneration = DetermineGenerationFromFile(contractPath, log, reference.Version, candidateRefs, ignoredRefs); } else { log.LogError($"Cannot resolve indirect dependency {referenceName}, Version={reference.Version}"); } } if (assemblyGeneration == null) { log.LogError($"Could not determine generation for {referenceName}, {reference.Version}. File did not exist and isn't a known mapping."); } if (maxGeneration == null) { maxGeneration = assemblyGeneration; } else if (assemblyGeneration != null && assemblyGeneration > maxGeneration) { maxGeneration = assemblyGeneration; } } } log.LogMessage(LogImportance.Low, $"Generation of {assemblyPath} is {_generationIdentifier}{maxGeneration}."); _generationCache.Add(assemblyPath, maxGeneration); _cycleStack.RemoveAt(_cycleStack.Count - 1); return(maxGeneration); }
private void ValidateSupport() { var runtimeFxSuppression = GetSuppressionValues(Suppression.PermitRuntimeTargetMonikerMismatch) ?? new HashSet <string>(); // validate support for each TxM:RID foreach (var validateFramework in _frameworks.Values) { NuGetFramework fx = validateFramework.Framework; Version supportedVersion = validateFramework.SupportedVersion; Target compileTarget; if (!_report.Targets.TryGetValue(fx.ToString(), out compileTarget)) { Log.LogError($"Missing target {fx.ToString()} from validation report {ReportFile}"); continue; } var compileAssetPaths = compileTarget.CompileAssets.Select(ca => ca.PackagePath); bool hasCompileAsset, hasCompilePlaceHolder; NuGetAssetResolver.ExamineAssets(Log, "Compile", ContractName, fx.ToString(), compileAssetPaths, out hasCompileAsset, out hasCompilePlaceHolder); // resolve/test for each RID associated with this framework. foreach (string runtimeId in validateFramework.RuntimeIds) { string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}"; Target runtimeTarget; if (!_report.Targets.TryGetValue(target, out runtimeTarget)) { Log.LogError($"Missing target {target} from validation report {ReportFile}"); continue; } var runtimeAssetPaths = runtimeTarget.RuntimeAssets.Select(ra => ra.PackagePath); bool hasRuntimeAsset, hasRuntimePlaceHolder; NuGetAssetResolver.ExamineAssets(Log, "Runtime", ContractName, target, runtimeAssetPaths, out hasRuntimeAsset, out hasRuntimePlaceHolder); if (null == supportedVersion) { // Contract should not be supported on this platform. bool permitImplementation = HasSuppression(Suppression.PermitImplementation, target); if (hasCompileAsset && (hasRuntimeAsset & !permitImplementation)) { Log.LogError($"{ContractName} should not be supported on {target} but has both compile and runtime assets."); } else if (hasRuntimeAsset & !permitImplementation) { Log.LogError($"{ContractName} should not be supported on {target} but has runtime assets."); } if (hasRuntimePlaceHolder && hasCompilePlaceHolder) { Log.LogError($"{ContractName} should not be supported on {target} but has placeholders for both compile and runtime which will permit the package to install."); } } else { if (validateFramework.IsInbox) { if (!hasCompileAsset && !hasCompilePlaceHolder) { Log.LogError($"Framework {fx} should support {ContractName} inbox but was missing a placeholder for compile-time. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } else if (hasCompileAsset) { Log.LogError($"Framework {fx} should support {ContractName} inbox but contained a reference assemblies: {String.Join(", ", compileAssetPaths)}. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } if (!hasRuntimeAsset && !hasRuntimePlaceHolder) { Log.LogError($"Framework {fx} should support {ContractName} inbox but was missing a placeholder for run-time. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } else if (hasRuntimeAsset) { Log.LogError($"Framework {fx} should support {ContractName} inbox but contained a implementation assemblies: {String.Join(", ", runtimeAssetPaths)}. You may need to add <InboxOnTargetFramework Include=\"{fx.GetShortFolderName()}\" /> to your project."); } } else { Version referenceAssemblyVersion = null; if (!hasCompileAsset) { Log.LogError($"{ContractName} should be supported on {target} but has no compile assets."); } else { var referenceAssemblies = compileTarget.CompileAssets.Where(ca => IsDll(ca.PackagePath)); if (referenceAssemblies.Count() > 1) { Log.LogError($"{ContractName} should only contain a single compile asset for {target}."); } foreach (var referenceAssembly in referenceAssemblies) { referenceAssemblyVersion = referenceAssembly.Version; if (!VersionUtility.IsCompatibleApiVersion(supportedVersion, referenceAssemblyVersion)) { Log.LogError($"{ContractName} should support API version {supportedVersion} on {target} but {referenceAssembly.LocalPath} was found to support {referenceAssemblyVersion?.ToString() ?? "<unknown version>"}."); } } } if (!hasRuntimeAsset) { if (HasSuppression(Suppression.PermitMissingImplementation, target)) { Log.LogMessage($"Suppressed: {ContractName} should be supported on {target} but has no runtime assets."); } else { // Contract should not be supported on this platform. Log.LogError($"{ContractName} should be supported on {target} but has no runtime assets."); } } else { var implementationAssemblies = runtimeTarget.RuntimeAssets.Where(ra => IsDll(ra.PackagePath)); Dictionary <string, PackageAsset> implementationFiles = new Dictionary <string, PackageAsset>(); foreach (var implementationAssembly in implementationAssemblies) { Version implementationVersion = implementationAssembly.Version; if (!VersionUtility.IsCompatibleApiVersion(supportedVersion, implementationVersion)) { Log.LogError($"{ContractName} should support API version {supportedVersion} on {target} but {implementationAssembly.LocalPath} was found to support {implementationVersion?.ToString() ?? "<unknown version>"}."); } // Previously we only permitted compatible mismatch if Suppression.PermitHigherCompatibleImplementationVersion was specified // this is a permitted thing on every framework but desktop (which requires exact match to ensure bindingRedirects exist) // Now make this the default, we'll check desktop, where it matters, more strictly if (referenceAssemblyVersion != null && !VersionUtility.IsCompatibleApiVersion(referenceAssemblyVersion, implementationVersion)) { Log.LogError($"{ContractName} has mismatched compile ({referenceAssemblyVersion}) and runtime ({implementationVersion}) versions on {target}."); } if (fx.Framework == FrameworkConstants.FrameworkIdentifiers.Net && referenceAssemblyVersion != null && !referenceAssemblyVersion.Equals(implementationVersion)) { Log.LogError($"{ContractName} has a higher runtime version ({implementationVersion}) than compile version ({referenceAssemblyVersion}) on .NET Desktop framework {target}. This will break bindingRedirects. If the live reference was replaced with a harvested reference you may need to set <Preserve>true</Preserve> on your reference assembly ProjectReference."); } string fileName = Path.GetFileName(implementationAssembly.PackagePath); if (implementationFiles.ContainsKey(fileName)) { Log.LogError($"{ContractName} includes both {implementationAssembly.LocalPath} and {implementationFiles[fileName].LocalPath} an on {target} which have the same name and will clash when both packages are used."); } else { implementationFiles[fileName] = implementationAssembly; } if (!implementationAssembly.TargetFramework.Equals(fx) && !runtimeFxSuppression.Contains(fx.ToString())) { // the selected asset wasn't an exact framework match, let's see if we have an exact match in any other runtime asset. var matchingFxAssets = _report.UnusedAssets.Where(i => i.TargetFramework != null && i.TargetFramework.Equals(fx) && // exact framework // Same file Path.GetFileName(i.PackagePath).Equals(fileName, StringComparison.OrdinalIgnoreCase) && // Is implementation (i.PackagePath.StartsWith("lib") || i.PackagePath.StartsWith("runtimes")) && // is not the same source file as was already selected i.LocalPath != implementationAssembly.LocalPath); if (matchingFxAssets.Any()) { Log.LogError($"When targeting {target} {ContractName} will use {implementationAssembly.LocalPath} which targets {implementationAssembly.TargetFramework.GetShortFolderName()} but {String.Join(";", matchingFxAssets.Select(i => i.PackagePath))} targets {fx.GetShortFolderName()} specifically."); } } } } } } } } // Set output items AllSupportedFrameworks = _frameworks.Values.Where(fx => fx.SupportedVersion != null).Select(fx => fx.ToItem()).OrderBy(i => i.ItemSpec).ToArray(); }
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."); } }
private void ValidateIndex() { if (SkipIndexCheck) { return; } if (PackageIndexes == null || PackageIndexes.Length == 0) { return; } var index = PackageIndex.Load(PackageIndexes.Select(pi => pi.GetMetadata("FullPath"))); PackageInfo info; if (!index.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 allDlls = _report.Targets.Values.SelectMany(t => t.CompileAssets.NullAsEmpty().Concat(t.RuntimeAssets.NullAsEmpty())); var allAssemblies = allDlls.Where(f => f.Version != null); var assemblyVersions = new HashSet <Version>(allAssemblies.Select(f => VersionUtility.As4PartVersion(f.Version))); var thisPackageVersion = VersionUtility.As3PartVersion(NuGetVersion.Parse(PackageVersion).Version); foreach (var fileVersion in assemblyVersions) { Version packageVersion; // determine if we're missing a mapping for this package if (!info.AssemblyVersionInPackageVersion.TryGetValue(fileVersion, out packageVersion)) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} is missing an assembly version entry for {fileVersion} 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(packageVersion) && packageVersion != thisPackageVersion) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} indicates that assembly version {fileVersion} is contained in non-stable package version {packageVersion} which differs from this package version {thisPackageVersion}."); } } } var orphanedAssemblyVersions = info.AssemblyVersionInPackageVersion .Where(pair => pair.Value == thisPackageVersion && !assemblyVersions.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."); } // if no assemblies are present in this package nor were ever present if (assemblyVersions.Count == 0 && info.AssemblyVersionInPackageVersion.Count == 0) { // if in the native module map if (index.ModulesToPackages.Values.Any(p => p.Equals(PackageId))) { // ensure the baseline is set if (info.BaselineVersion != thisPackageVersion) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} is missing an baseline entry(s) for native module {PackageId}. Please run /t:UpdatePackageIndex on this project to commit an update."); } } else { // not in the native module map, see if any of the modules in this package are present // (with a different package, as would be the case for runtime-specific packages) var moduleNames = allDlls.Select(d => Path.GetFileNameWithoutExtension(d.LocalPath)); var missingModuleNames = moduleNames.Where(m => !index.ModulesToPackages.ContainsKey(m)); if (missingModuleNames.Any()) { Log.LogError($"PackageIndex from {String.Join(", ", PackageIndexes.Select(i => i.ItemSpec))} is missing ModulesToPackages entry(s) for {String.Join(", ", missingModuleNames)} to package {PackageId}. Please add a an entry for the appropriate package."); } } } }
private void UpdateFromValues(PackageIndex index, string id, NuGetVersion version, IEnumerable <Version> assemblyVersions, IEnumerable <string> dllNames) { PackageInfo info = GetOrCreatePackageInfo(index, id); if (UpdateStablePackageInfo) { try { IEnumerable <Version> allStableVersions = NuGetUtility.GetAllVersionsForPackageId(id, includePrerelease: false, includeUnlisted: false, Log, CancellationToken.None); info.StableVersions.AddRange(allStableVersions); } catch (NuGetProtocolException) { Log.LogWarning("Failed fetching stable nuget package versions from one or more of your feeds. Make sure you are connected to the internet and that all your feeds are reachable."); } } var packageVersion = VersionUtility.As3PartVersion(version.Version); // if we have a stable version, add it to the stable versions list if (!version.IsPrerelease) { info.StableVersions.Add(packageVersion); } var assmVersions = new HashSet <Version>(assemblyVersions.NullAsEmpty().Where(v => v != null)); // add any new assembly versions info.AddAssemblyVersionsInPackage(assmVersions, packageVersion); // try to find an identity package to also add a mapping in the case this is a runtime package if (id.StartsWith("runtime.")) { foreach (var dllName in dllNames) { PackageInfo identityInfo; if (index.Packages.TryGetValue(dllName, out identityInfo)) { identityInfo.AddAssemblyVersionsInPackage(assmVersions, packageVersion); } } } // remove any assembly mappings which claim to be in this package version, but aren't in the assemblyList var orphanedAssemblyVersions = info.AssemblyVersionInPackageVersion .Where(pair => pair.Value == packageVersion && !assmVersions.Contains(pair.Key)) .Select(pair => pair.Key); if (orphanedAssemblyVersions.Any()) { // make sure these aren't coming from a runtime package. var runtimeAssemblyVersions = index.Packages .Where(p => p.Key.StartsWith("runtime.") && p.Key.EndsWith(id)) .SelectMany(p => p.Value.AssemblyVersionInPackageVersion) .Where(pair => pair.Value == packageVersion) .Select(pair => pair.Key); orphanedAssemblyVersions = orphanedAssemblyVersions.Except(runtimeAssemblyVersions); } foreach (var orphanedAssemblyVersion in orphanedAssemblyVersions.ToArray()) { info.AssemblyVersionInPackageVersion.Remove(orphanedAssemblyVersion); } // if no assemblies are present in this package nor were ever present if (assmVersions.Count == 0 && info.AssemblyVersionInPackageVersion.Count == 0) { // if in the native module map if (index.ModulesToPackages.Values.Any(p => p.Equals(id))) { // ensure the baseline is set info.BaselineVersion = packageVersion; } } }