Skip to content

Commit

Permalink
[DSC]Improve module generation for versioned release install (#32196)
Browse files Browse the repository at this point in the history
* [DSC]Generate psd1 file to include version

* Don't expose build paths on release builds

* Stop-Process with -Force to stop first run process
  • Loading branch information
jaimecbernardo committed Apr 3, 2024
1 parent 7b89482 commit a24ffb3
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 102 deletions.
4 changes: 2 additions & 2 deletions doc/devdocs/settingsv2/dsc-configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ PowerToys.Settings.exe set FancyZones.FancyzonesEditorHotkey "Shift+Ctrl+Alt+F"

# How DSC is implemented

We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` file. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.
We use `PowerToys.Settings.DSC.Schema.Generator` to generate the bulk of `PowerToysConfigure.psm1` and `PowerToysConfigure.psd1` files. It also uses dotnet reflection capabilities to inspect `PowerToys.Settings.UI.Lib.dll` assembly and generate properties for the modules we have. The actual generation is done as a `PowerToys.Settings.DSC.Schema.Generator.csproj` post-build action.

# Debugging DSC resources

Expand All @@ -62,7 +62,7 @@ After that, start a new `pwsh` session and `cd` to `src\dsc\Microsoft.PowerToys.
$env:PSModulePath += ";$pwd"
```

Now build `PowerToys.sln` and **move** `src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1` temporarily to `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder, so it's located alongside with the generated `Microsoft.PowerToys.Configure.psm1`.
You should have the generated `Microsoft.PowerToys.Configure.psm1` and `Microsoft.PowerToys.Configure.psd1` files inside the `src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\0.0.1\` folder.

This will allow DSC to discover our DSC Resource module. See [PSModulePath](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_psmodulepath?view=powershell-7.4#long-description) for more info.

Expand Down
4 changes: 2 additions & 2 deletions installer/PowerToysSetup/Core.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
Type="integer"
Value="1"
KeyPath="yes"/>
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
<RemoveFolder Id="RemoveThisFolder" On="uninstall" />
<RemoveFolder Id="RemovePowerToysDscVerFolder" Directory="PowerToysDscVerFolder" On="uninstall" />
Expand All @@ -79,7 +79,7 @@
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
<Component Id="PowerToysDSC" Win64="yes" Guid="C52AECA0-DA73-49B8-BB49-31EF6640FF1F">
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
</Component>
</Directory>
Expand Down

This file was deleted.

134 changes: 127 additions & 7 deletions src/dsc/PowerToys.Settings.DSC.Schema.Generator/DSCGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ class {{module.Name}} {
}
}


""";
}

Expand Down Expand Up @@ -223,8 +222,9 @@ public static string EmitModuleFileContents(SettingsStructure[] moduleSettings,

var enumsBlock = string.Join(DoubleNewLine, enumsToEmit.Select(EmitEnumDefinition));
var version = interop.CommonManaged.GetProductVersion().Replace("v", string.Empty);
var outputResult = string.Empty;

return $$"""
outputResult += $$"""
#region enums
enum PowerToysConfigureEnsure {
Absent
Expand All @@ -244,16 +244,20 @@ class PowerToysConfigure {
[bool] $Debug = $false

{{modulesResourcePropertiesBlock}}

""";

#if DEBUG
// Only output PowerToysSettings local build for debug builds. No need to expose release build locations.
outputResult += $$"""
[string] GetPowerToysSettingsPath() {
# Obtain PowerToys install location
if ($this.Debug -eq $true) {
$SettingsExePath = "{{debugSettingsPath}}"
} else {
$installation = Get-CimInstance Win32_Product | Where-Object {$_.Name -eq "PowerToys (Preview)" -and $_.Version -eq "{{version}}"}

if ($installation) {
$SettingsExePath = Join-Path (Join-Path $installation.InstallLocation WinUI3Apps) PowerToys.Settings.exe
# Handle spaces in the path
$SettingsExePath = "`"$SettingsExePath`""
} else {
throw "PowerToys installation wasn't found."
Expand All @@ -263,6 +267,27 @@ [string] GetPowerToysSettingsPath() {
return $SettingsExePath
}

""";
#else
outputResult += $$"""
[string] GetPowerToysSettingsPath() {
$installation = Get-CimInstance Win32_Product | Where-Object {$_.Name -eq "PowerToys (Preview)" -and $_.Version -eq "{{version}}"}

if ($installation) {
$SettingsExePath = Join-Path (Join-Path $installation.InstallLocation WinUI3Apps) PowerToys.Settings.exe
$SettingsExePath = "`"$SettingsExePath`""
} else {
throw "PowerToys installation wasn't found."
}

return $SettingsExePath
}

""";
#endif

outputResult += $$"""

[PowerToysConfigure] Get() {
$CurrentState = [PowerToysConfigure]::new()
$SettingsExePath = $this.GetPowerToysSettingsPath()
Expand Down Expand Up @@ -337,21 +362,116 @@ [void] Set() {
}

# Stop any running PowerToys instances
Stop-Process -Name "PowerToys.Settings" -PassThru | Wait-Process
$PowerToysProcessStopped = Stop-Process -Name "PowerToys" -PassThru
Stop-Process -Name "PowerToys.Settings" -Force -PassThru | Wait-Process
$PowerToysProcessStopped = Stop-Process -Name "PowerToys" -Force -PassThru
$PowerToysProcessStopped | Wait-Process

foreach ($change in $ChangesToApply) {
Start-Process -FilePath $SettingsExePath -Wait -Args "$change"
}

# If the PowerToys was stopped, restart it.
# If the PowerToys process was stopped, restart it.
if ($PowerToysProcessStopped -ne $null) {
Start-Process -FilePath $SettingsExePath
}
}
}
#endregion DscResources
""";

return outputResult;
}

public static string EmitManifestFileContents()
{
var version = interop.CommonManaged.GetProductVersion().Replace("v", string.Empty);
var generatedDate = DateTime.Now.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture);

return $$"""
#
# Module manifest for module 'Microsoft.PowerToys.Configure'
#
# Generated by: Microsoft Corporation
#
# Generated on: {{generatedDate}}
#

@{

# Script module or binary module file associated with this manifest.
RootModule = 'Microsoft.PowerToys.Configure.psm1'

# Version number of this module.
ModuleVersion = '{{version}}'

# ID used to uniquely identify this module
GUID = '778ed7a1-489d-4dc9-b0f2-2da3b1fe14cb'

# Author of this module
Author = 'Microsoft Corporation'

# Company or vendor of this module
CompanyName = 'Microsoft'

# Copyright statement for this module
Copyright = '(c) Microsoft Corporation. All rights reserved.'

# Description of the functionality provided by this module
Description = 'The module enables settings configuration for an installed PowerToys application.'

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = '*'

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()

# Variables to export from this module
VariablesToExport = @()

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()

# DSC resources to export from this module
DscResourcesToExport = @(
'PowerToysConfigure'
)

# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{

PSData = @{

# Tags applied to this module. These help with module discovery in online galleries.
# Tags = @()

# A URL to the license for this module.
# LicenseUri = ''

# A URL to the main website for this project.
# ProjectUri = ''

# A URL to an icon representing this module.
# IconUri = ''

# ReleaseNotes of this module
# ReleaseNotes = ''

# Prerelease string of this module
# Prerelease = ''

# Flag to indicate whether the module requires explicit user acceptance for install/update/save
# RequireLicenseAcceptance = $false

# External dependent modules of this module
# ExternalModuleDependencies = @()

} # End of PSData hashtable

} # End of PrivateData hashtable

}


""";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@

<PropertyGroup>
<GeneratedDSCModule>"$(ProjectDir)..\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(Version)\Microsoft.PowerToys.Configure.psm1"</GeneratedDSCModule>
<GeneratedDSCManifest>"$(ProjectDir)..\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(Version)\Microsoft.PowerToys.Configure.psd1"</GeneratedDSCManifest>
</PropertyGroup>

<!-- The following sections assume that the machine we're building on is always x64. That means we won't be able to run/inspect arm64 executables, therefore we must always execute x64 generator. -->

<Target Name="PostBuildAction" AfterTargets="Build" Outputs="$(GeneratedDSCModule)" Condition="'$(Platform)'!='ARM64'">
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;$(SolutionDir)x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule)" />
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;$(SolutionDir)x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule) $(GeneratedDSCManifest)" />
</Target>

<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Platform)'=='ARM64'">
Expand Down
30 changes: 23 additions & 7 deletions src/dsc/PowerToys.Settings.DSC.Schema.Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,35 @@ internal sealed class Program
{
public static int Main(string[] args)
{
if (args.Length != 2)
if (args.Length < 2)
{
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <output path>");
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <module output path> <manifest output path>");
return 1;
}

var dllPath = args[0];
var outputPath = args[1];
var moduleOutputPath = args[1];
var manifestOutputPath = string.Empty;

bool documentationMode = Path.GetExtension(outputPath) == ".md";
bool sampleMode = Path.GetExtension(outputPath) == ".yaml";
bool documentationMode = Path.GetExtension(moduleOutputPath) == ".md";
bool sampleMode = Path.GetExtension(moduleOutputPath) == ".yaml";

if (!documentationMode && !sampleMode)
{
if (args.Length < 3)
{
Console.WriteLine("Usage: Generator.exe <PowerToys.Settings.UI.Lib.dll path> <module output path> <manifest output path>");
return 1;
}
else
{
manifestOutputPath = args[2];
}
}

try
{
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
Directory.CreateDirectory(Path.GetDirectoryName(moduleOutputPath));

var assembly = Assembly.LoadFrom(dllPath);
var moduleSettings = Introspection.ParseModuleSettings(assembly);
Expand All @@ -46,11 +60,13 @@ public static int Main(string[] args)
}
else
{
var manifestFileContents = DSCGeneration.EmitManifestFileContents();
File.WriteAllText(manifestOutputPath, manifestFileContents);
var debugSettingsPath = Path.Combine(Directory.GetParent(dllPath).FullName, "PowerToys.Settings.exe");
outputFileContents = DSCGeneration.EmitModuleFileContents(moduleSettings, generalSettings, debugSettingsPath);
}

File.WriteAllText(outputPath, outputFileContents);
File.WriteAllText(moduleOutputPath, outputFileContents);
}
catch (Exception ex)
{
Expand Down

0 comments on commit a24ffb3

Please sign in to comment.