Presently with our build some assembly classes have the #nullable enable/disable/restore attribute set in some of the source files.
As a way of improving our quality I would like to make the build fail if someone sets this pre-processor directive.
If the project is set to <Nullable>enable</Nullable> or <Nullable>disable</Nullable>, that is fine, but within a class file make it fail.
Is there a way to make this happen or do I just need to go through the source files and resolve them. (But that doesn't stop someone in the future bypassing by doing this again).
Related
In a C# project, we increment versions manually and have a top-level file MyAssemblyInfo.cs containing
[assembly: AssemblyVersion("31.1.13")]
[assembly: AssemblyFileVersion("31.1.13")]
[assembly: AssemblyInformationalVersion("31.1.13-V1.0.0")]
I'd like to avoid repeating the version. For example, with the C preprocessor, one could write this as
#define VERSION "31.1.13"
[assembly: AssemblyVersion(VERSION)]
[assembly: AssemblyFileVersion(VERSION)]
[assembly: AssemblyInformationalVersion(VERSION "-V1.0.0")]
Is there a way to achieve this in C# without using any external tools?
[assembly: AssemblyFileVersion(AppVersion.Version)]
[assembly: AssemblyInformationalVersion(AppVersion.Version)]
// you can use + here but the format you use shows as invalid version for me
[assembly: AssemblyVersion(AppVersion.Version)]
// this must be at the end of the global declarations
internal class AppVersion
{
public const string Version = "31.1.13";
}
The short answer to your question is no you cannot use pre-processor directives in this manner. One work-around is stated in the other answer and involves defining a constant. This would be perhaps the only solution for old-style projects.
If you are using the SDK-style project format however, AssemblyInfo attributes can be set in the project file. The following properties correspond to the attributes defined above:
InformationalVersion -> AssemblyInformationalAttribute
AssemblyVersion -> AssemblyVersion
FileVersion -> AssemblyFileVersion
Version -> Can map to any/all of the above if they are omitted
Has special logic to remove suffix patterns when setting File/Assembly version
All other Assembly attributes can be set via properties so long as GenerateAssemblyInfo is true. This requires you to remove your AssemblyInfo.cs file to prevent duplication of those attributes.*
The way I have typically solved this is to define my own set of version-related properties in my project file (see notes below for why). For example, you might have the following:
<PropertyGroup>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<BaseVersion>31.1.13</BaseVersion>
<InfoSuffix>v1.0.0</InfoSuffix>
</PropertyGroup>
<PropertyGroup>
<!-- note: "-v1" is invalid in an Assembly Version; I assumed you meant this on the Informational one -->
<InformationalVersion>$(BaseVersion)-$(InfoSuffix)</InformationalVersion>
<AssemblyVersion>$(BaseVersion)</AssemblyVersion>
<FileVersion>$(BaseVersion)</FileVersion>
</PropertyGroup>
You could pass any of these properties on the command line to MSBuild or dotnet build if you wanted to explicitly override them:
dotnet build -p:InfoSuffix=v2.0+1234
# alternatively, specify the MSBuild properties directly
dotnet build -p:FileVersion=1.2.3.4 -p:InformationalVersion=5.6.7.8
If you have multiple projects you could set all of these properties inside of a Directory.Build.props file so that they apply to all of your projects at once.
Some Notes
First, if you are using the Directory.Build.props mechanism and running any sort of post-processing tasks or something like SourceLink (which appends info to the Informational version) you will need to move the second property group into a Directory.Build.targets file instead. You'd also need to do this if you wanted to pass the properties explicitly to other tooling, such as dotnet pack.
Second, MSBuild has a series of other version-related properties**. These include (but are not limited to):
SourceRevisionId - appended to the Informational version (our InfoSuffix)
VersionPrefix - A Base version
VersionSuffix - Sets prerelease label
PackageVersion - generates nuget version
You can use these in combination with those specified above instead. You can even pass them as properties on the command-line if you wanted to override the values in the project/props/targets files.
Now you might wonder why I bother defining my own properties instead of using the built-in ones. The reason is that MSBuild has some strange and sometimes unexpected behavior depending on how combinations of these properties are set. Anecdotally, I noticed different behavior depending on which values I was setting and whether I was building in VS, on the command line, or using dotnet pack. It is because of this I prefer defining my own properties and then using them to explicitly set the others. It also allowed me to use conditional MSBuild logic to set certain parts of the version easier. Though I do admit, your mileage may vary.
* If you want to keep the AssemblyInfo.cs file for other attributes, you'd have to disable their automatic generation by leveraging the individual GenerateX MSBuild properties.
** For a good explanation of some of these properties, see this blog post.
I hava a quite complex solution, containing 10 projects aside from Test projects.
It is a network of distributed applications & services that communicate using remoting; therefore having the proper referenced assemblies (& versions) is crucial. That's why I want the whole thing to be compiled and schrink-wrapped in ONE build.
One of the applications is a demo/analysis-tool that runs a subprocess of another - much bigger - application based on the user's input and displays the results; That way engineers have a tool to help tweak their settings for "the big computation". Obviously that subprocess is contained in another assembly, and a big part of te results presented to the engineers is generated by
#if ENABLE_TRACE_MATCHING
Trace.WriteLine("Some engineering output");
#endif
My problem is that Conditional Compilation Symbols in the project settings are limited to that project's assembly, and do not propagate over referenced assemblies.
How can I configure my build in such a way that all projects will be built without ENABLE_TRACE_MATCHING being defined, except for the one debug/analysis-app project where all referenced projects/assemblies must be compiled with ENABLE_TRACE_MATCHING being defined
I also cannot replace #if ENABLE_TRACE_MATCHING by #if DEBUG, since that would enable a whole lot of different output our engineers wouldn't know how to handle.
Thanks in advance.
PS: If you think my code smells, then I agree. Additionally: It's mostly not my code ;)
You need to learn more about Microsoft Build, which is an out-of-the-box Microsoft .NET tool present in any framework's installation.
Using MSBuild you can define these "symbols" (properties) and a batch of commands (targets).
That's you can create a MSBuild script that imports default Visual Studio targets from all projects in your solution, and declare in the script these properties ("symbols").
In fact, the property to set such symbols already exists: "DefineConstants".
So, since you have it, you can have that MSBuild script that provides that property value, re-declaring it there, so, ALL MSBuild targets will be knowing about these symbols.
EDIT:
Check this other question too:
msbuild, defining Conditional Compilation Symbols
I was experimenting with the Assembly and File version number. Though my program runs well from the IDE, but after creating a Setup file and installing the application crashes with InvalidDeploymentException.
What should I do to resolve the matter?
The [AssemblyVersion] and [AssemblyFileVersion] attributes play different roles. [AssemblyVersion] is only visible to managed code and is important for the GAC. Whenever a make a breaking change in the assembly's public interface you should bump this number up.
The compiler embeds an unmanaged resource in an assembly with the /win32res command line option. This includes the VERSIONINFO resource, readable by all unmanaged code, including the shell. It determines what you see when you right-click the assembly in Explorer and look at the Details property page. The "File version" value shown there is set by the [AssemblyFileVersion] attribute. The [AssemblyVersion] value isn't visible there, Explorer doesn't (yet) know how to read that.
It is up to you to decide how to use this attribute. The crash indicates that there's some minimum sanity checking going on in the deployment code, never tried to get it wrong myself to see what would happen. Making them the same would however make a lot of sense.
Microsoft uses [AssemblyFileVersion] a different way, they automatically increment it for each build and nail [AssemblyVersion] down. That's a good idea and the strategy I use. What is however quite ironic is that the automatic version increment feature works exactly backwards, it can only auto-increment [AssemblyVersion]. Sigh.
Try using the fusion log viewer to see what's not being loaded in your deployed app.
I have turned on "Treat warnings as errors" for my VS project which mean that I get errors for missing documentation (nice reminder for this particular project).
However, part of the code is generated by a custom tool which does not insert xml documentation so I'm looking for away to ignore the missing xml documentation for the generated code only, not for the entire project. I have no influence on the actual file generated and cannot really insert anything in the file (as it is regenerated frequently by the tool) so I looking for something existing outside the generated file (the classes that are generated are partial, if that helps)
The best way to avoid having warnings or Code Analysis error on generated code is to decorate your generated classes with the GeneratedCodeAttribute and make the code file ends with the *.generated.cs pattern.
If your code files also have a file header, you should had these tags:
//----------------------
// <auto-generated>
// Tool description
// </auto-generated>
//----------------------
This is not mandatory, but if you have code file header it is a good practice.
This way, FxCop and other tools like StyleCop will not analyse your code anymore.
What is abnormal is that your code generation tool is not decortating your code elements with the attribute mentioned above. Try to look if there is an option to enable in your tool settings or contact the developing team.
EDIT:
Does the generated classes are partial classes and do the actual classes name and number changes often? Because if the generated code content is not moving a lot, what you can do is simply create another code file and just declare the generated partial class to decorate them with the GeneratedCodeAttribute. One time it saved my life (and my time!).
EDIT: See comments indicating that this doesn't work with C# 4. I'm not clear whether it worked in earlier versions of the compiler. The C# 4 spec makes this pretty clear though. Section 2.5.8.1 states:
A #pragma warning restore directive restores all or the given set of warnings to the state that was in effect at the beginning of the compilation unit. Note that if a particular warning was disabled externally, a #pragma warning restore (whether for all or the specified warning) will not re-enable that warning.
Jeff has a workaround in a blog post - basically to reprocess autogenerated code as part of the build.
As Tom says, you can add an "ignore" to the whole project (Build / Suppress Warnings - enter 1591 as the warning number) - but then you can restore the warning yourself at the top of each of your non-generated files:
#pragma warning restore 1591
It's pretty ugly, but it works (I've just tested it).
The best you will be able to do in this is suppress the particular warning in the project that contains the generated code. You can do this in the Project Properties on the Build tab. It's not ideal, but short of putting the pragmas in the generated code, it is the best you can do.
I've written an abstract base class for unit tests that sets up just enough environment for our tests to run. The class exposes some of the runtime environment bits as properties whose types vary test by test (the property types are type arguments specified in the inheriting, concrete test class).
This is all well and good, except a co-worker noticed that he can't view any of the class' properties in the debugger. Turns out the reason is that he had no fields defined in his inheriting class, and the CLR optimized something or other away, so the debugger couldn't display the properties. Is it possible to prevent this in the base class somehow, or do I have to resort to telling everyone they need to define at least one field which is used somewhere during the tests?
Edit:
Sounds like a likely culprit should be the optimization/debug settings. That said, I'm building the app from Visual Studio in Debug mode, I've double-checked that all projects are set for a debug build, and none of the projects in this solution have the Optimize flag set.
Perhaps it would also be relevant to note that I'm using MSTest and the Visual Studio test runner.
Edit 2:
By "can't view properties" I'm referring to when I evaluate the property in Quickwatch and get a red exclamation mark and a text "Could not evaluate expression" error text. And lest you think I'm entirely off base with my suspicions, adding an instance field that gets initialized in the test initialize method makes the problem go away...
Edit 3:
Checked the build output. I notice that the compiler is invoked with these options:
/debug+
/debug:full
/optimize-
/define:DEBUG,TRACE
I should think that would be enough to stop this from happening, but there you go. :)
I've encountered this same problem before, and it's invariably due to the fact that Debug mode has been turned off in some way. Try checking each of the following:
The current build configuration for the solution and the appropiate project(s) is Debug.
In the Build tab of the property pages, the Optimize code checkbox is unchecked.
If this is all correct, then I recommend you paste the text written to the Output window here so can we can potentially spot any more unusual cause of the issue.
Make sure your arent trying to debug your release build.
All of these compile settings set behind these configurations.
The debug version is one for debugging ;-)
In my case, the project configuration was correct, but my code was the result of a decompilation from ILSpy, and it had assembly attributes like the following:
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
Removing these attributes fixed the debugger...