I use a Directory.Build.props file that has a certain group of properties that I want included only if the project has GeneratePackageOnBuild set to true, or when the project is packaged.
This is for the latest version of .Net 7.
Example below does not appear to be working.
Directory.Build.props
<PropertyGroup Condition="'$(GeneratePackageOnBuild)' == 'true'">
<PackageIcon>favicon.ico</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
Project.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
The Directory.Build.props file is imported before the content of the project file. The GeneratePackageOnBuild property hasn't yet been set to true when the "'$(GeneratePackageOnBuild)' == 'true'" condition is evaluated.
Move your PropertyGroup to the Directory.Build.targets file. The Directory.Build.targets file is imported after the content of the project file.
Contrary to what the names may seem to imply, there are no special restrictions. Both the Directory.Build.props file and the Directory.Build.targets file can contain properties, items, and targets.
In practice Directory.Build.props is hardly ever actually needed. Only when a property must be set before the project SDK (i.e. Sdk="Microsoft.NET.Sdk") logic is evaluated is there a need for the Directory.Build.props file. Everything else either can go or must go (because of dependencies) in the Directory.Build.targets file.
Related
I have an API project that has a few libraries as references. Each of those libraries creates a documentation XML file by including
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml</DocumentationFile>
in the project.
When I build my API project, it pulls all the XML files from each of the referenced libraries, except one.
Here's the main property group of one that does copy over:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml
</DocumentationFile>
</PropertyGroup>
And here's the one for the project that doesn't copy over:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml
</DocumentationFile>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
I've tried removing GenerateDocumentationFile, but that makes no difference.
The XML does get generated, but remains in the library's bin. It never gets copied to the API project's bin directory.
Both of these libraries are referenced via project references.
It is possible that the library whose XML is not being copied over is being referenced inside a nuget that the API references. Could that be the cause?
I need the XML files for including example values in the Swagger.
I am studying C# in my HDN school with Visual Studio.
In Windows there is a way to generate automaticaly the technical documentation from modules by going through Project > Properties and by clicking on "XML documentation file".
On Visual Studio for Mac I do not see this functionality.
May someone help me ? Does it exist or should I write manually the /// up to the module ?
You can also add a configuration to the .csproj so the documentation file gets generated with each build. Follow this SO:
https://stackoverflow.com/a/47118584/4122889
Add this code to you .csproj file
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Or create a Directory.Build.Props file in the root of your repo and add this:
<Project>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
More info on Directory.Build.Props (or .Targets):
https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
I noticed in new .NET Core projects there is no AssemblyInfo.cs file created. I have seen that you can still set assembly attributes such as AssemblyVersion and so forth.
Are there still any valid reasons to use an AssemblyInfo.cs file?
You can absolutely create an AssemblyInfo.cs file and configure your assembly like you did in the past. Of course, since the properties are set using assembly attributes, you do not need to use AssemblyInfo but can choose any other file name or even an existing one.
That being said, the reason that the AssemblyInfo.cs is no longer included in the default templates is that the new SDK-style project type supports setting this information within the csproj project file.
So the usual approach to setting the version of your assembly would be to set the Version property within your project file (or have that automatically set as part of your build process). For example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<Version>1.2.3</Version>
</PropertyGroup>
…
</Project>
Since this is a MSBuild property, you can also set this during the build process e.g. with dotnet build /p:Version=1.2.3.
There are also the properties VersionPrefix and VersionSuffix which can be used to automatically construct version numbers from the environment (e.g. Git commit ids, or build numbers).
In addition to the version related properties, there are also some more NuGet properties you can set in the project file, which makes the AssemblyInfo.cs mostly redundant.
According to migration guide, these days, we have few flexible ways to set up assembly attributes.
Use old-style AssemblyInfo.cs file (create manually).
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("...")]
[assembly: System.Reflection.AssemblyCopyrightAttribute("...")]
[assembly: System.Reflection.AssemblyDescriptionAttribute("...")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("...")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("...")]
[assembly: System.Reflection.AssemblyProductAttribute("...")]
[assembly: System.Reflection.AssemblyTitleAttribute("...")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0-dev01234567")]
Use MSBuild's properties to generate assembly attributes on build time (may be static in your.csproj or passed via command line arguments like /property:Version=1.0.0-dev01234567).
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Company>...</Company>
<Copyright>...</Copyright>
<Description>...</Description>
<Product>...</Product>
<AssemblyTitle>...</AssemblyTitle>
<Version>1.0.0-dev01234567</Version>
</PropertyGroup>
...
</Project>
Note: you may merge both solutions but avoid duplicates of assembly attributes.
I'm using Visual Studio 2022 and .NET Core 6.
Right click on the project in Solution Explorer
Edit Project File
Here is an example project file containing Version, AssemblyVersion and FileVersion:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
...
</Project>
Reasons for still using an AssemblyInfo.cs file might include
you want to share some of the AssemblyInfo across projects,
which you can do with a file
you might have a code-generation process that spits out the assemblyinfo
the project file format doesn't yet support all the attributes you might want to use. The project Sdk knows how to auto-generate a limited set of [AssembyAttributes] from Xml Elements with matching names in the csproj file, but it doesn't support autogeneration of arbitrary [AssembyAttributes] or other metadata for your assembly.
AssemblyInfo.cs is “just” a source code file, you might have other metadata – whether AssemblyAttributes or classes or other – you want to keep all in one easily found place.
From Your Project -> Select Properties
On left panel select Package/General
At field Assembly version and File version enter your version (ex: 1.0.0.0)
We have a solution created and maintained via VisualStudio2017 in which our .csprojs are placed inside virtual-folders like so:
Solution.sln
\- VirtualFolder1
\- Foo.Common.Bar.csproj -> Bar\bin
\- Foo.Common.Ping.csproj -> Ping\bin
\- Foo.Common.Pong.csproj -> Pong\bin
\- VirtualFolder2
\- Foo.Utils.Bar.csproj -> Utils.Bar\bin
\- Foo.Utils.Ping.csproj -> Utils.Ping\bin
\- Foo.Utils.Pong.csproj -> Utils.Pong\bin
As expected each and every .csproj file already contains a section which defines where the output path should be:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>[Bar/bin or Ping/bin etc]</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>[Bar/bin or Ping/bin etc]</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
We want to build all .Common .csproj and .Utils .csproj projects en-masse into their respective output folders without having to specify them in our msbuild-script (which is invoked by JenkinsCI btw) one by one. To achieve that we have tried:
<ItemGroup>
<ALL_PROJECTS_IN_SOLUTION_EXCEPT_TESTS
Include="$(_codeFolderpath)\**\*.csproj"
/>
</ItemGroup>
<MSBuild
Projects="#(ALL_PROJECTS_IN_SOLUTION_EXCEPT_TESTS)"
Properties="Platform=$(Platform);Configuration=$(Configuration)"
BuildInParallel="true"
/>
This however results in the following errors for all our .csproj:
The OutputPath property is not set for project [...].csproj
This is strange given the fact that the OutputPath is defined in our .csproj files (as shown above).
If we specify the 'Output' property then the problem goes away of course however what we really want is these projects to output themselves into their respective appropriate output directories (shown above). How can one go about achieving this?
Looks like you have one separate project(Build Project) used to build the .Common .csproj and .Utils .csproj projects. And the script you write above is defined in a target in the Build Project. (Hope I didn't misunderstand.)
According to your error message The OutputPath property is not set... There is no OutputPath property defined in the Common..csproj or Utils..csproj.
If so, I suggest you use a folder structure like this:
Solution.sln
\- VirtualFolder1
\- Foo.Common.Bar.csproj -> Foo.Common.Bar\bin
\- Foo.Common.Ping.csproj -> Foo.Common.Ping\bin
\- Foo.Common.Pong.csproj -> Foo.Common.Pong\bin
\- VirtualFolder2
\- Foo.Utils.Bar.csproj -> Foo.Utils.Bar\bin
\- Foo.Utils.Ping.csproj -> Foo.Utils.Ping\bin
\- Foo.Utils.Pong.csproj -> Foo.Utils.Pong\bin
Because to get the same structure you want I think maybe there has much more complex work:
1.With no OutputPath in .csproj file, we can create a Directory.Build.props file in the Directory above its path to control the output path.
2.Pass the OutputPath property in your MSBuild Task. In this situation you need to get the second name for .common.csproj and .utils.csproj projects and add conditions like:
<MSBuild
Projects="#(ALL_PROJECTS_IN_SOLUTION_EXCEPT_TESTS)"
Properties="Platform=$(Platform);Configuration=$(Configuration);OutputPath=xxx\ThirdName\bin"
BuildInParallel="true"
Condition="judge if the common.csproj files"
/>
<MSBuild
Projects="#(ALL_PROJECTS_IN_SOLUTION_EXCEPT_TESTS)"
Properties="Platform=$(Platform);Configuration=$(Configuration);OutputPath=xxx\SecondName.ThirdName\bin"
BuildInParallel="true"
Condition="judge if the utils.csproj files"
/>
So both of these two directions may help achieve your particular goal, but the work would be much more than we expect.
As a workaround:
What's reason you must put them in Utils.Bar\bin folder instead of Foo.Utils.Bar\bin folder? The latter one is pre-defined property for the Foo.Utils.Bar.csproj file. SO we can easily use the $(ProjectDir) or $(ProjectName) to represent it. You can Create a Directory.Build.props file, add the script below:
<Project>
<PropertyGroup>
<OutputPath>$(ProjectDir)bin\$(Configuration)</OutputPath>
</PropertyGroup>
</Project>
In this way, when loading the project files in VS, what you need to do is to build the solution. You won't need to build the Build project any more. And since you're using the virtual-path which I haven't tried, maybe you can use <OutputPath>AbsolutePathOfMyOutput\$(ProjectName)bin\$(Configuration)</OutputPath>
Update:(Haven't noticed your edit until today.)
According to your edit, you've set the output path in the .csproj.
Here are two suggestions:
1.If you build them in VS ID: Every time after you do some modify to xx.csproj outside VS IDE by notepad or what, I suggest you right-click the project to unload and reload the project file before you build them
2.Please check if the entire error message you get looks like this:
error : The OutputPath property is not set for project 'ConsoleApp1.csproj'. Please check to make sure that
you have specified a valid combination of Configuration and Platform for this project. Configuration='' Platform='An
yCPU'.
Since your OutputPath property are defined in PropertyGroup for Debug|AnyCPU and Release|AnyCPU.If you haven't passed the corresponding parameters to msbuild.exe, the process can't read the OutPutPath property from these ProppertyGroups.
For example: You define the OutPutPath in Debug|AnyCPU and Release|AnyCPU. Then the real value of Configuration and Platform you pass is Debug|X86, or Null|AnyCPU or CustomDebug|AnyCPU, and you haven't defined OutPutPath in this kind of Combination(PropertyGroup), you'll get the error messgae xxx not set.
To resolve this: Make sure you pass the correct Configuration and Platform combination can resolve it. Or define OutPutPath in the Combination you actually use.
I want to configure my main output path to something like
C:\Company\UpdaterLauncher\Worker\Version
Where version is my AssemblyInfo.Version in string.
So a new folder each time I decide to change the assembly version.
I know I can change output all time.. But it's annoying.
Is this possible to use something like "C:\Company\UpdaterLauncher\Worker\{AssemblyVersion}" for visual output path to interprete it and build where I want?
I looked a bit in documentation and didn't found anything like this...
Which way do you build the project? By msbuild command-line or within VS IDE?
First direction: Let's read the assembly version number before the build starts, then
pass it to outputpath property.
I've written a script trying to read the version before the build starts. But not completely work:(
E.g: Using a class library project as the example.
Right-click the project and choose edit the xx.csproj, add the script (From In property to FourthNum property) into the PropertyGroup:
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DAB28A16-73AD-4EC5-9F8D-E58CE3EC84BE}</ProjectGuid>
......
<In>$([System.IO.File]::ReadAllText('$(MSBuildProjectDirectory)\properties\AssemblyInfo.cs'))</In>
<Pattern>\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+).(\d+)</Pattern>
<FirstNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[1].Value)</FirstNum>
<SecondNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[2].Value)</SecondNum>
<ThirdNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[3].Value)</ThirdNum>
<FourthNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[4].Value)</FourthNum>
</PropertyGroup>
It will read the assembly version number from AssemblyInfo.cs. If I have an assembly whose assembly version is 3.13.8.5. Then the FirstNum=3, SecondNum=13 ...
And set the outputpath as: <OutputPath>C:\Company\UpdaterLauncher\Worker\$(FirstNum).$(SecondNum).$(ThirdNum).$(FourthNum)</OutputPath>
Reload the project and build it. You can find the build output there C:\Company\UpdaterLauncher\Worker\3.13.8.5.
Note:
1.In this way, since we will build it in both debug and release mode. We need to set the outputpath value in both propertygroup for debug and release.(2 places)
2.Since we only define the output depending on version, the debug output and release will all locates in same folder. So I think the <OutputPath> would be better like:
<OutputPath>C:\Company\UpdaterLauncher\Worker\$(FirstNum).$(SecondNum).$(ThirdNum).$(FourthNum)\$(Configuration)</OutputPath>
3.This script won't work immediately after you change the version in VS IDE.
Via Command-line: It works well, every time we change the version number and build it, the output is correct.
Within VS IDE: Every time after we change the version, it needs us to unload and reload the project file by right-clicking the project, then it will work. So I say it isn't that perfect.(I would think this issue has something to do with when and how the VS loads the project file)
Second Direction: The build output actually is copy the related
assemblies to output folder. So we can copy or move the output content
to the directory after the build we want by copy or move task.
We can check this issue, using GetAssemblyIdentity to get the info after the build.
Using the way above to get version number, name it $(MyVersion). Then use a after-build target to copy the output to the specified folder.
<Target Name="CopyToSpecificFolder" AfterTargets="build">
<GetAssemblyIdentity
AssemblyFiles="$(OutputPath)$(AssemblyName).dll">
<Output
TaskParameter="Assemblies"
ItemName="MyAssemblyIdentities"/>
</GetAssemblyIdentity>
<PropertyGroup>
<MyVersion>%(MyAssemblyIdentities.Version)</MyVersion>
</PropertyGroup>
<ItemGroup>
<Out Include="$(OutputPath)*.*" />
</ItemGroup>
<Copy DestinationFolder="C:\Company\UpdaterLauncher\Worker\$(MyVersion)" SourceFiles="#(Out)"/>
</Target>
Add this script into the xx.csproj file. In the bottom of it like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
......
<Target Name="CopyToSpecificFolder" AfterTargets="build">
......
</Target>
</Project>
It works well in whether VS IDE or by command-line. And it's for class project, if you're developing a .exe project, change the $(AssemblyName).dll to $(AssemblyName).exe.