Where do I set version of Asp.NET Core project? [duplicate] - asp.net-core

Since dotnet core moved back to the .csproj format, there is a new autogenerated MyProject.AssemblyInfo.cs which contains, among others:
[assembly: AssemblyCompany("MyProject")]
[assembly: AssemblyVersion("1.0.0.0")]
Note that this is automatically regenerated every build.
Previously, the file was found in the /obj/ directory, now it appears to be only in memory as the file can't be found on disk and clicking the error message does not open any file.
This is the error message:
Since they are defined there, I can't define them myself in the classical AssemblyInfo.cs.
Where/how can I define the Company and Version of a project?

As you've already noticed, you can control most of these settings in .csproj.
If you'd rather keep these in AssemblyInfo.cs, you can turn off auto-generated assembly attributes.
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
If you want to see what's going on under the hood, checkout Microsoft.NET.GenerateAssemblyInfo.targets inside of Microsoft.NET.Sdk.

Those settings have moved into the .csproj file.
By default, they don't show up but you can discover them from Visual Studio 2017 in the project properties Package tab.
Once saved those values can be found in MyProject.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<Version>1.2.3.4</Version>
<Authors>Author 1</Authors>
<Company>Company XYZ</Company>
<Product>Product 2</Product>
<PackageId>MyApp</PackageId>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<NeutralLanguage>en</NeutralLanguage>
<Description>Description here</Description>
<Copyright>Copyright</Copyright>
<PackageLicenseUrl>License URL</PackageLicenseUrl>
<PackageProjectUrl>Project URL</PackageProjectUrl>
<PackageIconUrl>Icon URL</PackageIconUrl>
<RepositoryUrl>Repo URL</RepositoryUrl>
<RepositoryType>Repo type</RepositoryType>
<PackageTags>Tags</PackageTags>
<PackageReleaseNotes>Release</PackageReleaseNotes>
</PropertyGroup>
In the file explorer properties information tab, FileVersion is shown as "File Version" and Version is shown as "Product version"

I do the following for my .NET Standard 2.0 projects.
Create a Directory.Build.props file (e.g. in the root of your repo)
and move the properties to be shared from the .csproj file to this file.
This also enables central management of these shared properties in a multi project solution, allowing for example to set the copyright and/or version numbers only once for all projects.
MSBuild will pick it up automatically and apply them to the autogenerated AssemblyInfo.cs.
They also get applied to the nuget package when building one with dotnet pack or via the UI in Visual Studio 2017.
See https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build
Example:
<Project>
<PropertyGroup>
<Company>Some company</Company>
<Copyright>Copyright © 2020</Copyright>
<AssemblyVersion>1.0.0.1</AssemblyVersion>
<FileVersion>1.0.0.1</FileVersion>
<Version>1.0.0.1</Version>
<!-- ... -->
</PropertyGroup>
</Project>

You can always add your own AssemblyInfo.cs, which comes in handy for InternalsVisibleToAttribute, CLSCompliantAttribute and others that are not automatically generated.
Adding AssemblyInfo.cs to a Project
In Solution Explorer, right click on <project name> > Add > New Folder.
Name the folder "Properties".
Right click on the "Properties" folder, and click Add > New Item....
Select "Class" and name it "AssemblyInfo.cs".
Suppressing Auto-Generated Attributes
If you want to move your attributes back to AssemblyInfo.cs instead of having them auto-generated, you can suppress them in MSBuild as natemcmaster pointed out in his answer.

Adding to NightOwl888's answer, you can go one step further and add an AssemblyInfo class rather than just a plain class:

I want to extend this topic/answers with the following. As someone mentioned, this auto-generated AssemblyInfo can be an obstacle for the external tools. In my case, using FinalBuilder, I had an issue that AssemblyInfo wasn't getting updated by build action. Apparently, FinalBuilder relies on ~proj file to find location of the AssemblyInfo. I thought, it was looking anywhere under project folder. No. So, changing this
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
did only half the job, it allowed custom assembly info if built by VS IDE/MS Build. But I needed FinalBuilder do it too without manual manipulations to assembly info file. I needed to satisfy all programs, MSBuild/VS and FinalBuilder.
I solved this by adding an entry to the existing ItemGroup
<ItemGroup>
<Compile Remove="Common\**" />
<Content Remove="Common\**" />
<EmbeddedResource Remove="Common\**" />
<None Remove="Common\**" />
<!-- new added item -->
<None Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
Now, having this item, FinalBuilder finds location of AssemblyInfo and modifies the file. While action None allows MSBuild/DevEnv ignore this entry and no longer report an error based on Compile action that usually comes with Assembly Info entry in proj files.
C:\Program Files\dotnet\sdk\2.0.2\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.Sdk.DefaultItems.targets(263,5):
error : Duplicate 'Compile' items were included. The .NET SDK includes 'Compile' items from your project directory by default.
You can either remove these items from your project file, or set the 'EnableDefaultCompileItems' property to 'false' if you want to explicitly include them in your project file.
For more information, see https://aka.ms/sdkimplicititems. The duplicate items were: 'AssemblyInfo.cs'

Thanks, this helped me a lot.
In my case, building the project Blazor Server Side Website was successful both on Release and Debug, but publishing the website still failed with the Duplicate Attribute error, which confused me a bit.
The solution was to add <GenerateAssemblyInfo>false</GenerateAssemblyInfo> both to the .csproj and .pubxml file:
Path: <Project>/Properties/PublishProfiles/<ProfileName>.pubxml:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
...
<!-- Add the line below -->
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>

With .NET 5+, you can use AssemblyMetadata:
<AssemblyMetadata Include="Bar" Value="Baz" />

Related

Visual Studio 2017 and the new .csproj InternalsVisibleTo

Where do I put InternalsVisibleTo from AssemblyInfo in the new Visual Studio 2017 .csproj project file?
Just in case anyone would like to put InternalsVisibleTo within a .csproj file instead of AssemblyInfo.cs (a possible scenario is to have a naming convention between a project under test and a test project), you can do it like this:
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
Having this the following code will be generated
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyProject.Test")]
inside auto-generated AssemblyInfo.cs (e.g. for Debug configuration and .NET Standard 2.0 target)
/obj/Debug/netstandard2.0/MyProject.AssemblyInfo.cs
Additional Info
In case you're on .NET Core 3.1 and this approach isn't working, you may have to explicitly generate assembly info by adding the following to your .csproj file:
<PropertyGroup>
<!-- Explicitly generate Assembly Info -->
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>
As of .NET 5 (and newer .NET versions) this actually works once added to your csproj:
<ItemGroup>
<InternalsVisibleTo Include="YourProject.Tests.Unit" />
</ItemGroup>
Work and discussion around this feature can be seen on this PR on dotnet's GitHub repo.
To clarify Hans Passant's comment above, you simply have to add InternalsVisibleTo to any cs file in your project. For example, I created an AssemblyInfo.cs file in the root of the project and then added the following content (only):
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=<ADD_KEY_HERE>")]

MSBuild - Project-specific targets for solution does not work

I have a solution that has multiple projects in it, including a web application. I want MSBuild to execute "WebPublish" target against the web application project and "default target" for all other projects in the solution.
This MSDN article says that I can do it specifying the command line
msbuild SlnFolders.sln /t:NotInSlnfolder:Rebuild;NewFolder\InSolutionFolder:Clean
But I never could make it work - MSBuild return an error, something like "NotInSlnFolder:Rebuild" target does not exist. It does not matter what target to specify, Build, Rebuild or Clean - it does not work in any case.
How can I achieve my goal of specifying project-specific targets for a solution?
The MSDN documentation does not work. Or have I missed something?
NOTE: This workaround is not officially supported by Microsoft, so there is no guarantee that it will work forever.
Short Answer
In folder with the SLN file, create the file before.{YourSolution}.sln.targets, with the following content: (Replace what in curly brackets to whatever you need.)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="{MyCompany_MyProduct_WebApp:WebPublish}">
<MSBuild
Condition="'%(ProjectReference.Identity)' == '{$(SolutionDir)MyCompany.MyProduct.WebApp\MyCompany.MyProduct.WebApp.csproj}'"
Projects="#(ProjectReference)"
Targets="{WebPublish}"
BuildInParallel="True"
ToolsVersion="4.0"
Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"
SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" />
</Target>
</Project>
After that you can execute the command line:
msbuild {YourSolution}.sln /t:{MyCompany_MyProduct_WebApp:WebPublish}
Long Answer
If you add environment variable MSBUILDEMITSOLUTION, setting its value to 1, MSBuild will not delete temporary files generated for the solution and projects.
This will allow you to find {YourSolution}.sln.metaproj and {YourSolution}.sln.metaproj.tmp files generated in the solution folder, which are just standard MSBuild project files.
For MSBuild 3.5, the generated file is {YourSolution}.sln.cache and is retained regardless of environment variables. Analyzing those files, you will understand low-level details of the process and to see the customization opportunities available.
After executing MSBuild with some project-specific target in the .Metaproj file you will find out that the list of project-specific targets is hardcoded and only standard targets are supported (Build, Rebuild, Clean, Compile, Publish; note: Publish and WebPublish are not the same). MSBuild 3.5 only generates Clean, Rebuild and Publish targets as well as a target with just the project's name that means "Build".
You also can see that NotInSlnfolder:Rebuild is just a name of an autogenerated target. In reality MSBuild does not parse it and does not care about project names and location. Also note that the autogenerated target names specify the project name with solution folders hierarchy if it's in one, e.g. SolFolder\SolSubfolder\ProjectName:Publish.
One more critically important thing you will find: The MSBuild Target Name does not support dots. All dots in project names are replaced with underscores. For example, for a project named MyCompany.MyProduct.Components you will have to specify in the command line:
/t:MyCompany_MyProduct_Components:Rebuild
That's why even standard project-specific target Build didn't work - my project name contained dots.
Analyzing file {YourSolution}.sln.metaproj.tmp, you will find out that at runtime it tries to import targets from file named before.{YourSolution}.sln.targets and after.{YourSolution}.sln.targets, if those files exist. This has a key to the workaround for this MSBuild limitation/bug.
You can open your solution file in text editor and check whether following line is exist or not if not then you can add
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> inside the <Project> tag.
Hope this help you.

Changing EXE name based on compilation constant

I have a project in VB.NET 2010 (compiling to x86, .NET 2.0 runtime) that I want to compile into two separate EXEs - a "lite" version and a "full" version.
Unfortunately I cannot make two separate projects as it uses the Adobe Reader COM control - and sharing a form using that control between two projects seems to confuse the IDE (something to do with COM Interop, I assume - if someone knows how to share a form hosting the adobe reader control, that would solve my problem too).
I have found this thread:
Change name of exe depending on conditional compilation symbol however I don't have any MSBuild experience so I need more explicit instructions.
On the "My Project>Compile" tab there is a "Build Events..." button. I was wondering if anyone knows how to set a conditional compilation constant and use that to determine the EXE name (or change it after build).
If all else fails I can rename the EXE manually I suppose, but I'd prefer it to be automated.
If having two separate projects within your solution isn't acceptable, you'll want to look into creating your own MSBuild script.
Here is an example of a custom MSBuild script that will allow you to define your custom compilation constants at build time, and then build your two versions of your application ("Full" and "Lite"):
<Project DefaultTargets="BuildAll" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildVersionFull>FULL</BuildVersionFull>
<BuildVersionLite>LITE</BuildVersionLite>
</PropertyGroup>
<ItemGroup>
<Projects Include="$(MSBuildProjectDirectory)\MyApp.vbproj" />
</ItemGroup>
<Target Name="BuildAll" DependsOnTargets="BuildFull;BuildLite" />
<Target Name="BuildFull">
<MSBuild Projects="#(Projects)" Properties="DefineConstants=$(BuildVersionFull);OutputPath=binFull\;BaseIntermediateOutputPath=objFull\;AssemblyName=MyApp_Full" />
</Target>
<Target Name="BuildLite">
<MSBuild Projects="#(Projects)" Properties="DefineConstants=$(BuildVersionLite);OutputPath=binLite\;BaseIntermediateOutputPath=objLite\;AssemblyName=MyApp_Lite" />
</Target>
</Project>
What you'll need to do:
Create a new file and save it the same directory as your application's project file as "MyBuild.xml".
Change to reflect the name of your application's project file.
Open the Visual Studio Command Prompt and run "msbuild ".
Within your project, you can use the "FULL" and "LITE" conditional compilation constants to determine whether to compile certain statements:
#If FULL Then
' Compile code for the "Full" version.
#End If

MSBuild - Determine a solution's _PublishedWebsites

I am writing a web development targets file and would like to programmatically determine the name of the directory that appears beneath "_PublishedWebsites".
I currently have to use this:
$(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild)\_PublishedWebsites\ MyWebApplication
Any ideas?
(I am not using this for solutions with more than one website to publish)
The new Web Publishing Pipeline (WPP) in .NET 4.0 has a method for controlling the output location.
First, you need to opt-in to WPP during the execution of the CopyWebApplication target. Set the following MSBuild properties, either at command line or in the MSBuild project file:
<PropertyGroup>
<UseWPP_CopyWebApplication>True</UseWPP_CopyWebApplication>
<PipelineDependsOnBuild>False</PipelineDependsOnBuild>
</PropertyGroup>
The command line-variant is:
/p:UseWPP_CopyWebApplication=True /p:PipelineDependsOnBuild=False
Next, create a new MSBuild targets file in the same directory as your project and name it "ProjectName.wpp.targets" where "ProjectName" is the filename of your project, minus the extension. In other words, if you have "MyWebsite.csproj" you need to create "MyWebsite.wpp.targets". I find it helps to add the targets file to the project as well. It's not required, but it makes it easier to edit.
In the new targets file, you will need to override the WebProjectOutputDir property. Only do this when CopyWebApplication will be called - in other words, when the "OutDir" is redirected away from the "OutputPath":
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebProjectOutputDir Condition="'$(OutDir)' != '$(OutputPath)'">$(OutDir)Websites\MyCustomFolderName</WebProjectOutputDir>
</PropertyGroup>
</Project>
That's it - you should be good to go. You can test it locally by setting the OutDir property. Don't forget the trailing backslash:
msbuild MyWebsite.csproj /p:OutDir=C:\Development\WebOutputTest\

How to always execute a target in MSBuild

I have an MSBuild file that manipulates the AssemblyInfo file before the application is compiled. At the end of the build, it restores the AssemblyInfo file. It does this by backing up the file, manipulating it, and then after build time, restoring the file.
This works fairly well except when an error occurs during the build. It then does not restore the original file. Is there a way I can tell MSBuild to execute a target at the end of a build no matter if it succeeded or failed?
Based on your last comment to the original question I would take another approach, and forget the approach you are currently taking. You should know that your version info doesn't have to be in the AssemblyInfo.cs file. It can be in any code file, just as long as you only have attributes AssemblyVersion and AssemblyFileVersion defined once each. With that being said what I would do is follow these steps:
Remove AssemblyVersion & AssemblyFileVersion from AssemblyInfo.cs
Create a new file, name it whatever you want want in my case I put it at Properties\VersionInfo.cs. Do not add this file to the project.
Edit the project file to include that file into the list of file to be compiled only when you want it
Let's expand a bit on #3. When you build a .NET project, the project itself is an MSBuild file. Inside that file you will find an item declared Compile. This is the list of files that will be sent to the compiler to be compiled. You can dynamically include/exclude files from that list. In you case you want to include the VersionInfo.cs file only if you are building on the build server (or whatever other condition you define). For this example I defined that condition to be if the project was building in Release mode. So for Release mode VersionInfo.cs would be sent to the compiler, and for other builds not. Here are the contents of VersionInfo.cs
VersionInfo.cs
[assembly: System.Reflection.AssemblyVersion("1.2.3.4")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.3.4")]
In order to hook this into the build process you have to edit the project file. In that file you will find an element (maybe more than 1 depending on project type). You should add a target similar to the following there.
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(Configuration)'=='Release' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
Here what I've done here is to define a target, BeforeCompile, which is a well-known target that you can override. See this MSDN article about other similar targets. Basically this is a target which will always be called before the compiler is invoked. In this target I add the VersionInfo.cs to the Compile item only if the Configuration property is set to release. You could define that property to be whatever you wanted. For instance if you have TFS as your build server then it could be,
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(TeamFoundationServerUrl)'!='' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
Because we know that TeamFoundationServerUrl is only defined when building through TFS.
If you are building form the command line then something like this
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(IncludeVersionInfo)'=='true' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
And when you build the project just do msbuild.exe YourProject.proj /p:IncludeVersion=true. Note: this will not work when building a solution.
What about changing the problem:
Add a "template" AssemblyInfo.cs.template to version control that represents your "ideal" AssemblyInfo.cs with regex hooks in there
Before build, copy the template to the real and apply your regexes
Add some kind of subversion ignore for AssemblyInfo.cs (I'm no svn expert, but I'm pretty sure there is a way you can tell it to ignore certain files)
In the event that your devs need to add some kind of customization that would normally appear in an AssemblyInfo.cs (eg InternalsVisibleTo), then get them to add it to a different .cs file that IS checked in.
As a further refinement, combine Sayed's solution with mine and remove version info stuff from the actual AssemblyInfo.cs and have a VersionInfo.cs.template that is checked in, that creates a VersionInfo.cs in BeforeBuild.
I never used it, but from the documentation it seems that the OnError Element is useful to what you're trying to achieve.
Causes one or more targets to execute,
if the ContinueOnError attribute is
false for a failed task.