multi-platform native libraries in C# nuspec - c#

We have a C# project (.net Core) which is cross-platform. It uses native libraries (C++), which are different for each platform.
We know that you can specify different frameworks in a single nuspec file, but this case is different:
There will be a single C# DLL file
There will be different native libraries (like a.linux.so and a.windows.dll)
We want be able to install only the libraries that are pertinent to a specific OS.
What is the recommended mechanism?

First, make sure you are using SDK-based projects to ensure you get correct dependency trimming for target frameworks like netstandard* (instead of using a .nuspec file).
The goal is to separate your native runtime assets into a runtimes subfolder inside your NuGet folder so that the layout is:
\lib\YourManagedCode.dll
\runtimes\win-x86\native\some.dll
\runtimes\win-x64\native\some.dll
\runtimes\linux-x64\native\libsome.so
\runtimes\osx-x64\native\some.dylib
An example project file could look like this, assuming you already have runtimes folder:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Update="runtimes\**" Pack="true" PackagePath="runtimes" />
</ItemGroup>
</Project>
If you need to test locally, you can also add CopyToOutputDirectory="PreserveNewest" to ensure the files are copied to the output. .NET Core should be able to resolve files located in runtimes directories for methods annotated with [DllImport("some")].
The resulting NuGet package can be referenced from both .NET Core and .NET Framework projects. If some "RID-fallback" (e.g. win10-x64 => win-x64) does not work on .NET Framework projects, make sure this project also references a recent version of the Microsoft.NETCore.Platforms package (it provides NuGet with a graph of known values, has nothing much to do with .NET Core itself..).

Related

Adding dll reference in .Net standard 2.0 project

I want to add Dll reference of system assembliesSystem.Runtime.Caching assembly in my .net Standard 2.0 project(using VS2019). Due to some policy restrictions nuget usage is not possible so we need to add a dll reference(assuming target system has same version of .net framework installed),
How can add this reference so that it works in different systems without using path?
I tried following without any success:
Adding with reference manager dialog. This adds hint path(relative) which might not work in some systems(or when directory is changed).
Adding <PackageReference> adds nuget package which is restricted hence can not be used.
Tried adding override to Microsoft.Common.targets as
<AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == ''">{GAC}</AssemblySearchPaths> in csproj so that GAC is searched first.
Note: Third party package management or nuget local feed is not being accepted.
Also msbuild.common.targets file has {TargetFrameworkDirectory} option for resolving assemblies, so I tried adding <Reference Include="System.Xml"> to check if msbuild would search in target framework binaries but I still get same error.
In old style .net framework csproj structure(not SDK style) Adding something like <Reference Include="System.Xml"> used to work at runtime and compile time both, but it does not anymore.
Does DLL reference as mentioned above does not work in .net standard and core anymore?
You don't want to fight with current .NET/MSBuild architecture and NuGet. If you cannot create a local NuGet server you need to provide your path to libraries on your own. MSBuild is smart enough to find packages but if you will not provide any feed he will not resolve all of them. Under the hood, MSBuild has hardcoded paths that search for libraries as a fallback but these paths change over time when the framework evolves.
.NET Framework isn't equal to .NET Core, it's the standalone solution not related to Windows anymore so you shouldn't assume it will work as the previous framework.
Only one stable solution is to include all libs inside the project folder.
Reference still works but you need to supply a path like that(otherwise MSBuild probably will struggle to find libraries):
<ItemGroup>
<Reference Include="lib\$(TargetFramework)\*.dll" HintPath="%(FilePath)" />
</ItemGroup>
I think you should speak with your client and tell him he needs a more robust solution than fighting with a chosen framework because it's detrimental for him and you.

How do I make NuGet aware of a specific version of a package while referencing a multi-targeted class library?

We have three projects:
WebApp.csproj, ASP.NET WebForms app compiled against .NET Framework 2.0
Api.csproj, ASP.NET WebApi 2 compiled against .NET Framework 4.5
Lib.csproj, compiled against .NET Framework 2.0 and referenced by both of the projects above.
Lib.csproj has been recreated in the new .NET project format (the one used by .NET Core) and made to target both .NET Fw 2.0 and 4.5 with the <TargetFrameworks>net20;net45</TargetFrameworks> technique.
So far so good, I can reference different NuGet packages by writing the appropriate sections in Lib.csproj like so:
<ItemGroup Condition="'$(TargetFramework)' == 'net20'">
<PackageReference Include="NLog" Version="2.1.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<PackageReference Include="NLog" Version="4.7.8.0" />
</ItemGroup>
and I can point Api.csproj to the version of Lib.csproj targeting net45, like so:
<ProjectReference Include="..\..\Lib\Lib.csproj" AdditionalProperties="TargetFramework=net45">
<Project>{32e77bcf-152c-4b64-be37-a13f49cdcab6}</Project>
<Name>Lib</Name>
</ProjectReference>
The net20 version references NLog 2.1.0, the net45 one NLog 4.7.8.
The problem: now I want to use NLog 4.7.8 in my Api.csproj, but when I click on Manage Packages for Solution on the solution that includes Api.csproj and Lib.csproj, the referenced version appears to be 2.1.0.
I tried selecting both projects and installing it, however I rightly get an error because NLog 4.7.8 is not compatible with net20.
So really, the problem is that NuGet does not seem to be aware that the project I'm referencing is a multi-target one, and only sees the version of NLog installed in the net20 version even though Api.csproj is targeting the right "flavor" of Lib.csproj (and if I go and check the binaries, the version of NLog.dll that gets copied to the output folders is the right one for each of my targets).
I tried looking on the interwebs, but to no avail.
To be fair: the version of NLog that gets copied to Api.csproj's bin folder is the right one, so it's not a showstopper. But it is mildly annoying, because NuGet shows the wrong version of my dependency, and then it's hard to tell what version we're actually using.
For a bit of context: this app is obviously ancient, at least WebApp.csproj. Due to performance reasons, we are making Api.csproj use async/await, and since that project references code that could be made async in Lib.csproj but obviously wouldn't compile under .NET 2, we chose to make Lib.csproj multi-targeted.
The AdditionalProperties here shouldn't be necessary:
<ProjectReference Include="..\..\Lib\Lib.csproj" AdditionalProperties="TargetFramework=net45">
<Project>{32e77bcf-152c-4b64-be37-a13f49cdcab6}</Project>
<Name>Lib</Name>
</ProjectReference>
I strongly recommend updating all your projects to use SDK-style csproj format if possible. This isn't possible for legacy (pre-Core) web projects, which is the case for WebApp.csproj and Api.csproj. However, even those projects can use some parts of the newer csproj format.
Specifically, they can use PackageReference. I assume your existing web projects are using nuget.config, but they can be changed to use PackageReference without actually making them SDK-style csprojs. NuGet has some issues with transitive dependencies when nuget.config is used, so I just recommend using PackageReference everywhere.

What is the content of .Net publish resulting exe?

I published a small C# .Net Core 2.2 console application as executable using the following command from this post:
dotnet publish -c Release -r win10-x64
The generated files contain both the classic ConsoleApp.dll, but also the executable that I was expecting to be generated ConsoleApp.exe.
My question is why there was still the DLL generated, since all its code, I suppose, could have been compiled to the .exe, as in a .Net Framework application?
On the other hand, I tried to decompile the .exe file with ILSpy but the content from it does not seem to be managed code. In this case I also suppose that the .exe file is just calling the DLL using the dotnet command. Is this assumption right?
Below is the Console Application .csproj file content:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Management" Version="4.7.0-preview3.19551.4" />
</ItemGroup>
<ItemGroup>
<None Update="input.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Very interesting question. I'll try to cover it as much as I possibly can.
With the introduction of .net core 3.0 came the Single file publish as described in the design documentation of .Net. So If you have a .net 3.0 application then you can use the /p:PublishSingleFile=true to bundle everything in a single executable.
Watch out for the /p:PublishTrimmed=true option, as treeshaking can and probably will cause problems with reflection code, as access to it is not covered by the tree shaking.
For previous versions of net core, you need to use one of the packagers like wrap or Costura.
As per the exe file, I'll mention somthing from the wrap documentation:
The final self-contained single binary application consists of two
parts: 1) runner and 2) the compressed target application executable
and dependencies.
The dll that is created by the standard publisher is multi platform and non specific to windows. So the executable file has all the code needed to create the process in windows and call the actual code that is in the dll that can be used on any platform. It's just a wrapper.
More information about the executable Microsoft .net core deploying docs
Self-contained deployment. Unlike FDD, a self-contained deployment
(SCD) doesn't rely on the presence of shared components on the target
system. All components, including both the .NET Core libraries and the
.NET Core runtime, are included with the application and are isolated
from other .NET Core applications. SCDs include an executable (such as
app.exe on Windows platforms for an application named app), which is a
renamed version of the platform-specific .NET Core host, and a .dll
file (such as app.dll), which is the actual application.
I am not sure about your assumption. But use the following command it will generate only two files, one is .exe and other is .pdb in the publish directory inside win10-x64 directory.
dotnet publish -r win10-x64 -c Release /p:PublishSingleFile=true /p:PublishTrimmed=true

What are the differences between Microsoft.NET.Sdk and Microsoft.NET.Sdk.Web

I have a solution with two host projects (one is a Web Host and the other is a Generic Host) and a class library project referenced by those two host projects.
In the Sdk attribute of the root tag <Project> of the *.csproj file, both host projects (web host and generic host) are using Microsoft.NET.Sdk.Web, but the class library project uses the Microsoft.NET.Sdk.
The two host projects references the Microsoft.AspNetCore.App metapackage.
The class library project are using Microsoft.NETCore.App, but it references some ASP.NET Core packages individually (packages of Microsoft.AspNetCore.App that are not in Microsoft.NETCore.App).
About the correct SDK and metapackages:
1) In the generic host project, should I use pure .NET Core (Microsoft.NET.Sdk and Microsoft.NETCore.App) instead of ASP.NET Core (Microsoft.NET.Sdk.Web and Microsoft.AspNetCore.App) since it is not a web project?
2) In the class library project, is it fine to use Microsoft.NET.Sdk with Microsoft.AspNetCore.App to avoid the possibility to reference different versions of packages that belong to Microsoft.AspNetCore.App (to avoid for example, Microsoft.AspNetCore.App#2.1.0 in the host project and Microsoft.Extensions.Configuration#2.0.0 in the class library project)? Or I can only use Microsoft.AspNetCore.App metapackage with Microsoft.NET.Sdk.Web SDK?
3) What difference does it make to use Microsoft.NET.Sdk or Microsoft.NET.Sdk.Web? The docs says that "The SDK, as the layering document describes, is a set of MSBuild tasks and targets that can build .NET Core code.", but why do we need to have both of them? In practice, what Microsoft.NET.Sdk.Web does that Microsoft.NET.Sdk doesn't?
Ad (1) and (3): What are the differences between the "core" and and web SDKs, how do these affect generic host apps?
The most important differences are:
Default Items
The web SDK has different definitions and globbing patterns for which files to include in your published application.
E.g. when you have an appsettings.json file, projects using the web sdk will automatically include it since there are patterns in place that ensure that .config, .json files and all files in a wwwroot folder are all part of the publish output. See the MSBuild source code on GitHub for these patterns.
If you have a generic host and don't use the Web SDK, you may need to add code to the csproj file to specify which files to copy to the publish directory (or use an IDE to change the "copy to output directory" setting which also includes files in the publish output but will also copy them to the build output):
<ItemGroup>
<None Update="*.json" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
Web Publish logic
Another essential part of the Web SDK is that it contains the deployment logic for web applications.
If you plan to use publish profiles (.pubxml files) or deploy to azure or filesystems using MSBuild / MSDeploy, you will need this publishing logic.
Ad (2): Which SDK to use for class libraries?
For maximum compatibility when publishing public libraries (e.g. via NuGet), use the core SDK and reference individual packages with the lowest possible version - e.g. 2.1.0 / 2.1.1.
If you develop a class library containing razor views, you will need to use the Microsoft.NET.Sdk.Razor SDK to get razor tooling (e.g. when you use the dotnet new razorclasslib template).
For libraries and test projects where you want to use the same meta package reference as the application, things are a bit complicated at the moment but it's going to get better:
For ASP.NET Core 2.1 tools(!) (CLI 2.1.*), I suggest using the non-web SDK for class libraries and use the version 2.1.1 of that package. Don't ever upgrade it, even if NuGet offers you an upgrade.
For test projects in 2.1 tools(!) (CLI 2.1.*), it is a bit different and tricky, see Integration and unit tests no longer work on ASP.NET Core 2.1 failing to find assemblies at runtime
Beginning in 2.2 tools (CLI 2.2.100+), the version-less package references to ASP.NET Core metapackages are moved to the core SDK so you can develop libraries and test projects for both ASP.NET Core 2.1 and 2.2 using the ""core"" SDK (provided you use tools 2.2.100+) using version-less package references:
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
In .NET Core / ASP.NET Core 3.0, you will be able to reference the framework via a new mechanism altogether (no web-SDK needed):
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

How Do You Reference a .NET Standard Library from a .NET Framework 4.5 Console Application in Visual Studio 2017?

I have finally installed Visual Studio 2017.2 and am trying to get my first project working, but am running into some trouble that I hope to address here.
I have a very simple .NET Standard Library described as the following project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
</PropertyGroup>
</Project>
And a very simple .NET Framework console application that references the above .NET Standard library, and is described as the following project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net45</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>
When I build my console application, I get the following build error:
C:\Program Files\dotnet\sdk\1.0.4\NuGet.targets(97,5): error : Project Common is not compatible with net45 (.NETFramework,Version=v4.5). Project Common supports: netstandard1.6 (.NETStandard,Version=v1.6)
I saw this question and tried some of the suggestions provided there, but none of them worked. So, this appears to be a different problem. Please note that this occurs during the build of my solution and not referencing (explicit) NuGet packages in any way.
Finally, if it helps, I have a solution that demonstrates this issue here:
https://github.com/Mike-EEE/Stash/blob/master/VS2017.Multi/VS2017.dotNetFramework.sln
.NET Framework 4.5 only supports using .net standard libraries targeting .NET Standard 1.0 or 1.1. Since your library targets 1.6, the tooling does the right thing here and errors out (since your library may use APIs not available in .NET Framework 4.5). If you published the library as NuGet package and consumed it via a package reference, the package restore would error out as well (with an error saying that the package is incompatible).
There is some confusion about which .NET Standard version a .NET Framework version supports especially since there is preview tooling available ("2.0") that changes these versions. The ".NET platforms support" table in the documentation therefore contains two lines about the supported versions. In your case however, both versions limit .NET Framework 4.5 to .NET Standard 1.1.
for .net framework projects to be compatible with .net standard libraries you must acquire the NETStandard.Library from the nuget.
Now i cannot find any official resource that states exactly why this is a must, but from what i understand the NETStandard.Library has the necessary links to make a map from .NET Standard API's to .NET Framework.
If you want more info i suggest to read the official docs of NET Standard.

Categories

Resources