Last year I added .NET Standard 2.0 support to the Network library. I did achieve this by creating a second (.NET Standard) project, and basically copy + paste the sourcecode. With some adjustments it was ready to go.
But since I add features on demand, it is really bothersome to change the same thing in both projects. It would be great to just create one code-base and simply change the compile target.
Pre-Compile statements aren't an option, because the .NET 4.x version does additionally include some NuGet packages, which aren't available for .NET Standard.
The solution I can currently think of is, to create a shared library, including all the cross-project classes. Or is there a much smoother solution?
Solved the Problem with the suggested solution. The .csproj Looks like following
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Remove="packages\**" />
<EmbeddedResource Remove="packages\**" />
<None Remove="packages\**" />
</ItemGroup>
<ItemGroup>
<!-- PackageReferences for all TargetFrameworks -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46' ">
<!-- PackageReferences for net46 TargetFramework -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' ">
<!-- PackageReferences for standard2.0 TargetFramework -->
</ItemGroup>
</Project>
The only issue currently: I can't use the NuGet Package-Manager. I have to add every entry manually into the correct ItemGroup.
EDIT: The manual edit is only required if the packages are not supported by both TargetFrameworks. Simply Change in the Settings -> NuGet-Paket-Manager -> Default Format -> PackageReference
You can add source files from an existing project to another project as a link.
project a
somefile.cs
project b
Right click on project b, add existing item...navigate to somefile.cs in project a, and then add
You can edit the file from either project...so be careful.
Related
Okay,
I am really struggling by creating a nuget package for a BCL.
During the development, I have pushed created some *.scss files for my blazor components. Now, I want to use these components in a separate nuget package and consume it within other projects.
So far so good, but unfortunately all my css-files aren't within the nuget package.
What I've tried:
All my components has it's own *.scss file. After building the project, there are some css files, e.g. HelloWorld.razor.css.
When I am runing the dotnet pack command, there aren't any css files within the nuget package.
I've already included the EmbeddedResource Element to my csproj file, now I can see all the css file, but my BlazorApp won't apply the styles at all.
I've already studied the documentation (https://learn.microsoft.com/en-us/aspnet/core/blazor/components/css-isolation) but I still not able to enable the styles.
I have also tryied to reference the project directly to my BlazorApp, and it worked. Is it a NuGet issue?
My csproj looks like follows:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyVersion>1.0.12.0</AssemblyVersion>
<FileVersion>1.0.12.0</FileVersion>
<Version>1.0.12-rc.33</Version>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
<DisableScopedCssBundling>false</DisableScopedCssBundling>
<ScopedCssEnabled>true</ScopedCssEnabled>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="**\**\*.css" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Delegate.SassBuilder" Version="1.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.2" />
</ItemGroup>
</Project>
I have a Xamarin Forms application. I'd like to use the latest C# version in platform specific projects (i.e. Xamarin.iOS and Xamarin.Android). It looks like I can add <LangVersion>latest</LangVersion> to the .csproj files. However, I'm not sure where to add it exactly. I see a lot of PropertyGroup tags in the project files (usually one for each simulator and release type). Do I need to add it to every PropertyGroup? I need the latest language version to be available when debugging and in production.
Generally, if you use the latest version of Target framework, the version of C# will be the latest. Then you will not need to set the version of C# manually.
Right click the solution of Xamarin Forms, then click Properties, then can set the Target framework as follow.
You will refer to this official document to know its explains.
And also can set C# version manually in .csproj as follow:
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
Here is the C# language version reference.
James Montamagno posted a tutorial on his Blog here: https://montemagno.com/use-csharp-8-everywehre/
You just have to change the <LangVersion /> value of your .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.3.0.947036" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup>
</Project>
if you want to change the language of your platform specific code just add:
<PropertyGroup>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
to your ProjectName.iOS.csproj or ProjectName.Android.csproj.
I am converting a WPF App from Framework 4.7.2 to .NetCore 3.1
Entire project all source under discussion is here:
https://github.com/BicycleMark/LatestSweeper
I am using the side by side / same folder technique so that both Framework and new Core can use same project and subfolders.
Sweeper.csproj
SweperCore.csproj
exist in same folder
the Sweeper.csproj compiles and runs just fine as before.
The SweeperCore.csproj comes back with:
Severity Code Description Project File Line Suppression State
Error NETSDK1005 Assets file 'D:\Src\Github\Sweeper\Sweeper\obj\project.assets.json' doesn't have a target for
'.NETCoreApp,Version=v3.1'. Ensure that restore has run and that you have included 'netcoreapp3.1' in the TargetFrameworks for your project. SweeperCore C:\Program Files\dotnet\sdk\3.1.300\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets 234
I have examined that it is not about the packages as csproj references no pkg files.
Here is the .NetCore SweperCore.csproj contents:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<OutputPath>bin_core\Debug\</OutputPath>
<IntermediateOutputPath>out_core\Debug\</IntermediateOutputPath>
</PropertyGroup>
</Project>
Here is the image from Framework app:
Problem:
The net472 and the netcoreapp3.1 projects are both using the ./obj/ folder for intermediate build files. Obviously this is a problem, if both projects are loaded in Visual Studio.
Solution
Replace the IntermediateOutputPath or BaseIntermediateOutputPath (both should work) for one project. This will make side by side usage in Visual Studio possible. I've cloned your repo and managed to build both apps sucessfully without unloading one of them.
Due to the fact that the BaseIntermediateOutputPath must be evaluated before any Microsoft.Common properties are being used, overriding it needs more work, than I've expected:
Modify the SweeperCore.csproj as followed:
Remove the Sdk Attribute in the root Project Tag:
<Project>
Add the following PropertyGroup and Import as the first childs of <Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj_core</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Sdk="Microsoft.NET.Sdk.WindowsDesktop" Project="Sdk.props" />
Add this PropertyGroup to remove the net472 out/ folder from the core app:
<ItemGroup>
<Compile Remove="obj\**" />
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
<Page Remove="obj\**" />
</ItemGroup>
Add this Import as the last child before </Project>.
<Import Sdk="Microsoft.NET.Sdk.WindowsDesktop" Project="Sdk.targets" />
Original Source: https://github.com/microsoft/msbuild/issues/1603
Here I have encoded the full answer in a tip and trick on CodeProject
https://www.codeproject.com/Tips/5272662/Simple-Side-By-Side-Migration-Project-Template-for
I have tried to read in Microsoft documentations as well in Nuget and Dotnet and was unable to understand the corresponding behavior.
When working on a C# project along with .csproj I have encountered the following,
Here is an example of what I want to achieve:
I am working for instance with the following .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Authors>Confluent Inc.;Andreas Heider</Authors>
<Description>Confluent's .NET Client for Apache Kafka</Description>
<Copyright>Copyright 2016-2020 Confluent Inc., Andreas Heider</Copyright>
<PackageProjectUrl>https://github.com/confluentinc/confluent-kafka-dotnet/</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/LICENSE</PackageLicenseUrl>
<PackageIconUrl>https://raw.githubusercontent.com/confluentinc/confluent-kafka-dotnet/master/confluent_logo.png</PackageIconUrl>
<PackageReleaseNotes>https://github.com/confluentinc/confluent-kafka-dotnet/releases</PackageReleaseNotes>
<PackageTags>Kafka;Confluent;librdkafka</PackageTags>
<PackageId>Confluent.Kafka</PackageId>
<Title>Confluent.Kafka</Title>
<AssemblyName>Confluent.Kafka</AssemblyName>
<VersionPrefix>1.4.3</VersionPrefix>
<TargetFrameworks>net45;net46;netcoreapp2.1;netstandard1.3;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Confluent.Kafka.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="librdkafka.redist" Version="1.4.2">
<PrivateAssets Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">None</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="PeterO.Cbor" Version="3.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Console" Version="4.3.0" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
</Project>
As you can see in the last ItemGroup there is a Condition=" '$(TargetFramework)' == 'netstandard1.3',
what I am trying to figure out is from where dotnet takes this argument $(TargetFramework) from.
By the way, it seems to be variable notation $(SomeVariable) (not only TargetFramework) and it would be great for me to understand from where and how these variables are being pulled from.
Any leads?
Thanks!
As there is already a very detailed answer on some mechanics, here are a few helpful pieces of information to know that can help detail the understanding of the behavior:
the "SDK=..." part imports things at the top and bottom of the project.
It is actually syntactical sugar for <Import> statements at the top and bottom ends of your project content. This means that logic by the SDK (and its deeper imports) can both set defaults that you can use in your project (automatic Sdk.props import at the top) and process values you set in the project (automatic Sdk.targets import at the very bottom).
<PropertyGroup> elemts are processed before <ItemGroup> elements.
MSBuild evaluates static content (i.e. not inside a <Target>) in a very specific order: Property Groups and Imports then Item Definition Groups and then Item Groups.
This means you can use $(FooBar) in an <ItemGroup> at the very top even if the <PropertyGroup> defining <FooBar> i at the very bottom.
The SDK itself uses this behavior - your $(TargetFramework) (singular!, if set. more on that later) is used in some property groups in the imported SDK targets to set TargetFrameworkIdentifier, TargetFrameworkVersion and TargetFrameworkMoniker - here's the code.
When the processing of <PropertyGroup>s is complete, this means that on the pass for <ItemGroup> elements, you can use $(TargetFrameworkIdentifier).
A similar mechansim is used for some item groups that the sdk has before your project content that can be controlled by properties set in the project (DefaultItemExcludes, EnableDefaultCompileItems etc).
TargetFrameworks (plural) projects actually build multiple times.
If a project has no TargetFramework but a TargetFrameworks (plural) property, it causes multiple sub-builds for which the TargetFramework property will be set. This means that everything is evaluated once for an "outer" build and then once per target framework for "inner builds". Here's the code
$(SomeVariable) is the syntax for resolving an MSBuild property. These are key-value pairs, which can be defined by environment variables, in arguments to MSBuild, by a <PropertyGroup> elements within an MSBuild file, and by MSBuild targets using the CreateProperty task.
TargetFramework is somewhat of a special property. If the project only builds for one target framework, then the property is set explicitly in the project file, e.g.:
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
In your case, your project can be built for multiple frameworks, as specified in the TargetFrameworks property (notice the plural), e.g.:
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net462</TargetFrameworks>
</PropertyGroup>
When you build this project with e.g. dotnet, it actually builds the project multiple times, once for each target framework, by essentially setting the TargetFramework property via argument when calling MSBuild.
If you want to see behind the magic a bit, here are two ways to get very verbose information on what MSBuild is doing:
Use the -pp argument to have MSBuild resolve all the magic .NET Core-ish stuff, as well as all imported project files, into a single project file for you to inspect. This won't actually run the build.
Use the -bl argument to run the build, but log everything MSBuild does in a binary format. You can then view the .binlog file using this GUI.
Additional resources for MSBuild:
MSBuild documentation (written primarily from the perspective of the classic Windows-only .NET Framework)
Additions to the project file format made for "SKU-style" project files (used in .NET Core, .NET Standard, and .NET 5+).
I am using Sdk projects targeting .NET Framework 4.7.2. The project structure is:
SmokeTests
|
+--UITests
| |
| +--Common
|
+ NonUITests
|
+--Common
where:
the SmokeTests project references both UITests and NonUITests using ProjectReferences
UITests and NonUITests both reference Common using a ProjectReference
UITests, NonUITests and Common reference some NuGet packages using PackageReferences
the SmokeTests project has no source code, but it does have some content files.
I use the SmokeTests project as a roll up project. When I build it, its bin\debug\net472 directory contains all the binaries and symbols I want to have in the nuget package, i.e.:
The NuGet dependencies of UITests, NonUITests and Common
The dlls of UITests, NonUITests and Common
The PDBs
The SmokeTests csproj looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<PackageId>SmokeTests</PackageId>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>true</IncludeBuildOutput>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ContentTargetFolders>content</ContentTargetFolders>
</PropertyGroup>
<ItemGroup>
<Content Include="**\*.ps1" Exclude="PSTests\run.ps1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NonUITests\NonUITests.csproj" />
<ProjectReference Include="..\UITests\UITests.csproj" />
</ItemGroup>
</Project>
I also have a Directory.Build.props file:
<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions>
<Version>$([System.Text.RegularExpressions.Regex]::Match($(BuildName), `\d+\.\d+(?:.\d+)?(?:.\d+)?`))</Version>
<Version Condition="'$(Version)' == ''">1.0.0.0</Version>
<SourceRevisionId>$(Revision)</SourceRevisionId>
<Company>My Company, Inc.</Company>
<Copyright>Copyright © $([System.DateTime]::Now.Year) by My Company, Inc. All rights reserved.</Copyright>
<Product>Smoke Tests</Product>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
</Project>
When I build the solution, the produced SmokeTests.1.0.0.nupkg file does not contain any of the binaries, except the SmokeTests.dll itself. Clearly not what I want.
How can I get everything from bin\debug\net472 into the produced NuGet package without specifying a nuspec file?
I can always hack an after build step that would manipulate the nupkg file, but I want a proper way to do it.
EDIT 1
Judging by the amount of responses either the question is plain stupid or nobody uses .Net core. Posted it here as well - https://social.msdn.microsoft.com/Forums/vstudio/en-US/aa25cb08-ff95-4d81-b0c3-9c4a395f9999/how-to-pack-the-products-from-multiple-projects-into-one-nuget-without-any-nuspec-file?forum=msbuild
Could it be that SO lost its charm?
EDIT 2
I added the following properties to PublicLib.csproj to produce a NuGet package:
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>$(AssemblyName)</PackageId>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>true</IncludeBuildOutput>
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
And it almost works, here is the content of the PublicLib.1.0.0.nupkg\lib\netstandard2.0 folder:
But IncludedLib.pdb is missing.
So, after long hours of inspecting the NuGet.Build.Tasks.Pack.targets I have arrived at the following code for my roll up project SmokeTests:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>$(AssemblyName)</PackageId>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>true</IncludeBuildOutput>
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<ContentTargetFolders>content</ContentTargetFolders>
<GenerateNuspecDependsOn>MyCustomizePacking</GenerateNuspecDependsOn>
</PropertyGroup>
<Target Name="MyCustomizePacking" Returns="#(NuGetPackInput);#(_BuildOutputInPackage);#(_TargetPathsToSymbols)">
<ItemGroup>
<NuGetPackInput Remove="#(BuiltProjectOutputGroupKeyOutput);#(DebugSymbolsProjectOutputGroupOutput)"/>
<_BuildOutputInPackage Remove="#(BuiltProjectOutputGroupKeyOutput)"/>
<_TargetPathsToSymbols Remove="#(DebugSymbolsProjectOutputGroupOutput)"/>
<NuGetPackInput Include="#(ReferenceCopyLocalPaths);#(AllItemsFullPathWithTargetPath)" />
<_BuildOutputInPackage Include="%(ReferenceCopyLocalPaths.Identity)" >
<TargetFramework>$(TargetFramework)</TargetFramework>
</_BuildOutputInPackage>
<_BuildOutputInPackage Include="%(AllItemsFullPathWithTargetPath.Identity)" >
<TargetFramework>$(TargetFramework)</TargetFramework>
</_BuildOutputInPackage>
</ItemGroup>
</Target>
<ItemGroup>
<Content Include="**\*.ps1" Exclude="PSTests\run.ps1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NonUITests\NonUITests.csproj" />
<ProjectReference Include="..\UITests\UITests.csproj" />
</ItemGroup>
</Project>
The only way I could find to pack all my project and package references into the NuGet was to hook into the process through GenerateNuspecDependsOn by injecting my target MyCustomizePacking. This target does the following:
Remove the SmokeTests.dll and SmokeTests.pdb from the relevant item groups, because this is a roll up project with no code on its own, I do not need its dll or pdb inside the package.
Include #(ReferenceCopyLocalPaths) and #(AllItemsFullPathWithTargetPath) in the relevant item groups.
Seems to work,but I am not happy with my implementation. I wish there was better support for this.