public override bool Execute() { if (SuggestedRedirects == null || SuggestedRedirects.Length == 0) { Log.LogMessageFromResources("GenerateBindingRedirects.NoSuggestedRedirects"); OutputAppConfigFile = null; return(true); } var redirects = ParseSuggestedRedirects(); var doc = LoadAppConfig(AppConfigFile); if (doc == null) { return(false); } XElement runtimeNode = doc.Root .Nodes() .OfType <XElement>() .FirstOrDefault(e => e.Name.LocalName == "runtime"); if (runtimeNode == null) { runtimeNode = new XElement("runtime"); doc.Root.Add(runtimeNode); } else { UpdateExistingBindingRedirects(runtimeNode, redirects); } var ns = XNamespace.Get("urn:schemas-microsoft-com:asm.v1"); var redirectNodes = from redirect in redirects select new XElement( ns + "assemblyBinding", new XElement( ns + "dependentAssembly", new XElement( ns + "assemblyIdentity", new XAttribute("name", redirect.Key.Name), new XAttribute("publicKeyToken", ResolveAssemblyReference.ByteArrayToString(redirect.Key.GetPublicKeyToken())), new XAttribute("culture", String.IsNullOrEmpty(redirect.Key.CultureName) ? "neutral" : redirect.Key.CultureName)), new XElement( ns + "bindingRedirect", new XAttribute("oldVersion", "0.0.0.0-" + redirect.Value), new XAttribute("newVersion", redirect.Value)))); runtimeNode.Add(redirectNodes); var writeOutput = true; if (FileSystems.Default.FileExists(OutputAppConfigFile.ItemSpec)) { try { var outputDoc = LoadAppConfig(OutputAppConfigFile); if (outputDoc.ToString() == doc.ToString()) { writeOutput = false; } } catch (System.Xml.XmlException) { writeOutput = true; } } if (AppConfigFile != null) { AppConfigFile.CopyMetadataTo(OutputAppConfigFile); } else { OutputAppConfigFile.SetMetadata(ItemMetadataNames.targetPath, TargetName); } if (writeOutput) { using (var stream = FileUtilities.OpenWrite(OutputAppConfigFile.ItemSpec, false)) { doc.Save(stream); } } return(!Log.HasLoggedErrors); }
private static bool ByteArrayMatchesString(Byte[] a, string s) { return(!String.Equals(ResolveAssemblyReference.ByteArrayToString(a), s, StringComparison.OrdinalIgnoreCase)); }
private void UpdateExistingBindingRedirects(XElement runtimeNode, IDictionary <AssemblyName, string> redirects) { ErrorUtilities.VerifyThrow(runtimeNode != null, "This should not be called if the \"runtime\" node is missing."); var assemblyBindingNodes = runtimeNode.Nodes() .OfType <XElement>() .Where(e => e.Name.LocalName == "assemblyBinding"); foreach (var assemblyBinding in assemblyBindingNodes) { // Each assemblyBinding section could have more than one dependentAssembly elements var dependentAssemblies = assemblyBinding.Nodes() .OfType <XElement>() .Where(e => e.Name.LocalName == "dependentAssembly"); foreach (var dependentAssembly in dependentAssemblies) { var assemblyIdentity = dependentAssembly .Nodes() .OfType <XElement>() .FirstOrDefault(e => e.Name.LocalName == "assemblyIdentity"); if (assemblyIdentity == null) { // Due to MSDN documentation (https://msdn.microsoft.com/en-us/library/0ash1ksb(v=vs.110).aspx) // assemblyIdentity is required subelement. Emitting a warning if it's not there. Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MissingNode", "dependentAssembly", "assemblyBinding"); continue; } var bindingRedirect = dependentAssembly .Nodes() .OfType <XElement>() .FirstOrDefault(e => e.Name.LocalName == "bindingRedirect"); if (bindingRedirect == null) { // Due to xsd schema and MSDN documentation bindingRedirect is not required subelement. // Just skipping it without a warning. continue; } var name = assemblyIdentity.Attribute("name"); var publicKeyToken = assemblyIdentity.Attribute("publicKeyToken"); if (name == null || publicKeyToken == null) { continue; } var nameValue = name.Value; var publicKeyTokenValue = publicKeyToken.Value; var culture = assemblyIdentity.Attribute("culture"); var cultureValue = culture?.Value ?? String.Empty; var oldVersionAttribute = bindingRedirect.Attribute("oldVersion"); var newVersionAttribute = bindingRedirect.Attribute("newVersion"); if (oldVersionAttribute == null || newVersionAttribute == null) { continue; } var oldVersionRange = oldVersionAttribute.Value.Split(MSBuildConstants.HyphenChar); if (oldVersionRange.Length == 0 || oldVersionRange.Length > 2) { continue; } var oldVerStrLow = oldVersionRange[0]; var oldVerStrHigh = oldVersionRange[oldVersionRange.Length == 1 ? 0 : 1]; if (!Version.TryParse(oldVerStrLow, out Version oldVersionLow)) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MalformedVersionNumber", oldVerStrLow); continue; } if (!Version.TryParse(oldVerStrHigh, out Version oldVersionHigh)) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MalformedVersionNumber", oldVerStrHigh); continue; } // We cannot do a simply dictionary lookup here because we want to allow relaxed "culture" matching: // we consider it a match if the existing binding redirect doesn't specify culture in the assembly identity. foreach (var entry in redirects) { if (IsMatch(entry.Key, nameValue, cultureValue, publicKeyTokenValue)) { string maxVerStr = entry.Value; var maxVersion = new Version(maxVerStr); if (maxVersion >= oldVersionLow) { // Update the existing binding redirect to the RAR suggested one. var newName = entry.Key.Name; var newCulture = entry.Key.CultureName; var newPublicKeyToken = entry.Key.GetPublicKeyToken(); var newProcessorArchitecture = entry.Key.ProcessorArchitecture; var attributes = new List <XAttribute>(4) { new XAttribute("name", newName), new XAttribute( "culture", String.IsNullOrEmpty(newCulture) ? "neutral" : newCulture), new XAttribute( "publicKeyToken", ResolveAssemblyReference.ByteArrayToString(newPublicKeyToken)) }; if (newProcessorArchitecture != 0) { attributes.Add(new XAttribute("processorArchitecture", newProcessorArchitecture.ToString())); } assemblyIdentity.ReplaceAttributes(attributes); oldVersionAttribute.Value = "0.0.0.0-" + (maxVersion >= oldVersionHigh ? maxVerStr : oldVerStrHigh); newVersionAttribute.Value = maxVerStr; redirects.Remove(entry.Key); Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.OverlappingBindingRedirect", entry.Key.ToString(), bindingRedirect.ToString()); } break; } } } } }
/// <summary> /// Going through all the binding redirects in the runtime node, if anyone overlaps with a RAR suggested redirect, /// we update the existing redirect and output warning. /// </summary> private void UpdateExistingBindingRedirects(XElement runtimeNode, IDictionary <AssemblyName, string> redirects) { ErrorUtilities.VerifyThrow(runtimeNode != null, "This should not be called if the \"runtime\" node is missing."); var assemblyBindingNodes = runtimeNode.Nodes() .OfType <XElement>() .Where(e => e.Name.LocalName == "assemblyBinding"); foreach (var assemblyBinding in assemblyBindingNodes) { var dependentAssembly = assemblyBinding.Nodes() .OfType <XElement>() .FirstOrDefault(); if (dependentAssembly == null) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MissingNode", "dependentAssembly", "assemblyBinding"); continue; } var assemblyIdentity = dependentAssembly.Nodes() .OfType <XElement>() .Where(e => e.Name.LocalName == "assemblyIdentity") .FirstOrDefault(); if (assemblyIdentity == null) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MissingNode", "assemblyIdentity", "dependentAssembly"); continue; } var bindingRedirect = dependentAssembly.Nodes() .OfType <XElement>() .Where(e => e.Name.LocalName == "bindingRedirect") .FirstOrDefault(); if (bindingRedirect == null) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MissingNode", "bindingRedirect", "dependentAssembly"); continue; } var name = assemblyIdentity.Attribute("name"); var nameValue = name.Value; var publicKeyToken = assemblyIdentity.Attribute("publicKeyToken"); var publicKeyTokenValue = publicKeyToken.Value; var culture = assemblyIdentity.Attribute("culture"); var cultureValue = culture == null ? String.Empty : culture.Value; if (name == null || publicKeyToken == null) { continue; } var oldVersionAttribute = bindingRedirect.Attribute("oldVersion"); var newVersionAttribute = bindingRedirect.Attribute("newVersion"); if (oldVersionAttribute == null || newVersionAttribute == null) { continue; } var oldVersionRange = oldVersionAttribute.Value.Split('-'); if (oldVersionRange == null || oldVersionRange.Length == 0 || oldVersionRange.Length > 2) { continue; } var oldVerStrLow = oldVersionRange[0]; var oldVerStrHigh = oldVersionRange[oldVersionRange.Length == 1 ? 0 : 1]; Version oldVersionLow, oldVersionHigh; if (!Version.TryParse(oldVerStrLow, out oldVersionLow)) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MalformedVersionNumber", oldVerStrLow); continue; } if (!Version.TryParse(oldVerStrHigh, out oldVersionHigh)) { Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.MalformedVersionNumber", oldVerStrHigh); continue; } // We cannot do a simply dictionary lookup here because we want to allow relaxed "culture" matching: // we consider it a match if the existing binding redirect doesn't specify culture in the assembly identity. foreach (var entry in redirects) { if (IsMatch(entry.Key, nameValue, cultureValue, publicKeyTokenValue)) { string maxVerStr = entry.Value; var maxVersion = new Version(maxVerStr); if (maxVersion >= oldVersionLow) { // Update the existing binding redirect to the RAR suggested one. var newName = entry.Key.Name; var newCulture = entry.Key.CultureName; var newPublicKeyToken = entry.Key.GetPublicKeyToken(); var newProcessorArchitecture = entry.Key.ProcessorArchitecture; var attributes = new List <XAttribute>(4); attributes.Add(new XAttribute("name", newName)); attributes.Add(new XAttribute("culture", String.IsNullOrEmpty(newCulture) ? "neutral" : newCulture)); attributes.Add(new XAttribute("publicKeyToken", ResolveAssemblyReference.ByteArrayToString(newPublicKeyToken))); if (newProcessorArchitecture != 0) { attributes.Add(new XAttribute("processorArchitecture", newProcessorArchitecture.ToString())); } assemblyIdentity.ReplaceAttributes(attributes); oldVersionAttribute.Value = "0.0.0.0-" + (maxVersion >= oldVersionHigh ? maxVerStr : oldVerStrHigh); newVersionAttribute.Value = maxVerStr; redirects.Remove(entry.Key); Log.LogWarningWithCodeFromResources("GenerateBindingRedirects.OverlappingBindingRedirect", entry.Key.ToString(), bindingRedirect.ToString()); } break; } } } }