Batch Updating/Changing Visual Studio Projects with PowerShell

Working on a giant codebase recently, I needed to check if all C# projects had Code Analysis turned on for all configurations. Given there were easily 30 to 40 different .CSPROJ files I was so not looking forward to manually going through them all one at a time in Visual Studio to check. The .CSPROJ files are just XML files and PowerShell handles XML great, so 10 minutes of messing around and I had a nice one-liner that reported all the .CSPROJ file names and which configurations didn’t have Code Analysis enabled.

dir -Recurse *.csproj | `
    ForEach-Object { Write-Host $_.FullName; (Get-Content $_)} | `
        ForEach-Object { $_.Project.PropertyGroup } | `
            Where-Object { ($_.Condition -ne $null) -and `
                           (($_.RunCodeAnalysis -eq $null) -or `
                           ($_.RunCodeAnalysis -eq “false”))} | `
                ForEach-Object { Write-Host $_.Condition does not have CodeAnalysis turned on}

In the code above, I added a lot of line continuation characters so it was easier to read in the blog. Even if you haven’t done much PowerShell, it should be pretty easy to follow. Because PowerShell loves you, declaring a variable as means you get the automatic properties for elements and XML is a piece of cake. PowerShell for the win!

So now I have all the projects that need to be updated, but I still have to do that manually. Well, no more! I’ve been fiddling with some code to do batch updating and finally got the time to sit down and finish it. In my WintellectPowerShell module, I’ve added a cmdlet, Set-ProjectProperties, that makes it super simple to batch change a bunch of .CSPROJ files so you’re in control of your build settings. My plan is to extend Set-ProjectProperties to work with .VBPROJ files and possibly .VCXPROJ files in the future. If you want that support sooner, please do fork the code in GitHub and add it.

Since I was working in WintellectPowerShell, I broke up the getting far too large main .PSM1 module and put each cmdlet into it’s own source file for easier maintenance. Additionally, I got rid of the external help file and moved all help back into the source code because it was a pain in the neck to deal with the editor and I wasn’t going to support auto updating help any time soon.

Please let me know ASAP if you find any issues or have feature ideas.

NAME
Set-ProjectProperties

SYNOPSIS
A script to make Visual Studio 2010 and higher project management easier.

SYNTAX
Set-ProjectProperties [[-paths] <String[]>] [-OverrideDefaultProperties] [[-Configurations] <String[]>]
[[-CustomGeneralProperties] <Hashtable>] [[-CustomConfigurationProperties] <Hashtable>] [<CommonParameters>]

DESCRIPTION
When you need to make a simple change to a number of Visual Studio projects,
it can be a large pain to manually go through and do those changes, especially
since it’s so easy to forget a project or mess up. This script’s job is to
automate the process so it’s repeatable and consistent.

If you do not specify any custom options, the script will automatically update
projects with the following settings.

[Note that at this time only C# projects are supported.]

C# Debug and Release configurations
—————
–    Treat warnings as errors
–    Check for arithmetic overflow and underflow
–    Enable code analysis with the default Code Analysis settings file.
–    Turn on creation of XML doc comment files.

This script is flexible and you can control down to setting/changing an
individual property if necessary. There are many examples in the Examples
section.

PARAMETERS
-paths <String[]>
This script can take pipeline input so you can easily handle deeply nested
project trees. Alternatively, you can put wildcards in this, but recursive
directories will not be searched.

Required?                    false
Position?                    1
Default value                
Accept pipeline input?       false
Accept wildcard characters?  false

-OverrideDefaultProperties [<SwitchParameter>]
If set, will not apply the default settings built into the script and only
take the properties to change with the CustomGeneralProperties and
CustomConfigurationProperties parameters.

Required?                    false
Position?                    named
Default value                False
Accept pipeline input?       false
Accept wildcard characters?  false

-Configurations <String[]>
The array of configurations you want to change in the project file these are
matching strings so if you specify something like ‘Debug|AnyCPU’ you are
narrowing down the configuration to search. The default is ‘Debug’ and
‘Release’.

Required?                    false
Position?                    2
Default value                @(“Debug”, “Release”)
Accept pipeline input?       false
Accept wildcard characters?  false

-CustomGeneralProperties <Hashtable>
The hash table for the general properties such as TargetFrameworkVersion,
FileAlignment and other properties on the Application or Signing tab when
looking at the project properties. The key is the property name and the
value is either the string, or a script block that will be called to do
custom processing. The script block will be passed the XML for all the
global project properties so it can do additional processing.

Required?                    false
Position?                    3
Default value                @{}
Accept pipeline input?       false
Accept wildcard characters?  false

-CustomConfigurationProperties <Hashtable>
The hash table for the properties such as TreatWarningsAsErrors and
RunCodeAnalysis which are per build configuration(s). Like the
CustomGeneralProperties, the hash table key is the property to set and the
value is the string to set or a script block for advanced processing. The
script block will be passed the current configuration. See the examples
for how this can be used.

Required?                    false
Position?                    4
Default value                @{}
Accept pipeline input?       false
Accept wildcard characters?  false

<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer and OutVariable. For more information, see
about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).

INPUTS
The Visual Studio project files to change.

OUTPUTS

NOTES

Obviously, to maximize your usage you should be familiar with all the
properties in Visual Studio project files and the properties in them.
See http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx for more information.

————————– EXAMPLE 1 ————————–

C:PS>dir -recurse *.csproj | Set-ProjectProperties

Recursively updates all the C# project files in the current directory with
all the default settings.

————————– EXAMPLE 2 ————————–

C:PS>dir A.csproj | `
Set-ProjectProperties `
-CustomGeneralProperties @{“AssemblyOriginatorKeyFile” = “c:devConsoleApplication1.snk”}

Updates A.CSPROJ to the default settings and adds the strong name key to the
general properties. When specifying the AssemblyOriginatorKeyFile this script
will treat file correctly and make it a relative path from the .CSPROJ folder
location. When specifying a file, use the full path to the file so everything
works correctly.

————————– EXAMPLE 3 ————————–

C:PS>dir B.csproj | `
Set-ProjectProperties `
-CustomConfigurationProperties @{ “CodeAnalysisRuleSet” = “c:devWintellectRuleSet.ruleset”}

Updates B.CSPROJ to the default settings and sets all configurations to
enable Code Analysis with the custom rules file specified. Always specify the
full path to the custom ruleset file as the script will handle making all
references to it relative references in the configurations.

If you specify one of the default Code Analysis rules files that shipped with
Visual Studio, the script properly handles those as well. You can find all the
default ruleset files by looking in the
“<VS Install Dir>Team ToolsStatic Analysis ToolsRule Sets” folder.

————————– EXAMPLE 4 ————————–

C:PS>dir C.csproj | Set-ProjectProperties `
-OverrideDefaultProperties `
-Configurations “Release” `
-CustomConfigurationProperties @{ “DefineConstants” =
{
param($config)
$defines = $config.GetElementsByTagName(“DefineConstants”)
$defines[0].InnerText = $defines[0].InnerText + “;FOOBAR”
}
}

Updates C.CSPROJ by only adding a new define to only the Release configuration,
keeping any existing define and not using the default changes.

RELATED LINKS
https://training.atmosera.com/cs/blogs/jrobbins/default.aspx
http://code.training.atmosera.com

Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X