From a03803658cfd77554146a5d7e5c2a984de856e64 Mon Sep 17 00:00:00 2001 From: Antony Male Date: Sun, 3 Apr 2016 19:52:21 +0100 Subject: [PATCH] Use powershell, and powershell only, for Stylet.Start NuGet's 'content' approach broke horribly when we tried to upgrade: it removed App.xaml.cs. Using powershell through and through is more powerful: we can have a package which immediately uninstalls itself, which means it doesn't remain part of the user's package set. We can also be smarter about upgrading the project: we don't nuke anything that's in App.xaml, we don't need the user to manually delete MainWindow.xaml, and we can keep anything that's currently in MainWindow.xaml --- .gitattributes | 1 + NuGet/start/tools/install.ps1 | 244 ++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 NuGet/start/tools/install.ps1 diff --git a/.gitattributes b/.gitattributes index 87ed07e..155255a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.cs eol=crlf *.xaml eol=crlf +*.ps1 eol=crlf diff --git a/NuGet/start/tools/install.ps1 b/NuGet/start/tools/install.ps1 new file mode 100644 index 0000000..1242f95 --- /dev/null +++ b/NuGet/start/tools/install.ps1 @@ -0,0 +1,244 @@ +param($installPath, $toolsPath, $package, $project) + +# Testing: call with Invoke-Expression "path\to\install.ps1" +# $project = Get-Project + +$rootNamespace = $project.Properties.Item("RootNamespace").Value +$rootPath = $project.Properties.Item("LocalPath").Value + + +# Modify App.xaml + +$appXamlPath = [System.IO.Path]::Combine($rootPath, "App.xaml") + +if (![System.IO.File]::Exists($appXamlPath)) +{ + Write-Host "WARNING: Not modifying App.xaml as it doesn't exist" +} +else +{ + $doc = [System.Xml.Linq.XDocument]::Load($appXamlPath) + + $styletNs = [System.Xml.Linq.XNamespace]::Get("https://github.com/canton7/Stylet") + $ns = $doc.Root.GetDefaultNamespace() + $localNs = $doc.Root.GetNamespaceOfPrefix("local") + + $startupUri = $doc.Root.Attribute("StartupUri") + if ($startupUri -ne $null) + { + $startupUri.Remove() + } + + $existingApplicationLoader = $doc.Root.Descendants($styletNs.GetName("ApplicationLoader")) | Select -First 1 + if ($existingApplicationLoader -ne $null) + { + Write-Host "Not modifying App.xaml as it already has an element" + } + else + { + if ($doc.Root.Attribute([System.Xml.Linq.XNamespace]::Xmlns.GetName("s")) -eq $null) + { + $doc.Root.Add((New-Object System.Xml.Linq.XAttribute([System.Xml.Linq.XNamespace]::Xmlns.GetName("s"), $styletNs))) + } + + $resources = $doc.Root.Element($ns.GetName("Application.Resources")) + $existingResources = @($resources.Nodes()) + + $bootstrapper = New-Object System.Xml.Linq.XElement($localNs.GetName("Bootstrapper")) + $bootstrapperProperty = New-Object System.Xml.Linq.XElement($styletNs.GetName("ApplicationLoader.Bootstrapper"), $bootstrapper) + $applicationLoader = New-Object System.Xml.Linq.XElement($styletNs.GetName("ApplicationLoader"), $bootstrapperProperty) + + if ($existingResources.Count -gt 0) + { + $mergedDictionaries = New-Object System.Xml.Linq.XElement($ns.GetName("ResourceDictionary.MergedDictionaries"), $applicationLoader) + $resourceDictionary = New-Object System.Xml.Linq.XElement($ns.GetName("ResourceDictionary"), $mergedDictionaries, $existingResources) + $resources.ReplaceNodes($resourceDictionary) + } + else + { + $resources.Add($applicationLoader) + } + + $settings = New-Object System.Xml.XmlWriterSettings + $settings.Indent = $True + $settings.NewLineOnAttributes = $True + $settings.OmitXmlDeclaration = $True + $settings.IndentChars = " " + + $stringWriter = New-Object System.IO.StringWriter + $xmlWriter = [System.Xml.XmlWriter]::Create($stringWriter, $settings) + $doc.WriteTo($xmlWriter) + $xmlWriter.Close() + + $appXaml = $stringWriter.ToString(); + $stringWriter.Close() + + # Manually fudge the newlines after xmlns declarations, since XmlWriter won't do them itself + $appXaml = [System.Text.RegularExpressions.Regex]::Replace($appXaml, "Application\s+x:Class=", "Application x:Class=") + $appXaml = $appXaml.Replace(" xmlns=", "`r`n xmlns=") + $appXaml = $appXaml.Replace(" xmlns:", "`r`n xmlns:") + + [System.IO.File]::WriteAllText($appXamlPath, $appXaml) + } +} + +# Add bootstrapper + +# Only do this if Bootstrapper.cs doesn't already exist... +$existingBootstrapper = $project.ProjectItems | Where { $_.Name -eq "Bootstrapper.cs" } | Select -First 1 +if ($existingBootstrapper -ne $null) +{ + Write-Host "Not creating Bootstrapper.cs as it already exists" +} +else +{ + $bootstrapperContent = "using System; +using Stylet; +using StyletIoC; +using ${rootNamespace}.Pages; + +namespace ${rootNamespace} +{ + public class Bootstrapper : Bootstrapper + { + protected override void ConfigureIoC(IStyletIoCBuilder builder) + { + // Configure the IoC container in here + } + + protected override void Configure() + { + // Perform any other configuration before the application starts + } + } +} +" + $bootstrapperPath = [System.IO.Path]::Combine($rootPath, "Bootstrapper.cs") + [System.IO.File]::WriteAllText($bootstrapperPath, $bootstrapperContent) + $null = $project.ProjectItems.AddFromFile($bootstrapperPath) +} + +# Add Pages/ folder + +$pages = $project.ProjectItems | Where { $_.Name -Eq "Pages" } | Select -First 1 +if ($pages -eq $null) +{ + # This also creates the folder on disk + $pages = $project.ProjectItems.AddFolder("Pages") +} + +# Add Pages/ShellView.xaml + +# Only do this if ShellView doesn't already exist... +$existingShellView = $pages.ProjectItems | Where { $_.Name -eq "ShellView.xaml" } | Select -First 1 +if ($existingShellView -ne $null) +{ + Write-Host "Not renaming MainWindow.xaml to Pages/ShellView.xaml as Pages/ShellView.xaml already exists. " +} +else +{ + $mainWindow = ($project.ProjectItems | Where { $_.Name -Eq "MainWindow.xaml" } | Select -First 1) + if ($mainWindow -eq $null) + { + Write-Host "Creating Pages/ShellView.xaml from scratch, as MainWindow.xaml doesn't exist" + + $shellViewContent = " + + Hello Stylet! + + +" + + $shellViewCsContent = "using System; +using System.Windows; + +namespace ${rootNamespace}.Pages +{ + /// + /// Interaction logic for ShellView.xaml + /// + public partial class ShellView : Window + { + public ShellView() + { + InitializeComponent(); + } + } +} +" + } + else + { + $mainWindowPath = $mainWindow.Properties.Item("FullPath").Value + $mainWindowCsPath = $mainWindowPath + ".cs" + + $mainWindow.Remove() + + $shellViewContent = [System.IO.File]::ReadAllText($mainWindowPath) + $shellViewContent = $shellViewContent.Replace($rootNamespace + ".MainWindow", $rootNamespace + ".Pages.ShellView") + $shellViewContent = $shellViewContent.Replace('xmlns:local="clr-namespace:' + $rootNamespace + '"', 'xmlns:local="clr-namespace:' + $rootNamespace + '.Pages"') + $shellViewContent = $shellViewContent.Replace('Title="MainWindow"', 'Title="Stylet Start Project"') + $shellViewContent = $shellViewContent.Replace('mc:Ignorable="d"', "mc:Ignorable=""d"" + d:DataContext=""{d:DesignInstance local:ShellViewModel}""") + $shellViewContent = [System.Text.RegularExpressions.Regex]::Replace($shellViewContent, "\s*", " + Hello Stylet! + ") + + $shellViewCsContent = [System.IO.File]::ReadAllText($mainWindowCsPath) + $shellViewCsContent = $shellViewCsContent.Replace("namespace " + $rootNamespace, "namespace " + $rootNamespace + ".Pages") + $shellViewCsContent = $shellViewCsContent.Replace("class MainWindow", "class ShellView") + $shellViewCsContent = $shellViewCsContent.Replace("public MainWindow()", "public ShellView()") + $shellViewCsContent = $shellViewCsContent.Replace("/// Interaction logic for MainWindow.xaml", "/// Interaction logic for ShellView.xaml") + + [System.IO.File]::Delete($mainWindowPath) + [System.IO.File]::Delete($mainWindowCsPath) + } + + $shellViewPath = [System.IO.Path]::Combine($rootPath, "Pages", "ShellView.xaml") + $shellViewCsPath = $shellViewPath + ".cs" + + [System.IO.File]::WriteAllText($shellViewPath, $shellViewContent) + [System.IO.File]::WriteAllText($shellViewCsPath, $shellViewCsContent) + + $shellView = $pages.ProjectItems.AddFromFile($shellViewPath) + # This should have been added automagically, but just in case... + $null = $shellView.ProjectItems.AddFromFile($shellViewCsPath) +} + +# Add Pages/ShellViewModel.cs + +# Only do this if ShellViewModel doesn't already exist +$existingShellViewModel = $pages.ProjectItems | Where { $_.Name -eq "ShellViewModel.cs" } | Select -First 1 +if ($existingShellViewModel -ne $null) +{ + Write-Host "Not creating Pages/ShellViewModel.cs as it already exists" +} +else +{ + $shellViewModelContent = "using System; +using Stylet; + +namespace ${rootNamespace}.Pages +{ + public class ShellViewModel : Screen + { + } +} +" + + $shellViewModelPath = [System.IO.Path]::Combine($rootPath, "Pages", "ShellViewModel.cs") + [System.IO.File]::WriteAllText($shellViewModelPath, $shellViewModelContent) + $null = $pages.ProjectItems.AddFromFile($shellViewModelPath) +} + +Uninstall-Package Stylet.Start