Skip to content

Instantly share code, notes, and snippets.

@Wind010
Last active December 23, 2022 04:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wind010/e836e72d326116bb551580a384debaff to your computer and use it in GitHub Desktop.
Save Wind010/e836e72d326116bb551580a384debaff to your computer and use it in GitHub Desktop.
Update the TargetFramework/TargetFrameworks tag in .csproj
###
# Usage:
# Test/DryRun and just report what would have changed.
# UpdateTargetFrameworks.ps1 -TargetFrameworks @('net6.0') -UpdateWithXmlDoc $true -Path "SomePath" -DryRun $true
# Do the update with XPATH via XmlDocument:
# UpdateTargetFrameworks.ps1 @('net6.0') $true
# Do the update with replace:
# UpdateTargetFrameworks.ps1 @('net6.0') $false
###
param(
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$false)]
[array]
$TargetFrameworks,
[Parameter(Mandatory=$false, Position=1, ValueFromPipeline=$false)]
[bool]
$UpdateWithXmlDoc = $true,
[Parameter(Mandatory=$false, Position=2, ValueFromPipeline=$false)]
[string]
$Path = ".",
[Parameter(Mandatory=$false, Position=3, ValueFromPipeline=$false)]
[bool]
$DryRun = $false
)
$ErrorActionPreference = 'Stop'
$supportedTargetFrameworks = @('net7.0', 'net6.0', 'net5.0', 'netstandard2.1', 'netcoreapp3.1')
$supportedTargetFrameworks = [System.Collections.Generic.HashSet[string]]$supportedTargetFrameworks
function Initialize()
{
if ( -not ([System.Collections.Generic.HashSet[string]]$TargetFrameworks).IsSubSetOf($supportedTargetFrameworks))
{
Write-Error "$TargetFrameworks contains unsupported framework. Only $supportedTargetFrameworks are supported."
}
[string] $targetFrameworksValue = ""
if ($TargetFrameworks.Length -eq 0)
{
Write-Error "At least one TargetFramework must be defined."
}
if ($TargetFrameworks.Length -eq 1)
{
$targetFrameworksValue = $TargetFrameworks[0]
}
else
{
$targetFrameworksValue = $TargetFrameworks.Join(';')
}
return $targetFrameworksValue
}
function UpdateWithXmlDocument()
{
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$false)]
[string] $projectPaths,
[Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$false)]
[string] $targetFrameworksValue
foreach($_ in $projectPaths)
{
[string] $path = $_.FullName
$xml = New-Object System.Xml.XmlDocument
$xml.Load($path)
$ErrorActionPreference = 'SilentlyContinue'
$targetFrameworkNode = $xml.SelectSingleNode("//Project/PropertyGroup/TargetFramework")
#$targetFrameworkNode = $propertyGroup.SelectSingleNode("TargetFramework")
$ErrorActionPreference = 'Stop'
# TargetFramework
if ($targetFrameworkNode)
{
if ($TargetFrameworks.Length -gt 1)
{
$newNode = $xml.CreateElement("TargetFrameworks")
$newNode.InnerText = $targetFrameworksValue
Write-Host "Setting from '$($newNode.OuterXml)' to '$($newNode.OuterXml)' for '$path'..."
$targetFrameworkNode = $newNode
}
elseif( -not ($targetFrameworkNode.InnerText -like $targetFrameworksValue))
{
Write-Output "Updating from '$($targetFrameworkNode.OuterXml)' to '$targetFrameworksValue' for '$path'..."
$targetFrameworkNode.InnerText = $targetFrameworksValue
}
else
{
Write-Host "Skipping '$path' since TargetFramework matches source='$($targetFrameworkNode.InnerText)' and target='$targetFrameworksValue'"
}
continue
}
else
{
$ErrorActionPreference = 'SilentlyContinue'
$targetFrameworksNode = $xml.SelectSingleNode("//Project/PropertyGroup/TargetFrameworks")
$ErrorActionPreference = 'Stop'
}
# TargetFrameworks
if ($targetFrameworksNode)
{
if ($TargetFrameworks.Length -eq 1)
{
$newNode = $xml.CreateElement("TargetFramework")
$newNode.InnerText = $targetFrameworksValue
Write-Host "Setting from '$($targetFrameworksNode.OuterXml)' to '$($newNode.OuterXml)' for '$path'..."
$targetFrameworksNode = $newNode
}
elseif( -not [System.Collections.Generic.HashSet[string]]$targetFrameworksValueNode.InnerText -eq [System.Collections.Generic.HashSet[string]] $TargetFrameworks)
{
Write-Output "Updating from '$($targetFrameworksValueNode.OuterXml) to '$targetFrameworksValue' for $path"
$targetFrameworksNode.InnerText = $targetFrameworksValue
}
else
{
Write-Host "Skipping '$path' since TargetFrameworks matches source='$($targetFrameworksNode.InnerText)' and target='$targetFrameworksValue'"
}
continue
}
else
{
Write-Error "Expected TargetFramework or TargetFrameworks under //Project/PropertyGroup in '$path'"
}
if ($dryRun)
{
Write-Host "Dry Run was set. Nothing changed."
continue
}
$xml.Save($pathobject)
}
}
[string] $targetFrameworksValue = Initialize
$projectPaths = Get-ChildItem -include *.csproj -Recurse -Path $Path
if ($UpdateWithXmlDoc)
{
UpdateWithXmlDocument $projectPaths $targetFrameworksValue
}
else
{
$pattern = '<TargetFramework(s)?>.*' # Use single quotes to avoid string interpolation. Python equivalent of r""/r''.
$targetFrameworkTag = "<TargetFramework>$targetFrameworksValue</TargetFramework>"
if ($TargetFrameworks.Length -gt 1)
{
$targetFrameworkTag = $targetFrameworkTag.Replace("TargetFramework", "TargetFrameworks")
}
$projectPaths | % {
Write-Host "Processing '$_' and replacing TargetFramework with $targetFrameworkTag..."
(Get-Content $_.FullName) -Replace $pattern, $targetFrameworkTag | Set-Content -Encoding UTF8 -Path $_.FullName
Write-Output (Get-Content $_.FullName)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment