Relationship between PlatformTarget and RuntimeIdentifier in .NET 5 - c#

I'm confused about the relationship between RuntimeIdentifier and PlatformTarget in .NET 5.
My .NET 5 C# project runs and publishes fine, but PlatformTarget is 'AnyCPU' and RuntimeIdentifier is 'win-x64'.
From Microsoft's documentation, RuntimeIdentifier is "used to identify target platforms where the application runs".
https://learn.microsoft.com/en-us/dotnet/core/rid-catalog
Isn't this what 'PlatformTarget' is supposed to do?
I tried using RuntimeIdentifers (plural) which I've seen before (perhaps .NET Core 3?), but the project doesn't compile with this:
<RuntimeIdentifiers>win-x64;win-x86</RuntimeIdentifiers>
From my current project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> -> RuntimeIdentifier is required since SelfContained is 'true'
...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
...
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
...
</Project>

The term of "AnyCPU" means that it will have binaries that is compatible for both 32bit and 64bit. This means it is not meant to be specific for either 32bit and 64bit, and this "AnyCPU" is mostly used in older .NET Framework project, not commonly used in SDK project style that is mainly used in .NET Core projects.
These .NET Core projects include single targeted .NET Core project or multi targeted .NET Core projects, such as those that has with multiple target (usually known as TFM).
Since .NET 5.0, the TFM can be cross platform TFM such as net5.0 or .NET 5.0 target with specific OS, such as net5.0-windows or net5.0-ios. IF you want to specify .NET 5.0 class libraries that runs on Windows for 64bit, you have to specify both TFM and RuntimeIdentifier (usually called RID).
Therefore we should not use the older "AnyCPU" with combined Conditional "Release|AnyCPU". By default, any TFM without RID is always the same as having AnyCPU in these SDK project model.
For example, for targeting .NET 5.0 on Windows on both 64bit and 32bit:
<PropertyGroup>
<TargetFramework>net5.0-windows;</TargetFramework>
<RuntimeIdentifier>win-x64;win-x86</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
</PropertyGroup>
The SDK projects has attribute Sdk="..." in the header, and this SDK will drive the reference resolutions for your project.
I recommend you to remove those PropertyGroup that has older "Release|AnyCPU" or "Debug|AnyCPU" combinations, because these combined MSBuild project syntax is not relevant for any .NET Core SDK project model.
One thing to be noted is when you have more than one RID like those in the sample above, you have to run publish for each RID you support.
Therefore, in order to have both 64bit and 32bit, you have to publish separately for each RID.
SO you have to run these:
dotnet publish yourproject.csproj -r win-x64
dotnet publish yourproject.csproj -r win-x86
See also the official doc on publishing single file executable:
https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file

It's true that the pile of properties is difficult to understand (Platform, Platforms, PlatformTarget, TargetFramework (Moniker), and RuntimeIdentifier). Here is my current take on the properties - keep in mind that I have still have questions because of the results of my experiments).
Platform, Platforms, and PlatformTarget: These properties are kind of synonyms (Platforms plural is required to hold multiple platform specifiers) and refer to the bitness/CPU architectures such as AnyCPU, x86, x64, and Arm. I always think "hardware" for this property.
TargetFramework: This specifies a NET Framework API version that the app is intended to run on (net48, net5.0, wp (windows phone), netcore, netcoreapp2.1, etc). Some of these are cross-platform (cross-hardware?).
RuntimeIdentifier: The runtime specifier is the cross-platform glue part of the stack and sits here in the abstraction level stack: app->framework->runtime->platform. If your library was configured for both x86 and x64, msbuild publishes the variations to bin/debug/framework/win-x86/ and bin/debug/framework/win-x64/ (so that win-x86 and win-64 and other runtimes are siblings in the output folder tree).
You must publish/build your project once (with different property values) for each runtime that you use.
To the original question, you can see that AnyCPU is a platform specifier and RIDs are runtime identifiers. The RID monikers often include x86/x64 in their names for communication convenience, but those strings do not specify the platform. Only the Platform specifier can specifier the platform.
In operation, you can specify Platform AnyCPU for building the app (indicating the app binary is appropriate for x86, x64, arm, etc., and then use a specific runtime in the publication operation (which will assign the 32/64 bitness etc. to the published binary).
My NET 5 project files require the definition of both a Platform and a TargetFramework if no MSBuild override arguments are provided. MSBuild assumes win-x64 for the runtime on my projects that define Platform=AnyCPU and TargetFramework=net5.0-windows7.0.
It's worth saying that MSBuild can get its properties from three places: the MSBuild project file; the Visual Studio dialog sheets (probably override the project file values; and the MSBuild command line (override the project file values).

Related

How to globally specify android and ios are not supported in a .net 5.0 app

I'm using System.IO.Ports in a .net 5.0 project. This generates warnings as follows:
warning CA1416: This call site is reachable on all platforms. 'SerialPort.Close()' is unsupported on: 'ios', 'android'.
I can put guards in to check the platform before calling SerialPort methods, but I calls these in a lot of places.
Instead I was wondering if there was a way to tell .net at the project level that I don't support ios and android.
I don't want to suppress all CA1416 warnings because I do support:
Windows and...
Linux
...and want to be warned if I use something not available on those platforms.
TL; DR: essentially your question is divided into two parts and sadly only one appears to be possible.
1. How to exclude an OS from your project
OP:
How to globally specify android and ios are not supported in a .net 5.0 app
I can put guards in to check the platform before calling SerialPort methods, but I calls these in a lot of places.
Yes. Instead of allowing all destinations that say net5.0 provides, you can fine-tune your output by using the plural <TargetFrameworks> element (note the "s") instead of the default <TargetFramework>1.
Here's an example project that targeting net5.0:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
The above project will let your app compile for all OSs including Windows, Mac OS, Android and iOS.
Instead I was wondering if there was a way to tell .net at the project level that I don't support ios and android.
Yes. To achieve that we would use OS-specific TFMs instead of the general net5.0. 1
change <TargetFramework> to <TargetFrameworks>
change net5.0 to net5.0-windows;net5.0-macos
...like so:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0-windows;net5.0-macos</TargetFrameworks>
</PropertyGroup>
</Project>
With this iOS and Android support have been removed.
2. How to exclude Linux
OP:
I don't want to suppress all CA1416 warnings because I do support Windows and Linux
This is where it gets tricky as Microsoft has not defined a TFM for Linux (too many flavours?) so the only way to specify that you want to target Linux is to use net5.0. Linux support is implied. Unfortunately this re-activates support for iOS and Android the very thing we just removed. It also activated MacOS which you might not care about.
1 Target frameworks in SDK-style projects
, MSFT
Paraphrasing from the warning documentation;
You can specify multiple "target framework moniker" (TFM) in your projects <TargetFramework> or <TargetFrameworks> tags. If the TFM has a target platform, the .NET SDK injects a SupportedOSPlatform attribute with the targeted platform name in the AssemblyInfo.cs file, which is consumed by the analyzer.
The analyzer doesn't seem to care which frameworks you target, just the SupportedOSPlatform / UnsupportedOSPlatform attributes compiled into your assembly.
While you can't specify a set of target frameworks that would allow windows and linux, but nothing else. That doesn't stop you from defining multiple SupportedOSPlatform attributes in your AssemblyInfo.cs;
using System.Runtime.Versioning;
[assembly: SupportedOSPlatform("windows")]
[assembly: SupportedOSPlatform("linux")]
Which I believe should limit how the CA1416 warnings are generated. Even if your project references <TargetFramework>net5.0</TargetFramework>.

Publish .NET Core app without EXE and runtimes

My question is more academic than a real problem: .NET Core has three different publish modes (see here). I'm interested in framework-dependent deployment (FDD) and framework-dependent executable (FDE).
As far as I understand, with framework-dependent deployment the publish folder will contain the project DLL, 3rd party DLLs and the runtimes. The app can only be started via dotnet run.
Using framework-dependent executable, the publish folder will contain the project DLL, the project EXE and 3rd party DLLs, but no runtimes. The app can be both launched via the EXE or dotnet run.
Now I'd like to host an ASP.NET Core Web API within IIS. The .NET Core runtime is installed system-wide. So I don't need the EXE and the included runtimes, but both FDE and FDD contain either one of them.
Now my question: is there any publish mode so that the publish folder contains a mixture of FDE and FDD, i. e. the DLL, 3rd party DLLs, no EXE and no runtime?
changing application .csproj file like below prevents exe to be written to publish path:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<UseAppHost>false</UseAppHost>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Here <UseAppHost>false</UseAppHost> makes trick

Enviroment.GetCommandLineArgs()[0] is a .dll file insted of of an .exe file [duplicate]

I created a .NET Core application (v1.1) in Visual Studio 2017. When I compile it, I get a DLL file produced instead of the expected EXE file for the built project. I did check the csproj file and confirmed the output type is set to exe, but no dice.
Why is Visual Studio 2017 is still producing a DLL file?
I'm sure it's a quick setting somewhere that I forgot...
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core.EF.SqlServer\Core.EF.SqlServer.csproj" />
</ItemGroup>
</Project>
Update 2019:
.NET Core 3.0+ projects will now include an executable for the platform you build on by default. This is just a shim executable and your main logic is still inside a .dll file.
But .NET Core 3.0 also introduced single-file deployments so deploying with
dotnet publish -r win-x64 -p:PublishSingleFile=True --self-contained false
will create a single .exe file containing all your dependencies. You can change --self-contained to true to also include the .NET Core Runtime as well so .NET Core does not need to be installed globally on the target machine.
Original
.NET Core applications are supposed to be .dllfiles. OutputType set to Exe in this case means "executable" and does everything necessary to ensure that the output is runnable (entry point from Main() method, .runtimeconfig.json file). The resulting DLL file is meant to be run using:
dotnet yourapp.dll
This DLL file works across all platforms that are supported by the .NET Core runtime (Windows, Linux, and macOS). This is called a "portable" or "framework dependent" deployment.
If you want really a .exe file, consider self-contained deployments. This will create an output that contains its own copy of the .NET Core runtime and an yourapp.exe file - but it also increases the size of the published application and it needs to be updated when new versions of the runtime are released.
Also, the resulting application only works on the operating system published for.
Refer to .NET Core application deployment for more details on the deployment options and how to set them up.
In Visual Studio 2017:
Right click on your project and select Publish (In Visual Studio 2019, click on menu Build → Publish <projectName>)
Select 'Folder' and create a new profile
In tab 'Publish', click 'Configure...'
Select Deployment Mode: Self-contained, Target Runtime: win-x86 (or win-x64)
Save
Publish
In the folder <Your project>\bin\Debug\netcoreapp2.1\win-x86\ you will see the EXE file:
Starting with .NET Core 2.2 you can build framework-dependent executables
Although building a self-contained deployment can be a good solution, it has its own drawbacks. (See R.Titov and Martin Ullrichs' answers on SCD-s.)
Fortunately, .NET Core 2.2 supports the building of so called framework-dependent executable-s, that are essentially a wrapper binary (.exe on Windows) around the standard dll-s.
This way you have all the advantages (and disadvantages) of the standard framework-dependent deployment (again, see Martin's answer), but you have a convenient way to launch it, without having to call it through the dotnet CLI.
You can publish your app as a Framework-Dependent Executable using the following syntax:
dotnet publish -c Release -r <RID> --self-contained false
Where RID is the usual runtime identifier, e.g. win-x64 or whatever platform you wish to build for (see the catalog here).
That's how you do a self-contained publish with command-line in any OS:
dotnet publish C:\src\App\App.csproj -c release -r win-x64 -o output-win-x64
Besides, you might want to get the output decreased from typical ~60 MB for a simple Hello World app to ~30 MB by using ILLink.
Also, you might want to go further and get a single .exe file of a size at around 5 MB and use ILCompiler. See this reply.
The other answers are good, but what I find sometimes convenient is:
Not have it self-contained because the target machine is likely to have .NET Core of the correct version installed. This cuts on number of the DLL files I need to ship.
Not have to specify dotnet on the command line
For this, a bat file wrapper can be used, similar to these lines:
#ECHO OFF
REM see http://joshua.poehls.me/powershell-batch-file-wrapper/
SET SCRIPTNAME=%~d0%~p0%~n0.dll
SET ARGS=%*
dotnet "%SCRIPTNAME%" %ARGS%
EXIT /B %ERRORLEVEL%
If your application ends up in yourapp.dll, name the bat file yourapp.bat and place it along side the DLL file. Now instead of dotnet yourapp.dll params you can call yourapp params.
Note that the context of this answer is in-house tooling, so all the developers using the utility will have a pretty standard development machine setup. If this is to be distributed to an external customer who is running who knows what on their boxes, the self-contained option is far superior.

How do I target .NET Standard 2.0 in a freshly created .NET Core 2.0 web app?

I've just created a fresh project using dotnet new web. My Google-foo may be failing me, but I didn't find anything relating to my answer (please link to another SO answer, or relevant documentation if I've missed something obvious).
If I want to ensure this new project is .NET Standard 2.0 compliant, what do I now do?
It is not inherently possible to run a netstandard project as an executable. Since netstandard was designed to be used for libraries.
In order to develop your web application entirely in netstandard2.0, you would have to create a separate project that targets either .NET Core or .NET Framework to execute your library that contains your web app (developed using .NET Standard).
1. Executable Project (ex: console app)
-- Target Framework: netcoreapp2.0 / net462
2. Web Application Project (library)
-- Target Framework: netstandard2.0
You can use the following steps to change the target framework of your project.
Step 1. Target the desired framework
Right-click on your project and select Edit *****.csproj
In the .csproj file, you need to replace the target framework to the .NET Framework.
Example .csproj file:
<Project Sdk="Microsoft.NET.Sdk.Web"> //<-- note the .Web for the web template
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
For a list of the Target Framework Moniker (TFM) (ie, net47, netstandard2.0, netcoreapp2.0, etc.*) you can check this link out:
https://learn.microsoft.com/en-us/dotnet/standard/frameworks
Step 2. Run dotnet restore
Go to your output window and run dotnet restore.
Note: Sometimes Visual Studio may misbehave (depending on which update you have installed), so you may have to close and re-open your Visual Studio. Otherwise, sometimes a clean/re-build may do the trick.
Targeting both frameworks
You can pick one or the other, or even target both frameworks.
<TargetFrameworks>netcoreapp2.0; net47</TargetFrameworks> //<-- note the plural form!
NET Standard is for class libraries. Applications must target netcoreapp* where * is a version number. The following shows the compatibility matrix: https://learn.microsoft.com/en-us/dotnet/standard/net-standard
For example, .NET Core 2 can consume .NET Standard version 2 and below.

Is Platform Toolset v110 incompatible with .NET 3.5?

This is with Visual Studio 2012 Ultimate Update 3.
I have a C# project that targets .NET 3.5. This project uses a C++/CLI dll which is also compiled for .NET 3.5.
I have noticed that if the C++ dll is compiled with Platform Toolset v110, then although I can add it to the C# project, and see the types in Intellisense, the compiler itself doesn't see any of the types. It complains that they do not exist and that I am missing an assembly reference. This does not happen if it is compiled with Platform Toolset v90.
If I then re-target the C# project to .NET 4 or 4.5, then it sees the types alright. The types do exist in the assembly and can be seen in the object explorer or Ildasm.
It therefore seems that the use of Platform Toolset v110 makes it impossible to use the dll from a .NET 3.5 project, even though the dll is compiled for .NET 3.5.
Is this normal and/or documented behavior? Is there any way around this other than either downgrading the C++/CLI project to Platform Toolset v90 or upgrading the C# project to .NET 4?
As always Toolset v110 does not support any .Net version other than 4.5. Officially, to change the target framework you must change the platform toolset to a version that supports the target .Net version (e.g. Windows 7 SDK for .net 2.0-3.5 SP1). This involves changing TargetFrameworkVersion in the project file as well as switching to another platform toolset.
That said, the toolset is just a bunch of msbuild rules so you may get the the compiler to retarget the .Net version by changing the vcxproj and global msbuild rules
in project file. Olga Arkhipova from the VC++ team comes up with this:
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<RealTargetFrameworkVersion>$(TargetFrameworkVersion)
</RealTargetFrameworkVersion>
Add a file to 'C:\Program Files (x86)\MSBuild\4.0\Microsoft.Common.Targets\ImportBefore
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion
Condition="'$(RealTargetFrameworkVersion)' != ''">
$(RealTargetFrameworkVersion)
</TargetFrameworkVersion>
</PropertyGroup>
</Project>

Categories

Resources