-
Notifications
You must be signed in to change notification settings - Fork 238
PowerShell v3/v4 Support #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
06f9a1e
cf0032b
906212f
a37888b
5cf832f
a519228
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
|
||
namespace Microsoft.PowerShell.EditorServices | ||
{ | ||
using System.Collections; | ||
using System.Management.Automation; | ||
using System.Management.Automation.Host; | ||
using System.Management.Automation.Runspaces; | ||
|
@@ -68,6 +69,14 @@ public PowerShellContextState SessionState | |
private set; | ||
} | ||
|
||
/// <summary> | ||
/// PowerShell Version of the current runspace. | ||
/// </summary> | ||
public Version PowerShellVersion | ||
{ | ||
get; private set; | ||
} | ||
|
||
#endregion | ||
|
||
#region Constructors | ||
|
@@ -109,7 +118,7 @@ private void Initialize(Runspace initialRunspace) | |
|
||
this.initialRunspace = initialRunspace; | ||
this.currentRunspace = initialRunspace; | ||
this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript); | ||
|
||
this.currentRunspace.Debugger.BreakpointUpdated += OnBreakpointUpdated; | ||
this.currentRunspace.Debugger.DebuggerStop += OnDebuggerStop; | ||
|
||
|
@@ -120,6 +129,21 @@ private void Initialize(Runspace initialRunspace) | |
// TODO: Should this be configurable? | ||
this.SetExecutionPolicy(ExecutionPolicy.RemoteSigned); | ||
|
||
var psVersionTable = this.currentRunspace.SessionStateProxy.GetVariable("PSVersionTable") as Hashtable; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any chance this could fail? Should we put a try/catch around it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If so I'd say make it into its own private function, something like GetPowerShellVersion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. Will do. |
||
if (psVersionTable != null) | ||
{ | ||
Version version; | ||
Version.TryParse(psVersionTable["PSVersion"] as string, out version); | ||
PowerShellVersion = version; | ||
} | ||
|
||
#if !PowerShellv3 | ||
if (PowerShellVersion > new Version(3,0)) | ||
{ | ||
this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript); | ||
} | ||
#endif | ||
|
||
this.SessionState = PowerShellContextState.Ready; | ||
} | ||
|
||
|
@@ -370,7 +394,12 @@ internal void BreakExecution() | |
{ | ||
Logger.Write(LogLevel.Verbose, "Debugger break requested..."); | ||
|
||
this.currentRunspace.Debugger.SetDebuggerStepMode(true); | ||
#if PowerShellv5 | ||
if (PowerShellVersion >= new Version(5, 0)) | ||
{ | ||
this.currentRunspace.Debugger.SetDebuggerStepMode(true); | ||
} | ||
#endif | ||
} | ||
|
||
internal void ResumeDebugger(DebuggerResumeAction resumeAction) | ||
|
@@ -568,7 +597,7 @@ private static string GetStringForPSCommand(PSCommand psCommand) | |
|
||
return stringBuilder.ToString(); | ||
} | ||
|
||
private void SetExecutionPolicy(ExecutionPolicy desiredExecutionPolicy) | ||
{ | ||
var currentPolicy = ExecutionPolicy.Undefined; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,12 +10,15 @@ | |
using Microsoft.PowerShell.EditorServices.Test.Shared.References; | ||
using Microsoft.PowerShell.EditorServices.Test.Shared.SymbolDetails; | ||
using Microsoft.PowerShell.EditorServices.Test.Shared.Symbols; | ||
using Microsoft.Win32; | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Management.Automation.Runspaces; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using System.Xml.Linq; | ||
using Xunit; | ||
|
||
namespace Microsoft.PowerShell.EditorServices.Test.Language | ||
|
@@ -25,6 +28,7 @@ public class LanguageServiceTests : IDisposable | |
private Workspace workspace; | ||
private LanguageService languageService; | ||
private PowerShellContext powerShellContext; | ||
private DirectoryInfo packageDirectory; | ||
|
||
public LanguageServiceTests() | ||
{ | ||
|
@@ -37,6 +41,11 @@ public LanguageServiceTests() | |
public void Dispose() | ||
{ | ||
this.powerShellContext.Dispose(); | ||
|
||
if (packageDirectory != null && packageDirectory.Exists) | ||
{ | ||
packageDirectory.Delete(true); | ||
} | ||
} | ||
|
||
[Fact] | ||
|
@@ -289,6 +298,96 @@ public void LanguageServiceFindsSymbolsInNoSymbolsFile() | |
Assert.Equal(0, symbolsResult.FoundOccurrences.Count()); | ||
} | ||
|
||
[Theory] | ||
[InlineData("3")] | ||
[InlineData("4")] | ||
[InlineData("5")] | ||
public void CompilesWithPowerShellVersion(string version) | ||
{ | ||
var assemblyPath = InstallPackage(string.Format("Microsoft.PowerShell.{0}.ReferenceAssemblies", version), "1.0.0"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome! This is a neat way to do it. Two things I'd recommend:
|
||
var projectPath = @"..\..\..\..\src\PowerShellEditorServices\PowerShellEditorServices.csproj"; | ||
FileInfo fi = new FileInfo(projectPath); | ||
var projectVersion = Path.Combine(fi.DirectoryName, version + ".PowerShellEditorServices.csproj"); | ||
|
||
var doc = XDocument.Load(projectPath); | ||
var references = doc.Root.Descendants().Where(m => m.Name.LocalName == "Reference"); | ||
var reference = references.First(m => m.Attribute("Include").Value.StartsWith("System.Management.Automation")); | ||
var hintPath = reference.Descendants().First(m => m.Name.LocalName == "HintPath"); | ||
hintPath.Value = assemblyPath; | ||
|
||
doc.Save(projectVersion); | ||
|
||
try | ||
{ | ||
Compile(projectVersion, version); | ||
} | ||
finally | ||
{ | ||
File.Delete(projectVersion); | ||
} | ||
} | ||
|
||
private void Compile(string project, string version) | ||
{ | ||
string msbuild; | ||
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0")) | ||
{ | ||
var root = key.GetValue("MSBuildToolsPath") as string; | ||
msbuild = Path.Combine(root, "MSBuild.exe"); | ||
} | ||
|
||
FileInfo fi = new FileInfo(project); | ||
|
||
var p = new Process(); | ||
p.StartInfo.FileName = msbuild; | ||
p.StartInfo.Arguments = string.Format(@" {0} /p:Configuration=Debug /t:Build /fileLogger /flp1:logfile=errors.txt;errorsonly /p:SolutionDir={1} /p:SolutionName=PowerShellEditorServices /p:DefineConstants=PowerShellv{2}", project, fi.Directory.Parent.Parent.FullName, version); | ||
p.StartInfo.UseShellExecute = false; | ||
p.StartInfo.CreateNoWindow = true; | ||
p.Start(); | ||
p.WaitForExit(60000); | ||
if (!p.HasExited) | ||
{ | ||
p.Kill(); | ||
throw new Exception("Compilation didn't complete in 60 seconds."); | ||
} | ||
|
||
if (p.ExitCode != 0) | ||
{ | ||
var errors = File.ReadAllText("errors.txt"); | ||
throw new Exception(errors); | ||
} | ||
} | ||
|
||
public string InstallPackage(string packageName, string packageVersion) | ||
{ | ||
var packageDir = Path.Combine(Path.GetTempPath(), "PowerShellPackages"); | ||
packageDirectory = new DirectoryInfo(packageDir); | ||
packageDirectory.Create(); | ||
|
||
var nuget = Path.Combine(Environment.CurrentDirectory, "NuGet.exe"); | ||
ProcessStartInfo si = new ProcessStartInfo(); | ||
|
||
var p = new Process(); | ||
p.StartInfo.FileName = nuget; | ||
p.StartInfo.Arguments = string.Format("install {0} -o {1} -Version {2}", packageName, packageDir, packageVersion); | ||
p.StartInfo.RedirectStandardOutput = true; | ||
p.StartInfo.RedirectStandardError = true; | ||
p.StartInfo.UseShellExecute = false; | ||
p.StartInfo.CreateNoWindow = true; | ||
p.Start(); | ||
p.WaitForExit(10000); | ||
if (!p.HasExited) | ||
{ | ||
p.Kill(); | ||
throw new Exception("Failed to download PowerShell NuGet packages required for this test."); | ||
} | ||
|
||
var packageFolder = packageName + "." + packageVersion; | ||
|
||
var assemblyPath = Path.Combine(packageDir, packageFolder); | ||
return Path.Combine(assemblyPath, @"lib\net4\System.Management.Automation.dll"); | ||
} | ||
|
||
private ScriptFile GetScriptFile(ScriptRegion scriptRegion) | ||
{ | ||
const string baseSharedScriptPath = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,7 +104,11 @@ | |
<ItemGroup> | ||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> | ||
</ItemGroup> | ||
<ItemGroup /> | ||
<ItemGroup> | ||
<Content Include="NuGet.exe"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The solution already has NuGet.exe in the root .nuget folder so you could probably just use that directly rather than including another one here. Might not be necessary at all if we pull in the reference assemblies as NuGet dependencies for the test project |
||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</Content> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | ||
<PropertyGroup> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we take out the HintPath? I dunno if it might be differnet somehow on other people's machines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I'll fix that. I'm using it for the tests but it should be put in dynamically.