Skip to content

Commit

Permalink
Incorporated New BindingRedirect Generation (#36)
Browse files Browse the repository at this point in the history
This feature offers consuming projects to choose from "Preview", "Overwrite", and "None" behaviours
  • Loading branch information
leusbj authored Apr 8, 2022
1 parent 2ea1062 commit 21e499d
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 127 deletions.
60 changes: 16 additions & 44 deletions docs/Binding_Redirects/Autogenerating-Binding-Redirects.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,30 @@
# Autogenerating Binding Redirects

The template will, on compile, generate a copy of the current `web.config` with any required binding redirects added.
This file is written to the `bin` folder with the name `AssemblyName.dll.config` where `AssemblyName` is the name of your assembly.
The template will, on compile, discover any required binding redirects.

You can set the `AssemblyName` by adding
```xml
<AssemblyName>MyAssemblyName</AssemblyName>
```
to your project file as normal.
You may see a build warning `Warning MSB3276 Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.`, and one of three possible actions can be taken with these discovered Binding Redirections

If you get the error `Warning MSB3276 Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190. <project name here>`, then you can manually copy the
## None
If you Would like No Action to be taken with these suggestions, set the `GeneratedBindingRedirectionsAction` to `None` in your project file.
```xml
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
...
</assemblyBinding>
</runtime>
<PropertyGroup>
<GeneratedBindingRedirectsAction>None</GeneratedBindingRedirectsAction>
</PropertyGroup>
```
section from the generated `.dll.config` file to your web.config file, replacing the existing `assemblyBinding` node.

Alternatively, since the `.dll.config` file is based on your `web.config` file, you can just overwrite your `web.config` file with the `.dll.config` file.

If you want this to happen automatically, you can add the following to your project file.
## Preview
You can also choose to `Preview` and generate a file `Web.BindingRedirects.config` that contains the suggested Binding Redirects.
```xml
<PropertyGroup>
<OverwriteAppConfigWithBindingRedirects>true</OverwriteAppConfigWithBindingRedirects>
<GeneratedBindingRedirectsAction>Preview</GeneratedBindingRedirectsAction>
</PropertyGroup>
```

This enables the following target in the SDK which will overwrite your web.config from the patched version if there are any changes.

## Overwrite
Alternatively the suggested Binding Redirects can be written directly to the `web.config`.
If you want this to happen automatically, you can add the following to your project file.
```xml
<Target Name="UpdateConfigWithBindingRedirects" AfterTargets="AfterBuild" Condition="'$(OverwriteAppConfigWithBindingRedirects)'=='true'">
<ItemGroup>
<_DllConfig Remove="@(_DllConfig)" />
<_AppConfig Remove="@(_AppConfig)" />
<_ConfigFile Remove="@(_ConfigFileHash)" />
<_DllConfig Include="$(OutDir)$(AssemblyName).dll.config" />
<_AppConfig Include="web.config" />
</ItemGroup>
<GetFileHash Files="@(_DllConfig)">
<Output TaskParameter="Hash" PropertyName="_DllConfigHash" />
<Output TaskParameter="Items" ItemName="_DllConfigFileHash" />
</GetFileHash>
<GetFileHash Files="@(_AppConfig)">
<Output TaskParameter="Hash" PropertyName="_AppConfigHash" />
<Output TaskParameter="Items" ItemName="_AppConfigFileHash" />
</GetFileHash>
<ItemGroup>
<_ConfigFileHash Include="@(_DllConfigFileHash)" />
<_ConfigFileHash Include="@(_AppConfigFileHash)" />
</ItemGroup>
<Message Text="%(_ConfigFileHash.Identity): %(_ConfigFileHash.FileHash)" />
<Warning Text="Replacing web.config due to changes during compile - This should clear warning MSB3276 on next compile" File="web.config" Condition="'$(_DllConfigHash)'!='$(_AppConfigHash)'" />
<Copy SourceFiles="$(OutDir)$(AssemblyName).dll.config" DestinationFiles="web.config" Condition="'$(_DllConfigHash)'!='$(_AppConfigHash)'" />
</Target>
<PropertyGroup>
<GeneratedBindingRedirectsAction>Overwrite</GeneratedBindingRedirectsAction>
</PropertyGroup>
```
57 changes: 7 additions & 50 deletions docs/Binding_Redirects/How-to-show-Suggested-Binding-Redirects.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,12 @@

The following may be useful if you need to see generated binding redirects.
e.g. if you want to manually add them to your `web.config`
```xml
<UsingTask TaskName="ShowBindingRedirects" TaskFactory="$(TaskFactory)" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<SuggestedBindingRedirects ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Reflection" />
<Using Namespace="System.Xml" />
<Using Namespace="Microsoft.Build.Framework" />
<Code Type="Fragment" Language="cs">
<![CDATA[
StringBuilder sb = new StringBuilder();
foreach(var sbr in SuggestedBindingRedirects) {
var an = new AssemblyName(sbr.ItemSpec);
var mvn = sbr.GetMetadata("MaxVersion");

byte []pt = an.GetPublicKeyToken();
sb.AppendLine("<assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">");
sb.AppendLine("\t<dependentAssembly>");
sb.Append("\t\t<assemblyIdentity name=\"");
sb.Append(an.Name);
sb.Append("\" publicKeyToken=\"");
if (pt is null) {
sb.Append("null");
} else {
for (int i=0;i<pt.GetLength(0);i++)
sb.AppendFormat("{0:x2}", pt[i]);
}
sb.Append("\" culture=\"");
sb.Append(an.CultureName);
sb.AppendLine("\" />");
sb.Append("\t\t<bindingRedirect oldVersion=\"0.0.0.0-");
sb.Append(mvn);
sb.Append("\" newVersion=\"");
sb.Append(mvn);
sb.AppendLine("\" />");
sb.AppendLine("\t</dependentAssembly>");
sb.AppendLine("</assemblyBinding>");
}
Log.LogMessage(MessageImportance.High,sb.ToString());
]]>
</Code>
</Task>
</UsingTask>
Add this into your csproj
```xml
<PropertyGroup>
<GeneratedBindingRedirectsAction>Preview</GeneratedBindingRedirectsAction>
</PropertyGroup>
```

<Target Name="ShowBindingRedirects" AfterTargets="ResolveAssemblyReferences">
<ShowBindingRedirects SuggestedBindingRedirects="@(SuggestedBindingRedirects)" Condition="'@(SuggestedBindingRedirects)'!=''" />
</Target>
```
And then look for a `Web.BindingRedirects.config` file in your project's Solution Explorer.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!--
====================================================================================================
GenerateBindingRedirects Behaviors
Traditionally (Microsoft.Common.CurrentVersion.targets) only Performs BindingRedirect calculations when
Project type is deemed to "need" redirects -> when OutputType == 'exe' or OutputType == 'winexe' (Microsoft.Common.CurrentVersion.targets)
AND
Not an "old" project, aka either
An SDK project type that incorporates Microsoft.NET.Sdk (Microsoft.NET.Sdk.BeforeCommon.targets)
OR
A newer Full Framework project TargetFrameworkIdentifier == '.NETFramework' and '$(TargetFrameworkVersion.TrimStart(vV))' >= '4.7.2' (Microsoft.Common.CurrentVersion.targets)
Additionally it chooses the filename of the config file to be updated as what exe projects want...
$(IntermediateOutputPath)$(TargetFileName).config -> the config file named after the assembly and in the IntermediateOutputPath
https://github.com/CZEMacLeod/MSBuild.SDK.SystemWeb/issues/34
This SDK Project Type will override these settings so that we can facilitate developers working with BindingRedirects in the web.config
Enable the Build Process to calculate Binding Redirects
GenerateBindingRedirectsOutputType(true - even though this project is not an .exe or .winexe)
AutoGenerateBindingRedirects (true - set explictly even though should be set by Microsoft.NET.Sdk.BeforeCommon.targets)
Take an action
OverwriteAppConfigWithBindingRedirects -> this is the SystemWeb Sdk Legacy property
GeneratedBindingRedirectsAction (Overwrite) -> this will conditionally change wich config file is written to
====================================================================================================
-->

<Project>

<PropertyGroup Label="Change the default BindingRedirects behavior for projects of this SDK type">
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<AutoGenerateBindingRedirects Condition=" '$(AutoGenerateBindingRedirects)' == '' ">true</AutoGenerateBindingRedirects>
</PropertyGroup>

<PropertyGroup Label="Set the desire default behavior of what to do with SuggestedBindingRedirects if not yet set"
Condition=" '$(GeneratedBindingRedirectsAction)' == ''">
<GeneratedBindingRedirectsAction>None</GeneratedBindingRedirectsAction>
<GeneratedBindingRedirectsAction Condition=" '$(OverwriteAppConfigWithBindingRedirects)' != 'true' ">Preview</GeneratedBindingRedirectsAction>
<GeneratedBindingRedirectsAction Condition=" '$(OverwriteAppConfigWithBindingRedirects)' == 'true' ">Overwrite</GeneratedBindingRedirectsAction>
</PropertyGroup>


<!--
====================================================================================================
SystemWebProject_ChooseConfigFileForGenerateBindingRedirects
This target should executes between
ResolveAssmblyReferences (where the "@(SuggestedBindingRedirects)" itemgroup is populated... based on the logic that resolves assemblies being referenced
and
GenerateBindingRedirects (where the suggestedBindingRedirects are written to disk into a config file at $(_GenerateBindingRedirectsIntermediateAppConfig)
Then we can choose where the suggestedBindingRedirects are written, if at all (Default Value is Overwrite unless set by project)
'$(GeneratedBindingRedirectsAction)' == 'Preview' -> Creates new Web.BindingRedirects.config file showing proposed changes
'$(GeneratedBindingRedirectsAction)' == 'Overwrite' -> Updates the $(AppConfig) aka web.config in the project root
'$(GeneratedBindingRedirectsAction)' == Something Unknown -> Prints message giving options of what the developer can choose from
In general we want to emit a "warning" whenever we either do, or don't do something to help developers find the property that drives this behavior
====================================================================================================
-->
<Target Name="SystemWebProject_ChooseConfigFileForGenerateBindingRedirects"
BeforeTargets="GenerateBindingRedirects"
Condition="'$(AutoGenerateBindingRedirects)' == 'true' and '$(GenerateBindingRedirectsOutputType)' == 'true' and @(SuggestedBindingRedirects->Count()) > 0 ">

<PropertyGroup Label="Set the location of the file to which the suggestedBindingRedirects should be written during the GenerateBindingRedirects Target">
<_GenerateBindingRedirectsIntermediateAppConfig Condition="'$(GeneratedBindingRedirectsAction)' == 'Preview' " >Web.BindingRedirects.config</_GenerateBindingRedirectsIntermediateAppConfig>
<_GenerateBindingRedirectsIntermediateAppConfig Condition="'$(GeneratedBindingRedirectsAction)' == 'Overwrite' " >$(AppConfig)</_GenerateBindingRedirectsIntermediateAppConfig>
</PropertyGroup>

<Warning Condition="'$(GeneratedBindingRedirectsAction)' != 'Preview' and '$(GeneratedBindingRedirectsAction)' != 'Overwrite'"
Text="Generated Binding Redirects have been applied only to the $(TargetFileName).config. You should incorporate them into the web.config. Consider setting &lt;GeneratedBindingRedirectsAction&gt;Preview&lt;/GeneratedBindingRedirectsAction&gt; to create a file containing the proposals. Consider setting &lt;GeneratedBindingRedirectsAction&gt;Overwrite&lt;/GeneratedBindingRedirectsAction&gt; to automatically update web.config with proposals." />

<Warning Condition="'$(GeneratedBindingRedirectsAction)' == 'Preview'"
Text="Generated Binding Redirects have been applied only to the Web.BindingRedirects.config. You should incorporate them into the web.config. Consider setting &lt;GeneratedBindingRedirectsAction&gt;Overwrite&lt;/GeneratedBindingRedirectsAction&gt; to automatically update web.config with proposals." />

<Warning Condition="'$(GeneratedBindingRedirectsAction)' == 'Overwrite'"
Text="Generated Binding Redirects have been applied automatically to the web.config. This warning will disappear on the next build." />

</Target>



</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<ItemGroup Condition="'$(EnableWebFormsDefaultItems)'=='true'">
<Content Include="Web.config" />
<_WebConfigConfigurations Include="$(Configurations)" />
<None Include="@(_WebConfigConfigurations->'Web.%(Identity).config')">
<None Include="@(_WebConfigConfigurations->'Web.%(Identity).config');Web.BindingRedirects.config">
<DependentUpon>Web.config</DependentUpon>
</None>
<Content Include="Web.*.config" Exclude="@(_WebConfigConfigurations->'Web.%(Identity).config')">
<Content Include="Web.*.config" Exclude="@(None)">
<DependentUpon>Web.config</DependentUpon>
</Content>
</ItemGroup>
Expand Down
6 changes: 0 additions & 6 deletions src/MSBuild.SDK.SystemWeb/Sdk/Sdk.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
<PublishProfileImported>true</PublishProfileImported>
<AppConfig>web.config</AppConfig>

<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
<ItemGroup>
<ProjectCapability Include="DotNetCoreWeb" />
Expand All @@ -18,10 +16,6 @@
<ProjectCapability Include="LegacyRazorEditor" />
</ItemGroup>

<PropertyGroup>
<OverwriteAppConfigWithBindingRedirects Condition="'$(OverwriteAppConfigWithBindingRedirects)'==''">false</OverwriteAppConfigWithBindingRedirects>
</PropertyGroup>

<Import Project="MSBuild.SDK.SystemWeb.DefaultPackages.props" />
<!-- Default item includes -->
<Import Project="MSBuild.SDK.SystemWeb.DefaultItems.props" />
Expand Down
29 changes: 4 additions & 25 deletions src/MSBuild.SDK.SystemWeb/Sdk/Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,13 @@
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Target Name="UpdateConfigWithBindingRedirects" AfterTargets="AfterBuild" Condition="'$(OverwriteAppConfigWithBindingRedirects)'=='true'">
<ItemGroup>
<_DllConfig Remove="@(_DllConfig)" />
<_AppConfig Remove="@(_AppConfig)" />
<_ConfigFile Remove="@(_ConfigFileHash)" />
<_DllConfig Include="$(OutDir)$(AssemblyName).dll.config" />
<_AppConfig Include="web.config" />
</ItemGroup>
<GetFileHash Files="@(_DllConfig)">
<Output TaskParameter="Hash" PropertyName="_DllConfigHash" />
<Output TaskParameter="Items" ItemName="_DllConfigFileHash" />
</GetFileHash>
<GetFileHash Files="@(_AppConfig)">
<Output TaskParameter="Hash" PropertyName="_AppConfigHash" />
<Output TaskParameter="Items" ItemName="_AppConfigFileHash" />
</GetFileHash>
<ItemGroup>
<_ConfigFileHash Include="@(_DllConfigFileHash)" />
<_ConfigFileHash Include="@(_AppConfigFileHash)" />
</ItemGroup>
<Message Text="%(_ConfigFileHash.Identity): %(_ConfigFileHash.FileHash)" />
<Warning Text="Replacing web.config due to changes during compile - This should clear warning MSB3276 on next compile" File="web.config" Condition="'$(_DllConfigHash)'!='$(_AppConfigHash)'" />
<Copy SourceFiles="$(OutDir)$(AssemblyName).dll.config" DestinationFiles="web.config" Condition="'$(_DllConfigHash)'!='$(_AppConfigHash)'" />
</Target>

<!-- Default item excludes -->
<Import Project="MSBuild.SDK.SystemWeb.DefaultItems.targets" />

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

<!-- Inject BindingRedirects targets after Microsoft.NET.Sdk is injected -->
<Import Project="MSBuild.SDK.SystemWeb.BindingRedirects.targets" />

<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" />
</Project>

0 comments on commit 21e499d

Please sign in to comment.