How to run dotnet tool in prebuild phase of an sdk project - .net-core

As part of my build process I want to run a dotnet tool before the compile.
I can add this section to my sdk project file:
<ItemGroup>
<PackageDownload Include="MyTool" Version="[1.0.1]" />
</ItemGroup>
Then the tool is downloaded and is available inside:
\Users\me\.nuget\packages\MyTool\1.0.1\tools\netcoreapp3.1\any\
I can then add a prebuild target like this:
<Target Name="PreBuild" BeforeTargets="CoreCompile">
<Exec Command="dotnet C:\Users\me\.nuget\packages\MyTool\1.0.1\tools\netcoreapp3.1\any\MyTool.dll <MyOptions> />
</Target>
This works, but obviously I do not want absolute references to my user profile (or version) in the path.
Is there a way to substitute path with an environment variable?
I have tried adding GeneratePathProperty="true" to the PackageDownload but $(PkgMyTool) is undefined.
I also tried referencing the tool with <PackageReference> but this fails due to SDK incompatibility. My Tool is netcore3.1 and this project is netstandard2.0.

You can use only the macros provided by the framework. You can find them here. Almost all of them are referring to the relative path of your project. I suggest you to copy your tool inside a project folder and you can make use of these macros.

The best solution I had success with thus far is this:
<Target Name="PreBuild" BeforeTargets="CoreCompile">
<Exec Command="dotnet tool update MyTool --tool-path=$(TargetDir)\tools --version=1.0.1" />
<Exec Command="$(TargetDir)\tools\MyTool <MyOptions> />
</Target>
And it DOES save me from the nitty gritty details like \netcoreapp3.1\any but it does not reuse the tool from NuGet cache, meaning it has to be downloaded on every build.
I still hope someone will provide a better answer.

Related

How to exclude certain file types from dotnet watch run?

Looking to get the following setup going:
I'm working on a Blazor app, and with the official css isolation bundler. I'm using Less though, and installed a Less transformer which creates the required css on build.
However, running my app via dotnet watch run, it often ends up in an endless loop now.
The reason for this is probably that dotnet watch run sees a change in the *.razor.css files, rebuilds, and the cycle just repeats on and on.
So here's my question:
How can I configure my csproj (new Net Standard format) to exclude **\*.razor.css from the watch process? It would also be fine it it just disappears from my VS solution as a whole.
Had a play with both answers above, but neither worked for me. I generate some files during the build and that may be why the other 2 don't work for me. According to Microsoft docs shared above (https://learn.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.1), to remove an item from watch you can also set it through the definition. Doing this worked for my scenario.
<ItemGroup>
<Content Remove="wwwroot\dist\**" />
<Content Include="wwwroot\dist\**" Watch="false" />
</ItemGroup>
I did have to add the Content Remove as otherwise the item is declared twice and the build will fail.
Please edit your .csproj file and add the following to it
<ItemGroup>
<Watch Exclude="**\*.razor.css" />
</ItemGroup>
More info at
https://learn.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.1
I had the same issue after coming here, ended up with:
<ItemGroup>
<Watch Remove="wwwroot\**\*" />
</ItemGroup>
Works nicely for me :)
I have a similar set-up except but I'm using SASS. I have a custom target to execute the command npm run sass, so the files are being generated as part of the dotnet build process, which made the watch re-trigger builds in an infinite loop.
In my case, the solution I found was like the following:
Alter the DefaultItemExcludes:
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);Features/**/*.css</DefaultItemExcludes>
</PropertyGroup>
Adjust sure the css compilation target to run before BeforeResGen instead of Compile (which I was using before):
<Target Name="CompileScopedScss" BeforeTargets="BeforeResGen">
<Exec Command="npm run sass -- %(ScopedScssFiles.FullPath) %(RootDir)%(Directory)%(FileName).css --no-source-map" />
</Target>
Include the css file and set Watch="False":
<ItemGroup>
<Content Include="Features/**/*.css" Watch="False" />
</ItemGroup>
I do this in a target after the previous target, but it seems to work also outside it.
Just using the <Content Update... Watch="False" /> wasn't enough in my case. The <Watch Remove="..." /> didn't work either in my case and I believe it could have something to do with the fact that the files are generated during the build.

dotnet core nuget package copying content files on restore

So I feel like I have come to the end of the rope here, but hoping someone knows more than I do here. I have some Typescript files, though that is mostly irrelevant as I am having this problem with all content files.
I am able to generate a nuget, or more precisely dotnet pack, nuget package that includes my content files in the package by using this in the .csproj of my parent project:
<ItemGroup>
<Content Include="Scripts\Utility.ts">
<Pack>true</Pack>
<PackagePath>contentFiles\Scripts\;content\Scripts</PackagePath>
</Content>
</ItemGroup>
I can browse the generated .nupkg and see that indeed the file was added to the package in both the content\Scripts and contentFiles\Scripts locations
The problem is that whenver I consume this package in my 'child' progect, that Typescript never gets copied into any folder of the child project, though I can see it extracted in the .nuget\packages\parent\... folders.
At first I thought it was something with my initial settings in the parent project, and it may be, but after trying what seems like everything in the book, that fails to copy the content files to the child project. I then tried going the dark path of trying to use Init.ps1 in the tools folder of my package, and though it was impossible to debug, it also seemed to run sporatically (I completely unistalled and reinstalled the package and it still failed to run most of the time.) This could be the way but I don't know why I can't get it to output to the Package Manager Console... maybe there's still hope with Init.ps1 but I can't seem to figure it out. Finally I see some potential with a nuget .targets file but I can's seem to grasp how to use it for my purpose either! I would love some feedback as to how to get this done.
From: Announcing NuGet 3.1 with Support for Universal Windows Platform
Importing content from a Nuget package was depreciated for projects using a project.json file in Nuget v3.1. Since then the project.json file has been dropped in favour of the new .csproj format. Importing content from a Nuget package should still work though if you're using the packages.config file instead.
Also mentioned is the fact that there are other package managers available for delivering content.
It looks to me like the answer in the new world is to create a node module containing utility.js and let npm deliver it to your project.
Possible Workaround:
I've looked at .targets to copy files and got this working, but it does run on each build - which may or may not be a problem for you. I can't do what I want with it.
In [PackageId].targets:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Either do this for all scripts in the Scripts/js folder -->
<Target Name="CopyScriptsToProject" BeforeTargets="Build">
<Message Text="Copying scripts to project" />
<ItemGroup>
<SourceScripts Include="$(MSBuildThisFileDirectory)..\..\content\Scripts\js\**\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(SourceScripts)" DestinationFiles="#(SourceScripts -> '$(MSBuildProjectDirectory)\wwwroot\js\%(RecursiveDir)%(Filename)%(Extension)')" Condition="!Exists('$(MSBuildProjectDirectory)\wwwroot\js\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
<!-- Or do this for the individual script -->
<Target Name="CopyUtilityScriptToProject" BeforeTargets="Build">
<Copy SourceFiles="$(MSBuildThisFileDirectory)..\..\content\Scripts\js\Utility.js" DestinationFiles="$(MSBuildProjectDirectory)\wwwroot\js\Utility.js" Condition="!Exists('$(MSBuildProjectDirectory)\wwwroot\js\Utility.js')" />
</Target>
</Project>
<!-- Note: condition can be removed from either if you want it to overwrite each build -->
and in the .csproj file (replacing [PackageId] with the name of your package):
<Project Sdk="Microsoft.NET.Sdk">
... any Globals for source control stuff ...
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>7.0.0</Version>
<PackageId>[PackageId]</PackageId>
</PropertyGroup>
... any PackageReference stuff ...
<ItemGroup Label="Packaging">
<Content Include="build\netcoreapp2.0\[PackageId].targets" PackagePath="build\netcoreapp2.0\[PackageId].targets" />
<!-- Either -->
<Content Include="Scripts\js\**\*.*" PackagePath="content\Scripts\js;contentFiles\Scripts\js" />
<!-- or -->
<Content Include="Scripts\js\Utility.js" PackagePath="content\Scripts\js;contentFiles\Scripts\js" />
</ItemGroup>
</Project>
There seemed to be a bug whereby when the <PackageId>[PackageId]</PackageId> wasn't set explicitly in the .csproj, the build targets didn't work. Although that may well be an issue with my development environment.
Apparently you need the any\any in the path (learn more) as well as to include <PackageCopyToOutput>true</PackageCopyToOutput>, like this:
<ItemGroup>
<Content Include="Scripts\js\Utility.js">
<Pack>true</Pack>
<PackagePath>contentFiles\any\any\wwwroot\js\;content\any\any\wwwroot\js\</PackagePath>
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>
You'll also need to precompile your TypeScript before including the .js files in the package
However, this still doesn't create a file there, just some strange reference to it.
In the end, we got it working with a .targets file, you can find a working repo here: https://github.com/NuGet/Home/issues/6743
Serj Sagan's answer got me on the right track, but it wasn't sufficient to deploy the content file to the bin directory (as he noted). I was able to get the file to be deployed by changing the package reference options in the consuming project's .csproj file, as follows:
<PackageReference Include="MyNuGetPackage" Version="0.0.0.1">
<IncludeAssets>all</IncludeAssets>
<PrivateAssets>analyzers;build</PrivateAssets>
</PackageReference>
It seems like the default for PrivateAssets is contentfiles;analyzers;build (documentation), which is not what we want in this case.
Simplified code and explanation from #PurplePiranha
TL;DR:
Basic .NET6 simplified sample code on Github
Step by Step guide
Selection of the files
First we need to select all the files that needs to get into the nuget package.
Add this to the <LibraryPackageName>.csproj:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup Label="Packaging">
<Content Include="<Your directory path>\<your file(s)>" />
</ItemGroup>
Multiple content lines are allowed.
Write a target
Make a target file to copy the files before (or after) the build to the bin directory:
The location and name of this file is important:
<root>\build\<LibraryPackageName>.targets
Now, make sure that it will get executed by referencing it in the <LibraryPackageName>.csproj by adding a content line:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup Label="Packaging">
<Content Include="build\<LibraryPackageName>.targets" PackagePath="build\<LibraryPackageName>.targets" />
<Content Include="filesToAdd\*.txt">
<Pack>true</Pack>
</Content>
</ItemGroup>
Eg: From the code in github:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup Label="Packaging">
<Content Include="build\PackageToGenerateFile.targets" PackagePath="build\PackageToGenerateFile.targets" />
<Content Include="filesToAdd/*.txt">
<Pack>true</Pack>
</Content>
</ItemGroup>
NOTE: By copying the files to the bin directory, the files are not part of your version control, but your package is!
Build and pack
In Visual Studio, right-click on the package name and select "Pack".
A new nuget package should be created in the bin directory of your library.
Use the nuget package
Install the nuget package now in your destination package.
Notice that the files are in the solution explorer, but not in a directory on your disk. They have a shortcut symbol.
Build the destination package
Check the bin directory.
The files should be copied to the location mentioned in the targets.

Why is my custom MSBuild script not able to copy project references when invoked from Visual Studio?

I am trying to simplify the deployment for one of our web projects by supplying an installer that takes care of everything the application needs to be initialized. I've chosen the WiX toolset for this and created a custom build script following this and partly this tutorial. To reproduce the problem I am facing, you can download the sources of the first tutorial link.
My general goal is to be able to create an installer directly from Visual Studio. The tutorial describes how to write a build script that generates the installer. Basically I want to invoke this script by clicking "Build" within Visual Studio (2012, if it matters).
First I've added a WiX project to the solution (relocate it to the setup-directory!). I've added all the setup-files and the required WiX references (UI, Util, IIS, SQL). By simply invoking the build now, WiX complains about an undefined var.publishDir variable. So I ran the supplied build script using the following command inside the VS developer console:
msbuild /t:Build;CreateInstaller;DeleteTmpFiles build_setup.build
First this gave me an error 9009 (command not found) when trying to invoke the heat tool for harvesting. I fixed this by replacing the "$(WixPath)heat" call with "$(WiX)\bin\heat". After that everything worked as desired, so I started hooking into the build process of the .wixproj file in order to call the build script from there. I did this by defining the following build targets (right below the item groups):
<Target Name="Build">
<MSBuild Projects="build_setup.build" Targets="Build;CreateInstaller" Properties="" />
</Target>
<Target Name="Rebuild">
<MSBuild Projects="build_setup.build" Targets="Build;CreateInstaller;DeleteTmpFiles" Properties="" />
</Target>
<Target Name="Clean">
<MSBuild Projects="build_setup.build" Targets="DeleteTmpFiles" Properties="" />
</Target>
Also I checked, if the installer project is excluded from solution-wide builds inside the build configuration manager for all configurations (but especially Release|AnyCPU since this is the desired configuration for deployment). For sake of clarity I've fixed both MSBuild calls within the build_setup.build script and added my desired platform:
<!-- Define default target with name 'Build' -->
<Target Name="Build">
<!-- Compile whole solution in release mode -->
<MSBuild Projects="..\MyWeb\MyWeb.sln" Targets="ReBuild"
Properties="Configuration=Release;Platform=Any CPU" />
</Target>
<!-- Define creating installer in another target -->
<Target Name="CreateInstaller">
<RemoveDir Directories="$(PublishF)" ContinueOnError="false" />
<MSBuild Projects="..\MyWeb\MyWeb\MyWeb.csproj" Targets="ResolveReferences;_CopyWebApplication"
Properties="OutDir=$(Publish)bin\;WebProjectOutputDir=$(Publish);Configuration=Release;Platform=AnyCPU" />
<!-- ... -->
</Target>
Now when I invoke the build by right-clicking the installer project and "Build", the build is successfully triggered from Visual Studio - exactly what I want.
The problem begins with adding project references: I've added a new library project (defined a simple class in there, referenced the library from the website and created an instance of the library class there). As soon as I try to build the project from Visual Studio now, I get an error that the referenced assembly cannot be copied from the publish-directory, because it does not exist:
Microsoft.WebApplication.targets(175,5): error MSB3030: Could not copy file "...\WixWebDeploy\Setup\publish\bin\MyWeb.Model.dll" because it was not found.
The strange thing is, that everything works fine, if I invoke the build script from the developer console! Also this only affects project references (the project is published using the targets ResolveReferences and _CopyWebApplication). NuGet packages or static library references are copied without any problems.
I appreciate any ideas that point me into the right direction to tackle down this issue. For example I am interested in the difference between calling msbuild from console or Visual Studio (if there is any...). Thanks in advance!
I finally figured out how to fix this issue. The solution was to use OutputPath argument instead of OutDir call a command line script from MSBuild:
<Target Name="Build">
<Exec Command="echo Building solution with '$(MSBuildToolsPath)\msbuild'..." />
<!--<MSBuild Projects="build_setup.build" Targets="CreateInstaller" Properties="Configuration=Release;Platform=AnyCPU" />-->
<Exec Command="call build_setup.cmd $(MSBuildToolsPath) CreateInstaller" />
</Target>
<Target Name="Rebuild">
<Exec Command="echo Building solution with '$(MSBuildToolsPath)\msbuild'..." />
<!--<MSBuild Projects="build_setup.build" Targets="Build;CreateInstaller" Properties="" />-->
<Exec Command="call build_setup.cmd $(MSBuildToolsPath) Build CreateInstaller" />
</Target>
<Target Name="Clean">
<Exec Command="echo Building solution with '$(MSBuildToolsPath)\msbuild'..." />
<!--<MSBuild Projects="build_setup.build" Targets="DeleteTmpFiles" Properties="" />-->
<Exec Command="call build_setup.cmd $(MSBuildToolsPath) DeleteTmpFiles" />
</Target>
From the build_setup.cmd I call MSBuild again:
if [%1] == [] (
echo "Please supply the msbuild directory as first argument!"
exit 10
) else (
set MSBUildDir=%1
)
if [%2] == [] (
echo "You have to supply at least one of the following arguments: Build, CreateInstaller, DeleteTmpFiles!"
exit 11
) else if [%3] == [] (
echo "%MSBUildDir%\msbuild /t:%2 setup.build"
call %MSBUildDir%\msbuild /t:%2 setup.build
exit 0
) else if [%4] == [] (
echo "%MSBUildDir%\msbuild /t:%2;%3 setup.build"
call %MSBUildDir%\msbuild /t:%2;%3 setup.build
exit 0
) else (
echo "%MSBUildDir%\msbuild /t:%2;%3;%4 setup.build"
call %MSBUildDir%\msbuild /t:%2;%3;%4 setup.build
exit 0
)
This seams kinda redundant and maybe somebody got a better suggestion.

How build asp.net web site using nant script?

I am using nant-0.90-alpha1 to build asp.net 3.5 web site. I am unable do that. When I am using msbuild , it throwing error saying unknown tag msbuild. How can I build asp.net 3.5 website using nant?
nRk
The CodeCampServer project provides good examples for a variety of tasks using nant to build MS projects including using MSBuild. However it doesn't use the msbuild task. Here's an excerpt from the common.build file from CodeCampServer:
<target name="compile" depends="init">
<echo message="Build Directory is ${dir.build}" />
<exec program="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe"
commandline="${file.solution} /t:Clean /p:Configuration=${project.config} /v:q" workingdir="." />
<exec program="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe"
commandline="${file.solution} /t:Rebuild /p:Configuration=${project.config} /v:q" workingdir="." />
</target>
<msbuild> task is part of NAntContrib.
The <msbuild> task must be imported into your build script. Put the following element somewhere within your <project> element.
<project ...>
<loadtasks assembly="C:\Program Files\NAntContrib\NAnt.Contrib.Tasks.dll"/>
...
</project>
I believe NAnt will also pick up additional task libraries if the dlls are placed in the NAnt bin folder.

Building a ASP.NET solution from command-line?

How can I build an ASP.NET web application from the command line?
Try this in a .bat file, replace v4.0.30319 with the appropriate version:
CD C:\Windows\Microsoft.NET\Framework\v4.0.30319
msbuild "C:\inetpub\wwwroot\MyWebSite.sln"
Take a look at the devenv.exe /Build switch, you give it a solution file to build, e.g.
devenv.exe "C:\Documents and Settings\someuser\MySolution.sln" /build DEBUG
If you have more complex build requirements then look into MSBuild.
This is a round about way to do it, but you can use the MSBuild task as part of an msbuild project:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Solutions Include="*.sln" />
</ItemGroup>
<Target Name="Build" >
<MSBuild BuildInParallel="true" Projects="#(Solutions)" RebaseOutputs="true" />
</Target>
built with msbuildprojectname.proj from the command line.
This may seem like overkill, but you can then add extra stuff into the project (like restarting websites, zipping files, Virtual machine control even!, code coverage) that can help you setup and test the project.
As dumb as I might look, I have used "aspnet_compiler.exe" for compiling/deploying projects
http://social.msdn.microsoft.com/Forums/en-US/msbuild/thread/2ca80dfd-b7d7-4e09-98ff-891b1570f4db
To build a solution directly,
msbuild mysolution.sln
You'll find MSBuild in the Microsoft.NET folder in your Windows directory.
You may also want to pre-compile your Web site. Instructions for that are on MSDN here. If you use the Web Deployment project node (detailed at the bottom) then the deployment will happen automatically when you run msbuild on the solution file.

Resources