2012-05-31 9 views
10

Estoy escribiendo un paquete nuget para un servicio de Windows, la idea es que mis colegas puedan crear un servicio de Windows instale el paquete y todo de la configuración de registro predeterminada, las bibliotecas de entlib y otras tareas de mantenimiento de la casa se configurarán para ellos.¿Cómo puedo obtener nuget (powershell) para insertar elementos <DependentUpon> en el archivo csproj de destino

He conseguido casi todo para trabajar a excepción de una cosa que me está volviendo loco.

En la carpeta de contenido de mi directorio nuget tengo Service.cs y Service.Designer.cs, estos se agregan al csproj de destino pero no están relacionados.

Cuando miro el archivo csproj veo:

<Compile Include="Service.cs"> 
    <SubType>Component</SubType> 
</Compile> 
<Compile Include="Service.Designer.cs" /> 

Pero yo quiero ver:

<Compile Include="Service.cs"> 
    <SubType>Component</SubType> 
</Compile> 
<Compile Include="Service.Designer.cs"> 
    <DependentUpon>Service.cs</DependentUpon> 
</Compile> 

Cualquier idea, bastante seguro de que implicará el guión install.ps pero mi PowerShell las habilidades son inexistentes?

Como un lado, ¿puede nuget ser utilizado para eliminar/sobrescribir archivos? Hasta ahora parece que se salta.

+0

Le pregunté que debería formar parte de su pregunta. También sugiero probar esto en comparación con la última versión de NuGet, que es [1.8] (http://nuget.codeplex.com/releases). – Sumo

Respuesta

12

Bien después de alrededor de 2 días de powershell y msbuild infierno finalmente conseguí una solución de trabajo. Vea abajo. Un proyecto de llamada de advertencia de palabra. Save() es crucial; sin esto, recibirá una advertencia de "modificación de archivo conflictiva detectada". Si llama a project.Save() primero, simplemente se le pedirá que vuelva a cargar la solución una vez que haya terminado.

param($installPath, $toolsPath, $package, $project) 

#save the project file first - this commits the changes made by nuget before this  script runs. 
$project.Save() 

#Load the csproj file into an xml object 
$xml = [XML] (gc $project.FullName) 

#grab the namespace from the project element so your xpath works. 
$nsmgr = New-Object System.Xml.XmlNamespaceManager -ArgumentList $xml.NameTable 
$nsmgr.AddNamespace('a',$xml.Project.GetAttribute("xmlns")) 

#link the service designer to the service.cs 
$node = $xml.Project.SelectSingleNode("//a:Compile[@Include='Service.Designer.cs']", $nsmgr) 
$depUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns")) 
$depUpon.InnerXml = "Service.cs" 
$node.AppendChild($depUpon) 

#link the settings file to the settings.designer.cs 
$settings = $xml.Project.SelectSingleNode("//a:None[@Include='ServiceSettings.settings']", $nsmgr) 
$generator = $xml.CreateElement("Generator", $xml.Project.GetAttribute("xmlns")) 
$generator.InnerXml = "SettingsSingleFileGenerator" 
$settings.AppendChild($generator) 

$LastGenOutput = $xml.CreateElement("LastGenOutput", $xml.Project.GetAttribute("xmlns")) 
$LastGenOutput.InnerXml = "ServiceSettings.Designer.cs" 
$settings.AppendChild($LastGenOutput) 

#set the settings designer to be autogen 
$settingsDesigner = $xml.Project.SelectSingleNode("//a:Compile[@Include='ServiceSettings.Designer.cs']", $nsmgr) 
$autoGen = $xml.CreateElement("AutoGen", $xml.Project.GetAttribute("xmlns")) 
$autoGen.InnerXml = "True" 

$DesignTimeSharedInput = $xml.CreateElement("DesignTimeSharedInput", $xml.Project.GetAttribute("xmlns")) 
$DesignTimeSharedInput.InnerXml = "True" 

$AGDependentUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns")) 
$AGDependentUpon.InnerXml = "ServiceSettings.settings" 

$settingsDesigner.AppendChild($autoGen) 
$settingsDesigner.AppendChild($DesignTimeSharedInput) 
$settingsDesigner.AppendChild($AGDependentUpon) 

#fix up the project installer.  
$projectInstallerRes = $xml.Project.SelectSingleNode("//a:EmbeddedResource[@Include='ProjectInstaller.resx']", $nsmgr) 
$projectInstallerResDepUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns")) 
$projectInstallerResDepUpon.InnerXml = "ProjectInstaller.cs" 
$projectInstallerRes.AppendChild($projectInstallerResDepUpon) 

$projectInstallerDesigner = $xml.Project.SelectSingleNode("//a:Compile[@Include='ProjectInstaller.Designer.cs']", $nsmgr) 
$projectInstallerDesignerDepUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns")) 
$projectInstallerDesignerDepUpon.InnerXml = "ProjectInstaller.cs" 
$projectInstallerDesigner.AppendChild($projectInstallerDesignerDepUpon) 

#delete the bundled program.cs file. 
$prog = $xml.Project.SelectSingleNode("//a:Compile[@Include='Program.cs']", $nsmgr) 
$prog.SelectSingleNode("..").RemoveChild($prog) 

#delete the bundled service1 file 
$oldServiceFile = $xml.Project.SelectSingleNode("//a:Compile[@Include='Service1.cs']", $nsmgr) 
$oldServiceFile.SelectSingleNode("..").RemoveChild($oldServiceFile) 

$oldServiceDesignerFile = $xml.Project.SelectSingleNode("//a:Compile[@Include='Service1.Designer.cs']", $nsmgr) 
$oldServiceDesignerFile.SelectSingleNode("..").RemoveChild($oldServiceDesignerFile) 

#save the changes. 
$xml.Save($project.FullName) 

desvergonzada taponamiento: I'll do a full write up of the solution and issues I encountered on my blog cianm.com Hope esto ahorra alguien por ahí algún tiempo.

2

Aquí hay un pequeño ejemplo usando Build.Evaluation que es parte de Visual Studio 2010+

$buildProject = @([Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName))[0] 


$toEdit = $buildProject.Xml.Items | where-object { $_.Include -eq "Service.Designer.cs" } 
$toEdit.AddMetaData("DependentUpon", "Service.cs") 
# ... for the other files 

$project.Save() 
5

Creo que una manera más fácil de hacer esto (y posiblemente más compatible) es a través de la variable $project que apunta a la Proyecto DTE.

Nota: Estoy entrando en esto un año después, pero enfrentando un problema similar. Encontré esta respuesta y comencé a usarla, pero tuve problemas con la llamada Guardar al final de la secuencia de comandos de PowerShell: si tenía un paquete instalado que dependía del paquete con la secuencia de comandos de PowerShell, la llamada Guardar "rompió las cosas", por lo que los otros paquetes no pudieron instalarse correctamente. De todos modos, ahora estoy usando NuGet 2.5, pero el DTE no ha cambiado con ningún significado desde VS 2003, así que esto debería funcionar incluso en el NuGet anterior que estabas usando.

param($installPath, $toolsPath, $package, $project) 

# Selections of items in the project are done with Where-Object rather than 
# direct access into the ProjectItems collection because if the object is 
# moved or doesn't exist then Where-Object will give us a null response rather 
# than the error that DTE will give us. 

# The Service.cs will show with a sub-item if it's already got the designer 
# file set. In the package upgrade scenario, you don't want to re-set all 
# this, so skip it if it's set. 
$service = $project.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "Service.cs" -and $_.ProjectItems.Count -eq 0 } 

if($service -eq $null) 
{ 
    # Upgrade scenario - user has moved/removed the Service.cs 
    # or it already has the sub-items set. 
    return 
} 

$designer = $project.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "Service.Designer.cs" } 

if($designer -eq $null) 
{ 
    # Upgrade scenario - user has moved/removed the Service.Desginer.cs. 
    return 
} 

# Here's where you set the designer to be a dependent file of 
# the primary code file. 
$service.ProjectItems.AddFromFile($designer.Properties.Item("FullPath").Value) 

Esperemos que ayuda a la gente en el futuro en busca de lograr algo similar. Al hacerlo de esta manera, evita el problema de fallas en las instalaciones de paquetes dependientes porque el proyecto se está guardando en el medio de la instalación del paquete.

Cuestiones relacionadas