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;
                        }
                    }
                }
            }
        }
Beispiel #4
0
        /// <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;
                    }
                }
            }
        }