VS2017 + MSBuild Helpful “tricks”

I use VS2017 so I cannot say whether the below “tricks” work on previous versions of VS.

This post describes the following:

  • How to easily share common NuGet package information among all SDK-based projects in a single solution.
  • How to create a NETFramework project using the new SDK-based project format instead of just NETStandard or NETCore.
  • How to use Powershell scripts (.ps1) for pre/post build events instead of using shell commands/.bat scripts.

So, let’s get started.

 

How to easily share common NuGet package information among all SDK-based projects in a single solution

 

We solve this by using directory.build.props (please use the link for more details). This file is a special file in that MSBuild knows to look for it (along with directory.build.targets). If this file resides next to your .sln file, MSBuild will automatically apply the properties defined in directory.build.props to every project being built in the solution. You do not need to use the <Import> element to import the directory.build.props file — MSBuild does it automatically.

We also know that SDK project types, by default, will use built-in MSBuild properties to define NuGet package information like versions, authors, copyright, etc. So, we can define information common to all NuGet packages in the solution using the directory.build.props file. See example below.

 

directory.build.props:

<Project> <PropertyGroup Label="Common"> <Company>MyCompany, Inc.</Company> <Authors>$(Company)</Authors> <Copyright>Copyright © $(Company) $([System.DateTime]::Now.Year)</Copyright> <Trademark>$(Company)™</Trademark> <Product>$(Company) Projects</Product> <RepositoryType>Git</RepositoryType> <PackageLicenseUrl>https://github.com/MyUserName/MyProject/blob/master/LICENSE</PackageLicenseUrl> <PackageProjectUrl>https://github.com/MyUserName/MyProject/</PackageProjectUrl> <PackageIconUrl>https://github.com/MyUserName/MyProject/blob/master/package.ico</PackageIconUrl> <RepositoryUrl>https://github.com/MyUserName/MyProject.git/</RepositoryUrl> </PropertyGroup> </Project> 
  • Note: The Copyright value also generates the current year during build. Change this to a hard-coded year if you do not want this behavior.

I usually create a solution folder “.msbuild” and create the directory.build.props file in there. This way, I gain all the benefits of editing the file using VS instead of having to navigate to it via File Explorer and opening it up in another text editor. Since solution folders are “virtual” , the props file ends up in the same directory as the .sln file. With the above file, all of those common NuGet properties will be applied to every project in the solution. Only SDK-based projects will automatically take advantage of those properties, however. It is worth noting that if an SDK project has already defined those properties then the property values defined “closer to the project” will be used instead. Be sure to remove relevant properties from each SDK project unless you want it to overwrite certain properties defined in the directory.build.props file.

 

How to create a NETFramework project using the new SDK-based project format instead of just NETStandard or NETCore

 

This one is simple. Create a new .NET Standard class library. Right-click on the project and click on “Edit <your_project>.csproj”. Change the value in the <TargetFramework> element to a Target Framework Moniker (TFM) associated with a .NET Framework version. For example, if I wanted to target 4.7.2, I would change the value to be “net472”. Supported target frameworks and their TFM’s can be found here. Your project is now using the new SDK format as well as targeting .NET Framework.

  • Note: I do not know if it is still the case, but the SDK project type doesn’t seem to support designer files yet (like those from WPF/Winform apps). So, as a principal, I would say avoid trying to use the new SDK project type if your library will contain designer files from WPF/Winforms/etc.

 

How to use Powershell scripts (.ps1) for pre/post build events instead of using shell commands/.bat scripts

This one is one of my favorites because I really hate trying to write shell scripts that attempt to do anything more complex than “Hello World”. This doesn’t require SDK-project types, by the way. I’d rather write my pre and post build scripts using Powershell. This is what we are going to do to accomplish this:

  • Add a new folder to a project to hold your powershell scripts. I usually create a “.msbuild” folder to hold them. (I like prefixing ‘.’ in front of folders under projects to contain files that are not code/resources/etc.)
  • Add ‘BeforeBuild.ps1’ and ‘AfterBuild.ps1’ scripts files to the “.msbuild” folder. You can add a “Text File” and just rename the file appropriately.
  • Add the appropriate pre/post build event commands by right-clicking on the project, go to Properties and then navigating to the Build Events tab.

 

Pre-build event:

powershell.exe -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command ^ .'$(ProjectDir).msbuildBeforeBuild.ps1' ^ -SolutionPath:'$(SolutionPath)' ^ -ProjectPath:'$(ProjectPath)' ^ -TargetPath:'$(TargetPath)' ^ -Configuration:'$(Configuration)' ^ -Platform:'$(Platform)' if %ERRORLEVEL% NEQ 0 exit %ERRORLEVEL% exit 0 

Post-build event:

powershell.exe -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command ^ .'$(ProjectDir).msbuildAfterBuild.ps1' ^ -SolutionPath:'$(SolutionPath)' ^ -ProjectPath:'$(ProjectPath)' ^ -TargetPath:'$(TargetPath)' ^ -Configuration:'$(Configuration)' ^ -Platform:'$(Platform)' if %ERRORLEVEL% NEQ 0 exit %ERRORLEVEL% exit 0 

BeforeBuild.ps1 AND AfterBuild.ps1:

# # TIP: Use [IO.Path]::GetDirectoryName($YOUR_PATH_VARIABLE) to retrieve the directory of a path. # TIP: Use [IO.Path]::GetFileName($YOUR_PATH_VARIABLE) to retrieve the filename of a path. # param ( [string]$SolutionPath, [string]$ProjectPath, [string]$TargetPath, [string]$Configuration, [string]$Platform ) try { ########################## # WRITE CUSTOM CODE HERE # ########################## } catch { Write-Error $_.Exception.Message exit 1 #causes the build to fail } exit 0 
  • Note 1: If you didn’t add the scripts to the .msbuild directory, make sure to modify the pre/post build events accordingly to use the correct path.
  • Note 2: Copy the .ps1 script above for BOTH the BeforeBuild.ps1 and AfterBuild.ps1 scripts.
  • Note 3: If you want to use more properties from MSBuild than the ones provided, add a new script argument to the appropriate script(s) and modify the appropriate build event(s) to pass in the value to the script(s) from MSBuild.
  • Note 4: If the script fails, the build will fail. I didn’t want a script to run unsuccessfully without failing the build.
  • Note 5: Make sure the “Build Action” for both .ps1 scripts is set to “None” and they aren’t being copied to the output directory. Verify this by clicking on a .ps1 file and looking at the Properties window.

 

Hope you guys find this useful in some way. I am currently working on a useful way to hook per-solution powershell build scripts AND settings (via some .props file) for CI builds.

submitted by /u/pgmr87
[link] [comments]

Leave a Reply