AssemblyName property in Directory.Build.targets has no effect - msbuild

I am trying to override the AssemblyName property defined in .NET projects in my Azure Pipeline. (The purpose is to standardize assembly names for NuGet packages.)
It works when I create a Directory.Build.props file right before building with MSBuild.
The problem is that this only works if the AssemblyName has not been defined in the .csproj file. Otherwise the .csproj property takes precedence.
According to the docs, I should be able to use Directory.Build.targets to override .csproj properties:
https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2022#choose-between-adding-properties-to-a-props-or-targets-file
If you need to override properties, do it in a .targets file, after
all user-project customizations have had a chance to take effect.
This is what the Directory.Build.targets file contains:
<Project>
<Target Name="IncludeProjectMatchMessage" Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''" BeforeTargets="Build">
<Message Text="The custom Directory.Build.targets properties will be applied to this project $(MSBuildProjectName), AssemblyName = $(packageName)" Importance="high" />
</Target>
<PropertyGroup Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Company>MyCompany</Company>
<AssemblyName>$(packageName)</AssemblyName>
</PropertyGroup>
</Project>
I have also tried this, with the same result:
<Project>
<Target Name="IncludeProjectMatchMessage" Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''" BeforeTargets="Build">
<Message Text="The custom Directory.Build.targets properties will be applied to this project $(MSBuildProjectName), AssemblyName = $(packageName)" Importance="high" />
<PropertyGroup Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Company>MyCompany</Company>
<AssemblyName>$(packageName)</AssemblyName>
</PropertyGroup>
</Target>
</Project>
The text message is displayed during the MSBuild, but the properties are not applied.
Why is that, and how do I fix it?

I could not get .targets to work, nor could I find any documentation on the properties that it is possible to override using this method.
Instead, I added a PS script that modifies the .csproj file directly:
- task: PowerShell#2
displayName: Modify .csproj file
inputs:
targetType: 'inline'
script: |
# This script will overwrite csproj properties that are already defined.
# If they are not defined, no changes are made - values are not added.
# The case of missing properties is handled by adding a .props file in the next step.
# Note that changing the assembly name may break the build when the library contains xaml files.
# Read more below.
$path = "$(projectFolder)\${{ parameters.projectName }}.csproj"
Write-Host "Reading project file: " $path
[xml]$xml =(gc $path)
$projectAssemblyName = $xml.Project.PropertyGroup.AssemblyName
Write-Host "AssemblyName: " $projectAssemblyName
if ($projectAssemblyName -like "$(packageName)")
{
Write-Host "AssemblyName corresponds to package name."
}
else
{
Write-Host $projectAssemblyName "is different from package name:" $(packageName)
Write-Host "Overwriting AssemblyName with " $(packageName)
$xml.Project.PropertyGroup |? AssemblyName |% {$_.AssemblyName="$(packageName)"}
}
$xml.Project.PropertyGroup |? Company |% {$_.Company="MyCompany"}
$xml.Project.PropertyGroup |? GenerateDocumentationFile |% {$_.GenerateDocumentationFile="True"}
$xml.Save($path)
$fileContent = Get-Content $path
Write-Host "Success. File contents: " $fileContent

Related

Publish extra files in Core 3 - before I could use publishOptions (project.json)

I'm trying to implement "Feature folders/slices" in my project...
https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/september/asp-net-core-feature-slices-for-asp-net-core-mvc
In the link above they write:
To support publishing correctly, you’ll also need to update project.json’s publishOptions to include the Features folder:
JavaScript
Copy
"publishOptions": {
"include": [
"wwwroot",
"Views",
"Areas/**/*.cshtml",
"Features/**/*.cshtml",
"appsettings.json",
"web.config"
]
},
But project.json and publishOptions does not exists in Core 3.
How can I solve this in Core 3?
This post solved my problem...
Is it possible to deploy an uncompiled ASP.NET Razor Pages website?
Add reference to Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.
Add startup services.AddRazorPages().AddRazorRuntimeCompilation();.
In Properties -> PublishProfiles -> YourProfile.pubxml, add...
<ItemGroup>
<ViewFiles Include="$(ProjectDir)\Features\**\*.cshtml" />
<JsFiles Include="$(ProjectDir)\Features\**\*.js" />
</ItemGroup>
<Target Name="CopyViewFilesAfterPublish" AfterTargets="Publish">
<Copy SourceFiles="#(ViewFiles)" DestinationFolder="$(PublishDir)\Features\%(RecursiveDir)" />
<Copy SourceFiles="#(JsFiles)" DestinationFolder="$(PublishDir)\Features\%(RecursiveDir)" />
</Target>
And that's it :)

Blazor: How to include npm dependencies into a Razor Class Library?

I am trying to create a Blazor component in a Razor Class Library which uses npm dependencies. In my wwwroot I created a package.json:
{
"name": "mycomponent",
"version": "0.0.1",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "UNLICENSED",
"dependencies": {
"d3": "^5.14.2"
}
}
In my csproj I have:
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="npm install" WorkingDirectory="$(ProjectDir)wwwroot" />
</Target>
And I set my project to create a .nupkg.
When I now run dotnet publish MyComponent -c Release, I get the MyComponent.1.0.0.nupkg but its staticwebassets folder only has the top level content of wwwroot in there.
So, how do I get the dependencies from the node_modules folder in there? Do I have to run some sort of packager which generates a minified package in the root folder or am I missing something different?
I found a solution which adds webpack to the game. Here is how I did it:
If you do not already have a package.json, run npm init inside wwwroot to generate one
Move everything in wwwroot but package.json and package-lock.json to wwwroot/src
Install webpack and some loaders:
npm install --save-dev webpack css-loader file-loader style-loader
Add an appropriate webpack.config.js:
var path = require('path');
module.exports = {
mode: 'production',
entry: [ './src/main.js', './src/style.css' ],
module: {
rules: [
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.(jpe?g|png|gif|svg)$/i, use: 'file-loader' }
]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.bundle.js',
publicPath: '_content/MyRazorClassLibrary/dist/' // << See note
}
};
Note: The publicPath is used for rewriting the path when run inside Blazor. Webpack packs the result in a way that all assets are relative to the main JS file. However Blazor expects the data at the path _content/ProjectName, which is accounted for using publicPath.
Add this to the csproj file:
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="npm install" WorkingDirectory="$(ProjectDir)wwwroot" />
<Exec Command="webpack" WorkingDirectory="$(ProjectDir)wwwroot" />
</Target>
<ItemGroup>
<Content Remove="wwwroot\package-lock.json" />
<Content Remove="wwwroot\package.json" />
<Content Remove="wwwroot\src\**" />
<Content Remove="wwwroot\webpack.config.js" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\package-lock.json" />
<None Include="wwwroot\package.json" />
<None Include="wwwroot\src\**" />
<None Include="wwwroot\webpack.config.js" />
</ItemGroup>
This will cause the src directory and npm and webpack specific files to not be included in the final NuGet package. Plus, it'll run npm install and webpack.
For this to work, you must install webpack-cli:
npm install -g webpack webpack-cli
Now, when you run dotnet build -c Release MyRazorClassLibrary, and set your library to generate a NuGet package on build, you will get a nice nupkg with everything baked in.
Sorry there is no support for this in Blazor. Your best approach is to document the requirements on external dependencies for anyone using the library.
I'd point out that not everyone uses NPM (I avoid it like the plague) and have different approaches to client-side packages, so if this is a "public" package it might be too prescriptive an approach to require NPM. If it's internal use only that's fine, but you're on your own as to how to handle bundling and packaging.
There is an issue tracking how static resources are handled

Msbuild ignoring build errors

Context
I have a task integration testing and code coverage which I execute in my jenkins pipeline.
The tools used is dotcover and Nunit.
Nunit is executed throught dotcover during the integration test build when the configuration is Integration.
Problem
When I execute the configuration Integration in visual studio with some tests in error, then the build failed, everything are ok, but when the same configuration are executed with msbuild, it doesn't return any error code then jenkins pipelin doesn't fail.
The situation put us in delicate way because we can't trust our build pipeline anymore.
I looking for a solution on web for some days and I still on the same point, it's why I asking for your help here.
Thank you for helping.
Files
jenkinsfile
node('BUILD_PROJECT') {
stage ('Checkout')
{
checkout scm
}
stage ('Build')
{
bat '"C:/Program Files (x86)/NuGet/nuget.exe" restore -NonInteractive MySolution.sln'
bat "\"C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin/MSBuild.exe\" /p:Configuration=Release;AssemblyVersion=0.1.0.${env.BUILD_NUMBER} /maxcpucount:8 MySolution.sln"
}
stage ('Integration')
{
bat "\"C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin/MSBuild.exe\" /p:Configuration=Integration /maxcpucount:8 MySolution.sln"
}
stage ('Publish Coverage')
{
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: 'Solution/IntegrationProject/bin/Integration/TestResult',
reportFiles: 'ProjectCoverageReport.html',
reportName: 'Project Coverage Report'
]
}
stage ('Setup')
{
bat "\"C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin/MSBuild.exe\" /p:Configuration=Setup;Platform=x86;AssemblyVersion=0.1.0.${env.BUILD_NUMBER} /maxcpucount:8 MySolution.sln"
}
stage ('Archive')
{
archiveArtifacts artifacts: 'Solution/SetupProject/bin/x86/Setup/MySetup.exe'
}
}
In IntegrationProject.csproj
<Target Name="CoverageReport" AfterTargets="CopySqlFiles" Condition="$(Configuration) == Integration">
<Exec Command="..\packages\JetBrains.dotCover.CommandLineTools.2018.1.3\tools\dotCover.exe analyse /TargetExecutable=..\packages\NUnit.ConsoleRunner.3.8.0\tools\nunit3-console.exe /ReturnTargetExitCode /TargetArguments="$(TargetPath)" /Filters=-:nunit.framework;-:IntegrationProjectTest;-:type=MyNamespace.View.*;-:type=*Test /TargetWorkingDir=$(TargetDir) /Output=$(TargetDir)\TestResult\MyCoverageReport.html /ReportType=HTML" />
</Target>
You should be able to make use of the dotcover parameter ReturnTargetExitCode to get the return code from nunit.
<Target Name="CoverageReport" AfterTargets="CopySqlFiles" Condition="$(Configuration) == Integration">
<Exec Command="..\packages\JetBrains.dotCover.CommandLineTools.2018.1.3\tools\dotCover.exe analyse ^
/TargetExecutable=..\packages\NUnit.ConsoleRunner.3.8.0\tools\nunit3-console.exe ^
/ReturnTargetExitCode ^
/TargetArguments="$(TargetPath)" ^
/Filters=-:nunit.framework;-:IntegrationProjectTest;-:type=MyNamespace.View.*;-:type=*Test ^
/TargetWorkingDir=$(TargetDir) ^
/Output=$(TargetDir)\TestResult\MyCoverageReport.html ^
/ReportType=HTML
/ReturnTargetExitCode">
<Output TaskParameter="ExitCode" PropertyName="DotCoverExitCode" />
</Exec>
<Message Text="Unit Tests Failed!" Condition="$(DotCoverExitCode) != '0'"/>
</Target>

Remove empty directories using msbuild

How can I process a path recursively so that once the processing completes there are no empty directories under the path.
For example, say we have C:\Dir1\Dir2\Dir3 and there are no files in any of these directories. The outcome should be the removal of all three directories.
I would like to accomplish this without the use of custom tasks.
Something like this should work, didn't check the performance of counting thousands of files though just to get array length...
<Project DefaultTargets="Foo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo">
<ItemGroup>
<Directories Include="$([System.IO.Directory]::GetDirectories('D:\foo', '*', System.IO.SearchOption.AllDirectories))" />
<Directories>
<Files>$([System.IO.Directory]::GetFiles("%(Directories.Identity)", "*", System.IO.SearchOption.AllDirectories).get_Length())</Files>
</Directories>
</ItemGroup>
<RemoveDir Directories="#(Directories)" Condition="%(Files)=='0'" />
</Target>
</Project>
Using an Exec Task running PowerShell:
MSBuild
<Project DefaultTargets="DefaultTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CleanPath>C:\MyDirAboveDir1</CleanPath>
</PropertyGroup>
<Target Name="DefaultTarget">
<Exec Command="PowerShell .\RemoveDir.ps1 '$(CleanPath)'" />
</Target>
</Project>
PowerShell RemoveDir.ps1
Param ([string]$folderpath = $(throw "provide folderpath"))
$folders = Get-ChildItem $folderpath -recurse -force | ? {$_.PSIsContainer}
if ($folders -ne $null)
{
[array]::Reverse($folders)
foreach($folder in $folders)
{
Write-Host "Examining contents of $($folder.fullname)"
$childitems = Get-Childitem $folder.fullname -recurse -force | ? { $_.PSIsContainer -eq $false }
if($childitems -eq $null)
{
"Remove folder: " + $folder.FullName
Remove-Item $folder.FullName -Recurse -Force
}
else
{
Write-host "Files found in $folder, skipping delete"
}
$childitems = $null
}
}
else
{
Write-Host "no sub folders found"
}
Courtesy to Guy Ellis Rocks: Powershell script to remove empty directories

PSake Error Executing MSBuild Command

I have 3 projects in the solution,
A WPFApplication and 2 ClassLibrary projects
When i build the Solution i get error below..
properties {
$base_dir = resolve-path .
$build_dir = "$base_dir\build"
$buildartifacts_dir = "$build_dir\BuildArtifacts"
$sln_file = "$base_dir\Hello.sln"
}
task default -depends Compile
task Clean {
Write-Host "Cleaning solution" -ForegroundColor Green
remove-item -force -recurse $buildartifacts_dir -ErrorAction
SilentlyContinue
}
task Init -depends Clean {
Write-Host "Creating BuildArtifacts directory" -ForegroundColor Green
new-item $buildartifacts_dir -itemType directory
}
task Compile -depend Init {
Write-Host "Compiling ---" $sln_file -ForegroundColor Green
Exec { msbuild $sln_file "/p:OutDir=$build_artifacts_dir"
/p:Configuration=Release /v:quiet }
}
i get the following error's -- what am i doing wrong?
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(2868­,9):
error MSB3023: No destination specified for Copy. Please supply either "DestinationFiles" or "DestinationFolder". [D:\Nusrofe\GrokPSake2\ClassLibrary1\ClassLibrary1.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3471­,9):
error MSB4044: The "FindUnderPath" task was not given
a value for the required parameter "Path". [D:\Nusrofe\GrokPSake2\ClassLibrary1\ClassLibrary1.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(2868­,9):
error MSB3023: No destination specified for Copy. Ple
ase supply either "DestinationFiles" or "DestinationFolder". [D:\Nusrofe\GrokPSake2\ClassLibrary2\ClassLibrary2.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3471­,9):
error MSB4044: The "FindUnderPath" task was not given
a value for the required parameter "Path". [D:\Nusrofe\GrokPSake2\ClassLibrary2\ClassLibrary2.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3471­,9):
error MSB4044: The "FindUnderPath" task was not given
a value for the required parameter "Path". [D:\Nusrofe\GrokPSake2\WpfApp\WpfApp.csproj]
build2.ps1:Error executing command: msbuild $sln_file
"/p:OutDir=$build_artifacts_dir" /p:Configuration=Release /v:quiet
Thanks --
Corku
Looks like in your Compile task you have a stray underscore in your $buildartifacts_dir variable. MSBuild probably doesn't know what to do because essentially you are passing it an empty location for the OutDir.