65.9K
CodeProject is changing. Read more.
Home

Visual Studio project initialization using NuGet

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (4 votes)

Jan 31, 2016

Apache

4 min read

viewsIcon

12789

downloadIcon

104

An article showing you how simple it is to create a NuGet package that initializes your newly created Visual Studio project with all the configuration needed.

Gentlemen, start your engines!

Contents

Introduction

Ever hesitated to create a new Visual Studio project because the sheer amount of configuration needed would be enough to get you out of your zone? Ever been frustrated because your new project doesn't work, and you have no idea why but guess it is missing some magic configuration?

This article aims at displaying how easy it is to create a NuGet package that initializes your Visual Studio project with all the configuration needed. Just install the package and you'll get the following:

Code Analysis

  • Enabled on build for all configurations
  • A custom ruleset added to the project
  • A custom dictionary added to the project

Security

  • Signed assembly on build
  • A strong name key added to the project

Basic knowledge of NuGet, and especially how a package is created, is a prerequisite. The documentation on GitHub is excellent and should be read before continuing with this article.

Repository on GitHub

The latest version is present on GitHub, and releases will be posted here. As always, all contribution is encouraged.

The NuGet package

All code displayed in this article relates to the following NuGet package.

The files from the content directory will be copied into the project when the package is installed. On top of that there are PowerShell scripts that are executed when the package is installed and uninstalled. The following chapters will go into detail what the scripts are doing, and why they are necessary.

Install.ps1

Here is the script executed when the package is installed. I will break down the script into blocks and describe their individual purpose.

param($installPath, $toolsPath, $package, $project)

###################################
#        Before Installing        #
###################################

Write-Host ("Installing Visual Studio Ignition to project " + $project.FullName)
$project.Save()
$projectRootElement = [Microsoft.Build.Construction.ProjectRootElement]::Open($project.FullName)

###################################
#          Code Analysis          #
###################################

## Set 'Build Action' to 'CodeAnalysisDictionary' on custom dictionary
Write-Host "Configuring build action on CustomDictionary.xml"
$item = $project.ProjectItems.Item("CustomDictionary.xml")
$item.Properties.Item("BuildAction").Value = [int]4

## Enable code analysis on build and add ruleset
# This is what we want to add to the project
#  <PropertyGroup Label="Ignition">
#    <RunCodeAnalysis>true</RunCodeAnalysis>
#    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
#  </PropertyGroup>

Write-Host "Configuring Code Analysis"
$propertyGroup = $projectRootElement.AddPropertyGroup()
$propertyGroup.Label = "Ignition"
$propertyGroup.AddProperty('RunCodeAnalysis', 'true')
$propertyGroup.AddProperty('CodeAnalysisRuleSet', 'AllRules.ruleset')

###################################
#             Signing             #
###################################

## Enable signing and specify strong name key file
# This is what we want to update on the project
#  <PropertyGroup Label="Ignition">
#    <SignAssembly>true</SignAssembly>
#    <AssemblyOriginatorKeyFile>StrongName.snk</AssemblyOriginatorKeyFile>
#  </PropertyGroup>

Write-Host "Configuring Signing"
$propertyGroup.AddProperty('SignAssembly', 'true')
$propertyGroup.AddProperty('AssemblyOriginatorKeyFile', 'StrongName.snk')

###################################
#        After Installing         #
###################################

$projectRootElement.Save()
Write-Host "Installed Visual Studio Ignition"

Before Installing

###################################
#        Before Installing        #
###################################

Write-Host ("Installing Visual Studio Ignition to project " + $project.FullName)
$project.Save()
$projectRootElement = [Microsoft.Build.Construction.ProjectRootElement]::Open($project.FullName)

The script code that executes first saves the project. It is important that the project file on disk is in sync with the project representation in memory before we start making any changes, otherwise we risk ending up with conflicting changes between the two.

After saving a ProjectRootElement is created based on the project file. It is the abstract representation of the project and all modifications to the project will be done through this instance.

Code Analysis

###################################
#          Code Analysis          #
###################################

## Set 'Build Action' to 'CodeAnalysisDictionary' on custom dictionary
Write-Host "Configuring build action on CustomDictionary.xml"
$item = $project.ProjectItems.Item("CustomDictionary.xml")
$item.Properties.Item("BuildAction").Value = [int]4

## Enable code analysis on build and add ruleset
# This is what we want to add to the project
#  <PropertyGroup Label="Ignition">
#    <RunCodeAnalysis>true</RunCodeAnalysis>
#    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
#  </PropertyGroup>

Write-Host "Configuring Code Analysis"
$propertyGroup = $projectRootElement.AddPropertyGroup()
$propertyGroup.Label = "Ignition"
$propertyGroup.AddProperty('RunCodeAnalysis', 'true')
$propertyGroup.AddProperty('CodeAnalysisRuleSet', 'AllRules.ruleset')

Due to the nature of how NuGet installs a package, this script is executed after the following files have been added to the project:

  • CustomDictionary.xml - The custom dictionary
  • AllRules.ruleset - The custom ruleset

Although the files have been added, some manual configuration is necessary in order for Code Analysis to work as expected. First we change the property Build Action on CustomDictionary.xml to CodeAnalysisDictionary. This makes Code Analysis respect your custom words.

Next step requires adding new project properties, thus we create a new property group and label it Ignition. The name itself isn't important, it could as well have been called Sepp Blatter. The important thing is that it is something we want removed, in the case of the property group when the NuGet package is uninstalled. In this new property group we add two properties, one that enables Code Analysis on build for all configurations, and another that specifies the custom ruleset as the rules to abide when running the analysis.

Signing

###################################
#             Signing             #
###################################

## Enable signing and specify strong name key file
# This is what we want to update on the project
#  <PropertyGroup Label="Ignition">
#    <SignAssembly>true</SignAssembly>
#    <AssemblyOriginatorKeyFile>StrongName.snk</AssemblyOriginatorKeyFile>
#  </PropertyGroup>

Write-Host "Configuring Signing"
$propertyGroup.AddProperty('SignAssembly', 'true')
$propertyGroup.AddProperty('AssemblyOriginatorKeyFile', 'StrongName.snk')

This script is executed after the following file has been added to the project:

  • StrongName.snk - The custom strong name key file

Again, only because the file has been added doesn't mean that the assembly will be signed with the specific key file. Lets add two more properties in the group previously created. First we add a property that enables signing, and then another that specifies the strong name key file.

After Installing

###################################
#        After Installing         #
###################################

$projectRootElement.Save()
Write-Host "Installed Visual Studio Ignition"

Now when we are all done with the configuration we save the project.

The NuGet package installation process is now done, and we have a project that is ready for our awesome code!

Uninstall.ps1

Here is the script executed when the package is uninstalled. I will break down the script into blocks and describe their individual purpose.

param($installPath, $toolsPath, $package, $project)

function RemoveIgnitionPropertyGroups($projectRootElement) {
    # If there are any PropertyGroups with a label of "Ignition" they will be removed
    $propertyGroupsToRemove = @()
    
    foreach($propertyGroup in $projectRootElement.PropertyGroups) {
        if($propertyGroup.Label -and [string]::Compare("Ignition", $propertyGroup.Label, $true) -eq 0) {
            # Remove this property group
            $propertyGroupsToRemove += $propertyGroup
        }
    }

    foreach ($propertyGroup in $propertyGroupsToRemove) {
        $propertyGroup.Parent.RemoveChild($propertyGroup)
    }
}

###################################
#       Before Uninstalling       #
###################################

Write-Host ("Uninstalling Visual Studio Ignition from project " + $project.FullName)
$project.Save()
$projectRootElement = [Microsoft.Build.Construction.ProjectRootElement]::Open($project.FullName)

###################################
#     Code Analysis & Signing     #
###################################

Write-Host "Removing configuration of Code Analysis"
RemoveIgnitionPropertyGroups -projectRootElement $projectRootElement

###################################
#       After Uninstalling        #
###################################

Write-Host "Uninstalled Visual Studio Ignition"

Before Uninstalling

###################################
#       Before Uninstalling       #
###################################

Write-Host ("Uninstalling Visual Studio Ignition from project " + $project.FullName)
$project.Save()
$projectRootElement = [Microsoft.Build.Construction.ProjectRootElement]::Open($project.FullName)

This script is the same as when installing. It saves the project to disk and creates a ProjectRootElement instance.

Code Analysis & Signing

function RemoveIgnitionPropertyGroups($projectRootElement) {
    # If there are any PropertyGroups with a label of "Ignition" they will be removed
    $propertyGroupsToRemove = @()
    
    foreach($propertyGroup in $projectRootElement.PropertyGroups) {
        if($propertyGroup.Label -and [string]::Compare("Ignition", $propertyGroup.Label, $true) -eq 0) {
            # Remove this property group
            $propertyGroupsToRemove += $propertyGroup
        }
    }

    foreach ($propertyGroup in $propertyGroupsToRemove) {
        $propertyGroup.Parent.RemoveChild($propertyGroup)
    }
}

###################################
#     Code Analysis & Signing     #
###################################

Write-Host "Removing configuration"
RemoveIgnitionPropertyGroups -projectRootElement $projectRootElement

To remove the Code Analysis and signing configuration, the only thing we need to do is to remove the property group labeled Ignition. To be on the safe side we remove all property groups with the specific label. Really not necessary but we might save a situation where we had a failed installation.

After Uninstalling

###################################
#       After Uninstalling        #
###################################

Write-Host "Uninstalled Visual Studio Ignition"

At this point we just write a log message about the successful uninstallation.

Now what?

You have seen all code required to create a NuGet package that initializes a new Visual Studio project. Now create a package that meets your specific needs. Download the source or fork the repository on GitHub. Everything is Apache v2.0 licensed, thus free for you to use.

History

  • 1 January 2016 (v1.0.1): Initial version.