In our project, we define two three build configurations, Release, Debug and 'Debug-plus-contracts'. These are defined as below:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug-plus-contracts'">
<DefineConstants>TRACE;DEBUG;CONTRACTS_FULL</DefineConstants>
<CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsRuntimeThrowOnFailure>False</CodeContractsRuntimeThrowOnFailure>
...
We define two debug configurations because 'Debug-plus-contracts' only builds on machines with Code Contracts for .NET installed
My question is, what happens to a line such as Contract.Requires(source.Any()) when it's compiled in each configuration? Suppose it's violated at runtime, in which configurations will anything happen?
I ask because I heard (somewhere) that in the Debug configuration, Contract.Requires is compiled to Debug.Assert . But evidence suggests this isn't true, we have some Contract.Requires that fail in Debug-plus but not in Debug.
Contract.Requires is declared as
[ConditionalAttribute("CONTRACTS_FULL")]
public static void Requires(
bool condition
)
[ConditionalAttribute("CONTRACTS_FULL")]
public static void Requires(
bool condition,
string userMessage
)
which means that if the CONTRACTS_FULL symbol is not defined, any call to Requires will be removed by the compiler completely. The check will not be performed at all.
If you do define the CONTRACTS_FULL symbol, but you don't have Code Contracts installed at build time or do not use its rewriter, any call to Requires will throw an exception regardless of whether the check passes, telling you that without the Code Contracts rewriter, it won't work.
Note: this is different from Contract.Requires<TException>. The latter does not have the same ConditionalAttribute, so will fail in any configuration unless the Code Contracts rewriter is used.
Related
I'm hoping to see CA1062, Validate Argument of public methods. But I'm not. It seems Code analysis is running because I see CA1822 for some items.
Here's an example of code I'm hoping would trigger CA1062.
public class FooController : BaseController<IFooProxy>
{
[HttpPost]
[Route("bar")]
public async Task<bool> Bar(string baz)
{
bool hasXyz = baz.Contains("xyz");
Console.WriteLine(hasXyz);
This is the csproj settings:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>preview</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
It's actually just a desperate attempt to turn everything on... I find myself missing the previous method, with a property page in the project to select rule set.
StyleCop is long gone, if the above doesn't work somehow, is there any open source way to run the rules?
If you want to enable all rules in .net 5, you should use:
<AnalysisLevel>5.0</AnalysisLevel>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
BTW in .net 6, it's:
<AnalysisMode>All</AnalysisMode>
https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#analysismode
I'm learning C#. (sorry I'm not native English speeker.)
I'm creating multi platform library in dotnet core.
I want to switch platform in one method.
I tryed RuntimeInformation.IsOSPlatform() then successed I want to do.
I heard that I can change the code to be executed for each OS by the property of project.csproj.
but I couldn't do that.
I want to do this.
project.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>MemoryInfo</PackageId>
<Version>1.0.0</Version>
<Authors>foobar</Authors>
<Company>foobar</Company>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
//(add comment) Switch Native Code
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="Linux/native.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsLinux)' == 'true'">
<Compile Include="Windows/native.cs" />
</ItemGroup>
</Project>
Program.cs
~~
public long GetMemorySize() //library method
{
NativeMemoryinfo mem = new NativeMemoryinfo();
return mem.GetMemorySize(); //access to each platform code
}
~~
Linux/native.cs
~~
class NativeMemoryinfo
{
public long GetMemorySize()
{
Code for Linux(/proc/meminfo...)
}
}
~~
Windows/native.cs
~~
class NativeMemoryinfo
{
public long GetMemorySize()
{
Code for Windows(Kernel32....)
}
}
~~
Maybe, I think I'm misunderstood or in the wrong way.
Can I do this way?
my research, I found something called "interop" this, but what is the relationship with this?
Thank you.
The feature you are trying to use with csproj is normally used to target the different frameworks in .NET, not to target different OS. See this: https://learn.microsoft.com/en-us/dotnet/standard/frameworks
What you are trying to do can be achieved by interfaces. Have an interface INativeMemoryinfo and initialize it to the windows or linux version based on if (RuntimeInformation.IsOSPlatform() == "").
Side note: since you are working o a library, I suggest using .net standard instead of .net core.
if (RuntimeInformation.IsOSPlatform() == "").
I have these build configurations:
These platform configurations:
And these compiler conditionals:
NET40
NET45
My solution is a huge API that consists in 20 solutions, some of those solutions consumes Async keywords and other beneffits that are available only from .NetFx 4.5.
That part of the code I have it in a conditional in this way:
#If NET45 then
Sub Async
...
End Sub
#Else
Sub
...
End Sub
#End If
Then, what I'm trying to do is clear, the .NetFx 4.5 build configurations should compile the block of the NET45 conditional, and the .NetFx 4.0 build configurations should compile the block of the #Else part.
The problem I found is that if I change the application target framework in the project settings, the change persist in all the other build configurations, and I would like to avoid that persistance.
So how I can do this?.
Note:
I marked this question with the C# tag because is a general Visual Studio environment question, but I will clarify that my solutions are written in Vb.Net, because I know there are some big differences between the C# project settings and also their compiler params so maybe a C# advanced answer could not help me.
My suggestion is to get rid of conditional statements in code by moving platform/target/etc sencitive code in partial files. Then I would go to project file and would make the icluded files sensitive on particular condition using all the fuctionality ms-build provides
Example:
Create brand new VB Console App in Visual Studio
add three class files ClassDotNetFeatures40.vb, ClassDotNetFeatures45.vb, GenericClass.vb
Add the following code
in GenericClass.vb
Partial Public Class GenericClass
Public Sub Hello()
Console.Write("Hello ")
End Sub
End Class
in ClassDotNetFeatures40.vb
Partial Public Class GenericClass
Public Sub Word()
Console.Write("4.0 Word!")
End Sub
End Class
in
ClassDotNetFeatures45.vb
Public Class GenericClass
Public Sub Word()
Console.Write("4.5 Word!")
End Sub
End Class
Put the following code in Module1.vb
Sub Main()
Dim o = New GenericClass()
o.Hello()
o.Word()
End Sub
Save all
Right click on your solution and press Unload Project
Right click on the project file and press Edit Project
Find the following lines:
<Compile Include="ClassDotNetFeatures40.vb" />
<Compile Include="ClassDotNetFeatures45.vb" />
and replace them with
<Compile Condition="'$(Configuration)' == 'Debug'" Include="ClassDotNetFeatures40.vb" />
<Compile Condition="'$(Configuration)' == 'Release'" Include="ClassDotNetFeatures45.vb" />
press save
right click on project file and press Reload
now when you run the project undo debug you will get:
Hello 4.0 Word!
undo release you willl get:
Hello 4.5 Word!
You will need to change project files manually (I've played with csproj - hopefully vbproj works in the same way).
All project configurations properties described in the sections like this one:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
...
</PropertyGroup>
Please notice Condition statement - that describes that this particular property set specified for Debug, AnyCPU configuration.
What you need to do is to move TargetFrameworkVersion property from general top level to configuration-specific levels, something like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<!-- general properties here - removing framework related... -->
<!--<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>-->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<!-- Use 4.0 for Debug -->
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<!-- other properties here... -->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<!-- Use 4.5 for Release -->
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<!-- other properties here... -->
</PropertyGroup>
Please notice that VS.Net GUI does NOT support this, and will not display correct values in the project Properties window; though it will use these values for build.
Depending on complexity of your solution, you might found some other artifacts, as VS.Net will not reload project properly, but at least that should work with build from console.
Additionally, you might need to use similar conditional "hacks" to reference correct libraries.
I'm having trouble in a standard WinForms app with Conditional Compilation
I have 2 .csproj's that reference the same Program.cs file (They live in the same folder on disk too)
In Project1.csproj I have a conditional compilation symbol defined called CONDITION_1
In Project2.csproj I have a conditional compilation symbol defined called CONDITION_2
static void Main()
{
#if CONDITION_1
DoSomething();
#elif CONDITION_2
DoSomethingElse();
#else
DoAnotherThing();
#endif
ContinueDoingStuff();
}
These symbols are defined the project settings for "All Configurations". In my debug environment everything works great. However when I do a fresh checkout of source and build on my build machine I open up Project2.exe in my decompiler and I notice that my source is like this
static void Main()
{
DoAnotherThing();
ContinueDoingStuff();
}
If I open up the solution file in Visual Studio and do a regular build (No clean, no rebuild, no code changes)
I open up the exe and notice the correct decompiled source for Project2.exe...
static void Main()
{
DoSomethingElse();
ContinueDoingStuff();
}
Any ideas? Is there a possibility of conditional symbols not being set correctly at compile time?
Alright, Alright. I figured it out. I feel a little silly, but at the same time this would be easy to miss.
Here's an excerpt of my .csproj
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<ResGenToolArchitecture>Managed32Bit</ResGenToolArchitecture>
<OutputPath>..\BinPC\Release\</OutputPath>
<DefineConstants>CONDITION_2</DefineConstants>
<Optimize>false</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
and here's the one for Release|AnyCPU
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<ResGenToolArchitecture>Managed32Bit</ResGenToolArchitecture>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\BinPC\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
In Visual Studio, I had the CONDITION_2 set for the x86 target platform, but not AnyCPU. This was true of both .csproj's. However, The first csproj was building in Release|x86 mode by default, and the second was building in Release|AnyCPU (which didn't have the symbol defined as shown above)
Long story short, lesson learned. Always always check your symbol definitions in the project.
Right click the .csproj file, click properties.
In the build tab, Select All Configurations, and All Platforms(<-- This is what I forgot)
Now define the conditional compilation symbol and it will get set for all configurations
I have some code which makes use of Extension Methods, but compiles under .NET 2.0 using the compiler in VS2008. To facilitate this, I had to declare ExtensionAttribute:
/// <summary>
/// ExtensionAttribute is required to define extension methods under .NET 2.0
/// </summary>
public sealed class ExtensionAttribute : Attribute
{
}
However, I'd now like the library in which that class is contained to also be compilable under .NET 3.0, 3.5 and 4.0 - without the 'ExtensionAttribute is defined in multiple places' warning.
Is there any compile time directive I can use to only include the ExtensionAttribute when the framework version being targetted is .NET 2?
The linked SO question with 'create N different configurations' is certainly one option, but when I had a need for this I just added conditional DefineConstants elements, so in my Debug|x86 (for instance) after the existing DefineConstants for DEBUG;TRACE, I added these 2, checking the value in TFV that was set in the first PropertyGroup of the csproj file.
<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">RUNNING_ON_4</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>
You don't need both, obviously, but it's just there to give examples of both eq and ne behavior - #else and #elif work fine too :)
class Program
{
static void Main(string[] args)
{
#if RUNNING_ON_4
Console.WriteLine("RUNNING_ON_4 was set");
#endif
#if NOT_RUNNING_ON_4
Console.WriteLine("NOT_RUNNING_ON_4 was set");
#endif
}
}
I could then switch between targeting 3.5 and 4.0 and it would do the right thing.
I have a few suggestions for improving on the answers given so far:
Use Version.CompareTo(). Testing for equality will not work for later framework versions, yet to be named. E.g.
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
will not match v4.5 or v4.5.1, which typically you do want.
Use an import file so that these additional properties only need to be defined once. I recommend keeping the imports file under source control, so that changes are propagated along with the project files, without extra effort.
Add the import element at the end of your project file, so that it is independent of any configuration specific property groups. This also has the benefit of requiring a single additional line in your project file.
Here is the import file (VersionSpecificSymbols.Common.prop)
<!--
******************************************************************
Defines the Compile time symbols Microsoft forgot
Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx
*********************************************************************
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5.1')))) >= 0">$(DefineConstants);NETFX_451</DefineConstants>
<DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) >= 0">$(DefineConstants);NETFX_45</DefineConstants>
<DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) >= 0">$(DefineConstants);NETFX_40</DefineConstants>
<DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) >= 0">$(DefineConstants);NETFX_35</DefineConstants>
<DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) >= 0">$(DefineConstants);NETFX_30</DefineConstants>
</PropertyGroup>
</Project>
Add Import Element to Project File
Reference it from your .csproj file by adding at the end, before the tag.
…
<Import Project="VersionSpecificSymbols.Common.prop" />
</Project>
You will need to fix up the path to point to the common/shared folder where you put this file.
To Use Compile Time Symbols
namespace VersionSpecificCodeHowTo
{
using System;
internal class Program
{
private static void Main(string[] args)
{
#if NETFX_451
Console.WriteLine("NET_451 was set");
#endif
#if NETFX_45
Console.WriteLine("NET_45 was set");
#endif
#if NETFX_40
Console.WriteLine("NET_40 was set");
#endif
#if NETFX_35
Console.WriteLine("NETFX_35 was set");
#endif
#if NETFX_30
Console.WriteLine("NETFX_30 was set");
#endif
#if NETFX_20
Console.WriteLine("NETFX_20 was set");
#else
The Version specific symbols were not set correctly!
#endif
#if DEBUG
Console.WriteLine("DEBUG was set");
#endif
#if MySymbol
Console.WriteLine("MySymbol was set");
#endif
Console.ReadKey();
}
}
}
A Common “Real Life” Example
Implementing Join(string delimiter, IEnumerable strings) Prior to .NET 4.0
// string Join(this IEnumerable<string> strings, string delimiter)
// was not introduced until 4.0. So provide our own.
#if ! NETFX_40 && NETFX_35
public static string Join( string delimiter, IEnumerable<string> strings)
{
return string.Join(delimiter, strings.ToArray());
}
#endif
References
Property Functions
MSBuild Property Evaluation
Can I make a preprocessor directive dependent on the .NET framework version?
Conditional compilation depending on the framework version in C#
Property groups are overwrite only so this would knock out your settings for DEBUG, TRACE, or any others. - See MSBuild Property Evaluation
Also if the DefineConstants property is set from the command line anything you do to it inside the project file is irrelevant as that setting becomes global readonly. This means your changes to that value fail silently.
Example maintaining existing defined constants:
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">V2</CustomConstants>
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">V4</CustomConstants>
<DefineConstants Condition=" '$(DefineConstants)' != '' And '$(CustomConstants)' != '' ">$(DefineConstants);</DefineConstants>
<DefineConstants>$(DefineConstants)$(CustomConstants)</DefineConstants>
This section MUST come after any other defined constants since those are unlikely to be set up in an additive manner
I only defined those 2 because that's mostly what I'm interested in on my project, ymmv.
See Also: Common MsBuild Project Properties
Pre-defined symbols for target frameworks are now built into the version of MSBuild that is used by the dotnet tool and by VS 2017 onwards. See https://learn.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-a-target-framework for the full list.
#if NET47
Console.WriteLine("Running on .Net 4.7");
#elif NETCOREAPP2_0
Console.WriteLine("Running on .Net Core 2.0");
#endif
I would like to contribute with an updated answer which solves some issues.
If you set DefineConstants instead of CustomConstants you'll end up, in the Conditional Compilation Symbols Debug command line, after some framework version switch, with duplicated conditional constants (i.e.: NETFX_451;NETFX_45;NETFX_40;NETFX_35;NETFX_30;NETFX_20;NETFX_35;NETFX_30;NETFX_20;).
This is the VersionSpecificSymbols.Common.prop which solves any issue.
<!--
*********************************************************************
Defines the Compile time symbols Microsoft forgot
Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx
*********************************************************************
Author: Lorenzo Ruggeri (lrnz.ruggeri#gmail.com)
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Choose>
<When Condition=" $(TargetFrameworkVersion) == 'v2.0' ">
<PropertyGroup>
<CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
</PropertyGroup>
</When>
<When Condition=" $(TargetFrameworkVersion) == 'v3.0' ">
<PropertyGroup>
<CustomConstants >$(CustomConstants);NETFX_30</CustomConstants>
<CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
</PropertyGroup>
</When>
<When Condition=" $(TargetFrameworkVersion) == 'v3.5' ">
<PropertyGroup>
<CustomConstants >$(CustomConstants);NETFX_35</CustomConstants>
<CustomConstants >$(CustomConstants);NETFX_30</CustomConstants>
<CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5.1')))) >= 0">$(CustomConstants);NETFX_451</CustomConstants>
<CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) >= 0">$(CustomConstants);NETFX_45</CustomConstants>
<CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) >= 0">$(CustomConstants);NETFX_40</CustomConstants>
<CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) >= 0">$(CustomConstants);NETFX_35</CustomConstants>
<CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) >= 0">$(CustomConstants);NETFX_30</CustomConstants>
<CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('2.0')))) >= 0">$(CustomConstants);NETFX_20</CustomConstants>
</PropertyGroup>
</Otherwise>
</Choose>
<PropertyGroup>
<DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
</PropertyGroup>
</Project>
Use reflection to determine if the class exists. If it does, then dynamically create and use it, otherwise use the .Net2 workaround class which can be defined, but not used for all other .net versions.
Here is code I used for an AggregateException which is .Net 4 and greater only:
var aggregatException = Type.GetType("System.AggregateException");
if (aggregatException != null) // .Net 4 or greater
{
throw ((Exception)Activator.CreateInstance(aggregatException, ps.Streams.Error.Select(err => err.Exception)));
}
// Else all other non .Net 4 or less versions
throw ps.Streams.Error.FirstOrDefault()?.Exception
?? new Exception("Powershell Exception Encountered."); // Sanity check operation, should not hit.