dotnet publish sln having projects with multiple target frameworks fails - .net-core

I have a solution with many projects. Some target frameworknetcoreapp2.1, some other target framework netstandard2.0 and one project has a double target framework
<TargetFrameworks>netstandard2.0;net471</TargetFrameworks>
I'd want to have a artifact for win10 with a single command:
dotnet publish MySolution.sln -c Release -o "targetFolder" -r win10-x64
With this command I have this error while building the project with double target framework. Here's the errors:
C:\Program Files\dotnet\sdk\2.1.402\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.CrossTargeting.targets(31,5) error : The 'Publish' target is not supported without specifying a target framework. The current project targets multiple frameworks, please specify the framework for the published application.
The error is clear. At the end I find that dll compiled in the output directory and it seems like it is a netstandard2.0 dll because my application still works.
I don't like dirty things so, how can I solve my problem?
I would avoid to call N times the "dotnet publish" command if possible.

Don't use dotnet publish with the same output directory on a solution. Especially not with the "-r" argument.
It is dangerous because:
libraries don't have the right trimming behaviour for netstandard facade packages
libraries may have odd behaviour when publishing with "-r", especially for netstandard<2.0 dependencies. (they'd end up copying the .NET Core 1.0/1.1 implementation(!) assemblies)
you may end up with different NuGet dependencies in the output (transitive dependencies)
Copy-to-output/publish-directory items may end up overwriting each other, it may even lead to build failures
Call it individually for all application (console app, web app) projects or create an MSBuild file that publishes these applications.

Related

AssemblyInfo ingrored when building from GitLab-Runner

On a Windows machine there is a GitLab-Runner run from a domain user with admin rights. When I log as this user and call dotnet build -c release to build an ASP.NET Core app, the dll has all the information from the AssemblyInfo.cs file. When I do the same as part of a CI job, the produced dll is missing all this information (for example the version number).
The AssemblyInfo.cs file is not part of the repository, instead, it is produced by a prebuild event (using gitWCRev.exe tool). However after running some tests I can see that the AssemblyInfo.cs is actually generated when the job is run by the runner.
Any help as to why the file is ignored and how to overcome this issue would be appreciated.
At first I thought that this might be related to Pre-build task of Visual Studio project fails in GitLab Runner issue, but I don't get any build errors.
On the same machine, I build a .Net Framework app which has the same AssemblyInfo setup, but is compiled using msbuild /property:Configuration=Release by the runner and the produced dll file has all the expected information.
It turns out the problem was partially related to the AssemblyInfo.cs file not being part of the repository.
SDK-style csproj normally don't list the files in the project, but figure them out based on the folder contents. When dotnet build was run, AssemblyInfo.cs wasn't present in the project directory (GitLab-Runner usually clears out files not present in the repository before starting a job/pipeline), so build tools had no idea they needed to load it. It made no difference that the file was being created by the build tools.
The solution proved to be creating an empty AssemblyInfo.cs file before running dotnet build. This way build tools knew they needed to compile it. Actual compilation still happened after prebuild events, so all the needed information was there.
I created the empty AssemblyInfo.cs file using PowerShell:
New-Item -Path "Properties/AssemblyInfo.cs" -ItemType File
Also, checking the build logs helped me finally figure it out. To get the build logs I've called build tools like this:
dotnet build -c release /flp:v=diag
The .Net Framework app didn't have this problem because it wasn't an SDK-style project. All the files needed for compilation were listed in the project file and build tools knew to load them, even if they were created during the prebuild event.

How to pack .NET Core projects recursively without running pack on the entire solution?

I have a solution of a hundred plus .NET Core projects. Not all of them needs to be packed, but only those which are transitive dependencies of a few special projects.
However, when I run dotnet pack it attempts to pack all kinds of projects that it should not and there are errors here and there. I would like instead to run pack on the special projects only in a recursive fashion, so that only them and their transitive dependencies (project references, of course) are packed.
I figured I can implement it by scripting around the dotnet list reference command, but it does not sound right. There must be a better way to do it.
EDIT 1
The solution must work on the command line where we have dotnet and msbuild and possibly nuget, but no VS IDE.
You can modify your project settings to generate *.nupkg file during dotnet build, without explicit dotnet pack call. And as soon as dependencies get builded automatically when "parent" project builds - you will receive nuget packages prepared for all dependencies too when you run dotnet build for "parent" project only.
For each project that should produce nuget package add this lines into csproj file:
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
Or, instead, you may enable checkbox "Generate NuGet package on build" from Visual Studio, in project properties ("Package" tab) - this will add same line into project file.

dotnet build ignore --framework param for projects with only FrameworkTarget

I work with .NET Core SDK version 2.1.302. My solution has two type of projects: libraries and web. All libraries are targeted to .NET Standard 2.0: <TargetFramework>netstandard2.0</TargetFramework> and web projects have multiple targets: <TargetFrameworks>net462;netcoreapp2.0</TargetFrameworks>
I have two CI builds: for Windows which uses net462 and build in docker based on linux with netcoreapp2.0.
In the docker build to build my solution I use the following line of code:
RUN dotnet build ./MySolution.sln --configuration Release --framework netcoreapp2.0
And build fails with the rrors like this:
Assets file '/app/MyLibraryProject/obj/project.assets.json' doesn't have a target for '.NETCoreApp,Version=v2.0'. Ensure that restore has run and that you have included 'netcoreapp2.0' in the TargetFrameworks for your project. [/app/MyLibraryProject.csproj]
It happens because as I mentioned before my library projects are targeted only one framework - netstandard2.0
So, my question is how to deal with this situation? How should I specify that projects with only one target framework should ignore --framework param?
In the interests of making sure the answer is noticed, I found that the best way around this was #José Pedro's solution from the above comment stream.
In the csproj file, I put a condition on the TargetFramework element. Now it looks as follows:
<TargetFrameworks Condition="'$(CoreOnly)' != 'True'">net472;netcoreapp2.1</TargetFrameworks>
<TargetFramework Condition="'$(CoreOnly)' == 'True'">netcoreapp2.1</TargetFramework>
It will then by default compile both, but you can pass a CoreOnly parameter to only compile the .NET Core framework.
dotnet build MySolution.sln /p:CoreOnly=True
Another possible solution is to check which framework is available and set the <TargetFramework> dynamically in the build time:
<FrameworkDescription>$([System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription)</FrameworkDescription>
<TargetFramework Condition="$(FrameworkDescription.Contains('NET 5'))">net5</TargetFramework>
<TargetFramework Condition="$(FrameworkDescription.Contains('NET 7'))">net7</TargetFramework>
It will make dotnet build just work without any additional parameters on a system where only net5 or only net7 is installed.

Compile a .NET Core application as an EXE file using Visual Studio 2017

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.

.NET5 on OSX - C# errors in DNVM shell (so close..)

I'm trying to get an old project to run on the new .NET VM, I've gotten through a few obstacles at this point. For a while I couldn't get DNVM to recognize my project as a .NET project, until I added a couple missing files from a sample project, and used dnvm . kestrel instead of dnvm . web and then I got back about 80mb worth of C# errors...
So I went from not being recognized as a project, to being recognized as a shitty one :/
Anybody beat this level yet? Thank you in advance.
Two questions for you
When you say that you tried dnvm . kestrel, do you mean that you tried dnx . kestrel?
Did you run dnu restore to download dependencies before running dnu . kestrel?
Three command line programs
There are three command line programs that you will use to run a .NET 5 app, and it's easy to confuse them. dnvm is the version manager, dnu is the utility, and dnx is the runtime. It's the runtime not the version manager that you use from your project's root folder to start the kestrel web server.
Startup up an app
Here's the very short version of how to start up a .NET 5 app. After using dnvm to install .NET 5, and after creating a project (and optionally a solution,) you need to use both dnu and dnx in this sequence.
From your solution's folder, run dnu restore. This will download your project's dependencies. If you have only a project and no solution, run this from your project's folder instead.
From your project's folder, run dnx . kestrel to run the web application in the browser.
It sounds like you did number (2) without having first done number (1).
Let me know whether you're able to reach the next level. Also, have you read these articles?
http://docs.asp.net/en/latest/getting-started/installing-on-mac.html
http://docs.asp.net/en/latest/tutorials/your-first-mac-aspnet.html

Resources