.NET: Different target frameworks via project configuration - c#

I would like to have two different .net framework targets via configuration in Visual Studio 2015. While for references, you can edit the CSPROJ file and add a conditions, this does not seem to work for the TargetFrameworkVersion in the first PropertyGroup of the file. I have the impression that any Condition in that element causes VS to completely ignore this element and to fall back to the default value of "v4.0".
Is there any way I can get different target framework versions for different configurations?
This is what I tried in the CSPROJ file:
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
...
<!-- this is what VS2015 would put into the file:
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
-->
<!-- this is what does not work: -->
<TargetFrameworkVersion Condition="'$(Configuration)' == 'OLD_Debug' OR '$(Configuration)' == 'OLD_Release'">v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion Condition="'$(Configuration)' == 'NEW_Debug' OR '$(Configuration)' == 'NEW_Release'">v4.0</TargetFrameworkVersion>
...
</PropertyGroup>
...
</Project>
A similar approach with conditions for the assembly references works fine.
EDIT
I found a similar Stackoverflow question:
Targetting multiple .net framework versions by using different project configurations and tried the approach suggested in the non-accepted answer to remove the TargetFrameworkVersion from the first PropertyGroup block, and edit the later <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'OLD_Debug|AnyCPU' "> blocks to contain it, but my assembly is still compiled for framework 3.5 no matter which configuration I use. At least if I look at the assembly from Powershell using [System.Reflection.Assembly]::LoadFrom("C:\PATH\MyAssembly.dll").ImageRuntimeVersion, I always get version 2, not 4.

The approach found in this answer to a similar question works:
Keep the first PropertyGroup without configuration specific settings, remove the TargetFrameworkVersion element from it. And add the TargetFrameworkVersion settings to the configuration specific PropertyGroups which are in the file anyway, just double them for debug/release:
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'OLD_Debug|AnyCPU' ">
...
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'OLD_Release|AnyCPU' ">
...
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'NEW_Debug|AnyCPU' ">
...
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'NEW_Release|AnyCPU' ">
...
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</PropertyGroup>
...
</Project>
I verified this as follows:
My assembly references version 2.0.0.0 of the mscorlib assembly for the 3.5 framework (OLD_... configurations), and version 4.0.0.0 of mscorlib for the 4.0 framework (NEW_... configurations).
Using ILSpy, I found that the 3.5 version of my assembly has no attribute for the target framework, as this was only introduced since version 4, but the version 4 framework appears as an attribute of the assembly:
[assembly: TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")]

Related

Build error when packaging multiple nuget targets

I'm attempting to make a Nuget package for multiple framework targets (.netstandard20 and net46). I am able to build the solution and the outputs appear on disk as expected, but whenever I try to package them (Visual Studio 2019 or msbuild directly), I'm getting the following error:
error MSB4057: The target "_GetBuildOutputFilesWithTfm" does not exist in the project.
I'm not using a .nuspec file and the developer packages of my target frameworks are installed correctly.
Here's my .csproj (with nuget metadata excluded for brevity) - it's pretty basic:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
<ReleaseVersion>1.0</ReleaseVersion>
<PackageVersion>1.0</PackageVersion>
...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType></DebugType>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net46'">
<PackageReference Include="NuGet.Build.Tasks.Pack" Version="5.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="NuGet.Build.Packaging" Version="0.2.2" />
</ItemGroup>
</Project>
Does anyone have any ideas??
Thanks
Removing references to NuGet.Build.Tasks.Pack and NuGet.Build.Packaging allows targeting only .NET Standard 2.0 and getting the desired behavior.

.NET Core - Image resources not included / compiled in library

I would like to include some PNG image files into an assembly (.dll) for use in a WPF application. We previously targeted .NET Framework 4.8 which worked fine but since migrating the project to .NET Standard 2.1 the resources are being ignored.
Using the .NET Framework 4.8 does store the resources as wanted, which means that there is a [AssemblyName].g.resources entry containing a directory-like structure with the images in it. When switching to "netstandard2.1" as target platform, the resources are not included.
The .csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
<ApplicationIcon />
<Win32Resource />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\AssemblyInfo.App.cs" Link="Properties\AssemblyInfo.App.cs" />
</ItemGroup>
<ItemGroup>
<Resource Include="**\*.png" />
</ItemGroup>
</Project>
The expected result is a resource hierarchy compiled into the library file so that the images can be accessed directly from .xaml. We did this successfuly with .NET Framework as target platform but it won't work with .NET Standard 2.1 or .NET Core 3.0.
To fix it you just need to change
<Project Sdk="Microsoft.NET.Sdk">
to
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
in your project file.
Here are the details and root cause of this problem.

Visual Studio C# project in Monodevelop - Errors for GenerateSatelliteAssemblies and GenerateTargetFrameworkMonikerAttribute

I am trying to setup a C# project that is compatible with both Windows 10 / Visual Studio 2015 and Ubuntu 16.04 / MonoDevelop.
In the MonoDevelop project options, I set the target framework to Mono / .NET 4.5 and configuration to Debug|x86.
The .csproj file now looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{myprojectsguid}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>myproject</RootNamespace>
<AssemblyName>myproject</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
When I build, I get this message:
Erzeuge Projektmappe: myproject (Debug|x86)
myproject (Debug|x86) wird erzeugt
Build started 26.05.2016 22:05:49.
__________________________________________________
Project "pathto/myproject.csproj" (Build target(s)):
Target PrepareForBuild:
Configuration: Debug Platform: x86
Target GenerateSatelliteAssemblies:
No input files were specified for target GenerateSatelliteAssemblies, skipping.
Target GenerateTargetFrameworkMonikerAttribute:
Skipping target "GenerateTargetFrameworkMonikerAttribute" because its outputs are up-to-date.
Done building project "pathto/myproject.csproj".-- FAILED
Build FAILED.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.0785520
--------------------- Fertig ---------------------
Erzeugung erfolgreich.
The message seems to contradict itself. The English part says "build failed", while the localized/German part says "build successful". But no executable was created.
I did not find any applicable information about GenerateSatelliteAssemblies or GenerateTargetFrameworkMonikerAttribute.
Do I need to change the project settings even further? Do I need to supply some missing files?
Update:
Apparently, some packages were missing. So I ran sudo apt-get install mono-complete. Now the project builds successfully.
Though, the notifications about GenerateSatelliteAssemblies and GenerateTargetFrameworkMonikerAttribute are still there. Looks like they don't break the build. I still wonder what they mean.
(I'm just posting the answer that the asker wrote in the question.)
For the asker, it turned out that some packages were missing. The asker ran sudo apt-get install mono-complete, and then the project would build successfully.

How can a project be built as a portable or a normal class library depending on the selected configuration?

Context: We have a common library project called 'THEPROJECT' that is shared with a Xamarin mobile solution and a web project in visual studio. This shared library is a PCL Profile259 due to restrictions in Xamarin. In our web application solution we would like to mark the data models with the appropriate attributes for DataContext to work correctly (as we want to use LinqToSql).
Attempted Solution: We decided to look into the project for 'THEPROJECT' and change the msbuild in a way that would allow it to exist in a state of portable library / normal class library depending on the selected configuration in visual studio.
We have looked at the solution in this post however it is only part of the solution. We can get the project to exist as one or the other, however it requires a manual change to the project file. Now we get the error loading the following project. The for a portable project must be '.NETPortable'.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">DebugDesktop</Configuration>
<Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>
<ProjectGuid>{426E7BD8-9DA8-4E15-9512-72E7C632B037}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>THEPROJECT</RootNamespace>
<AssemblyName>THEPROJECT</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
<BuildMode>Desktop</BuildMode>
</PropertyGroup>
<!-- Define the Build Configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugDesktop|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Desktop\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<BuildMode>Desktop</BuildMode>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseDesktop|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Desktop\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<BuildMode>Desktop</BuildMode>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugPCL|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\PCL\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<BuildMode>Portable</BuildMode>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleasePCL|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\PCL\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<BuildMode>Portable</BuildMode>
</PropertyGroup>
<!-- Determine the correct properties for PCL/Desktop -->
<Choose>
<When Condition=" '$(BuildMode)' == 'Portable' ">
<PropertyGroup>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
<TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
<DynamicImportPath>$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets</DynamicImportPath>
</PropertyGroup>
</When>
<When Condition=" '$(BuildMode)' == 'Desktop' ">
<PropertyGroup>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<DynamicImportPath>$(MSBuildToolsPath)\Microsoft.CSharp.targets</DynamicImportPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Data.Linq" />
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\mobileapplication\packages\Newtonsoft.Json.8.0.2\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\sqlite-net-extensions\SQLiteNetExtensions\SQLiteNetExtensions-PCL.csproj">
<Project>{f723017d-ede5-49cc-a84f-881c067c6004}</Project>
<Name>SQLiteNetExtensions-PCL</Name>
</ProjectReference>
<ProjectReference Include="..\..\sqlitenetpcl\src\SQLite.Net\SQLite.Net.csproj">
<Project>{4971d437-0694-4297-a8cc-146ce08c3bd9}</Project>
<Name>SQLite.Net</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(DynamicImportPath)" />
<!-- Source/Resx etc... -->
</Project>
To answer the question about making a project be "Portable" or "Normal" depending on the configuration, when the error that pops up is something about the <TargetFrameworkIdentifier> must be .NETPortable:
Remove all the <ProjectTypeGuids> from the configuration.
Those project type GUIDs give Visual Studio some GUI things that have to do with portable projects only, so when the configuration is not portable it causes the error you got. But since you're comfortable editing the XML, you obviously don't need that stuff. Plus you can continue to use the GUI to edit project properties with the project type GUIDs removed.
At least as of Visual Studio 2015.
When you create a Portable Class Library you need to create your library as a Windows 8.1 Portable Library you can't use Universal Libraries for this, which it sounds like you did by the constraints of Xamarin. The Windows 8.1 Portable Library is a kind of hybrid library that allows classic .net to communicate with the new lightweight versions of .net.
The issue with the Portable Class Library is it can't reference a a Classic Class Library via project reference, you can only reference it via DLL reference. You do not need to mess around with the XML if you are uncomfortable editing that file just find the compiled DLL and add the reference manually. If you choose to do this you will need to manually update the build dependencies or you may have broken references.
It seems like the issue isn't entirely in your .csproj but it is also in your project.json. I have been noticing visual studio not generating the proper project.json file. I have to tinker with the targets in the UI to get it to generate correctly. Once I get the initial project.json to generate I have no other issues.
Prior to doing anything we need to wire up our Portable Class Library to handle a Classic Class Library. The first step is updating the target framework from the properties to target .net 4.5 or whatever version you prefer.
Once you do this confirm that the project.json file looks something like this:
{
"supports": {},
"dependencies": {},
"frameworks": {
".NETPortable,Version=v4.5,Profile=Profile259": {}
}
}
Once your Portable Class Library is targeting the correct version you can then add a DLL reference to the compiled .net 4.5 assembly. I have made no additional changes to the csproj file for the portable class library but here is the source to that so you can take a look.
Notice how the ClassLibrary1 is referenced:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.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>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7C4EB968-4C17-438C-B981-97EDC865F312}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PortableLibrary</RootNamespace>
<AssemblyName>PortableLibrary</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="ClassLibrary1">
<HintPath>..\ClassLibrary1\bin\Debug\ClassLibrary1.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="project.json" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
This should eliminate issues assuming your web project is ASP.NET Core 1.0. If you are running a Classic ASP.NET application you may still have to create the build rules.
TLDR:
Looks like there is an issue with your project.json file, make sure it is getting generated correctly and you can't directly reference .net 4.5 via project reference you have to reference the DLL.

Changing csproj OutputType based on project configuration

I need to build a C# project as either WinExe or Library depending on the project's configuration.
I've tried both of these methods with no luck:
1) In the general PropertyGroup:
<OutputType Condition=" '$(Configuration)' == 'Release' ">WinExe</OutputType>
<OutputType Condition=" '$(Configuration)' == 'Debug' ">Library</OutputType>
2) In a conditional PropertyGroup:
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputType>WinExe</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputType>Library</OutputType>
</PropertyGroup>
Neither of these methods work and the OutputType is always WinExe. The odd thing is that if I change all instances of WinExe to Library, then it's always Library. This is making me think that it is reading them successfully, but either in a strange order or that WinExe takes precedence over Library.
Any ideas?
In the top of your .csproj file you will have two sections that look a bit like this:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputType>Library</OutputType>
<!-- Other properties go here -->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputType>Exe</OutputType>
<!-- Other properties go here -->
</PropertyGroup>
Add your OutputType elements to these two conditional PropertyGroup sections and make sure that you remove all other OutputType elements - I've just tested it and it does exactly what you are asking for.
Yes this is very similar to what you have already done, but I know for a fact that the above method works because I've just tried it - my only guess is that something somewhere else in your build is messing things up.

Categories