dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.21k stars 1.35k forks source link

<AutoGenerateBindingRedirects> doesn't work for class libraries #1310

Open tmat opened 7 years ago

tmat commented 7 years ago

The property is ignored unless the containing project is an .exe.

Extensible frameworks/apps that load plugins might support loading configuration for each plugin, including binding redirects, from a .dll.config file next to the plugin .dll. For example, a unit-testing framework such as xunit. See also proposal https://github.com/Microsoft/msbuild/issues/1309, which might also make use of this feature.

I propose the restriction on project type is removed and binding redirects are generated for any project that sets AutoGenerateBindingRedirects to true.

tmat commented 7 years ago

//cc @ericstj

nguerrera commented 7 years ago

cc @piotrpMSFT @krwq @dsplaisted There is a similar requirement for the CLI team

tmat commented 7 years ago

Also there need to be a way how to specify the target framework for which the redirects need to be generated. Is there is a way to do so for a class library?

kdowswell commented 7 years ago

this issue is blocking me from using Microsoft.AspNetCore.TestHost in a .net 4.6.1 test project targeting asp.net core .net 4.6.1 in VS 2017. Manually adding the binding redirect attributes to my test proj doesn't help. :(

bradphelan commented 7 years ago

The magic sauce seems to be to add

<PropertyGroup>
  <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

but I don't understand why there are two props with basically the same meaning. I found this here

and then my libraries started generating binding redirects.

bradphelan commented 7 years ago

But now it stopped working again.... totally inconsistent behavior :(

bradphelan commented 7 years ago

Oh yes it does. I've figured it out. ;) When set the below on a library project

<PropertyGroup>
  <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

(1) If there are no binding conflicts then the *.config file is not generated. (2) If there are binding conflicts then the *.config file is generated.

JoseFMP commented 7 years ago

Over here I tried to activate both ` true

true

` but no effect.

Also, no effect either in library or exe file. Like... redirects not created at all.

bradphelan commented 7 years ago

If you don't need the binding redirects then it will not generate them. At least that is what I have found.

JoseFMP commented 7 years ago

I need them. That's why my software does not start.

danrozenberg commented 7 years ago

I am also suffering from this. I get the MSB3276 warning, but adding

``

true true `` Does nothing... I am using the new MSBuild-based tooling (that means, using ``
tmat commented 7 years ago

This should work:

  <Target Name="ForceGenerationOfBindingRedirects"
          AfterTargets="ResolveAssemblyReferences"
          BeforeTargets="GenerateBindingRedirects"
          Condition="'$(AutoGenerateBindingRedirects)' == 'true'">
    <PropertyGroup>
      <!-- Needs to be set in a target because it has to be set after the initial evaluation in the common targets -->
      <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
    </PropertyGroup>
  </Target>
danrozenberg commented 7 years ago

tmat, it does work, thank you! Is there any way to see which binding redirections were made?

tmat commented 7 years ago

Yes, there should be a .dll.config file generated to the output directory.

nguerrera commented 7 years ago

Fixing this (not requiring any specific output type) is required to unblock https://github.com/Microsoft/vstest/issues/792

danrozenberg commented 7 years ago

tmat, although I said it worked earlier, I was mistaken, I had changed the command line directory to another project and didn't realize I was building something that didn't have MSB3276 to begin with. At this point, I am still getting that warning, even when using the Target solution. Sorry about that...

danrozenberg commented 7 years ago

To be honest, I think what I am experiencing is an issue with the warning showing up even when the binding redirection is being done...so the warning trigger is being too sensitive...

I do get the .dll.config file with the correct redirections, either using the Target technique, or any other technique that sets the AutoGenerateBindingRedirects to be turned on.

I might have to find a more appropriate issue to discuss this... thanks for the help so far, though!

JoseFMP commented 7 years ago

@tmat Your solution worked for me... thank you so much! So now the redirects are created. Surprising to see how many... I envision it's going to be a hard time to work with .NET Standard libraries. Based on the fallacy of interoperability between frameworks and platforms....

dasMulli commented 7 years ago

It shouldn't need to be set in a target since the common targets only set it to true, never to false (am I missing something?). At least it worked for a few users on SO: https://stackoverflow.com/questions/43995432/could-not-load-file-or-assembly-microsoft-extensions-dependencyinjection-abstrac/43996389 https://stackoverflow.com/questions/43955266/interface-in-netstandard-1-1-library-does-not-have-implementation-in-net-4-61/43955719

JoseFMP commented 7 years ago

I am aware it worked for others. But for me the only thing that worked was setting it with the target solution @tmat proposed. The rest did not generate any binding at all.

ericstj commented 7 years ago

To understand why static properties might be different than the target you can look at the evaluated project by using msbuild /pp. I suspect that the order of import and/or the contents of the targets themselves differs between the projects.

ericstj commented 7 years ago

/cc @bradwilson

MichaelKetting commented 7 years ago

I might have a duplicate issue here: https://github.com/dotnet/sdk/issues/1595

Edit: I've done extensive tests (see my issue in https://github.com/dotnet/sdk/issues/1595) and the problem is related to using Package References but not explictly setting both <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> and <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> in the project file.

JoseFMP commented 7 years ago

@MichaelKetting in my case that did not influence anything. Unfortunately.

jnm2 commented 6 years ago

I would like to not have to put either of these in a .csproj targeting .NET Framework, regardless of output type. If the output type is DLL, I want a .config by default even if it's not a test DLL. It's critical information no matter where the DLL is used.

    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>

Right now (VS 15.4) I have to add both to every project.

nguerrera commented 6 years ago

@jnm2 You could add them to a single Directory.Build.props file that will be picked up by every project underneath its directory.

jnm2 commented 6 years ago

@nguerrera I hadn't thought of that, that's a nice workaround. But at the end of the day, I don't want to have to do either (or tell other people to do either in order to use my NUGet packages).

nguerrera commented 6 years ago

@AndyGerlicher can we get this fixed in 15.6? There is a lot of customer pain around getting non-exe test projects to generate binding redirects. I think the conditions on $(GenerateBindingRedirectsOutputType) should be removed. If AutoGenerateBindingRedirects is true, then generate irrespective of output type. cc @Petermarcu

jaredpar commented 6 years ago

Agree this is critical for getting test libraries to work correctly in a Net Standard world.

bradwilson commented 6 years ago

AFAIK, none of the test frameworks support netstandard, and for good reason: it's not a platform, it's an API set. A netstandard library is not intended to be "runnable".

jnm2 commented 6 years ago

I wouldn't mind being able to build a netcoreapp1.0 test DLL and execute it against .NET Core 1.0, 1.1, 2.0, etc. Likewise, I wouldn't mind being about to build a netstandard1.3 test DLL and execute it against those as well as .NET Framework, Xamarin, etc.

dasMulli commented 6 years ago

I wouldn't mind being about to build a netstandard1.3 test DLL and execute it against those as well as .NET Framework, Xamarin, etc.

That's currently very very hard to achieve - test hosts rely on the build system to produce the necessary assets for each platform. From generation of deps.json/runtimeconfig.json for .net core to netstandard shim injection for .net framework.

Such a "portable test" project would need runners that create projects behind the scenes referencing the test project / dlls.

This issue reflects the need to run build logic meant for "executable projects" for tests.

tmat commented 6 years ago

It is indeed possible to do this. The .NET SDK just doesn't support it currently. The Compile task can be executed once on the netstandardXX project and the results can then be deployed multiple times with corresponding runtime configuration files to mutliple deployment directories, one per target platform. I could easily imagine the project having settings like

<TargetFramework>netstandard1.3</TargetFramework>
<DeploymentFrameworks>net46;netcoreapp2.0<DeploymentFrameworks>

Then the unit test runners would run for each deployment framework.

bradwilson commented 6 years ago

I wouldn't mind being able to build a netcoreapp1.0 test DLL and execute it against .NET Core 1.0, 1.1, 2.0, etc.

This is fine.

Likewise, I wouldn't mind being about to build a netstandard1.3 test DLL and execute it against those as well as .NET Framework, Xamarin, etc.

This is not. You should use multi-targeting instead.

jnm2 commented 6 years ago

This is not. You should use multi-targeting instead.

I disagree. Why not? Just because it's hard for tooling authors?

bradwilson commented 6 years ago

Then the unit test runners would run for each deployment framework.

A full build is required. Just multi-target and be done with it. These suggested workarounds are, frankly, nonsense in the world where we have <TargetFrameworks>. dotnet xunit already supports multi-targeting against both net452+ and netcoreapp1.0+ (example: https://github.com/xunit/xunit.integration/blob/master/dotnet-xunit/v2x_MultiTarget_DotNetSdk.csproj). This is what all multi-platform test runners should support.

bradwilson commented 6 years ago

I disagree. Why not?

Because, as stated above, netstandard is an API set, not a platform. Unit tests require a platform to run on, and without knowing the platform it will be run on, the build system as it stands today (and presumably for a short- to medium-term future) requires that knowledge to properly build for the platform.

Whether you agree with the decision the team made is irrelevant (today), as that's the world we live in. You want this to work today? Multi-target.

jnm2 commented 6 years ago

Whether you agree with the decision the team made is irrelevant (today), as that's the world we live in. You want this to work today? Multi-target.

Oh, I'm being totally realistic. I know multi-targeting is the only way to go right now. But you told me it's not fine to want to be able to run a .NET Standard binary against a selection of platforms and versions and I disagree. I don't think it's wrong to want to be able to execute a single DLL against more than one platform and version, whether the API set targeted is netcoreapp. or netstandard. or net*. Yes, some map to platforms and one doesn't, but at the end of the day they are all just API specs.

As an example, though I wouldn't likely find this useful in the real world, a net46 test dll could be executed against .NET Framework and .NET Core 2.0.

jaredpar commented 6 years ago

Multi-targeting the tests is the opposite of what I want. My tests can be written against Net Standard which means it can run on any runtime. Multi-targeting is a way to limit my tests to a very specific set of platforms. That's the opposite of portable code.

It means every time a new platform comes out i have to modify my tests to support it. Even though I've done nothing to the code other than say "hey this new platform exists".

I've spoken with the CLI and testing teams a couple of times about this. The preferred setup here is:

  1. Allow test libraries to target net standard
  2. Allow publishing to target libraries and exe projects
  3. Extend publishing to allow for targets not explicitly listed in the project file

Both 2 and 3 should be done anyways. They are general improvements to the ecosystem. Taken together though it makes authoring Net Standard test libraries a reality.

jnm2 commented 6 years ago

And it'll come as no surprise then that I want the same capabilities for netstandard console apps. In fact, not being able to do this is causing us at NUnit to have to create a matrix of separate csproj files, a cross product between unique DLLs targeting different frameworks and the list of platforms each unique DLL can be executed on. The new SDK doesn't really provide a way to relieve the complexity yet, but I think @tmat's proposed <DeploymentFrameworks> is exactly the type of thing I've been looking for.

JoseFMP commented 6 years ago

Any news on this issue?

jnm2 commented 6 years ago

Ugly workaround, but much nicer than creating a bunch of separate .csprojs: https://github.com/dotnet/sdk/issues/1561

@jaredpar

  1. Allow test libraries to target net standard
  2. Allow publishing to target libraries and exe projects
  3. Extend publishing to allow for targets not explicitly listed in the project file

I would like to be able to follow these in an issue other than this one which isn't strictly related.

Gonnagle commented 6 years ago

I was struggling to run NUnit tests on TeamCity while worked fine locally. Adding <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> seemed to resolve my issue and I finally got the needed redirects to appear on Project.Tests.dll.config on TeamCity as well. (Thank you @bradphelan & others <3)

I'm still a bit confused why it worked locally already only with <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>? Has this been addressed with some version of MSBuild already?

nguerrera commented 6 years ago

Note that web projects with web.config are incompatible with GenerateBindingRedirects because the edits have to be made to web.config in source. For these projects, you are instead supposed to double click on warnings in the error list to get web.config edited. It looks to me as though if we were to remove all consideration of GenerateBindingRedirectsOutputType, then we would regress the web.config case.

dasMulli commented 6 years ago

As I posted on https://github.com/dotnet/standard/issues/613#issuecomment-354393350, this will look like this: webapinetstd

dsyme commented 6 years ago

For me the solution was to add

<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />

to a unit test project

dsplaisted commented 6 years ago

@dsyme I'm pretty sure that package sets the OutputType to Exe

bradwilson commented 6 years ago

@dsplaisted Only for netcoreapp.

Vorval0 commented 5 years ago

In my case, the problem was caused by conflict of GenerateBindingRedirects and TransformXml (XDT transformation) msbuild targets. By default TransformXml overrides output of GenerateBindingRedirects.

<TransformXml Source="$(IntermediateOutputPath)$(TargetFileName).config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />

This solved my problem.

scalablecory commented 5 years ago

I've having a similar issue, not sure if it's the same one. Can you guys confirm if this matches your situation?

When I build the project, binding redirects are applied to the output config file. But, all the warnings still occur. It's as if the binding redirects are being added at the wrong stage of compilation.