问题
In relation to this previous question I am trying to create a batch file which as part must remove and add a reference to an XML *.csproj file. I have looked at this, this, this and this previous question but as a powershell n00b am unable to get it working (so far).
Can anyone help me with the following? I want to remove two specific references in a VS2010 csproj file (XML) and add a new reference.
I opened the csproj and the reference can be found at the following location
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ... -->
<!-- Omitted for brevity -->
<!-- ... -->
<ItemGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<AvailableItemName Include="Effect" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SomeDirectory\SomeProjectFile.csproj">
<Project>{AAB784E4-F8C6-4324-ABC0-6E9E0F73E575}</Project>
<Name>SomeProject</Name>
</ProjectReference>
<ProjectReference Include="..\AnotherDirectory\AnotherProjectFile.csproj">
<Project>{B0AA6A94-6784-4221-81F0-244A68C374C0}</Project>
<Name>AnotherProject</Name>
</ProjectReference>
</ItemGroup>
<!-- ... -->
<!-- Omitted for brevity -->
<!-- ... -->
</Project>
Basically I want to:
- remove these two references
- insert a new reference to a pre-compiled DLL specified by relative path
- OR Add an Assembly Reference Location to the project specified by relative path
As a very simple example I have tried the following powershell script to delete all the ProjectReference nodes. I pass the path to csproj as argument. I get the error Cannot validate the argument 'XML'. The Argument is null or empty
. I can confirm it is loading the csproj and saving it in-place unmodified so the path is correct.
param($path)
$MsbNS = @{msb = 'http://schemas.microsoft.com/developer/msbuild/2003'}
function RemoveElement([xml]$Project, [string]$XPath, [switch]$SingleNode)
{
$xml | Select-Xml -XPath $XPath | ForEach-Object{$_.Node.ParentNode.RemoveAll()}
}
$proj = [xml](Get-Content $path)
[System.Console]::WriteLine("Loaded project {0} into {1}", $path, $proj)
RemoveElement $proj "//ProjectReference" -SingleNode
# Also tried
# RemoveElement $proj "/Project/ItemGroup/ProjectReference[@Include=`'..\SomeDirectory\SomeProjectFile.csproj`']" -SingleNode
# but complains cannot find XPath
$proj.Save($path)
What am I doing wrong? Any comments/suggestions welcome :)
回答1:
I think the problem is that your XML file has a default namespace xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
. This causes problems with XPath. So you XPath //ProjectReference
will return 0 nodes. There are two ways to solve this:
- Use a namespace manager.
- Use namespace agnostic XPath.
Here's is how you could use a namespace manager:
$nsmgr = New-Object System.Xml.XmlNamespaceManager -ArgumentList $proj.NameTable
$nsmgr.AddNamespace('a','http://schemas.microsoft.com/developer/msbuild/2003')
$nodes = $proj.SelectNodes('//a:ProjectReference', $nsmgr)
Or:
Select-Xml '//a:ProjectReference' -Namespace $nsmgr
Here's how to do it using namespace agnostic XPath:
$nodes = $proj.SelectNodes('//*[local-name()="ProjectReference"]')
Or:
$nodes = Select-Xml '//*[local-name()="ProjectReference"]'
The second approach can be dangerous because if there were more than one namespace it could select the wrong nodes but not it your case.
回答2:
For posterities sake I will give the complete powershell scripts to add and remove references to csproj files. Please vote up Andy Arismedi's answer if you find this useful as he helped me find it. Feel free to give me a +1 also while you're at it ;-)
AddReference.ps1
# Calling convension:
# AddReference.PS1 "Mycsproj.csproj",
# "MyNewDllToReference.dll",
# "MyNewDllToReference"
param([String]$path, [String]$dllRef, [String]$refName)
$proj = [xml](Get-Content $path)
[System.Console]::WriteLine("")
[System.Console]::WriteLine("AddReference {0} on {1}", $refName, $path)
# Create the following hierarchy
# <Reference Include='{0}'>
# <HintPath>{1}</HintPath>
# </Reference>
# where (0) is $refName and {1} is $dllRef
$xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"
$itemGroup = $proj.CreateElement("ItemGroup", $xmlns);
$proj.Project.AppendChild($itemGroup);
$referenceNode = $proj.CreateElement("Reference", $xmlns);
$referenceNode.SetAttribute("Include", $refName);
$itemGroup.AppendChild($referenceNode)
$hintPath = $proj.CreateElement("HintPath", $xmlns);
$hintPath.InnerXml = $dllRef
$referenceNode.AppendChild($hintPath)
$proj.Save($path)
RemoveReference.ps1
# Calling Convention
# RemoveReference.ps1 "MyCsProj.csproj"
# "..\SomeDirectory\SomeProjectReferenceToRemove.dll"
param($path, $Reference)
$XPath = [string]::Format("//a:ProjectReference[@Include='{0}']", $Reference)
[System.Console]::WriteLine("");
[System.Console]::WriteLine("XPATH IS {0}", $XPath)
[System.Console]::WriteLine("");
$proj = [xml](Get-Content $path)
[System.Console]::WriteLine("Loaded project {0} into {1}", $path, $proj)
[System.Xml.XmlNamespaceManager] $nsmgr = $proj.NameTable
$nsmgr.AddNamespace('a','http://schemas.microsoft.com/developer/msbuild/2003')
$node = $proj.SelectSingleNode($XPath, $nsmgr)
if (!$node)
{
[System.Console]::WriteLine("");
[System.Console]::WriteLine("Cannot find node with XPath {0}", $XPath)
[System.Console]::WriteLine("");
exit
}
[System.Console]::WriteLine("Removing node {0}", $node)
$node.ParentNode.RemoveChild($node);
$proj.Save($path)
来源:https://stackoverflow.com/questions/8963328/how-do-i-use-powershell-to-add-remove-references-to-a-csproj