Getting InternalsVisibleTo to work when the build process signs the assembly with strong names?
F

2

6

In our shop, we are using Cruise Control & MSBuild to automate the builds of the product as part of continuous integration.

Part of the build is to sign the assemblies so they have strong names.

In our project files when we develop locally, it does not specify signing, as this is overridden by the MSBuild script.

This is all well and good until I decided I would like introduce unit tests that require me to use the InternalsVisibleTo attribute. I also began using a nice open source library which has unit tests that also use this technique.

This means that, on my machine, I can update AssemblyInfo.cs to use the following statement:

[assembly: InternalsVisibleTo("MyProject.Tests.UnitTests")]

and all is well.

However, checking this in breaks the build since the build machine signs the assemblies, that line would need to be updated to look like this instead:

[assembly: InternalsVisibleTo("MyProject.Tests.UnitTests, 
             PublicKey="magic key here..)"]

I have half a mind to not sign the assemblies and call it a day. However, "signing assemblies is a best practice"(repeat this mantra 3 times), and if we are getting any benefit from doing this, I don't want to remote it.

We don't install to the GAC, we're not particularly concerned about tampering, don't have to worry about 3rd parties who use our libraries, we update all our files at once when we update the app. The most identifiable benefit is someone in support can't copy some random version of an assembly into the run-time folder and have things appear to work for a little while.

I don't want to open the can of works involved w/ changing around 100 project files manually to enable signing. I also don't want to hack things up by marking things which should be internal as public, I don't want to get into binding redirects & I don't want to remove the unit tests.

I'd like all of the ease of not having strong names with all of the benefits of having strong names(if there are any), and I don't want to create a lot of extra work to do it. Is that too much to ask?

This post describes the problem and a solution well, but I'm not much of a MSBuild script guy to take advantage of it:

http://social.msdn.microsoft.com/forums/en-US/msbuild/thread/02df643c-956a-48bd-ac01-4a1016d91032/

What is the proper solution to this problem?

Any inputs and points of discussion are welcome.

Fiftieth answered 25/6, 2011 at 0:36 Comment(0)
M
4

You could patch the key information into the source files before compiling.

The following example uses the FileUpdate from the MSBuild Community Tasks and a (very crude) regex to patch key information into C# AssemblyInfo.cs files.

<ItemGroup>
    <AssemblyInfoFiles Include="$(MSBuildProjectDirectory)**/AssemblyInfo.cs"/>
</ItemGroup>
<FileUpdate
  Files="@(AssemblyInfoFiles)"
  Regex='(\[assembly:\s*InternalsVisibleTo\(\"[\w.]*\")(\)\])'
  ReplacementText='$1, PublicKey="$(StrongNamingPublicKey)"$2' />

However, I think modifying the projects to always strong-name sign would be the cleaner solution for eliminating one more difference between development and deployed environments, and could be easily scripted.

Mozart answered 25/6, 2011 at 10:51 Comment(3)
Thanks Thomas, I'm going to try this & will accept it as the answer once I get it to work. There are some issues to strong signing it everywhere, mostly in that I'm not sure how to script that change and I don't want to do it manually. Our existing build script, which was written by someone who knows MSBuild better than I does the signing & I'll just tweak it a bit rather than introduce major change.Fiftieth
I never did get this to work, but I'll mark it as the answer. The thing that didn't work is the ItemGroup named AssemblyInfoFiles... it would always be empty... any ideas why? The Include pattern is basically what you posted... Thanks!Fiftieth
The ItemGroup above assumes that the AssemblyInfo.cs files to patch are located in a directory below the build script; the ** wildcard will match any directory below the current one. See How to: Select the Files to Build for further information on MSBuild include syntax.Laceration
C
5

Use the same construct that you employ to disable signing a developer build to define a compiler symbol, eg. DO_NOT_SIGN, then, in your AssemblyInfo.cs, use this:

#if DO_NOT_SIGN
[assembly: InternalsVisibleTo("MyProject.Tests.UnitTests")]
#else
[assembly: InternalsVisibleTo("MyProject.Tests.UnitTests, PublicKey="magic key here..)"]
#endif

In my personal opinion it's more elegant than modifying the source code from the build script.

Cuspidate answered 25/6, 2011 at 13:41 Comment(1)
Thanks for the reply skolima. I guess the issue for this solution for me is I now have to manually go into X # of files and create this construct however many times it's needed, I or others have to remember to do it on new projects, and there's a bit of rework involved in the unlikely event the key gets changed. It does have the benefit of being visible & understandable to anybody, and not a mystery burried in the MSBuild script, which not everybody understands very well. I think the lazy programmer in me is going to go w/ the build script as there's already framework for it in our scripts.Fiftieth
M
4

You could patch the key information into the source files before compiling.

The following example uses the FileUpdate from the MSBuild Community Tasks and a (very crude) regex to patch key information into C# AssemblyInfo.cs files.

<ItemGroup>
    <AssemblyInfoFiles Include="$(MSBuildProjectDirectory)**/AssemblyInfo.cs"/>
</ItemGroup>
<FileUpdate
  Files="@(AssemblyInfoFiles)"
  Regex='(\[assembly:\s*InternalsVisibleTo\(\"[\w.]*\")(\)\])'
  ReplacementText='$1, PublicKey="$(StrongNamingPublicKey)"$2' />

However, I think modifying the projects to always strong-name sign would be the cleaner solution for eliminating one more difference between development and deployed environments, and could be easily scripted.

Mozart answered 25/6, 2011 at 10:51 Comment(3)
Thanks Thomas, I'm going to try this & will accept it as the answer once I get it to work. There are some issues to strong signing it everywhere, mostly in that I'm not sure how to script that change and I don't want to do it manually. Our existing build script, which was written by someone who knows MSBuild better than I does the signing & I'll just tweak it a bit rather than introduce major change.Fiftieth
I never did get this to work, but I'll mark it as the answer. The thing that didn't work is the ItemGroup named AssemblyInfoFiles... it would always be empty... any ideas why? The Include pattern is basically what you posted... Thanks!Fiftieth
The ItemGroup above assumes that the AssemblyInfo.cs files to patch are located in a directory below the build script; the ** wildcard will match any directory below the current one. See How to: Select the Files to Build for further information on MSBuild include syntax.Laceration

© 2022 - 2024 — McMap. All rights reserved.