Click here to Skip to main content
15,887,214 members
Articles / .NET

Visual Studio project initialization using NuGet

Rate me:
Please Sign up or sign in to vote.
4.78/5 (4 votes)
31 Jan 2016Apache4 min read 12.5K   103   7  
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.

Image 1

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.

Image 2

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.

PowerShell
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

PowerShell
###################################
#        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

PowerShell
###################################
#          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

PowerShell
###################################
#             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

PowerShell
###################################
#        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.

PowerShell
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

PowerShell
###################################
#       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

PowerShell
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

PowerShell
###################################
#       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.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Software Developer Axis Communications
Sweden Sweden
Got my first computer in the 90's and loved it even though it sounded like a coffeemaker.

Now getting paid for designing cool applications, and drinks the coffee instead of listening to it being made.

Comments and Discussions

 
-- There are no messages in this forum --