1+ //
2+ // Copyright (c) Microsoft. All rights reserved.
3+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+ //
5+
6+ using System . Linq ;
7+ using System . Threading . Tasks ;
8+
9+ namespace Microsoft . PowerShell . EditorServices . Session . Capabilities
10+ {
11+ using Microsoft . PowerShell . EditorServices . Utility ;
12+ using System ;
13+ using System . Collections . Generic ;
14+ using System . Collections . ObjectModel ;
15+ using System . Management . Automation ;
16+
17+ internal class DscBreakpointCapability : IRunspaceCapability
18+ {
19+ private string [ ] dscResourceRootPaths = new string [ 0 ] ;
20+
21+ private Dictionary < string , int [ ] > breakpointsPerFile =
22+ new Dictionary < string , int [ ] > ( ) ;
23+
24+ public async Task < List < BreakpointDetails > > SetLineBreakpoints (
25+ PowerShellContext powerShellContext ,
26+ string scriptPath ,
27+ BreakpointDetails [ ] breakpoints )
28+ {
29+ List < BreakpointDetails > resultBreakpointDetails =
30+ new List < BreakpointDetails > ( ) ;
31+
32+ // We always get the latest array of breakpoint line numbers
33+ // so store that for future use
34+ if ( breakpoints . Length > 0 )
35+ {
36+ // Set the breakpoints for this scriptPath
37+ this . breakpointsPerFile [ scriptPath ] =
38+ breakpoints . Select ( b => b . LineNumber ) . ToArray ( ) ;
39+ }
40+ else
41+ {
42+ // No more breakpoints for this scriptPath, remove it
43+ this . breakpointsPerFile . Remove ( scriptPath ) ;
44+ }
45+
46+ string hashtableString =
47+ string . Join (
48+ ", " ,
49+ this . breakpointsPerFile
50+ . Select ( file => $ "@{{Path=\" { file . Key } \" ;Line=@({ string . Join ( "," , file . Value ) } )}}") ) ;
51+
52+ // Run Enable-DscDebug as a script because running it as a PSCommand
53+ // causes an error which states that the Breakpoints parameter has not
54+ // been passed.
55+ await powerShellContext . ExecuteScriptString (
56+ hashtableString . Length > 0
57+ ? $ "Enable-DscDebug -Breakpoints { hashtableString } "
58+ : "Disable-DscDebug" ,
59+ false ,
60+ false ) ;
61+
62+ // Verify all the breakpoints and return them
63+ foreach ( var breakpoint in breakpoints )
64+ {
65+ breakpoint . Verified = true ;
66+ }
67+
68+ return breakpoints . ToList ( ) ;
69+ }
70+
71+ public bool IsDscResourcePath ( string scriptPath )
72+ {
73+ return dscResourceRootPaths . Any (
74+ dscResourceRootPath =>
75+ scriptPath . StartsWith (
76+ dscResourceRootPath ,
77+ StringComparison . CurrentCultureIgnoreCase ) ) ;
78+ }
79+
80+ public static DscBreakpointCapability CheckForCapability (
81+ RunspaceDetails runspaceDetails ,
82+ PowerShellContext powerShellContext )
83+ {
84+ DscBreakpointCapability capability = null ;
85+
86+ if ( runspaceDetails . Context != RunspaceContext . DebuggedRunspace )
87+ {
88+ using ( PowerShell powerShell = PowerShell . Create ( ) )
89+ {
90+ powerShell . Runspace = runspaceDetails . Runspace ;
91+
92+ // Attempt to import the updated DSC module
93+ powerShell . AddCommand ( "Import-Module" ) ;
94+ powerShell . AddArgument ( @"C:\Program Files\DesiredStateConfiguration\1.0.0.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psd1" ) ;
95+ powerShell . AddParameter ( "PassThru" ) ;
96+ powerShell . AddParameter ( "ErrorAction" , "SilentlyContinue" ) ;
97+
98+ PSObject moduleInfo = null ;
99+
100+ try
101+ {
102+ moduleInfo = powerShell . Invoke ( ) . FirstOrDefault ( ) ;
103+ }
104+ catch ( CmdletInvocationException e )
105+ {
106+ Logger . WriteException ( "Could not load the DSC module!" , e ) ;
107+ }
108+
109+ if ( moduleInfo != null )
110+ {
111+ Logger . Write ( LogLevel . Verbose , "Side-by-side DSC module found, gathering DSC resource paths..." ) ;
112+
113+ // The module was loaded, add the breakpoint capability
114+ capability = new DscBreakpointCapability ( ) ;
115+ runspaceDetails . AddCapability ( capability ) ;
116+
117+ powerShell . Commands . Clear ( ) ;
118+ powerShell . AddScript ( "Write-Host \" Gathering DSC resource paths, this may take a while...\" " ) ;
119+ powerShell . Invoke ( ) ;
120+
121+ // Get the list of DSC resource paths
122+ powerShell . Commands . Clear ( ) ;
123+ powerShell . AddCommand ( "Get-DscResource" ) ;
124+ powerShell . AddCommand ( "Select-Object" ) ;
125+ powerShell . AddParameter ( "ExpandProperty" , "ParentPath" ) ;
126+
127+ Collection < PSObject > resourcePaths = null ;
128+
129+ try
130+ {
131+ resourcePaths = powerShell . Invoke ( ) ;
132+ }
133+ catch ( CmdletInvocationException e )
134+ {
135+ Logger . WriteException ( "Get-DscResource failed!" , e ) ;
136+ }
137+
138+ if ( resourcePaths != null )
139+ {
140+ capability . dscResourceRootPaths =
141+ resourcePaths
142+ . Select ( o => ( string ) o . BaseObject )
143+ . ToArray ( ) ;
144+
145+ Logger . Write ( LogLevel . Verbose , $ "DSC resources found: { resourcePaths . Count } ") ;
146+ }
147+ else
148+ {
149+ Logger . Write ( LogLevel . Verbose , $ "No DSC resources found.") ;
150+ }
151+ }
152+ else
153+ {
154+ Logger . Write ( LogLevel . Verbose , $ "Side-by-side DSC module was not found.") ;
155+ }
156+ }
157+ }
158+
159+ return capability ;
160+ }
161+ }
162+ }
0 commit comments