2012-05-31 8 views
10

Sto scrivendo un pacchetto nuget per un servizio Windows, l'idea è che i miei colleghi possano creare un servizio Windows installare il pacchetto e tutto delle impostazioni di registrazione predefinite, le librerie entlib e altre attività di mantenimento della casa verranno impostate per esse.Come posso ottenere nuget (powershell) per inserire gli elementi <DependentUpon> nel file csproj di destinazione

Ho ottenuto praticamente tutto a lavoro tranne una cosa che mi sta facendo impazzire.

Nella cartella del contenuto della mia directory nuget sono presenti Service.cs e Service.Designer.cs che vengono aggiunti al csproj di destinazione ma non sono correlati.

Quando guardo il file csproj vedo:

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

Ma io voglio vedere:

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

Tutte le idee, abbastanza sicuro che coinvolgerà lo script install.ps ma il mio PowerShell le abilità sono inesistenti?

Come parte, è possibile utilizzare nuget per eliminare/sovrascrivere i file? Finora sembra proprio saltare.

+0

Ho chiesto come dovrebbe diventare parte della tua domanda. Suggerirei anche di provarlo contro l'ultima versione di NuGet, che è [1.8] (http://nuget.codeplex.com/releases). – Sumo

risposta

12

Bene, dopo circa 2 giorni di PowerShell e msbuild, ho finalmente trovato una soluzione funzionante. Vedi sotto. Una parola di avvertimento che chiama project.Save() è cruciale: senza questo si otterrà un avviso di "modifica di file in conflitto rilevato". Se chiami project.Save() per prima cosa ti verrà semplicemente richiesto di ricaricare la soluzione una volta che hai finito.

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) 

Shameless auto tamponamento: I'll do a full write up of the solution and issues I encountered on my blog cianm.com Spero che questo consente di risparmiare qualcuno là fuori un po 'di tempo.

2

Ecco un breve esempio utilizzando Build.Evaluation che fa parte di 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

Penso che un modo più semplice per fare questo (e forse più supportato) è attraverso la variabile $project che punta al Progetto DTE.

Nota: ci arriverò un anno dopo, ma di fronte a un problema simile. Ho trovato questa risposta e ho iniziato a usarla, ma ho avuto problemi con la chiamata di salvataggio alla fine dello script di PowerShell - se avessi installato un pacchetto che dipendeva dal pacchetto con lo script di PowerShell, la chiamata di salvataggio "ha rotto le cose" così gli altri pacchetti non possono essere installati correttamente. Ad ogni modo, ora sto usando NuGet 2.5, ma DTE non è cambiato con alcun significato da VS 2003, quindi questo dovrebbe funzionare anche nel vecchio NuGet che stavi 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) 

Speriamo che aiuta le persone in futuro cercando di realizzare qualcosa di simile. Facendolo in questo modo, si evita il problema con installazioni di pacchetti dipendenti non riuscite perché il progetto viene salvato durante l'installazione del pacchetto.