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; [xml](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 [xml] 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:\dev\ConsoleApplication1.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:\dev\WintellectRuleSet.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 Tools\Static Analysis Tools\Rule 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
    http://www.wintellect.com/cs/blogs/jrobbins/default.aspx
    http://code.wintellect.com