/// <summary> /// Fetches the right target from the targets section in a lock file, or attempts to find a "best match" if allowed. The "best match" logic /// is there to allow a design time build for the IDE to generally work even if something isn't quite right. Throws an exception /// if either the preferred isn't there and fallbacks aren't allowed, or fallbacks are allowed but nothing at all could be found. /// </summary> /// <param name="lockFile">The lock file JSON.</param> /// <param name="needsRuntimeIdentifier">Whether we must find targets that include the runtime identifier or one without the runtime identifier.</param> private JObject GetTargetOrAttemptFallback(JObject lockFile, bool needsRuntimeIdentifier) { var targets = (JObject)lockFile["targets"]; foreach (var preferredTargetMoniker in TargetMonikers) { var preferredTargetMonikerWithOptionalRuntimeIdentifier = GetTargetMonikerWithOptionalRuntimeIdentifier(preferredTargetMoniker, needsRuntimeIdentifier); var target = (JObject)targets[preferredTargetMonikerWithOptionalRuntimeIdentifier]; if (target != null) { return(target); } } // If we need a runtime identifier, let's see if we have the framework targeted. If we do, // then we can give a better error message. bool onlyNeedsRuntimeInProjectJson = false; if (needsRuntimeIdentifier) { foreach (var targetMoniker in TargetMonikers) { var targetMonikerWithoutRuntimeIdentifier = GetTargetMonikerWithOptionalRuntimeIdentifier(targetMoniker, needsRuntimeIdentifier: false); if (targets[targetMonikerWithoutRuntimeIdentifier] != null) { // We do have a TXM being targeted, so we just are missing the runtime onlyNeedsRuntimeInProjectJson = true; break; } } } if (onlyNeedsRuntimeInProjectJson) { GiveErrorForMissingRuntimeIdentifier(); } else { ThrowExceptionIfNotAllowingFallback(nameof(Strings.MissingFramework), TargetMonikers.First().ItemSpec); } // If we're still here, that means we're allowing fallback, so let's try foreach (var fallback in TargetMonikers) { var target = (JObject)targets[GetTargetMonikerWithOptionalRuntimeIdentifier(fallback, needsRuntimeIdentifier: false)]; if (target != null) { return(target); } } // Anything goes var enumerableTargets = targets.Cast <KeyValuePair <string, JToken> >(); var firstTarget = (JObject)enumerableTargets.FirstOrDefault().Value; if (firstTarget == null) { throw new ExceptionFromResource(nameof(Strings.NoTargetsInLockFile)); } return(firstTarget); }
/// <summary> /// Generates a table in markdown that lists the API version supported by /// various packages at all levels of NETStandard. /// </summary> /// <returns></returns> public override bool Execute() { if (PackageAssets == null || PackageAssets.Length == 0) { Log.LogError("PackageAssets argument must be specified"); return(false); } if (TargetMonikers == null || TargetMonikers.Length == 0) { Log.LogError("TargetMoniker argument must be specified"); return(false); } NuGetFramework[] compileFxs = TargetMonikers.Select(fx => NuGetFramework.Parse(fx)).ToArray(); NuGetFramework[] runtimeFxs = compileFxs; if (RuntimeTargetMonikers != null && RuntimeTargetMonikers.Length > 0) { runtimeFxs = RuntimeTargetMonikers.Select(fx => NuGetFramework.Parse(fx)).ToArray(); } LoadFiles(); CompileAssets = null; // find the best framework foreach (var compileFx in compileFxs) { var compileAssets = _resolver.ResolveCompileAssets(compileFx); if (compileAssets.Any()) { CompileAssets = compileAssets.Where(ca => !NuGetAssetResolver.IsPlaceholder(ca)) .Select(ca => PackageItemAsResolvedAsset(_targetPathToPackageItem[ca])) .ToArray(); Log.LogMessage($"Resolved compile assets from {compileFx.ToString()}: {String.Join(";", CompileAssets.Select(c => c.ItemSpec))}"); break; } } if (CompileAssets == null) { Log.LogError($"Could not locate compile assets for any of the frameworks {String.Join(";", compileFxs.Select(fx => fx.ToString()))}"); } RuntimeAssets = null; foreach (var runtimeFx in runtimeFxs) { var runtimeAssets = _resolver.ResolveRuntimeAssets(runtimeFx, TargetRuntime); if (runtimeAssets.Any()) { RuntimeAssets = runtimeAssets.Where(ra => !NuGetAssetResolver.IsPlaceholder(ra)) .SelectMany(ra => PackageItemAndSymbolsAsResolvedAsset(_targetPathToPackageItem[ra])) .ToArray(); Log.LogMessage($"Resolved runtime assets from {runtimeFx.ToString()}: {String.Join(";", RuntimeAssets.Select(r => r.ItemSpec))}"); break; } } if (RuntimeAssets == null) { Log.LogError($"Could not locate runtime assets for any of the frameworks {String.Join(";", runtimeFxs.Select(fx => fx.ToString()))}"); } return(!Log.HasLoggedErrors); }
/// <summary> /// Fetches the right target from the targets section in a lock file, or attempts to find a "best match" if allowed. The "best match" logic /// is there to allow a design time build for the IDE to generally work even if something isn't quite right. Throws an exception /// if either the preferred isn't there and fallbacks aren't allowed, or fallbacks are allowed but nothing at all could be found. /// </summary> /// <param name="lockFile">The lock file JSON.</param> /// <param name="needsRuntimeIdentifier">Whether we must find targets that include the runtime identifier or one without the runtime identifier.</param> private JObject GetTargetOrAttemptFallback(JObject lockFile, bool needsRuntimeIdentifier) { var targets = (JObject)lockFile["targets"]; foreach (var preferredTargetMoniker in TargetMonikers) { var preferredTargetMonikerWithOptionalRuntimeIdentifier = GetTargetMonikerWithOptionalRuntimeIdentifier(preferredTargetMoniker, needsRuntimeIdentifier); var target = (JObject)targets[preferredTargetMonikerWithOptionalRuntimeIdentifier]; if (target != null) { return(target); } } var preferredForErrorMessages = GetTargetMonikerWithOptionalRuntimeIdentifier(TargetMonikers.First(), needsRuntimeIdentifier); if (!AllowFallbackOnTargetSelection) { // If we're not falling back then abort the build throw new ExceptionFromResource("MissingEntryInLockFile", preferredForErrorMessages); } // We are allowing fallback, so we'll still give a warning but allow us to continue // In production ResolveNuGetPackageAssets, this call is LogWarningFromResources. // In our current use in dotnet\buildtools, we rely on the fallback behavior, so we just log // this as a message. Log.LogMessageFromResources("MissingEntryInLockFile", preferredForErrorMessages); foreach (var fallback in TargetMonikers) { var target = (JObject)targets[GetTargetMonikerWithOptionalRuntimeIdentifier(fallback, needsRuntimeIdentifier: false)]; if (target != null) { return(target); } } // Anything goes var enumerableTargets = targets.Cast <KeyValuePair <string, JToken> >(); var firstTarget = (JObject)enumerableTargets.FirstOrDefault().Value; if (firstTarget == null) { throw new ExceptionFromResource("NoTargetsInLockFile"); } return(firstTarget); }