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; ExamineAssets("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; 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 (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>"}."); } 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; } 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 override bool Execute() { LoadFiles(); LoadFrameworks(); var report = new PackageReport() { Id = PackageId, Version = PackageVersion, SupportedFrameworks = new Dictionary<string, string>() }; string package = $"{PackageId}/{PackageVersion}"; foreach (var framework in _frameworks.OrderBy(f => f.Key.ToString())) { var fx = framework.Key; var runtimeIds = framework.Value; var compileAssets = _resolver.ResolveCompileAssets(fx, PackageId); bool hasCompileAsset, hasCompilePlaceHolder; NuGetAssetResolver.ExamineAssets(Log, "Compile", package, fx.ToString(), compileAssets, out hasCompileAsset, out hasCompilePlaceHolder); MarkUsed(compileAssets); // start by making sure it has some asset available for compile var isSupported = hasCompileAsset || hasCompilePlaceHolder; if (runtimeIds.All(rid => !String.IsNullOrEmpty(rid))) { // Add Framework only (compile) target if all RIDs are non-empty. // This acts as a compile target for a framework that requires a RID for runtime. var reportTarget = new Target() { Framework = fx.ToString(), RuntimeID = null, CompileAssets = compileAssets.Select(c => GetPackageAssetFromTargetPath(c)).ToArray() }; report.Targets.Add(fx.ToString(), reportTarget); } foreach (var runtimeId in runtimeIds) { string target = String.IsNullOrEmpty(runtimeId) ? fx.ToString() : $"{fx}/{runtimeId}"; var runtimeAssets = _resolver.ResolveRuntimeAssets(fx, runtimeId); bool hasRuntimeAsset, hasRuntimePlaceHolder; NuGetAssetResolver.ExamineAssets(Log, "Runtime", package, target, runtimeAssets, out hasRuntimeAsset, out hasRuntimePlaceHolder); MarkUsed(runtimeAssets); if (!FrameworkUtilities.IsGenerationMoniker(fx) && !fx.IsPCL) { // only look at runtime assets for runnable frameworks. isSupported &= hasCompileAsset == hasRuntimeAsset; isSupported &= hasCompilePlaceHolder == hasRuntimePlaceHolder; } var nativeAssets = _resolver.ResolveNativeAssets(fx, runtimeId); MarkUsed(nativeAssets); var reportTarget = new Target() { Framework = fx.ToString(), RuntimeID = runtimeId, CompileAssets = compileAssets.Select(c => GetPackageAssetFromTargetPath(c)).ToArray(), RuntimeAssets = runtimeAssets.Select(r => GetPackageAssetFromTargetPath(r)).ToArray(), NativeAssets = nativeAssets.Select(n => GetPackageAssetFromTargetPath(n)).ToArray() }; report.Targets[target] = reportTarget; } if (isSupported) { // Find version // first try the resolved compile asset for this package var refAssm = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r))?.Substring(PackageId.Length + 1); if (refAssm == null) { // if we didn't have a compile asset it means this framework is supported inbox with a placeholder // resolve the assets without placeholders to pick up the netstandard reference assembly. compileAssets = _resolverWithoutPlaceholders.ResolveCompileAssets(fx); refAssm = compileAssets.FirstOrDefault(r => !NuGetAssetResolver.IsPlaceholder(r)); } var version = "unknown"; if (refAssm != null) { version = _targetPathToPackageItem[AggregateNuGetAssetResolver.AsPackageSpecificTargetPath(PackageId, refAssm)].Version?.ToString() ?? version; } report.SupportedFrameworks.Add(fx.ToString(), version); } } report.UnusedAssets = _unusedTargetPaths.Select(tp => GetPackageAssetFromTargetPath(tp)).ToArray(); report.Save(ReportFile); return !Log.HasLoggedErrors; }