I'm currently working on a Blazor WASM app and I'd like to set a custom version (formatted like YEAR.MONTH.DAY.REVISION) to all my built assemblies. My goal is to use a makefile that does some stuff and compile the solution while setting the assemblies version.
I've already tried an extension for Visual Studio 2022 called Auto Versioning V3 that let you customize the version format and set it during the build. The only problem is that if I launch dotnet build solution.csproj in the command prompt the extension does not set the version to the files (VS extensions work only inside the IDE (?)).
Every solution is really welcome, thanks!
You can use environment variables for that. In your csproj files, add these property groups:
<PropertyGroup Condition="'$(APPVERSION)' == ''">
<InformationalVersion>1.0.0.0</InformationalVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(APPVERSION)' != ''">
<InformationalVersion>$(APPVERSION)</InformationalVersion>
</PropertyGroup>
If there is a environment variable "APPVERSION" set during the build, this will be used as InformationalVersion of the ouptut assembly, otherwise it will default to 1.0.0.0. You will just have to set APPVERSION in your makefile.
Related
INTRODUCTION
I am building a class library which could be used by some legacy applications targetting .Net Framework 4.0 and new applications targetting .Net Framework 4.6.1
I am adding some database/Hibernate new code in the class library that requires .Net Framework 4.6.1. This new code is incompatible with .Net Framework 4.0 because the nuGet package FluentNHibernate 3.1.0 requires .Net Framework 4.6.1 and up. And legacy application does not require this functionnality.
WHAT I AM TRYING TO ACHIEVE
I am attempting to conditionnaly build the class library so one code base and the master git branch can be used to build a version compatible for either one or the other application.
So far, I have been able to:
Define a constant indicating the target framework (FWK40)
for use within .cs code to adjust code to target framework
Conditionally define the targetted framework (TargetFrameworkVersion)
Exclude files from build when not under the right TargetFrameworkVersion
Here is what the .CSPROJ looks so far (emphasis for the relevant adjustments):
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug.Net40|AnyCPU'">
<!-- Set target framework here -->
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug.Net40\</OutputPath>
<!-- Define Build-time constant here -->
<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0'" >FWK40</DefineConstants>
</PropertyGroup>
<ItemGroup>
<-- Conditionally include .cs files here -->
<Compile Include="Database\GeneralSQL.cs" Condition="'$(TargetFrameworkVersion)' != 'v4.0'" />
<Compile Include="Database\NamingStrategy.cs" Condition="'$(TargetFrameworkVersion)' != 'v4.0'" />
<ItemGroup>
<ItemGroup>
<-- In THEORY conditionally include PackageReference here -->
<PackageReference Include="FluentNHibernate" Condition="'$(TargetFramework)' != 'net40'" >
<Version>3.1.0</Version>
</PackageReference>
</ItemGroup>
THE RESULT SO FAR
What is happenning, is I am getting a nuGet error saying:
NU1202: Package FluentNHibernate 3.1.0 is not compatible with net40 (.NETFramework,Version=v4.0). Package FluentNHibernate 3.1.0 supports:
Failed to restore C:\_projets\repos\TestSolution\TestLibrary\TestLibrary.csproj (in 19 ms).
NuGet package restore failed. Please see Error List window for detailed warnings and errors.
Not withstanding this error, the assemblies all are generated properly, the DLL Class Library itself, as well as an EXE Console application using that class library AND so far they run properly.
THE PROBLEM
I haven't been able to conditionnaly include nuGet a PackageReference.
AND The nuGet error still causes MSBuild.exe to fail, which prevents the CI/CD pipeline to work properly..
WHAT I HAVE TRIED
I have tried many many ways to get rid of nuget NU1202 error message.
#1 I have tried initially with other conditions based on $(TargetFrameworkVersion) which works througout the .csproj but to no avail.
#2 According to official documentation, nuGet ReferencePackage only supports conditions based on $(TargetFramework) adding-a-packagereference-condition as shown in the sample .csproj above. YET THIS STILL DOES NOT WORK.
I haven't been able to figure out so far what exactly the Property TargetFramework looks like. I ran MSBUILD.EXE in Verbosity level diagnostics, which dumps all the Properties, but TargetFramework wasn't listed (while others were)
I have tried to "reverse" the condition == 'net461' so that if the expected value is incorrect, it won't be included and the error would disappear => no effect, errors still there
#3 I have tried to define myself the TargetFramework property
<!-- Set target framework here -->
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFramework>net40</TargetFramework>
The outcome would be much worse!
Visual Studio did not like it, the "Configuration Manager" broke down
it would not allow to set a specific Configuration for a project.
changing from Debug.Net40 to any other configuration (or loading the project) would show a nasty error message
Current solution contains incorrect configuration mappings. It may cause projects to not work properly
And the csproj was definitely not loaded properly either and project would be skip the build step.
WHERE I AM AT NOW
I am really stuck! Can't seem to find a way to make this work.
I would hate to have to have a branch master40 and a branch master just to handle this.
I would hate to have to have two csproj different files, unless I can somehow manage to share/include one into the other AND Visual Studio would not complain
The really really right thing would be to make the conditions on the ReferencePackage to actually work as intended.
What you want is multi-targeting.
The documentation you link seems to be only applicable for SDK-style projects.
AFAIK, multi-targeting is not available with the legacy-style project format.
I suggest migrating to the SDK-style project format.
A lot of things are much simpler there, not to mention better documented.
You can use a tool to do this, like hvanbakel/CsprojToVs2017 or dotnet/try-convert. Don't be fooled by its name and wrong usage of terminology.
(since this is also mixed up on SO all the time: yes, you can use the SDK-style format with .NET Framework TFMs; SDK-style has nothing to do with either TFM or Visual Studio version, but only with minimum required MSBuild version).
Once you have done that for your particular project, the documentation for multi-targeting applies and you can use Condition on $(TargetFramework) just like you have already done, in both your PackageReference and the Compile item group and pretty much anywhere you want.
I have two fairly straightforward C# projects: An executable that can build as either x86 or AnyCPU, which references (via <ProjectReference>) a DLL project that only has an AnyCPU configuration. This all works as expected within Visual Studio.
I am trying to build the x86 version of the executable project (and its dependencies) from the command line, with /p:Platform="x86". This causes the build of the DLL project to fail. (Whereas /p:Platform="AnyCPU" works, presumably because it is valid for both projects.)
The full command line I am using is:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild MyProject\MyProject.csproj /t:Build /p:Configuration="Release" /p:Platform="x86"
What are my options for getting this build to work from the command line? Preferably without modifying the DLL project at all, or modifying the projects in ways that interfere with using them normally in Visual Studio.
(The ultimate goal here is a batch file that can build a clean version of the project for distribution.)
Additional info:
Both projects have "Debug" and "Release" configurations. The executable project has "x86" and "AnyCPU" available under Platform. The DLL project has only "AnyCPU" available under Platform. The "Platform target" option matches the "Platform" in all cases. (There is no "Prefer 32-bit" option, as I am on VS2010.)
The error seems to be a compilation-related error ("no unsafe code allowed") in the DLL, which -- although I am not 100% sure -- seems to be because none of the <PropertyGroup> elements in the DLL project are being matched (due to Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " etc) that would specify necessary parameters for compilation (eg AllowUnsafeBlocks and DefineConstants).
Answering my own question... First of all, MSBuild can build solution files. This does exactly what you'd expect:
msbuild MySolution.sln /p:Configuration="Release" /p:Platform="x86"
The result is an x86 executable with an AnyCPU DLL (as the solution specifies).
There are a few people out on the internet suggesting that its behaviour is not a perfect match for Visual Studio in some obscure cases. But it seems to work just fine for my purposes. (I think they were having issues with the order things get built in.)
I knew that MSBuild could build a solution file, but -- oops -- I neglected to test it on my simple reproduction case, after it failed on the more complex thing I'm working on.
The above, alone, isn't enough for a fully satisfying answer, particularly if there is a need to customise things. The way MSBuild builds solution files is to create a dummy project file, based on the solution file. This can be inspected by first setting an environment variable like so:
set MSBuildEmitSolution=true
This will emit the dummy project file next to the solution file, which can then be inspected.
I haven't completely analysed what it is doing, but it looks fairly straightforwardly like it is using the <MSBuild> task with the Projects parameter that is itself passing in the solution-specified Configuration and Platform appropriate for each project. According to the documentation it seems to be using the ones specified in AdditionalProperties. (This also seems useful to know.)
For reference, here is some relevant code extracted from the generated project file:
<Target Name="Build" Outputs="#(CollectedBuildOutput)">
<MSBuild Projects="#(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)">
<Output TaskParameter="TargetOutputs" ItemName="CollectedBuildOutput" />
</MSBuild>
</Target>
Where #(ProjectReference) is grabbing data from:
<ItemGroup>
<ProjectReference Include="X:\Solution\MyProject\MyProject.csproj">
<ToolsVersion>
</ToolsVersion>
<SkipNonexistentProjects>False</SkipNonexistentProjects>
<AdditionalProperties>Configuration=Release; Platform=x86; VisualStudioVersion=10.0</AdditionalProperties>
<Configuration>Release</Configuration>
<Platform>x86</Platform>
</ProjectReference>
<ProjectReference Include="X:\Solution\DLLProject\DLLProject.csproj">
<ToolsVersion>
</ToolsVersion>
<SkipNonexistentProjects>False</SkipNonexistentProjects>
<AdditionalProperties>Configuration=Release; Platform=AnyCPU; VisualStudioVersion=10.0</AdditionalProperties>
<Configuration>Release</Configuration>
<Platform>AnyCPU</Platform>
</ProjectReference>
</ItemGroup>
(Note the different AdditionalProperties.)
I just installed the newly released Visual Studio 2017 Enterprise (RC). I'm having trouble getting it to work with Code Contracts, however. I have no problem using Code Contracts with Visual Studio 2015. Am I missing something?
As others have noted, Microsoft hasn't prioritized Code Contracts and its long-term support remains unclear (though there has been some ongoing discussion about language-level integration via Roslyn).
As of March 11th, 2017, however, community contributor Yaakov has, at least, updated the source code to include the Visual Studio 2017 build targets (thank you!). This version provides support for both static checking during compilation, as well as run-time validation using CCRewrite.
Note: This build does not provide configuration support via the project's properties pane. As such, code contracts will need to be configured by manually adding the appropriate properties to the csproj file. See #crimbo's answer below for a comprehensive list of properties.
Unfortunately, while these updates have been merged into the master code branch, they are neither reflected in Marketplace distribution or the official NuGet Package. As such, you need to download and compile the source code from the repository (which is easy; just use the supplied BuildCC.bat file).
Important: The static analysis for Code Contracts has a hard-coded dependency on .NET 3.5, which is no longer installed by default in either Windows 10 or Visual Studio 2017. As such, you'll want to ensure this "feature" is enabled (or download it separately); otherwise, you'll get a compile-time error.
Alternatively, as of June 15th, 2017—and later updated on February 6th, 2018—contributor Igor Bek has included this update in his NuGet Package, so the simplest approach is to just add CodeContracts.MSBuild to your packages.config via:
Install-Package CodeContracts.MSBuild -Version 1.12.0
Background: Igor Bek first put this package together as a proof-of-concept for the Code Contracts team, and it was later the basis for the official NuGet package (in v1.10.10126.2). Since Microsoft hasn't updated the official NuGet package, his is now the most up-to-date.
Given the current state of support, I wouldn't encourage people to adopt Code Contracts for new projects, but this should provide backward compatibility for developers who have already invested into Code Contracts for existing .NET Framework projects.
At this time of writing there are no contract definitions for VS2017, but you can get around it with the following if using Nuget package DotNet.Contracts:
Navigate to the CodeContracts nuget package directory (DotNet.Contracts.1.10.20606.1\MsBuild)
Copy the v14.0 folder
Rename it to v15.0
Everything should build as expected.
There is currently no version of Code Contracts for .NET which supports Visual Studio 2017. However the issue can be remedied, if you copy the following target file
C:\Program Files (x86)\MSBuild\4.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets
to the ImportAfter location of your VS2017 MSBuild:
C:\Program Files (x86)\Microsoft Visual Studio\2017\#YourVS2017Product#\MSBuild\15.0\Microsoft.Common.targets\ImportAfter
Note: Replace #YourVS2017Product# with your VS2017 product name in the above path, e.g. Community.
This will allow you to build with Code Contracts in VS2017, but will not solve the issue of the CC tab not showing in the Project Settings. For that you will still need to switch to VS2015.
The reasons that code contracts don't work in VS 2017 are:
Code contracts MSBuild files aren't Imported in the VS 2017 tree of msbuild files (easy to fix)
Code contracts configuration UI isn't present in VS 2017 project properties (easily fixed by include CodeContracts msbuild properties)
Certainly questions about the future of CodeContracts are valid, but you can implement the following to enable existing projects that use CodeContracts to build in VS 2017:
Add the contents of C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets to your csproj files (either directly or indirectly via an import). The most basic approach is to add this to your csproj file:
<PropertyGroup>
<CodeContractsInstallDir Condition="'$(CodeContractsInstallDir)'==''">C:\Program Files (x86)\Microsoft\Contracts\</CodeContractsInstallDir>
</PropertyGroup>
<Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)MsBuild\v$(VisualStudioVersion)\Microsoft.CodeContracts.targets" />
Note that the first PropertyGroup shouldn't be necessary if CodeContracts is installed, b/c CodeContractsInstallDir should be specified as an environment variable. In that case, you can get away by just adding
<Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)MsBuild\v$(VisualStudioVersion)\Microsoft.CodeContracts.targets" />
to your *.csproj files.
Specify all the CodeContracts properties in your *.csproj file (directly or indirectly via Import). Eg:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Code Contracts settings -->
<PropertyGroup>
<CodeContractsAssemblyMode>1</CodeContractsAssemblyMode>
<CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
<CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
<CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
<CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
<CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
<CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
<CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
<CodeContractsEnumObligations>False</CodeContractsEnumObligations>
<CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
<CodeContractsInferRequires>False</CodeContractsInferRequires>
<CodeContractsInferEnsures>False</CodeContractsInferEnsures>
<CodeContractsInferObjectInvariants>False</CodeContractsInferObjectInvariants>
<CodeContractsSuggestAssumptions>False</CodeContractsSuggestAssumptions>
<CodeContractsSuggestRequires>True</CodeContractsSuggestRequires>
<CodeContractsSuggestEnsures>False</CodeContractsSuggestEnsures>
<CodeContractsSuggestObjectInvariants>False</CodeContractsSuggestObjectInvariants>
<CodeContractsDisjunctiveRequires>False</CodeContractsDisjunctiveRequires>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs>
<CodeContractsCacheAnalysisResults>True</CodeContractsCacheAnalysisResults>
<CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
<CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
<CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<CodeContractsRuntimeCheckingLevel>ReleaseRequires</CodeContractsRuntimeCheckingLevel>
</PropertyGroup>
</Project>
If you have more than a few projects, I recommend putting these in a private nuget package and referencing that nuget package in each of your projects. The code contracts settings (from step 2) can go in your mycompany.codecontracts.props file, and the code contracts targets (from step 1) can go in your mycompany.codecontracts.targets file.
More info on packaging msbuild properties/targets in a nuget package here: https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package
I'm willing to provide an example on GitHub if there's sufficient interest.
I found the approaches suggested here were not straightforward, in particular that it would require the changes on each developer's machines and on the build server as well.
I decided to create my own very simplified version of Contract.Requires() that will need only global replace of the using declaration in caller classes.
using MYCommon.Diagnostics; //System.Diagnostics.Contracts;
When/if System.Diagnostics.Contracts will be available for VS 2017 and .NetStandard, it will be easy to revert to the proper version.
The actual class is:
/// <summary>
/// Contract.Requires(config != null); in VS 2017 not throw ArgumentNullException
/// The class is workaround for https://stackoverflow.com/questions/40767941/does-vs2017-work-with-codecontracts
/// </summary>
public class Contract
{
public static void Requires(bool condition, string message = null)
{
Requires<ArgumentNullException>(condition, message);
}
public static void Requires<TException>(bool condition, string message=null) where TException:Exception , new ()
{
if (!condition)
{
//https://stackoverflow.com/questions/41397/asking-a-generic-method-to-throw-specific-exception-type-on-fail/41450#41450
var e=default(TException);
try
{
message = message ?? "Unexpected Condition"; //TODO consider to pass condition as lambda expression
e = Activator.CreateInstance(typeof(TException), message) as TException;
}
catch (MissingMethodException ex)
{
e = new TException();
}
throw e;
}
}
}
The essential limitation is that typical usage Contract.Requires(param1!=null); doesn't allow me to throw the exception with the name of the parameter, and better usage is a bit longer:
Contract.Requires<ArgumentNullException>(param1!=null, "param1 is null");
As a follow-up to this question:
Use a single Visual Studio solution to build both x86 and x64 at the same time?
I'm using the technique in the accepted answer for C# projects and it works great. Is there a way to do the same thing for C++/CLI project (.vcxproj)? Using that technique doesn't seem to work (the "BeforeBuild" and "AfterBuild" targets seem to be ignored).
I'd even be okay with having the task be executed from the Before/AfterBuild Target of another C# project in the solution:
<Target Name="BeforeBuild">
<MSBuild Condition=" '$(Platform)' == 'x64' " Projects="MyCPPCLIProject.vcxproj" Properties="Platform=x86;" />
</Target>
When I do this, I get the following error (even though the project is in the solution):
C:\VS\MyCSProj.csproj(1773,5): error MSB3202: The project file "MyCPPCLIProject.vcxproj" was not found.
Try specifying something more for the project filename. Use $(SolutionDir) as part of the full path.
I have a need to build a website and several DLLs that it references in an x86 configuration. To date we have been using Web Deployment Projects to create Zip files of the resultant site and all it's required files. We need to continue to use WDPs however, they seem to have problems with the x86 build configuration.
In my project, when I build the solution in Release/x86 I get the following error.
Description File Line Column Project
Error 80 Could not load type 'WwwRooot.Default'. /WwwRooot.csproj/Default.aspx 1 1 WwwRooot.csproj_deploy
There are no build errors or warnings from the web application or any of the referenced class libraries.
I thought this might be something specific to the project I'm working on so to prove myself wrong I created a solution containing a Web Application (c#). I then used the Configuration Manager to create the x86 configuration by copying the Any CPU config. I checked the properties page an made sure the new config was set to build to x86, and it was. I built the solution without error.
Next I right clicked the Web App and added a WDP from the context menu.
Right clicked on the WDP and edited the project file. At this point I changed any references for AnyCPU to x86 so that the WDP has conditions of x86 build.
I rebuilt the solution in Release/x86 and everything builds fine.
Next I add a Class Library, use Configuration Manager to create an x86 config for this library, add a reference to the web app for the library and then rebuild all in Release/x86 and I receive the same error as detailed above.
Are WDPs compatible with x86 build?
If I remove the Class Library (and the reference) the solution (including the WDP) builds fine.
I am using Visual Studio 2008 SP1, with the appropriate WDPs installed, on 64Bit Windows 7 Pro.
Out of the box, Web Deployment Projects (WDP) don't work with x86 or x64 build configurations. This is because a Web Application built under one of these configurations outputs the resultant assemblies in a different place and the WDP doesn't know to look there for the DLLs.
There are a few things you'll need to do to get the WDP working with your x86 configuration.
Firstly, your WDP probably doesn't have an x86 configuration, you'll need to create one. Edit the deployment project using the XML editor in Visual Studio (or any text editor), near the top of the file will see a <propertyGroup> tag (usually the second one) with a condition Debug|AnyCPU like so:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>.\Debug</OutputPath>
<EnableUpdateable>true</EnableUpdateable>
<UseMerge>true</UseMerge>
</PropertyGroup>
Duplicate this whole tag and change the configuration to be Debug|x84:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>.\Debug</OutputPath>
<EnableUpdateable>true</EnableUpdateable>
<UseMerge>true</UseMerge>
</PropertyGroup>
Now save the file and open the configuration manager (Build menu > Configuration manager) and check your deployment project now has an x86 configuration.
Now edit the Web Application Project file using your text editor and locate the outputPath element within the Debug|x86 configuration. It should have a value of Bin\x86\Debug. This needs changing to Bin:
<!-- Before -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>Bin\x86\Debug\</OutputPath>
<!-- After -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>Bin\</OutputPath>
Save, close and reload you Web Application Project. We've now instructed the Web Application to put it's DLLs where the WDP expects to find them.
Set your build configuration to x86 and build the project.
Rinse and repeat for Release and any other build configurations you might have.
Try this
Put this command in post compilation events of your web project
xcopy "$(TargetDir)*.*" "$(TargetDir)..\..\" /Y
This command wil copy files from bin\x86\Debug to bin
It will work with Release configuration