Msbuild: Add support for non-string assembly attributes

Created on 11 Jul 2017  路  42Comments  路  Source: dotnet/msbuild

_From @AmadeusW on July 11, 2017 16:44_

There should be a way to generate non-string assembly attributes.
For example,

  <ItemGroup>
    <AssemblyAttribute Include="System.CLSCompliant">
      <_Parameter1>true</_Parameter1>
    </AssemblyAttribute>
  </ItemGroup>

generates

[assembly: System.CLSCompliant("true")]

which produces an error, because the parameter must be a boolean.

  • Are there any workarounds so that I can produce a correct attribute with msbuild today?
  • Would you take a PR to fix this?
  • If this is not the correct place to raise this issue, I'm sorry, but in the entirety of searchable internet, this repo is the closest to dealing with assembly attributes.

_Copied from original issue: dotnet/sdk#1402_

Most helpful comment

Sorry for being late to the party on this. I know it's closed, but I think it's worth considering reopening. In my opinion, this can be achieved very easily without breaking the existing behavior or requiring complex, brittle parameter _magic_.

Background

Most of the attributes that used to be placed in code files (ex: AssemblyInfo) have been replaced with first-class equivalents in MSBuild 15.0, save a handful of outliers. To my knowledge the following attributes do not have built-in build properties in MSBuild 15.0:

  • System.CLSCompliantAttribute
  • System.Reflection.AssemblyTrademarkAttribute
  • System.Runtime.InteropServices.ComVisibleAttribute

The AssemblyTrademarkAttribute can use the existing AssemblyAttribute build items, but the others cannot because they are not of type String.

While it's true that this limitation can be mitigated with a linked or dynamically generated code file, it would be far more preferable to have it supported as a first-class citizen. In large solutions, this information would typically be placed or imported into directory.build.props.

Proposed Solution

To enable this scenario, the existing implementation just needs to not wrap the provided value in double quotes. There currently is no validation on the input value matching the attributes because the compiler will handle that automatically; therefore, there is no need to include or otherwise formally support type semantics.

To achieve this behavior without breaking the existing behavior, I propose you simply add one new, optional metadata element - IsLiteral (Boolean). The default value will be false and continue to have the current behavior. Items which are marked as IsLiteral="true" will forego wrapping the value with double quotes. In a scenario where there are multiple parameters where some must be literal and others require double quotes, the developer can explicitly provide the &quot; escape sequence as necessary

<ItemGroup>
  <AssemblyAttribute Include="System.CLSCompliantAttribute" IsLiteral="true" _Parameter1="true" />
</ItemGroup>

```c#
[assembly: System.CLSCompliant(true)]

_Figure 1: Using IsLiteral to omitted surrounding quotes_

```xml
<ItemGroup>
  <AssemblyAttribute Include="System.Reflection.AssemblyTrademarkAttribute"
                     IsLiteral="true"
                     _Parameter1="&quot;My Company&quot;" />
</ItemGroup>

```c#
[assembly: System.Reflection.AssemblyTrademarkAttribute("My Company")]

_Figure 2: Using IsLiteral to with user-defined double quotes_

## Benefits
In addition to these basic scenarios, this approach would enable more advanced scenarios without additional modification. Consider a scenario where a developer would specify the attribute for an OWIN startup class. Today, this is often in the assembly information file (*.cs, *.vb, etc) or it is placed in an arbitrary file (ex: Startup.cs). This new support would allow it to go into a *.props file or the *.*proj directly.

```xml
<ItemGroup>
  <AssemblyAttribute Include="Microsoft.Owin.OwinStartup"
                     IsLiteral="true"
                     _Parameter1="typeof(Microsoft.Examples.Startup)" />
</ItemGroup>

c# [assembly: Microsoft.Owin.OwinStartup(typeof(Microsoft.Examples.Startup))]
_Figure 3: Using IsLiteral to insert an arbitrarily supported value such as a type_

All 42 comments

_From @dasMulli on July 11, 2017 20:26_

On Slack, a few ppl asked how to set ComVisibleAttribute using an <AssemblyAttribute> item for net* projects.

I'm searching for AssemblyAttribute in this repo and can't find any code with it. Where are the tasks that define this attribute?

(sorry, got distracted while writing this explanation of why this is the right place for this)

This is the right home for this issue, because it's a feature request for the WriteCodeFragment task that consumes the AssemblyAttribute items and emits code.

There's no way to do this currently. You can always add a standard .cs file that defines the attribute you want.

Why do you want to do this with MSBuild instead of C#? I'm not totally opposed to the idea but would rate it as pretty low-priority.

I'd like to do it with MSBuild for consistency with other attributes that we're defining.

Also, the snippet you pointed at refers to @(AssemblyAttributes) (plural) - is there some code that automatically maps singular and plural form of an XML tag to this code path?

You're right, I was pointing to the wrong implementation. The one you care about is in the Sdk.

I'd be interested in seeing proposals for how to carry the metadata needed to make this possible. It's already very ugly with _Parameter1 -- having something like _Parameter1_Type that maps to a fixed list of known types in WriteCodeFragment seems bad.

Team Triage:
Closing this as won't fix. This would be difficult to implement (map literal to type) and a fairly easy workaround (use a C# file). If this becomes a more frequent ask we could revisit this in the future.

Sorry for being late to the party on this. I know it's closed, but I think it's worth considering reopening. In my opinion, this can be achieved very easily without breaking the existing behavior or requiring complex, brittle parameter _magic_.

Background

Most of the attributes that used to be placed in code files (ex: AssemblyInfo) have been replaced with first-class equivalents in MSBuild 15.0, save a handful of outliers. To my knowledge the following attributes do not have built-in build properties in MSBuild 15.0:

  • System.CLSCompliantAttribute
  • System.Reflection.AssemblyTrademarkAttribute
  • System.Runtime.InteropServices.ComVisibleAttribute

The AssemblyTrademarkAttribute can use the existing AssemblyAttribute build items, but the others cannot because they are not of type String.

While it's true that this limitation can be mitigated with a linked or dynamically generated code file, it would be far more preferable to have it supported as a first-class citizen. In large solutions, this information would typically be placed or imported into directory.build.props.

Proposed Solution

To enable this scenario, the existing implementation just needs to not wrap the provided value in double quotes. There currently is no validation on the input value matching the attributes because the compiler will handle that automatically; therefore, there is no need to include or otherwise formally support type semantics.

To achieve this behavior without breaking the existing behavior, I propose you simply add one new, optional metadata element - IsLiteral (Boolean). The default value will be false and continue to have the current behavior. Items which are marked as IsLiteral="true" will forego wrapping the value with double quotes. In a scenario where there are multiple parameters where some must be literal and others require double quotes, the developer can explicitly provide the &quot; escape sequence as necessary

<ItemGroup>
  <AssemblyAttribute Include="System.CLSCompliantAttribute" IsLiteral="true" _Parameter1="true" />
</ItemGroup>

```c#
[assembly: System.CLSCompliant(true)]

_Figure 1: Using IsLiteral to omitted surrounding quotes_

```xml
<ItemGroup>
  <AssemblyAttribute Include="System.Reflection.AssemblyTrademarkAttribute"
                     IsLiteral="true"
                     _Parameter1="&quot;My Company&quot;" />
</ItemGroup>

```c#
[assembly: System.Reflection.AssemblyTrademarkAttribute("My Company")]

_Figure 2: Using IsLiteral to with user-defined double quotes_

## Benefits
In addition to these basic scenarios, this approach would enable more advanced scenarios without additional modification. Consider a scenario where a developer would specify the attribute for an OWIN startup class. Today, this is often in the assembly information file (*.cs, *.vb, etc) or it is placed in an arbitrary file (ex: Startup.cs). This new support would allow it to go into a *.props file or the *.*proj directly.

```xml
<ItemGroup>
  <AssemblyAttribute Include="Microsoft.Owin.OwinStartup"
                     IsLiteral="true"
                     _Parameter1="typeof(Microsoft.Examples.Startup)" />
</ItemGroup>

c# [assembly: Microsoft.Owin.OwinStartup(typeof(Microsoft.Examples.Startup))]
_Figure 3: Using IsLiteral to insert an arbitrarily supported value such as a type_

I do like this suggestion. Doing it in msbuild may still be beneficial in situations where a Directory.Build.* file is easier to use than shared AssemblyInfo.cs files.

I'd just argue that a name like TreatParametersAsLiterals is a bit more descriptive.

There any number of possible names:

  • IsQuoted (reverse to default to true)
  • OmitQuotes
  • OmitDoubleQuotes
  • RemoveQuotes
  • ...

I'm good with any name if it lands. While I can certainly appreciate schedule, this shouldn't require a large development effort at all to implement (IMHO). If the team approves the feature and can agree on a suitable metadata item name, I get the impression there are multiple folks in the community that would submit a PR for this.

Actually, this came up with us today. We would like to mark all of our sdk-style projects as ComVisible false, and adding an AssemblyInfo.cs to every project (even implicitly) instead of adding a property into a shared .props file doesn't feel good.

Proof-of-concept: https://github.com/Microsoft/msbuild/compare/master...dasMulli:feature/literal-code-fragment

Will allow e.g.:

  <ItemGroup>
    <AssemblyAttribute Include="System.Runtime.InteropServices.ComVisibleAttribute">
      <_Parameter1>false</_Parameter1>
      <_Treat_Parameter1AsLiteral>true</_Treat_Parameter1AsLiteral>
    </AssemblyAttribute>
  </ItemGroup>

@rainersigwald does this seem reasonable?

@dasMulli we'll see what @rainersigwald says. I think you've proven that this can work and isn't terribly complex. However, IMHO having an additional metadata item for every parameter is overkill. My proposal and continued position is that you only need a single metadata item (called [insert name] = true | false). Either all of the parameters are literal or none of them are. In 90%+ of the cases, there is only a single parameter anyway. Of the remaining 10%, there are very few parameter combinations where they aren't all the same type (e.g. all strings, integers, etc). As I demonstrated above, it's possible to achieve a _mixed_ combination by providing your own quoting.

Nice work putting this together. Hopefully that will grab the attention of the team and get them to roll it in.

Happy to turn it into a proper PR whatever the decision.

fyi I can see myself porting stuff to SDK-based csproj that needs
c# [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="log4net",Watch=true)]
for all executables, so a Directory.Build.targets with an item group conditioned on $(OutputType) would be easiest her.

@dasMulli this looks great.
@commonsensesoftware raises a good point that user can mix literals and strings by using a single parameter IsLiteral (or LiteralParameters?) and quotes around what's meant to be strings.

I'm not sure what you mean about porting stuff to SDK-based projects and log4net, but I'd recommend making a minimal PR to increase its chances of getting merged.

For what it's worth, I don't think introducing support for literals in the WriteCodeFragment task is coherent with the initial design. Let's not forget that the task allows a Language attribute which is supposed to establish in which language the code is written. This - and this alone - should know about the appropriate syntax to use when it comes to passing parameter values.

I do agree though that it would be convenient to use the task to write common boolean attributes such as ComVisible and CLSCompliant.

AFAIK, MSBuild _does_ recognize boolean expressions as such. Therefore, shouldn't the WriteCodeFragment task already have everything it needs to _at least_ support both string and boolean parameters?

Fixing just this would go a long way already, IMHO.

One of the problems would be recognising when to use bools, strings or something else. You could define a custom attribute class in custom code and let WriteCodeFragment emit some instances.

For "literal arguments", I'd assume that the literal matches the language used. If you switch between VB and C#, you may need to change the literal arguments to WriteCodeFragment.

Retorting to @AndyGerlicher's "won't fix" assessment

This would be difficult to implement

@dasMulli proved this is not at all complex

and a fairly easy workaround (use a C# file)

it's a real messy workaround when your solution has 143 projects and you try to nicely manage them with Directory.Build.targets

it's a real messy workaround when your solution has 143 projects and you try to nicely manage them with Directory.Build.targets

Agreed. Though it may not be as nice since the proposed change couples the target files to the language used - even just for C# and VB there's a difference between false and False.
But still better than having to manage some c# files, esp. if the values shall be autogenerated.

The workaround at the moment would be to have some files build\NotComVisble.cs and in the Directory.Build.targets adding $(MSBuildThisFileDirectory)build\NotComVisible.cs somewhere.

@Crono1981 I couldn't disagree more with:

_... I don't think introducing support for literals in the WriteCodeFragment task is coherent with the initial design_

If it's not coherent, then the name of the task is horribly wrong. I'm going out on a limb here, but I think most people would interpret _Write Code Fragment_ as just that. Granted, that doesn't mean the task can write _any_ code, but it certainly doesn't say _Write String Only Fragment_, _Write Attribute_, or something like that.

The expectation for the use of literals should be that of the author; otherwise, _caveat emptor_. If multi-targeting languages is something that is required, then obviously that's something the author needs to bake into their targets. As you suggest, using a language discriminator can easily be used to do this:

<ItemGroup Condition=" '$(Language)' == 'C#' ">
  <AssemblyAttribute Include="System.CLSCompliantAttribute" IsLiteral="true" _Parameter1="true" />
  <AssemblyAttribute Include="System.ComVisibleAttribute" IsLiteral="true" _Parameter1="true" />
</ItemGroup>

<ItemGroup Condition=" '$(Language)' == 'VB' ">
  <AssemblyAttribute Include="System.CLSCompliantAttribute" IsLiteral="true" _Parameter1="True" />
  <AssemblyAttribute Include="System.ComVisibleAttribute" IsLiteral="true" _Parameter1="True" />
</ItemGroup>

But even that is probably a non-issue because these items would already be split out into language-specific targets.

@dasMulli you're right. There shouldn't be any _interpretation_ of what the literal means. This would add unnecessary complexity. This complexity already exists to some degree because C# and VB have different string escaping rules. You can see this complexity in the task source code. Mistakes in the literal should the onus of the author.

A possible compromise would be limiting literal values to specific types of primitives, which would be neutral across languages; for example, Boolean and numbers. You _could_ fallback to the least common denominator as necessary. VB is not case-sensitive so true and false are still perfectly valid. Ultimately, I still think that would be limiting and add unnecessary complexity.

@dasMulli, @AmadeusW the _messiness_ is real. It can get even messier when you have additional variability. For example, some projects (the main source) are CLS-compliant and others (say tests) are not. You then end up with something in build\SharedAssemblyInfo.cs that looks like:

```c#
[assembly: ComVisible(false)]

if CLS_COMPLIANT

[assembly: CLSCompliant(true)]

endif

And `Directory.Build.targets` that look like:

```xml
<ItemGroup>
 <Compile Include="$(MSBuildThisFileDirectory)SharedAssemblyInfo.cs" Visible="false" />
</ItemGroup>

<PropertyGroup>
 <ClsCompliant Condition=" '$(ClsCompliant)' == '' ">true</ClsCompliant>
 <DefineConstants Condition=" '$(ClsCompliant)' == 'true' ">$(DefineConstants);CLS_COMPLIANT</DefineConstants>
</PropertyGroup>

Why was this closed exactly? Is there a solution to this now?

I think this should be reopened since it's still an issue. The only solution ATM would be to write a custom target to generate another Assembly.cs (see last line of this duplicate issue's comment: https://github.com/Microsoft/msbuild/issues/3412#issuecomment-397638805)

Almost certainly. It's a shame that this capability isn't available just yet, as this was a feature I was looking into implementing to provide custom attributes to the AssemblyInfo.cs

Hopefully this is reconsidered and implemented.

@jamesmcroft:

Hopefully this is reconsidered and implemented.

I would at least like to see it put in a backlog, as this is still a requested feature, with no 'good' workarounds, as I would love remove my basic AssemblyInfo.cs file and just incorporate it into my MSBuild files.

@AndyGerlicher @rainersigwald Is there anyway this could be reopened and re-triaged? The issue was closed over 2 years ago, and seems to have picked up more support from when it was opened originally. If not, would it be appropriate to open a new issue?

EDIT: Here is a copy of the comment (from @AndyGerlicher) with the original triage review, and why it was originally closed:

Team Triage:
Closing this as won't fix. This would be difficult to implement (map literal to type) and a fairly easy workaround (use a C# file). If this becomes a more frequent ask we could revisit this in the future.

Reopening to swap with #3412

This is more possible now than before because the non-CodeDOM path could be removed (CodeDOM is now available on Core).

I still don't prioritize it very highly personally, and I don't understand most of the pushback above on having source files that include the attributes. You could condition adding that Compile item on whatever you would have conditioned the attribute on.

Reopening to swap with #3412

Thank you for that.

I still don't prioritize it very highly personally, and I don't understand most of the pushback above on having source files that include the attributes. You could condition adding that Compile item on whatever you would have conditioned the attribute on.

Yes, that is definitely the reasonable approach (as I do here, minus the conditions).

I personally think it would be easier to be able to place assembly attributes in a file I already have, rather than have a second one for them. Also, I would be able to use my MSBuild properties as parameters for the attributes.

I still don't prioritize it very highly personally, and I don't understand most of the pushback above on having source files that include the attributes. You could condition adding that Compile item on whatever you would have conditioned the attribute on.

I imagine that personally you're well versed in msbuild and have no issues understanding .csproj files which are very dense with items and conditions. For the majority of folks, dealing with .csproj, msbuild and other infrastructure is an unnecessary pain. A lot of us will be very happy with simplifications to .csproj. Please prioritize the issue according to everyday users and not expert maintainers like yourself.

Please prioritize the issue according to everyday users and not expert maintainers like yourself.

My concern about this feature is that it feels _much_ more complex than using C# code (where the attribute syntax is well established and documented) + simple MSBuild conditions. Look at the examples of how this would have to be implemented in MSBuild in the proposals above. How would you debug a problem arising from lack of, say _Treat_Parameter1AsLiteral?

The simplest implementation that's possible today (include an AssemblyInfo.cs file in your project) is very user-visible, if sometimes more verbose than ideal. Throwing code generation into the mix is not an obvious win from a maintainability/understandability standpoint IMO.

@rainersigwald, I agree that this _could_ easily be over-engineered and made complex, but it doesn't _have_ to be. There a several workable proposals here. Pardon my own bias, but I'm merely suggesting an _All or Nothing_ approach using the single, Boolean metadata item IsLiteral per attribute, regardless of how many parameters there. This couldn't be any more straight forward to implement. Either all of the parameters are literal, including all of their required escaping, formatting, etc, with the onus on the author or they're not, which is the same behavior as today. Yes - you _could_ have a configuration per each parameter, but that would be clunky and make any sane person's eyes bleed.

Your assertion that you can simply include or link files into a project is a workable solution and is what I suspect most people are doing today, but it's incongruent with how other build aspects are achieved. Your argument would hold weight if this task didn't exist at all, but it does. I don't think anyone here is asking for full, arbitrary code generation, but why should we have to set some standard assembly attributes purely through MSBuild and others through a hodgepodge of duplicated or linked source files?

Now that I've worked with .NET SDK style projects for a couple of years, I can say that the only time I ever use AssemblyInfo.cs is to address this problem; otherwise, my solutions and projects simply do not have it anymore. I can also say that - historically, the AssemblyInfo.cs file has been a PIA because a number of input values are variable in a build such as the version number, strong-naming, etc. This has lead to numerous custom build extensions and community build tasks to address the problem. It stands to reason that those same issues are why this built-in task was created in the first place. I think I speak for everyone when I say we're just looking for a consistent, out-of-the-box way to define all assembly-level attributes in one or more projects though MSBuild.

For an experienced MSBuild user such as myself, this lack of capability is merely an inconvenience. On the other hand, it's not that hard for me to see that for a less experienced person setting up file linking or shared projects in a solution with more than a few projects could be quite a pain.

Unlike most of the folks in this thread, I am _not_ an experienced MSBuild user. I'm trying to update an existing legacy .csproj file to the new SDK format, so that my class library can target multiple frameworks.

What is the suggested way to express [assembly: ComVisible(false)] and [assembly: System.CLSCompliant(true)] in the new VS2017/VS2019 .csproj file format, given the fact that there's no support for literal values when using <AssemblyAttribute>? This ticket was originally closed in July 2017 with a suggested workaround ("_use a C# file_"), but that does nothing for me. Nor can I find anything on StackOverflow or docs.microsoft.com that would help me understand where to create this file and what would go in it.

@jehrenzweig If you're changing an existing project, I would expect a Properties\AssemblyInfo.cs to already exist. You can place those lines there.

If you don't already have such a file, I would recommend creating a new file named AssemblyInfo.cs in your project (the name is not important but this matches convention) and adding the attributes there. That is essentially what we do in this repo:

https://github.com/microsoft/msbuild/blob/3885a205f40944174a3e14396547f13db1196b82/src/Build/AssemblyInfo.cs#L42-L43

The best documentation on this I could find was Applying Attributes at the Assembly Level. I agree that it might be nice to link to that from ComVisible and ClsCompliant pages, which are commonly applied to the whole assembly. Are you interested in filing a doc bug to that effect?

I thought I left a comment days ago but maybe I forgot to finish writing/sending it or can't find it now..

I think at some point there was a talk about asking roslyn to implement a compiler feature that would let you specify assembly attributes directly as input to the compiler. This would also fix the need for creating temporary source code files, which esp. for the target framework attribute can be quite a pain.

@rainersigwald Yep, my legacy projects each contain a Properties\AssemblyInfo.cs file, and they do exactly what you mentioned wrt [assembly: ComVisible(false)] and [assembly: CLSCompliant(true)].

However, I assumed that as part of modernizing my .csproj files, I should transition away from the use of this static file and instead declare equivalent nodes in the new .csproj file, then set <GeneratePackageOnBuild>true</GeneratePackageOnBuild> to ensure that an equivalent file is generated in the /obj folder when the project is built. What I'm hearing in this thread is leading me to believe I should keep the static AssemblyInfo.cs as-is... or am I mistaken?

I'd be happy to file a doc bug. However, I've never done that before... how do I get that started?

This issue appears to have lost momentum, but it has come up for us recently. Is there a plan for this to move forward?

I ran into this concern today as well, wanting to deduplicate declaring AWS's LambdaSerializerAttribute

It would be pretty cool to get this. otherwise I feel like I'm mixing old and new patterns

Why not <CLSCompliant>True</CLSCompliant>?

I would like to see this feature too, to be able to add NUnit attribute to configure tests that can be run in parallell

[assembly:NUnit.Framework.Parallelizable(NUnit.Framework.ParallelScope.Fixtures)]

I will add yet another voice in calling for this work to be done.

Honestly, the current implementation feels like a poor design and a mistake. Enabling developers to add assembly attributes to the csproj with the current syntax and then _magically_ quoting the values that are generated is... not great. Magical quoting is not good and is an impediment to this syntax doing what it ought to do: replacing the old AssemblyInfo.cs files with a more modern, centralized place to configure your project. Why should I need to have both?

What is the point of having <AssemblyAttribute> AT ALL in its current state when it's so limited? Why would you add syntax that allows assembly attributes to be configured in the csproj file, but ONLY make that syntax work for a small subset of attributes because you also added magical quoting to the values? Sorry for the rant, but this is frustrating.

So here's another use case I don't see mentioned: the ParallelizeAttribute for unit testing. Its constructor doesn't even take parameters so you have to specify them like this: Parallelize(Workers = 6, Scope = ExecutionScope.MethodLevel)

It would be really nice to be able to remove the AssemblyInfo files from our test projects and be able to specify this at the csproj level. But without having some functionality where the the <_Parameter1> and <_Parameter2> are inserted literally and not quoted this is completely impossible.

Developers have been asking for this functionality for 3 years because the current implementation of <AssemblyAttribute> is poor. Please prioritize this. It's fine to say that AssemblyInfo.cs is well documented and people know how to use it. That doesn't make it GOOD. That just says to me that you have done a poor job of documenting <AssemblyAttribute> and that the tooling ought to be advertising it better. And it ought to actually WORK for all cases.

With the current state of CA1416 emitting incorrect platform warnings if you have any attributes defined in .cs files, this becomes a bigger issue (for us anyway!)

It is rather easy to write a target that gets fairly close:

  <PropertyGroup>
    <_LiteralAssemblyFile Condition="'$(Language)' == 'C#'">$(IntermediateOutputPath)LiteralAssemblyAttributes.cs</_LiteralAssemblyFile>
    <CoreCompileDependsOn>
      AddLiteralAssemblyAttributes;
      $(CoreCompileDependsOn);
    </CoreCompileDependsOn>
  </PropertyGroup>

  <Target Name="AddLiteralAssemblyAttributes"
          BeforeTargets="CoreCompile"
          Condition="'$(Language)' == 'C#'"
          Inputs="$(MSBuildAllProjects)"
          Outputs="$(_LiteralAssemblyFile)">

    <ItemGroup>
      <Compile Include="$(_LiteralAssemblyFile)" />
    </ItemGroup>

    <ItemGroup>
      <_LiteralAssemblyAttrDecls Include="// &lt;autogenerated /&gt;" />
      <_LiteralAssemblyAttrDecls Include="extern alias %(LiteralAssemblyAttributeExternAlias.Identity);"
                                 Condition="'%(LiteralAssemblyAttributeExternAlias.Identity)' != ''" />
      <_LiteralAssemblyAttrDecls Include="[assembly: %(LiteralAssemblyAttribute.Identity)(%(LiteralAssemblyAttribute._Parameters))]"
                                 Condition="'%(LiteralAssemblyAttribute.Identity)' != ''" />
    </ItemGroup>

    <WriteLinesToFile File="$(_LiteralAssemblyFile)" Encoding="UTF-8" Lines="@(_LiteralAssemblyAttrDecls)" Overwrite="true" />

  </Target>

That implementation allows for any combination of attribute arguments, because it just drops the content of the _Parameters metadata into the parenthesis. It also allows you to utilize extern aliases.

For the specific case of CLSCompliant, using that would look like this:

  <ItemGroup>
    <LiteralAssemblyAttribute Include="global::System.CLSCompliantAttribute" Condition="'$(IsCLSCompliant)' != ''">
      <_Parameters>$(IsCLSCompliant)</_Parameters>
    </LiteralAssemblyAttribute>
  </ItemGroup>

If the IsCLSCompliant property is set (no matter its value), the property will be applied.

Having something just like this, taking the entire parameter list, would be very useful to have in the SDK. Naturally, it would be specialized on language, but I don't think that the content of the parameter list is ever going to be very different between C# and VB, and most users are likely using only one or the other. I think its reasonable to just require users of this variant to specialize on the language currently being compiled.

I got here while trying to address the CA1014 warning for a brand new project. It's worth noting that another workaround is simply to suppress this warning.

<NoWarn>$(NoWarn);CA1014</NoWarn>

I'm not convinced that CA1014 is _obsolete_ (see discussion). I would prefer an easy way to mark my assembly as CLS compliant, but if the cost of is maintaining a legacy AssemblyInfo.cs file for new projects, that's not worth it IMO.

I've just created the pull request #6285 that would allow parameter types to be specified. It's similar to what has been mentioned in here, with a couple of improvements:

  1. It's mostly language-independent, so it will generate the correct code for C# and VB.NET. Literal values _may_ work across languages, but it depends on the exact syntax that is used (enums will work across both languages).
  2. If the attribute comes from mscorlib, the parameter types will be inferred, which means you don't need to specify any additional metadata. :tada:

Here are some examples:

_The original issue from @AmadeusW:_

<ItemGroup>
    <AssemblyAttribute Include="System.CLSCompliantAttribute">
        <_Parameter1>true</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Generates:

[assembly: System.CLSCompliantAttribute(true)]

or

<Assembly: System.CLSCompliantAttribute(True)>

_OwinStartup example from @commonsensesoftware:_

<ItemGroup>
    <AssemblyAttribute Include="Microsoft.Owin.OwinStartup">
        <_Parameter1>Microsoft.Examples.Startup</_Parameter1>
        <_Parameter1_TypeName>System.Type</_Parameter1_TypeName>
    </AssemblyAttribute>
</ItemGroup>

Generates:

[assembly: Microsoft.Owin.OwinStartup(typeof(Microsoft.Examples.Startup))]

or

<Assembly: Microsoft.Owin.OwinStartup(GetType(Microsoft.Examples.Startup))>

_log4net example from @dasMulli:_

<ItemGroup>
    <AssemblyAttribute Include="log4net.Config.XmlConfigurator">
        <ConfigFileExtension>log4net</ConfigFileExtension> <!-- Parameters are still treated as strings by default -->
        <Watch>True</Watch>
        <Watch_TypeName>System.Boolean</Watch_TypeName>
    </AssemblyAttribute>
</ItemGroup>

Generates:

[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="log4net", Watch=true)]

or

<Assembly: log4net.Config.XmlConfigurator(ConfigFileExtension:="log4net", Watch:=True)>

_nUnit example from @Socolin:_

<ItemGroup>
    <AssemblyAttribute Include="NUnit.Framework.Parallelizable">
        <_Parameter1>NUnit.Framework.ParallelScope.Fixtures</_Parameter1>
        <_Parameter1_IsLiteral>true</_Parameter1_IsLiteral>
    </AssemblyAttribute>
</ItemGroup>

Generates:

[assembly: NUnit.Framework.Parallelizable(NUnit.Framework.ParallelScope.Fixtures)]

or

<Assembly: NUnit.Framework.Parallelizable(NUnit.Framework.ParallelScope.Fixtures)>
Was this page helpful?
0 / 5 - 0 ratings