From fbb25d417dcf2bad3b501c06985d3eba45aa6697 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Mon, 4 May 2015 13:52:01 -0700 Subject: [PATCH 01/30] Adding support for UAP based Python applications --- .../SharedProject/CommonConfigProvider.cs | 112 +- .../SharedProject/CommonProjectConfig.cs | 10 + .../SharedProject/CommonPropertyPage.cs | 165 ++- .../Product/SharedProject/ConfigProvider.cs | 11 +- Common/Product/SharedProject/ProjectConfig.cs | 35 +- .../SharedProject/ProjectFileConstants.cs | 3 + Common/Product/SharedProject/ProjectNode.cs | 31 +- Python/Product/Debugger/Debugger.csproj | 1 + .../DebugEngine/AD7BoundBreakpoint.cs | 11 +- .../Debugger/DebugEngine/AD7Engine.cs | 6 + .../DebugEngine/Remote/PythonRemoteProcess.cs | 21 +- .../Debugger/Debugger/PythonProcess.cs | 25 +- .../Debugger/Debugger/PythonStackFrame.cs | 2 +- .../Debugger/Transports/TcpTransport.cs | 2 +- Python/Product/PythonTools/PythonConstants.cs | 6 +- Python/Product/PythonTools/PythonTools.csproj | 4 + .../PythonTools/PythonTools/Extensions.cs | 24 +- .../Project/ImportWizard/ImportSettings.cs | 3 +- .../ImportWizard/ProjectCustomizations.cs | 24 + .../Project/PythonProjectConfig.cs | 23 + .../PythonTools/Project/PythonProjectNode.cs | 45 +- .../PythonTools/visualstudio_py_debugger.py | 142 +-- .../visualstudio_py_remote_launcher.py | 114 +++ .../Uap/Debugger/PythonRemoteDebugEvents.cs | 118 +++ .../RemoteDebugServerComponent.vsdconfigxml | 36 + Python/Product/Uap/Extensions.cs | 144 +++ Python/Product/Uap/Guids.cs | 26 + .../Uap/Interpreter/PythonUapInterpreter.cs | 237 +++++ .../PythonUapInterpreterFactory.cs | 40 + .../PythonUapInterpreterFactoryProvider.cs | 47 + .../Uap/Microsoft.PythonTools.Uap.targets | 66 ++ .../Product/Uap/Project/PythonUapProject.cs | 299 ++++++ .../Uap/Project/PythonUapProjectConfig.cs | 961 ++++++++++++++++++ .../Uap/Project/PythonUapProjectFactory.cs | 34 + .../Uap/Project/PythonUapPropertyPage.cs | 60 ++ .../PythonUapPropertyPageControl.Designer.cs | 147 +++ .../Project/PythonUapPropertyPageControl.cs | 49 + .../Project/PythonUapPropertyPageControl.resx | 129 +++ Python/Product/Uap/Properties/AssemblyInfo.cs | 30 + Python/Product/Uap/PythonUapPackage.cs | 125 +++ Python/Product/Uap/Resources.Designer.cs | 81 ++ Python/Product/Uap/Resources.resx | 126 +++ .../Application_TemporaryKey.pfx | 0 .../BackgroundService/Package.appxmanifest | 67 ++ .../PythonBackgroundService.pyproj | 61 ++ .../PythonBackgroundService.vstemplate | 31 + .../Projects/BackgroundService/StartupTask.py | 1 + Python/Product/Uap/Uap.csproj | 323 ++++++ Python/Product/Uap/VSPackage.resx | 126 +++ .../Product/Uap/source.extension.vsixmanifest | 47 + Python/PythonTools.sln | Bin 168906 -> 222278 bytes Python/Setup/BuildRelease.ps1 | 1 + Python/Setup/MergeModule.wxi | 3 + .../Setup/PythonTools/PythonToolsFiles.proj | 7 +- Python/Setup/PythonTools/_wingpio.idb | Bin 0 -> 4525 bytes Python/Setup/PythonTools/_wini2c.idb | Bin 0 -> 6744 bytes Python/Setup/PythonTools/_winspi.idb | Bin 0 -> 12954 bytes .../PythonToolsInstaller.wixproj | 5 + .../PythonToolsInstaller.wxs | 15 + Python/Setup/Uap/Uap.wixproj | 33 + Python/Setup/Uap/Uap.wxs | 20 + Python/Setup/Uap/UapFiles.proj | 42 + Python/Setup/setup.proj | 1 + Python/products.settings | 4 + 64 files changed, 4229 insertions(+), 133 deletions(-) create mode 100644 Python/Product/PythonTools/visualstudio_py_remote_launcher.py create mode 100644 Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs create mode 100644 Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml create mode 100644 Python/Product/Uap/Extensions.cs create mode 100644 Python/Product/Uap/Guids.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreter.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs create mode 100644 Python/Product/Uap/Microsoft.PythonTools.Uap.targets create mode 100644 Python/Product/Uap/Project/PythonUapProject.cs create mode 100644 Python/Product/Uap/Project/PythonUapProjectConfig.cs create mode 100644 Python/Product/Uap/Project/PythonUapProjectFactory.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPage.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.resx create mode 100644 Python/Product/Uap/Properties/AssemblyInfo.cs create mode 100644 Python/Product/Uap/PythonUapPackage.cs create mode 100644 Python/Product/Uap/Resources.Designer.cs create mode 100644 Python/Product/Uap/Resources.resx create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py create mode 100644 Python/Product/Uap/Uap.csproj create mode 100644 Python/Product/Uap/VSPackage.resx create mode 100644 Python/Product/Uap/source.extension.vsixmanifest create mode 100644 Python/Setup/PythonTools/_wingpio.idb create mode 100644 Python/Setup/PythonTools/_wini2c.idb create mode 100644 Python/Setup/PythonTools/_winspi.idb create mode 100644 Python/Setup/Uap/Uap.wixproj create mode 100644 Python/Setup/Uap/Uap.wxs create mode 100644 Python/Setup/Uap/UapFiles.proj diff --git a/Common/Product/SharedProject/CommonConfigProvider.cs b/Common/Product/SharedProject/CommonConfigProvider.cs index 60879b8b55..164a43fa82 100644 --- a/Common/Product/SharedProject/CommonConfigProvider.cs +++ b/Common/Product/SharedProject/CommonConfigProvider.cs @@ -13,46 +13,134 @@ * ***************************************************************************/ using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using System.Collections.Generic; namespace Microsoft.VisualStudioTools.Project { /// - /// Enables the Any CPU Platform form name for Dynamic Projects. + /// Enables the Any CPU, x86, x64, and ARM Platform form names for Dynamic Projects. /// Hooks language specific project config. + /// Projects that are platform aware should have a PlatformAware and AvailablePlatforms + /// property for configuration handling to function correctly. + /// PlatformAware value can be true or false. AvailablePlatforms is a comma separated string of supported platforms (e.g. "x86, X64") + /// If the PlatformAware property is ommited then this provider will only provide "Any CPU" platform. /// internal class CommonConfigProvider : ConfigProvider { private CommonProjectNode _project; + private bool _isPlatformAware; public CommonConfigProvider(CommonProjectNode project) : base(project) { + bool appxPackage, windowsAppContainer; _project = project; + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.PlatformAware), out _isPlatformAware); + + if (!_isPlatformAware) { + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AppxPackage), out appxPackage); + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.WindowsAppContainer), out windowsAppContainer); + _isPlatformAware = appxPackage && windowsAppContainer; + } } #region overridden methods - protected override ProjectConfig CreateProjectConfiguration(string configName) { + protected override ProjectConfig CreateProjectConfiguration(string configName) { + if (_isPlatformAware) { + if (configName != null) { + var configParts = configName.Split('|'); + + if (configParts.Length == 2) { + var config = _project.MakeConfiguration(configName); + config.PlatformName = configParts[1]; + return config; + } + } + } + return _project.MakeConfiguration(configName); } public override int GetPlatformNames(uint celt, string[] names, uint[] actual) { - if (names != null) { - names[0] = "Any CPU"; + if (_isPlatformAware) { + var platforms = GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + else { + if (names != null) { + names[0] = AnyCPUPlatform; } - if (actual != null) { - actual[0] = 1; + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } } - return VSConstants.S_OK; + public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { + if (_isPlatformAware) { + var platforms = GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + else { + if (names != null) { + names[0] = AnyCPUPlatform; + } + + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } } - public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { - if (names != null) { - names[0] = "Any CPU"; + public override int GetCfgs(uint celt, IVsCfg[] a, uint[] actual, uint[] flags) { + if (_isPlatformAware) { + if (flags != null) { + flags[0] = 0; + } + + int i = 0; + string[] configList = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + string[] platformList = GetSupportedPlatformsFromProject(); + + if (a != null) { + foreach (string platformName in platformList) { + foreach (string configName in configList) { + a[i] = this.GetProjectConfiguration(configName + "|" + platformName); + i++; + if (i == celt) { + break; + } + } + if(i == celt) { + break; + } + } + } + else { + i = configList.Length * platformList.Length; + } + + if (actual != null) { + actual[0] = (uint)i; + } + + return VSConstants.S_OK; } - if (actual != null) { - actual[0] = 1; + return base.GetCfgs(celt, a, actual, flags); + } + + public override int GetCfgOfName(string name, string platName, out IVsCfg cfg) { + if (!string.IsNullOrEmpty(platName)) { + cfg = this.GetProjectConfiguration(name + "|" + platName); + + return VSConstants.S_OK; } + cfg = this.GetProjectConfiguration(name); return VSConstants.S_OK; } diff --git a/Common/Product/SharedProject/CommonProjectConfig.cs b/Common/Product/SharedProject/CommonProjectConfig.cs index 83b90daf55..7e2b28f645 100644 --- a/Common/Product/SharedProject/CommonProjectConfig.cs +++ b/Common/Product/SharedProject/CommonProjectConfig.cs @@ -28,6 +28,16 @@ public CommonProjectConfig(CommonProjectNode/*!*/ project, string configuration) _project = project; } + public virtual string Platform { + get { + return GetConfigurationProperty("Platform", false); + } + + set { + SetConfigurationProperty("Platform", value); + } + } + public override int DebugLaunch(uint flags) { IProjectLauncher starter = _project.GetLauncher(); diff --git a/Common/Product/SharedProject/CommonPropertyPage.cs b/Common/Product/SharedProject/CommonPropertyPage.cs index 06f53b33bc..168943a862 100644 --- a/Common/Product/SharedProject/CommonPropertyPage.cs +++ b/Common/Product/SharedProject/CommonPropertyPage.cs @@ -14,9 +14,13 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Drawing; +using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; +using Microsoft.Build.Construction; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; @@ -49,6 +53,10 @@ internal virtual CommonProjectNode Project { } } + internal virtual IEnumerable SelectedConfigs { + get; set; + } + protected void SetProjectProperty(string propertyName, string propertyValue) { // SetProjectProperty's implementation will check whether the value // has changed. @@ -59,6 +67,150 @@ protected string GetProjectProperty(string propertyName) { return Project.GetUnevaluatedProperty(propertyName); } + protected void SetUserProjectProperty(string propertyName, string propertyValue) { + // SetUserProjectProperty's implementation will check whether the value + // has changed. + Project.SetUserProjectProperty(propertyName, propertyValue); + } + + protected string GetUserProjectProperty(string propertyName) { + return Project.GetUserProjectProperty(propertyName); + } + + protected string GetConfigUserProjectProperty(string propertyName) { + if (SelectedConfigs == null) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + Project.CurrentConfig.GetPropertyValue("Configuration"), + Project.CurrentConfig.GetPropertyValue("Platform")); + + return GetUserPropertyUnderCondition(propertyName, condition); + } else { + StringCollection values = new StringCollection(); + + foreach (var config in SelectedConfigs) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + config.ConfigName, + config.Platform); + + values.Add(GetUserPropertyUnderCondition(propertyName, condition)); + } + + switch (values.Count) { + case 0: + return null; + case 1: + return values[0]; + default: + return ""; + } + } + } + + protected void SetConfigUserProjectProperty(string propertyName, string propertyValue) { + if (SelectedConfigs == null) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + Project.CurrentConfig.GetPropertyValue("Configuration"), + Project.CurrentConfig.GetPropertyValue("Platform")); + + SetUserPropertyUnderCondition(propertyName, propertyValue, condition); + } else { + foreach (var config in SelectedConfigs) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + config.ConfigName, + config.GetConfigurationProperty("Platform", false)); + + SetUserPropertyUnderCondition(propertyName, propertyValue, condition); + } + } + } + + private string GetUserPropertyUnderCondition(string propertyName, string condition) { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + + if (Project.UserBuildProject != null) { + if (conditionTrimmed.Length == 0) { + return Project.UserBuildProject.GetProperty(propertyName).UnevaluatedValue; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + ProjectPropertyGroupElement matchingGroup = null; + + foreach (ProjectPropertyGroupElement group in Project.UserBuildProject.Xml.PropertyGroups) { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) { + matchingGroup = group; + break; + } + } + + if (matchingGroup != null) { + foreach (ProjectPropertyElement property in matchingGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && (property.Condition == null || property.Condition.Length == 0)) { + return property.Value; + } + } + } + + } + + return null; + } + + /// + /// Emulates the behavior of SetProperty(name, value, condition) on the old MSBuild object model. + /// This finds a property group with the specified condition (or creates one if necessary) then sets the property in there. + /// + private void SetUserPropertyUnderCondition(string propertyName, string propertyValue, string condition) { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + const string userProjectCreateProperty = "UserProject"; + + if (Project.UserBuildProject == null) { + Project.SetUserProjectProperty(userProjectCreateProperty, null); + } + + if (conditionTrimmed.Length == 0) { + var userProp = Project.UserBuildProject.GetProperty(userProjectCreateProperty); + if (userProp != null) { + Project.UserBuildProject.RemoveProperty(userProp); + } + Project.UserBuildProject.SetProperty(propertyName, propertyValue); + return; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + ProjectPropertyGroupElement newGroup = null; + + foreach (ProjectPropertyGroupElement group in Project.UserBuildProject.Xml.PropertyGroups) { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) { + newGroup = group; + break; + } + } + + if (newGroup == null) { + newGroup = Project.UserBuildProject.Xml.AddPropertyGroup(); // Adds after last existing PG, else at start of project + newGroup.Condition = condition; + } + + foreach (ProjectPropertyElement property in newGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && (property.Condition == null || property.Condition.Length == 0)) { + property.Value = propertyValue; + return; + } + } + + newGroup.AddProperty(propertyName, propertyValue); + } + public bool Loading { get { return _loading; @@ -138,18 +290,19 @@ void IPropertyPage.SetObjects(uint count, object[] punk) { if (count > 0) { if (punk[0] is ProjectConfig) { - ArrayList configs = new ArrayList(); + if (_project == null) { + _project = (CommonProjectNode)((CommonProjectConfig)punk.First()).ProjectMgr; + } + + var configs = new List(); for (int i = 0; i < count; i++) { CommonProjectConfig config = (CommonProjectConfig)punk[i]; - if (_project == null) { - Project = (CommonProjectNode)config.ProjectMgr; - break; - } - configs.Add(config); } + + SelectedConfigs = configs; } else if (punk[0] is NodeProperties) { if (_project == null) { Project = (CommonProjectNode)(punk[0] as NodeProperties).HierarchyNode.ProjectMgr; diff --git a/Common/Product/SharedProject/ConfigProvider.cs b/Common/Product/SharedProject/ConfigProvider.cs index d6c028f245..1e1bdc0857 100644 --- a/Common/Product/SharedProject/ConfigProvider.cs +++ b/Common/Product/SharedProject/ConfigProvider.cs @@ -37,8 +37,11 @@ namespace Microsoft.VisualStudioTools.Project { [ComVisible(true)] internal abstract class ConfigProvider : IVsCfgProvider2 { internal const string configString = " '$(Configuration)' == '{0}' "; + internal const string configPlatformString = " '$(Configuration)|$(Platform)' == '{0}|{1}' "; internal const string AnyCPUPlatform = "Any CPU"; internal const string x86Platform = "x86"; + internal const string x64Platform = "x64"; + internal const string ARMPlatform = "ARM"; private ProjectNode project; private EventSinkCollection cfgEventSinks = new EventSinkCollection(); @@ -473,7 +476,7 @@ private string[] GetPlatformsFromProject() { string[] platforms = GetPropertiesConditionedOn(ProjectFileConstants.Platform); if (platforms == null || platforms.Length == 0) { - return new string[] { x86Platform, AnyCPUPlatform }; + return new string[] { x86Platform, AnyCPUPlatform, x64Platform, ARMPlatform }; } for (int i = 0; i < platforms.Length; i++) { @@ -487,7 +490,7 @@ private string[] GetPlatformsFromProject() { /// Return the supported platform names. /// /// An array of supported platform names. - private string[] GetSupportedPlatformsFromProject() { + internal string[] GetSupportedPlatformsFromProject() { string platforms = this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AvailablePlatforms); if (platforms == null) { @@ -523,7 +526,7 @@ private static string ConvertPlatformToVsProject(string oldPlatformName) { /// An array of available platform names /// A count of the actual number of platform names returned. /// The platforms array is never null. It is assured by the callers. - private static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) { + internal static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) { Utilities.ArgumentNotNull("platforms", platforms); if (names == null) { if (actual == null || actual.Length == 0) { @@ -564,7 +567,7 @@ private static int GetPlatforms(uint celt, string[] names, uint[] actual, string /// /// Get all the configurations in the project. /// - private string[] GetPropertiesConditionedOn(string constant) { + internal string[] GetPropertiesConditionedOn(string constant) { List configurations = null; this.project.BuildProject.ReevaluateIfNecessary(); this.project.BuildProject.ConditionedProperties.TryGetValue(constant, out configurations); diff --git a/Common/Product/SharedProject/ProjectConfig.cs b/Common/Product/SharedProject/ProjectConfig.cs index 187451f05d..6583008e4f 100644 --- a/Common/Product/SharedProject/ProjectConfig.cs +++ b/Common/Product/SharedProject/ProjectConfig.cs @@ -48,8 +48,19 @@ internal abstract class ProjectConfig : private IVsProjectFlavorCfg flavoredCfg; private List outputGroups; private BuildableProjectConfig buildableCfg; + private string platformName; #region properties + + public string PlatformName { + get { + return platformName; + } + set { + platformName = value; + } + } + internal ProjectNode ProjectMgr { get { return this.project; @@ -97,7 +108,20 @@ internal IList OutputGroups { #region ctors internal ProjectConfig(ProjectNode project, string configuration) { this.project = project; - this.configName = configuration; + if (configuration.Contains("|")) { // If configuration is in the form "|" + + string[] configStrArray = configuration.Split('|'); + if (2 == configStrArray.Length) { + this.configName = configStrArray[0]; + this.platformName = configStrArray[1]; + } + else { + throw new Exception(string.Format(CultureInfo.InvariantCulture, "Invalid configuration format: {0}", configuration)); + } + } + else { // If configuration is in the form "" + this.configName = configuration; + } var flavoredCfgProvider = ProjectMgr.GetOuterInterface(); Utilities.ArgumentNotNull("flavoredCfgProvider", flavoredCfgProvider); @@ -107,7 +131,7 @@ internal ProjectConfig(ProjectNode project, string configuration) { // if the flavored object support XML fragment, initialize it IPersistXMLFragment persistXML = flavoredCfg as IPersistXMLFragment; if (null != persistXML) { - this.project.LoadXmlFragment(persistXML, configName); + this.project.LoadXmlFragment(persistXML, configName, platformName); } } #endregion @@ -240,7 +264,12 @@ public virtual int GetProjectDesignerPages(CAUUID[] pages) { /// first part is the config name, 2nd part is the platform name /// public virtual int get_DisplayName(out string name) { - name = DisplayName; + if (!string.IsNullOrEmpty(PlatformName)) { + name = ConfigName + "|" + PlatformName; + + } else { + name = DisplayName; + } return VSConstants.S_OK; } diff --git a/Common/Product/SharedProject/ProjectFileConstants.cs b/Common/Product/SharedProject/ProjectFileConstants.cs index efd04b18e2..ff70be332b 100644 --- a/Common/Product/SharedProject/ProjectFileConstants.cs +++ b/Common/Product/SharedProject/ProjectFileConstants.cs @@ -89,6 +89,9 @@ public static class ProjectFileConstants { public const string FlavorProperties = "FlavorProperties"; public const string VisualStudio = "VisualStudio"; public const string User = "User"; + public const string PlatformAware = "PlatformAware"; + public const string AppxPackage = "AppxPackage"; + public const string WindowsAppContainer = "WindowsAppContainer"; } public static class ProjectFileAttributeValue { diff --git a/Common/Product/SharedProject/ProjectNode.cs b/Common/Product/SharedProject/ProjectNode.cs index 7c6cae1b8a..ebafc9d6ed 100644 --- a/Common/Product/SharedProject/ProjectNode.cs +++ b/Common/Product/SharedProject/ProjectNode.cs @@ -949,6 +949,12 @@ public override object GetProperty(int propId) { case __VSHPROPID.VSHPROPID_ExpandByDefault: return true; + case __VSHPROPID.VSHPROPID_DefaultEnableDeployProjectCfg: + return true; + + case __VSHPROPID.VSHPROPID_DefaultEnableBuildProjectCfg: + return true; + // Use the same icon as if the folder was closed case __VSHPROPID.VSHPROPID_OpenFolderIconIndex: return GetProperty((int)__VSHPROPID.VSHPROPID_IconIndex); @@ -2003,7 +2009,7 @@ protected virtual Guid[] GetConfigurationIndependentPropertyPages() { /// /// protected virtual Guid[] GetConfigurationDependentPropertyPages() { - return new Guid[0]; + return new Guid[] { Guid.Empty }; } /// @@ -2956,7 +2962,7 @@ protected internal virtual void ProcessDependentFileNodes(IList subitems protected internal virtual void LoadNonBuildInformation() { IPersistXMLFragment outerHierarchy = GetOuterInterface(); if (outerHierarchy != null) { - this.LoadXmlFragment(outerHierarchy, null); + this.LoadXmlFragment(outerHierarchy, null, null); } } @@ -3371,7 +3377,8 @@ protected void AddCATIDMapping(Type type, Guid catid) { /// /// Object that support being initialized with an XML fragment /// Name of the configuration being initialized, null if it is the project - protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName) { + /// Name of the platform being initialized, null is ok + protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName, string platformName) { Utilities.ArgumentNotNull("persistXmlFragment", persistXmlFragment); if (xmlFragments == null) { @@ -3395,15 +3402,20 @@ protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, if (child.Attributes.Count > 0) { string guid = String.Empty; string configuration = String.Empty; + string platform = String.Empty; if (child.Attributes[ProjectFileConstants.Guid] != null) guid = child.Attributes[ProjectFileConstants.Guid].Value; if (child.Attributes[ProjectFileConstants.Configuration] != null) configuration = child.Attributes[ProjectFileConstants.Configuration].Value; + if (child.Attributes[ProjectFileConstants.Platform] != null) + platform = child.Attributes[ProjectFileConstants.Platform].Value; if (String.Compare(child.Name, ProjectFileConstants.FlavorProperties, StringComparison.OrdinalIgnoreCase) == 0 && String.Compare(guid, flavorGuidString, StringComparison.OrdinalIgnoreCase) == 0 && ((String.IsNullOrEmpty(configName) && String.IsNullOrEmpty(configuration)) - || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0))) { + || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0)) + && ((String.IsNullOrEmpty(platformName) && String.IsNullOrEmpty(platform)) + || (String.Compare(platform, platformName, StringComparison.OrdinalIgnoreCase) == 0))) { // we found the matching fragment fragment = child.InnerXml; node = child; @@ -3463,7 +3475,7 @@ protected void PersistXMLFragments() { ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, out fragment, 1)); if (!String.IsNullOrEmpty(fragment)) { // Add the fragment to our XML - WrapXmlFragment(doc, root, flavor, null, fragment); + WrapXmlFragment(doc, root, flavor, null, null, fragment); } // While we don't yet support user files, our flavors might, so we will store that in the project file until then // TODO: Refactor this code when we support user files @@ -3471,7 +3483,7 @@ protected void PersistXMLFragments() { ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, out fragment, 1)); if (!String.IsNullOrEmpty(fragment)) { // Add the fragment to our XML - XmlElement node = WrapXmlFragment(doc, root, flavor, null, fragment); + XmlElement node = WrapXmlFragment(doc, root, flavor, null, null, fragment); node.Attributes.Append(doc.CreateAttribute(ProjectFileConstants.User)); } } @@ -3482,7 +3494,7 @@ protected void PersistXMLFragments() { string fragment; ErrorHandler.ThrowOnFailure(((ProjectConfig)config).GetXmlFragment(flavor, _PersistStorageType.PST_PROJECT_FILE, out fragment)); if (!String.IsNullOrEmpty(fragment)) { - WrapXmlFragment(doc, root, flavor, ((ProjectConfig)config).ConfigName, fragment); + WrapXmlFragment(doc, root, flavor, ((ProjectConfig)config).ConfigName, ((ProjectConfig)config).PlatformName, fragment); } } } @@ -5091,7 +5103,7 @@ internal void OnAfterProjectOpen() { this.projectOpened = true; } - private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string fragment) { + private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string platform, string fragment) { XmlElement node = document.CreateElement(ProjectFileConstants.FlavorProperties); XmlAttribute attribute = document.CreateAttribute(ProjectFileConstants.Guid); attribute.Value = flavor.ToString("B"); @@ -5100,6 +5112,9 @@ private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, attribute = document.CreateAttribute(ProjectFileConstants.Configuration); attribute.Value = configuration; node.Attributes.Append(attribute); + attribute = document.CreateAttribute(ProjectFileConstants.Platform); + attribute.Value = platform; + node.Attributes.Append(attribute); } node.InnerXml = fragment; root.AppendChild(node); diff --git a/Python/Product/Debugger/Debugger.csproj b/Python/Product/Debugger/Debugger.csproj index 78c71f2fd9..f80f6e6fde 100644 --- a/Python/Product/Debugger/Debugger.csproj +++ b/Python/Product/Debugger/Debugger.csproj @@ -82,6 +82,7 @@ global + diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs index 61332b3521..4a02742539 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs @@ -108,7 +108,16 @@ int IDebugBoundBreakpoint2.SetCondition(BP_CONDITION bpCondition) { } int IDebugBoundBreakpoint2.GetHitCount(out uint pdwHitCount) { - pdwHitCount = (uint)_breakpoint.GetHitCountAsync().GetAwaiter().GetResult(); + var remoteProcess = _engine.Process as Remote.PythonRemoteProcess; + if (remoteProcess != null && remoteProcess.TargetHostType == AD7Engine.TargetUap) { + // Target is UAP host and we will just assume breakpoint hit count is 1 from this + // remote debug type due to issues with communicating this command + pdwHitCount = 1; + } else { + var task = _breakpoint.GetHitCountAsync(); + pdwHitCount = (uint)task.GetAwaiter().GetResult(); + } + return VSConstants.S_OK; } diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs index 626412d044..73c39f03e6 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs @@ -89,6 +89,12 @@ public sealed class AD7Engine : IDebugEngine2, IDebugEngineLaunch2, IDebugProgra public const string DebugEngineId = "{EC1375B7-E2CE-43E8-BF75-DC638DE1F1F9}"; public const string DebugEngineName = "Python"; public static Guid DebugEngineGuid = new Guid(DebugEngineId); + public const string SourceDirectoryKey = "sd"; + public const string TargetDirectoryKey = "td"; + public const string TargetHostType = "host"; + + public const string TargetUap = "uap"; + /// /// Specifies the version of the language which is being debugged. One of /// V24, V25, V26, V27, V30, V31 or V32. diff --git a/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs b/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs index 8378c6473d..c1e24a034e 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs @@ -13,13 +13,13 @@ * ***************************************************************************/ using System; -using System.Diagnostics; using System.IO; -using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Text; +using System.Web; using System.Windows.Forms; +using Microsoft.PythonTools.Debugger.DebugEngine; using Microsoft.PythonTools.Debugger.Transports; using Microsoft.PythonTools.Parsing; @@ -37,10 +37,27 @@ internal class PythonRemoteProcess : PythonProcess { private PythonRemoteProcess(int pid, Uri uri, PythonLanguageVersion langVer) : base(pid, langVer) { Uri = uri; + ParseQueryString(); } public Uri Uri { get; private set; } + internal string TargetHostType { get; private set; } + + internal void ParseQueryString() { + if (Uri != null && Uri.Query != null) { + var queryParts = HttpUtility.ParseQueryString(Uri.Query); + + var sourceDir = queryParts[AD7Engine.SourceDirectoryKey]; + var targetDir = queryParts[AD7Engine.TargetDirectoryKey]; + + TargetHostType = queryParts[AD7Engine.TargetHostType]; + + if (!string.IsNullOrWhiteSpace(sourceDir) && !string.IsNullOrWhiteSpace(targetDir)) + AddDirMapping(new string[] { sourceDir, targetDir }); + } + } + /// /// Connects to and performs the initial handshake with the remote debugging server, verifying protocol signature and version number, /// and returns the opened stream in a state ready to receive further ptvsd commands (e.g. attach). diff --git a/Python/Product/Debugger/Debugger/PythonProcess.cs b/Python/Product/Debugger/Debugger/PythonProcess.cs index ad7cb7d06c..c1d8bf93dc 100644 --- a/Python/Product/Debugger/Debugger/PythonProcess.cs +++ b/Python/Product/Debugger/Debugger/PythonProcess.cs @@ -62,6 +62,7 @@ class PythonProcess : IDisposable { protected PythonProcess(int pid, PythonLanguageVersion languageVersion) { _pid = pid; _langVersion = languageVersion; + _dirMapping = new List(); } private PythonProcess(int pid) { @@ -185,6 +186,12 @@ public void Dispose() { GC.SuppressFinalize(this); } + internal void AddDirMapping(string[] mapping) { + if (mapping != null) { + _dirMapping.Add(mapping); + } + } + protected virtual void Dispose(bool disposing) { DebugConnectionListener.UnregisterProcess(_processGuid); @@ -627,6 +634,8 @@ private void HandleDebuggerOutput(Stream stream) { long tid = stream.ReadInt64(); string output = stream.ReadString(); + System.Diagnostics.Debug.WriteLine(output); + PythonThread thread; if (_threads.TryGetValue(tid, out thread)) { var outputEvent = DebuggerOutput; @@ -1043,16 +1052,14 @@ internal string MapFile(string file, bool toDebuggee = true) { string mapTo = mappingInfo[toDebuggee ? 1 : 0]; if (file.StartsWith(mapFrom, StringComparison.OrdinalIgnoreCase)) { - if (file.StartsWith(mapFrom, StringComparison.OrdinalIgnoreCase)) { - int len = mapFrom.Length; - if (!mappingInfo[0].EndsWith("\\")) { - len++; - } - - string newFile = Path.Combine(mapTo, file.Substring(len)); - Debug.WriteLine(String.Format("Filename mapped from {0} to {1}", file, newFile)); - return newFile; + int len = mapFrom.Length; + if (!mappingInfo[0].EndsWith("\\")) { + len++; } + + string newFile = Path.Combine(mapTo, file.Substring(len)); + Debug.WriteLine("Filename mapped from {0} to {1}", file, newFile); + return newFile; } } } diff --git a/Python/Product/Debugger/Debugger/PythonStackFrame.cs b/Python/Product/Debugger/Debugger/PythonStackFrame.cs index ceefa0e3f0..b9a40259df 100644 --- a/Python/Product/Debugger/Debugger/PythonStackFrame.cs +++ b/Python/Product/Debugger/Debugger/PythonStackFrame.cs @@ -204,7 +204,7 @@ public bool SetLineNumber(int lineNo) { /// And with the current statement being pass, the qualified name is "D.e in c in A.b". /// public string GetQualifiedFunctionName() { - var ast = _thread.Process.GetAst(_filename); + var ast = _thread.Process.GetAst(FileName); if (ast == null) { return FunctionName; } diff --git a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs index c17f8f1a48..e1fba47486 100644 --- a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs +++ b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs @@ -21,7 +21,7 @@ internal class TcpTransport : IDebuggerTransport { public const ushort DefaultPort = 5678; public Exception Validate(Uri uri) { - if (uri.PathAndQuery != "/") { + if (uri.AbsolutePath != "/") { return new FormatException("tcp:// URI cannot contain a path"); } return null; diff --git a/Python/Product/PythonTools/PythonConstants.cs b/Python/Product/PythonTools/PythonConstants.cs index 8c03ce41cd..825508593e 100644 --- a/Python/Product/PythonTools/PythonConstants.cs +++ b/Python/Product/PythonTools/PythonConstants.cs @@ -40,9 +40,9 @@ public static class PythonConstants { internal const string WebProjectFactoryGuid = "1b580a1a-fdb3-4b32-83e1-6407eb2722e6"; internal const string EditorFactoryGuid = "888888c4-36f9-4453-90aa-29fa4d2e5706"; internal const string ProjectNodeGuid = "8888881a-afb8-42b1-8398-e60d69ee864d"; - internal const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; - internal const string DebugPropertyPageGuid = "9A46BC86-34CB-4597-83E5-498E3BDBA20A"; - internal const string PublishPropertyPageGuid = "63DF0877-CF53-4975-B200-2B11D669AB00"; + public const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; + public const string DebugPropertyPageGuid = "9A46BC86-34CB-4597-83E5-498E3BDBA20A"; + public const string PublishPropertyPageGuid = "63DF0877-CF53-4975-B200-2B11D669AB00"; internal const string WebPropertyPageGuid = "76EED3B5-14B1-413B-937A-F6F79AC1F8C8"; internal const string EditorFactoryPromptForEncodingGuid = "CA887E0B-55C6-4AE9-B5CF-A2EEFBA90A3E"; diff --git a/Python/Product/PythonTools/PythonTools.csproj b/Python/Product/PythonTools/PythonTools.csproj index f99d53b296..3e7ec34a19 100644 --- a/Python/Product/PythonTools/PythonTools.csproj +++ b/Python/Product/PythonTools/PythonTools.csproj @@ -1153,6 +1153,10 @@ true PreserveNewest + + true + PreserveNewest + diff --git a/Python/Product/PythonTools/PythonTools/Extensions.cs b/Python/Product/PythonTools/PythonTools/Extensions.cs index 2347460773..2a783b45b0 100644 --- a/Python/Product/PythonTools/PythonTools/Extensions.cs +++ b/Python/Product/PythonTools/PythonTools/Extensions.cs @@ -28,6 +28,7 @@ using Microsoft.PythonTools.Editor.Core; using Microsoft.PythonTools.Intellisense; using Microsoft.PythonTools.Interpreter; +using Microsoft.PythonTools.InterpreterList; using Microsoft.PythonTools.Parsing.Ast; using Microsoft.PythonTools.Project; using Microsoft.PythonTools.Repl; @@ -43,15 +44,30 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudioTools; +using Microsoft.VisualStudioTools.Project; + +// Disables obsolete warning for ISmartTag* interfaces - http://go.microsoft.com/fwlink/?LinkId=394601 +#pragma warning disable 618 namespace Microsoft.PythonTools { - using Microsoft.PythonTools.InterpreterList; - using Task = System.Threading.Tasks.Task; #if INTERACTIVE_WINDOW using IReplEvaluator = IInteractiveEngine; #endif public static class Extensions { + internal static bool IsAppxPackageableProject(this ProjectNode projectNode) { + var appxProp = projectNode.BuildProject.GetPropertyValue(ProjectFileConstants.AppxPackage); + var containerProp = projectNode.BuildProject.GetPropertyValue(ProjectFileConstants.WindowsAppContainer); + var appxFlag = false; + var containerFlag = false; + + if (bool.TryParse(appxProp, out appxFlag) && bool.TryParse(containerProp, out containerFlag)) { + return appxFlag && containerFlag; + } else { + return false; + } + } + public static StandardGlyphGroup ToGlyphGroup(this PythonMemberType objectType) { StandardGlyphGroup group; switch (objectType) { @@ -702,8 +718,8 @@ internal static T Peek(this List list) { return list[list.Count - 1]; } - internal static Task StartNew(this TaskScheduler scheduler, Action func) { - return Task.Factory.StartNew(func, default(CancellationToken), TaskCreationOptions.None, scheduler); + internal static System.Threading.Tasks.Task StartNew(this TaskScheduler scheduler, Action func) { + return System.Threading.Tasks.Task.Factory.StartNew(func, default(CancellationToken), TaskCreationOptions.None, scheduler); } internal static int GetStartIncludingIndentation(this Node self, PythonAst ast) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs index 3f7bb01d1f..0db9e064df 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs @@ -45,7 +45,8 @@ internal class ImportSettings : DependencyObject { BottleProjectCustomization.Instance, DjangoProjectCustomization.Instance, FlaskProjectCustomization.Instance, - GenericWebProjectCustomization.Instance + GenericWebProjectCustomization.Instance, + UapProjectCustomization.Instance }; public ImportSettings(IInterpreterOptionsService service) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs index b70302caa8..4d527a928a 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs @@ -193,4 +193,28 @@ Dictionary groups project.AddImport(@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets"); } } + + class UapProjectCustomization : ProjectCustomization { + public static readonly ProjectCustomization Instance = new UapProjectCustomization(); + + private UapProjectCustomization() { } + + public override string DisplayName { + get { + return SR.GetString(SR.ImportWizardGenericWebProjectCustomization); + } + } + + public override void Process( + ProjectRootElement project, + Dictionary groups + ) { + ProjectPropertyGroupElement globals; + if (!groups.TryGetValue("Globals", out globals)) { + globals = project.AddPropertyGroup(); + } + + AddOrSetProperty(globals, "ProjectTypeGuids", "{c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e};{888888A0-9F3D-457C-B088-3A5042F75D52}"); + } + } } diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs index 4dcb084e1a..a39467aa4f 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs @@ -21,12 +21,35 @@ namespace Microsoft.PythonTools.Project { class PythonProjectConfig : CommonProjectConfig { private readonly PythonProjectNode _project; + private string _platform; public PythonProjectConfig(PythonProjectNode project, string configuration) : base(project, configuration) { _project = project; } + public override string Platform { + get { + return _platform; + } + set { + _platform = value; + } + } + + /// + /// The display name is a two part item + /// first part is the config name, 2nd part is the platform name + /// + public override int get_DisplayName(out string name) { + if (!string.IsNullOrEmpty(Platform)) { + name = ConfigName + "|" + Platform; + return VSConstants.S_OK; + } else { + return base.get_DisplayName(out name); + } + } + public override int DebugLaunch(uint flags) { if (_project.ShouldWarnOnLaunch) { var pyService = ProjectMgr.Site.GetPythonToolsService(); diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs index f1e1ca851c..737b03adb0 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs @@ -167,6 +167,19 @@ protected override Stream ProjectIconsImageStripStream { } } + protected internal override void SetCurrentConfiguration() { + base.SetCurrentConfiguration(); + + if (!IsProjectOpened) + return; + + if (this.IsAppxPackageableProject()) { + EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project; + + this.BuildProject.SetGlobalProperty(ProjectFileConstants.Platform, automationObject.ConfigurationManager.ActiveConfiguration.PlatformName); + } + } + internal int GetIconIndex(PythonProjectImageName name) { return ImageOffset + (int)name; } @@ -197,6 +210,9 @@ public override CommonFileNode CreateNonCodeFileNode(ProjectElement item) { return new PythonNonCodeFileNode(this, item); } + protected override ConfigProvider CreateConfigProvider() { + return new CommonConfigProvider(this); + } protected override ReferenceContainerNode CreateReferenceContainerNode() { return new PythonReferenceContainerNode(this); } @@ -307,14 +323,29 @@ public override int GenerateUniqueItemName(uint itemIdLoc, string ext, string su return base.GenerateUniqueItemName(itemIdLoc, ext, suggestedRoot, out itemName); } + public override MSBuildResult Build(string config, string target) { + if (this.IsAppxPackageableProject()) { + // Ensure that AnyCPU is not the default Platform if this is an AppX project + // Use x86 instead + var platform = this.BuildProject.GetPropertyValue(GlobalProperty.Platform.ToString()); + + if (platform == ProjectConfig.AnyCPU) { + this.BuildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), ConfigProvider.x86Platform); + } + } + return base.Build(config, target); + } + protected override void Reload() { - _searchPathContainer = new CommonSearchPathContainerNode(this); - this.AddChild(_searchPathContainer); - RefreshCurrentWorkingDirectory(); - RefreshSearchPaths(); - _interpretersContainer = new InterpretersContainerNode(this); - this.AddChild(_interpretersContainer); - RefreshInterpreters(alwaysCollapse: true); + if (!this.IsAppxPackageableProject()) { + _searchPathContainer = new CommonSearchPathContainerNode(this); + this.AddChild(_searchPathContainer); + RefreshCurrentWorkingDirectory(); + RefreshSearchPaths(); + _interpretersContainer = new InterpretersContainerNode(this); + this.AddChild(_interpretersContainer); + RefreshInterpreters(alwaysCollapse: true); + } OnProjectPropertyChanged += PythonProjectNode_OnProjectPropertyChanged; base.Reload(); diff --git a/Python/Product/PythonTools/visualstudio_py_debugger.py b/Python/Product/PythonTools/visualstudio_py_debugger.py index 6804212ff6..a82afbdcc5 100644 --- a/Python/Product/PythonTools/visualstudio_py_debugger.py +++ b/Python/Product/PythonTools/visualstudio_py_debugger.py @@ -28,6 +28,7 @@ from os import path import ntpath import runpy +import datetime from codecs import BOM_UTF8 try: @@ -39,6 +40,7 @@ import visualstudio_py_util as _vspu except ImportError: import ptvsd.visualstudio_py_util as _vspu + to_bytes = _vspu.to_bytes exec_file = _vspu.exec_file exec_module = _vspu.exec_module @@ -207,7 +209,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, tb): send_lock.release() - + # start sending debug events again cur_thread = get_thread_from_id(thread.get_ident()) if cur_thread is not None: @@ -218,7 +220,7 @@ def __exit__(self, exc_type, exc_value, tb): detach_process() # swallow the exception, we're no longer debugging return True - + _SendLockCtx = _SendLockContextManager() SEND_BREAK_COMPLETE = False @@ -309,7 +311,7 @@ def is_repr_round_tripping(x): class StackOverflowException(Exception): pass else: StackOverflowException = RuntimeError - + ASBR = to_bytes('ASBR') SETL = to_bytes('SETL') THRF = to_bytes('THRF') @@ -349,7 +351,7 @@ def should_send_frame(frame): KNOWN_ZIPS = set() def is_file_in_zip(filename): - parent, name = path.split(filename) + parent, name = path.split(path.abspath(filename)) if parent in KNOWN_DIRECTORIES: return False elif parent in KNOWN_ZIPS: @@ -378,7 +380,7 @@ def lookup_local(frame, name): while bits and obj is not None and type(obj) is types.ModuleType: obj = getattr(obj, bits.pop(0), None) return obj - + if sys.version_info[0] >= 3: _EXCEPTIONS_MODULE = 'builtins' else: @@ -448,7 +450,7 @@ def should_break(self, thread, ex_type, ex_value, trace): break_type = BREAK_TYPE_NONE return break_type - + def is_handled(self, thread, ex_type, ex_value, trace): if trace is None: # get out if we didn't get a traceback @@ -459,9 +461,9 @@ def is_handled(self, thread, ex_type, ex_value, trace): # don't break if this is not the top of the traceback, # unless the previous frame was not debuggable return True - + cur_frame = trace.tb_frame - + while should_send_frame(cur_frame) and cur_frame.f_code is not None and cur_frame.f_code.co_filename is not None: filename = path.normcase(cur_frame.f_code.co_filename) if is_file_in_zip(filename): @@ -470,11 +472,11 @@ def is_handled(self, thread, ex_type, ex_value, trace): if not is_same_py_file(filename, __file__): handlers = self.handler_cache.get(filename) - + if handlers is None: # req handlers for this file from the debug engine self.handler_lock.acquire() - + with _SendLockCtx: write_bytes(conn, REQH) write_string(conn, filename) @@ -506,7 +508,7 @@ def is_handled(self, thread, ex_type, ex_value, trace): cur_frame = cur_frame.f_back return False - + def add_exception(self, name, mode=BREAK_MODE_UNHANDLED): if name.startswith(_EXCEPTIONS_MODULE + '.'): name = name[len(_EXCEPTIONS_MODULE) + 1:] @@ -535,7 +537,7 @@ def should_debug_code(code): filename = path.normcase(code.co_filename) if not DEBUG_STDLIB: for prefix in PREFIXES: - if filename.startswith(prefix): + if prefix != '' and filename.startswith(prefix): return False for dont_debug_file in DONT_DEBUG: @@ -558,7 +560,7 @@ def breakpoint_path_match(vs_path, local_path): local_path_norm = path.normcase(local_path) if local_path_to_vs_path.get(local_path_norm) == vs_path_norm: return True - + # Walk the local filesystem from local_path up, matching agains win_path component by component, # and stop when we no longer see an __init__.py. This should give a reasonably close approximation # of matching the package name. @@ -574,7 +576,7 @@ def breakpoint_path_match(vs_path, local_path): # needed to, and matched all names on our way, so this is a match. if not path.exists(path.join(local_path, '__init__.py')): break - + local_path_to_vs_path[local_path_norm] = vs_path_norm return True @@ -582,26 +584,26 @@ def update_all_thread_stacks(blocking_thread = None, check_is_blocked = True): THREADS_LOCK.acquire() all_threads = list(THREADS.values()) THREADS_LOCK.release() - + for cur_thread in all_threads: if cur_thread is blocking_thread: continue - + cur_thread._block_starting_lock.acquire() if not check_is_blocked or not cur_thread._is_blocked: # release the lock, we're going to run user code to evaluate the frames cur_thread._block_starting_lock.release() - + frames = cur_thread.get_frame_list() - + # re-acquire the lock and make sure we're still not blocked. If so send # the frame list. cur_thread._block_starting_lock.acquire() if not check_is_blocked or not cur_thread._is_blocked: cur_thread.send_frame_list(frames) - + cur_thread._block_starting_lock.release() - + DJANGO_BREAKPOINTS = {} class DjangoBreakpointInfo(object): @@ -609,13 +611,13 @@ def __init__(self, filename): self._line_locations = None self.filename = filename self.breakpoints = {} - + def add_breakpoint(self, lineno, brkpt_id): self.breakpoints[lineno] = brkpt_id def remove_breakpoint(self, lineno): del self.breakpoints[lineno] - + @property def line_locations(self): if self._line_locations is None: @@ -727,7 +729,7 @@ def new_f(old_f, args, kwargs): tsk.tempval = new_f stackless.tasklet.setup(tsk, f, args, kwargs) return tsk - + def settrace(tsk, tb): if hasattr(tsk.frame, "f_trace"): tsk.frame.f_trace = tb @@ -738,7 +740,7 @@ def settrace(tsk, tb): stackless.tasklet.__call__ = __call__ if sys.platform == 'cli': self.frames = [] - + if sys.platform == 'cli': # workaround an IronPython bug where we're sometimes missing the back frames # http://ironpython.codeplex.com/workitem/31437 @@ -807,7 +809,7 @@ def trace_func(self, frame, event, arg): except (StackOverflowException, KeyboardInterrupt): # stack overflow, disable tracing return self.trace_func - + def handle_call(self, frame, arg): self.push_frame(frame) @@ -815,7 +817,7 @@ def handle_call(self, frame, arg): source_obj = get_django_frame_source(frame) if source_obj is not None: origin, (start, end) = source_obj - + active_bps = DJANGO_BREAKPOINTS.get(origin.name.lower()) should_break = False if active_bps is not None: @@ -870,7 +872,7 @@ def handle_call(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'call', arg) return self.trace_func - + def should_block_on_frame(self, frame): if not should_debug_code(frame.f_code): return False @@ -906,7 +908,7 @@ def handle_line(self, frame, arg): if self.should_block_on_frame(frame): # don't step complete in our own debugger / non-user code step_complete = True elif stepping == STEPPING_LAUNCH_BREAK or stepping == STEPPING_ATTACH_BREAK: - # If launching rather than attaching, don't break into inital Python code needed to set things up + # If launching rather than attaching, don't break into initial Python code needed to set things up if stepping == STEPPING_LAUNCH_BREAK and (not MODULES or not self.should_block_on_frame(frame)): handle_breakpoints = False else: @@ -991,7 +993,7 @@ def handle_line(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'line', arg) return self.trace_func - + def handle_return(self, frame, arg): self.pop_frame() @@ -1028,7 +1030,7 @@ def handle_return(self, frame, arg): # restore previous frames trace function if there is one if self.trace_func_stack: self.prev_trace_func = self.trace_func_stack.pop() - + def handle_exception(self, frame, arg): if self.stepping == STEPPING_ATTACH_BREAK: self.block_maybe_attach() @@ -1046,15 +1048,15 @@ def handle_exception(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'exception', arg) return self.trace_func - + def handle_c_call(self, frame, arg): # break points? pass - + def handle_c_return(self, frame, arg): # step out of ? pass - + def handle_c_exception(self, frame, arg): pass @@ -1068,7 +1070,7 @@ def block_maybe_attach(self): will_block_now = False attach_sent_break = True attach_lock.release() - + probe_stack() stepping = self.stepping self.stepping = STEPPING_NONE @@ -1084,7 +1086,7 @@ def block_cond(): return report_process_loaded(self.id) update_all_thread_stacks(self) self.block(block_cond) - + def async_break(self): def async_break_send(): with _SendLockCtx: @@ -1110,10 +1112,10 @@ def block(self, block_lambda, keep_stopped_on_line = False): """blocks the current thread until the debugger resumes it""" assert not self._is_blocked #assert self.id == thread.get_ident(), 'wrong thread identity' + str(self.id) + ' ' + str(thread.get_ident()) # we should only ever block ourselves - + # send thread frames before we block self.enum_thread_frames_locally() - + if not keep_stopped_on_line: self.stopped_on_line = self.cur_frame.f_lineno @@ -1133,7 +1135,7 @@ def block(self, block_lambda, keep_stopped_on_line = False): self.unblock_work() self.unblock_work = None self._is_working = False - + self._block_starting_lock.acquire() assert self._is_blocked self._is_blocked = False @@ -1143,7 +1145,7 @@ def unblock(self): """unblocks the current thread allowing it to continue to run""" assert self._is_blocked assert self.id != thread.get_ident() # only someone else should unblock us - + self._block_lock.release() def schedule_work(self, work): @@ -1152,26 +1154,26 @@ def schedule_work(self, work): def run_on_thread(self, text, cur_frame, execution_id, frame_kind, repr_kind = PYTHON_EVALUATION_RESULT_REPR_KIND_NORMAL): self._block_starting_lock.acquire() - + if not self._is_blocked: report_execution_error('', execution_id) elif not self._is_working: self.schedule_work(lambda : self.run_locally(text, cur_frame, execution_id, frame_kind, repr_kind)) else: report_execution_error('', execution_id) - + self._block_starting_lock.release() def run_on_thread_no_report(self, text, cur_frame, frame_kind): self._block_starting_lock.acquire() - + if not self._is_blocked: pass elif not self._is_working: self.schedule_work(lambda : self.run_locally_no_report(text, cur_frame, frame_kind)) else: pass - + self._block_starting_lock.release() def enum_child_on_thread(self, text, cur_frame, execution_id, frame_kind): @@ -1295,7 +1297,7 @@ def enum_child_locally(self, expr, cur_frame, execution_id, frame_kind): break key_repr = safe_repr(key) - + # Some objects are enumerable but not indexable, or repr(key) is not a valid Python expression. For those, we # cannot use obj[key] to get the item by its key, and have to retrieve it by index from enumerate() instead. try: @@ -1326,7 +1328,7 @@ def enum_child_locally(self, expr, cur_frame, execution_id, frame_kind): def get_frame_list(self): frames = [] cur_frame = self.cur_frame - + while should_send_frame(cur_frame): # calculate the ending line number lineno = cur_frame.f_code.co_firstlineno @@ -1373,7 +1375,7 @@ def get_frame_list(self): f_globals = cur_frame.f_globals if f_globals: # ensure globals to work with (IPy may have None for cur_frame.f_globals for frames within stdlib) self.collect_variables(vars, f_globals, cur_frame.f_code.co_names, treated, skip_unknown = True) - + frame_info = None if source_obj is not None: @@ -1415,9 +1417,9 @@ def get_frame_list(self): ) frames.append(frame_info) - + cur_frame = cur_frame.f_back - + return frames def collect_variables(self, vars, objects, names, treated, skip_unknown = False): @@ -1445,7 +1447,7 @@ def send_frame_list(self, frames, thread_name = None): write_bytes(conn, THRF) write_int(conn, self.id) write_string(conn, thread_name) - + # send the frame count write_int(conn, len(frames)) for firstlineno, lineno, curlineno, name, filename, argcount, variables, frameKind, sourceFile, sourceLine in frames: @@ -1453,16 +1455,16 @@ def send_frame_list(self, frames, thread_name = None): write_int(conn, firstlineno) write_int(conn, lineno) write_int(conn, curlineno) - + write_string(conn, name) write_string(conn, filename) write_int(conn, argcount) - + write_int(conn, frameKind) if frameKind == FRAME_KIND_DJANGO: write_string(conn, sourceFile) write_int(conn, sourceLine) - + write_int(conn, len(variables)) for name, type_obj, safe_repr_obj, hex_repr_obj, type_name, obj_len in variables: write_string(conn, name) @@ -1568,7 +1570,7 @@ def loop(self): pass except: traceback.print_exc() - + def command_step_into(self): tid = read_int(self.conn) thread = get_thread_from_id(tid) @@ -1584,7 +1586,7 @@ def command_step_out(self): assert thread._is_blocked thread.stepping = STEPPING_OUT self.command_resume_all() - + def command_step_over(self): # set step over tid = read_int(self.conn) @@ -1624,7 +1626,7 @@ def command_set_breakpoint_condition(self): breakpoint_id = read_int(self.conn) kind = read_int(self.conn) condition = read_string(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) if bp is not None: bp.condition_kind = kind @@ -1643,7 +1645,7 @@ def command_set_breakpoint_pass_count(self): def command_set_breakpoint_hit_count(self): breakpoint_id = read_int(self.conn) count = read_int(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) if bp is not None: bp.hit_count = count @@ -1651,7 +1653,7 @@ def command_set_breakpoint_hit_count(self): def command_get_breakpoint_hit_count(self): req_id = read_int(self.conn) breakpoint_id = read_int(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) count = 0 if bp is not None: @@ -1731,7 +1733,7 @@ def command_resume_all(self): if thread._is_blocked: thread.unblock() thread._block_starting_lock.release() - + def command_resume_thread(self): tid = read_int(self.conn) THREADS_LOCK.acquire() @@ -1844,11 +1846,11 @@ def command_enum_children(self): fid = read_int(self.conn) # frame id eid = read_int(self.conn) # execution id frame_kind = read_int(self.conn) # frame kind - + thread, cur_frame = self.get_thread_and_frame(tid, fid, frame_kind) if thread is not None and cur_frame is not None: thread.enum_child_on_thread(text, cur_frame, eid, frame_kind) - + def get_thread_and_frame(self, tid, fid, frame_kind): thread = get_thread_from_id(tid) cur_frame = None @@ -1877,7 +1879,7 @@ def command_detach(self): for callback in DETACH_CALLBACKS: callback() - + raise DebuggerExitException() def command_last_ack(self): @@ -1923,12 +1925,12 @@ def report_exception(frame, exc_info, tid, break_type): exc_name = get_exception_name(exc_type) exc_value = exc_info[1] tb_value = exc_info[2] - + if type(exc_value) is tuple: # exception object hasn't been created yet, create it now # so we can get the correct msg. exc_value = exc_type(*exc_value) - + excp_text = str(exc_value) with _SendLockCtx: @@ -2184,7 +2186,7 @@ def detach_threads(): THREADS_LOCK.acquire() THREADS.clear() THREADS_LOCK.release() - + BREAKPOINTS.clear() def new_thread(tid = None, set_break = False, frame = None): @@ -2246,11 +2248,11 @@ def __init__(self, old_out, is_stdout): def flush(self): if self.old_out: self.old_out.flush() - + def writelines(self, lines): for line in lines: self.write(line) - + @property def encoding(self): return 'utf8' @@ -2264,13 +2266,13 @@ def write(self, value): write_string(conn, value) if self.old_out: self.old_out.write(value) - + def isatty(self): return True def next(self): pass - + @property def name(self): if self.is_stdout: @@ -2330,8 +2332,8 @@ def print_exception(exc_type, exc_value, exc_tb): if tb: print('Traceback (most recent call last):') for out in traceback.format_list(tb): - sys.stdout.write(out) - + sys.stderr.write(out) + # print the exception for out in traceback.format_exception_only(exc_type, exc_value): sys.stdout.write(out) diff --git a/Python/Product/PythonTools/visualstudio_py_remote_launcher.py b/Python/Product/PythonTools/visualstudio_py_remote_launcher.py new file mode 100644 index 0000000000..1ded935b52 --- /dev/null +++ b/Python/Product/PythonTools/visualstudio_py_remote_launcher.py @@ -0,0 +1,114 @@ + # ############################################################################ + # + # Copyright (c) Microsoft Corporation. + # + # This source code is subject to terms and conditions of the Apache License, Version 2.0. A + # copy of the license can be found in the License.html file at the root of this distribution. If + # you cannot locate the Apache License, Version 2.0, please send an email to + # vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + # by the terms of the Apache License, Version 2.0. + # + # You must not remove this notice, or any other, from this software. + # + # ########################################################################### + +""" +Starts Debugging, expected to start with normal program +to start as first argument and directory to run from as +the second argument. +""" +import os +import sys +import ptvsd +import visualstudio_py_debugger as vspd + +def debug_remote( + file, + port_num, + debug_id, + wait_on_exception, + redirect_output, + wait_on_exit, + break_on_systemexit_zero, + debug_stdlib, + run_as +): + global BREAK_ON_SYSTEMEXIT_ZERO, DEBUG_STDLIB + BREAK_ON_SYSTEMEXIT_ZERO = break_on_systemexit_zero + DEBUG_STDLIB = debug_stdlib + + print('Remote launcher starting ptvsd attach wait with File: %s, Port: %d, Id: %s\n' % (file, port_num, debug_id)) + + ptvsd.enable_attach(debug_id, address = ('0.0.0.0', port_num), redirect_output = redirect_output) + ptvsd.wait_for_attach() + + # now execute main file + globals_obj = {'__name__': '__main__'} + if run_as == 'module': + vspd.exec_module(file, globals_obj) + elif run_as == 'code': + vspd.exec_code(file, '', globals_obj) + else: + vspd.exec_file(file, globals_obj) + +# arguments are port, debug id, normal arguments which should include a filename to execute + +# change to directory we expected to start from +port_num = int(sys.argv[1]) +debug_id = sys.argv[2] + +del sys.argv[0:3] + +wait_on_exception = False +redirect_output = False +wait_on_exit = False +break_on_systemexit_zero = False +debug_stdlib = False +run_as = 'script' + +for opt in [ + # Order is important for these options. + 'redirect_output', + 'wait_on_exception', + 'wait_on_exit', + 'break_on_systemexit_zero', + 'debug_stdlib' +]: + if sys.argv and sys.argv[0] == '--' + opt.replace('_', '-'): + globals()[opt] = True + del sys.argv[0] + +# set run_as mode appropriately +if sys.argv and sys.argv[0] == '-m': + run_as = 'module' + del sys.argv[0] + +if sys.argv and sys.argv[0] == '-c': + run_as = 'code' + del sys.argv[0] + +# preserve filename before we del sys +file = sys.argv[0] + +# fix sys.path to be the script file dir +sys.path[0] = '' + +# exclude ourselves from being debugged +vspd.DONT_DEBUG.append(os.path.normcase(__file__)) + +# remove all state we imported +del sys, os + +import sys + +debug_remote( + file, + port_num, + debug_id, + wait_on_exception, + redirect_output, + wait_on_exit, + break_on_systemexit_zero, + debug_stdlib, + run_as +) diff --git a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs new file mode 100644 index 0000000000..8c68c53ab9 --- /dev/null +++ b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs @@ -0,0 +1,118 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Xml.Linq; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger; +using Microsoft.VisualStudio.Debugger.ComponentInterfaces; +using Microsoft.VisualStudio.Debugger.Exceptions; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Debugger { + internal class PythonRemoteDebugEvents : IVsDebuggerEvents, IDebugEventCallback2, IDkmExceptionTriggerHitNotification { + private static readonly Lazy instance = new Lazy(); + + // This exception code is a Win32 exception that is expected to be thrown by the debug client to trigger this code flow + // i.e. whatever remote process is launching the debug server, should throw and catch this exception code before starting + // remote Python debug server to have automatic attach work + internal const uint RemoteDebugStartExceptionCode = 0xEDCBA987; + + public const string RemoteDebugExceptionId = "E7DD0845-FB1A-4A45-8192-44953C0ACC51"; + public static readonly Guid RemoteDebugExceptionGuid = new Guid(RemoteDebugExceptionId); + + public static PythonRemoteDebugEvents Instance { + get { return instance.Value; } + } + + public Func AttachRemoteProcessFunction { get; set; } + + public string AttachRemoteDebugXml { get; set; } + + public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { + try { + if (riidEvent == typeof(IDebugProgramCreateEvent2).GUID) { + Guid processId; + + // A program was created and attached + if (pProcess != null) { + if (VSConstants.S_OK == pProcess.GetProcessId(out processId)) { + DkmProcess dkmProcess = DkmProcess.FindProcess(processId); + + if (dkmProcess != null) { + var debugTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugStartExceptionCode); + + // Try to add exception trigger for when a remote debugger server is started for Python + dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, debugTrigger); + } + } + } + } + + return VSConstants.S_OK; + } finally { + if (pEngine != null && Marshal.IsComObject(pEngine)) { + Marshal.ReleaseComObject(pEngine); + } + + if (pProcess != null && Marshal.IsComObject(pProcess)) { + Marshal.ReleaseComObject(pProcess); + } + + if (pProgram != null && Marshal.IsComObject(pProgram)) { + Marshal.ReleaseComObject(pProgram); + } + + if (pThread != null && Marshal.IsComObject(pThread)) { + Marshal.ReleaseComObject(pThread); + } + + if (pEvent != null && Marshal.IsComObject(pEvent)) { + Marshal.ReleaseComObject(pEvent); + } + } + } + + void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTriggerHit hit, DkmEventDescriptorS eventDescriptor) { + var remoteProcessTask = default(System.Threading.Tasks.Task); + + using (var evt = new System.Threading.ManualResetEvent(false)) { + + ThreadHelper.Generic.Invoke(() => { + try { + var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; + + // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration + // of the debugger + const int exceptionParameterCount = 2; + + if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { + // If we have a port and debug id, we'll go ahead and tell the client we are present + if (Instance.AttachRemoteProcessFunction != null && Instance.AttachRemoteDebugXml != null) { + // Write back that debugger is present + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); + + // Write back the details of the debugger arguments + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Encoding.Unicode.GetBytes(Instance.AttachRemoteDebugXml)); + } + } + } finally { + evt.Set(); + } + }); + + evt.WaitOne(); + + eventDescriptor.Suppress(); + + // Start the task to attach to the remote Python debugger session + remoteProcessTask = System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); + } + } + + public int OnModeChange(DBGMODE dbgmodeNew) { + return VSConstants.S_OK; + } + } +} diff --git a/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml b/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml new file mode 100644 index 0000000000..1880da70bd --- /dev/null +++ b/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Python/Product/Uap/Extensions.cs b/Python/Product/Uap/Extensions.cs new file mode 100644 index 0000000000..576c1f360d --- /dev/null +++ b/Python/Product/Uap/Extensions.cs @@ -0,0 +1,144 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.PythonTools.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudioTools.Project; +using Microsoft.VisualStudioTools.Project.Automation; + +namespace Microsoft.PythonTools.Uap { + static class Extensions { + + internal static string GetProjectProperty(this IVsHierarchy projectNode, string name) { + return projectNode.GetProject().GetPythonProject().GetProperty(name); + } + + internal static bool IsAppxPackageableProject(this IVsHierarchy projectNode) { + var appxProp = projectNode.GetProjectProperty(ProjectFileConstants.AppxPackage); + var containerProp = projectNode.GetProjectProperty(ProjectFileConstants.WindowsAppContainer); + + return Convert.ToBoolean(appxProp) && Convert.ToBoolean(containerProp); + } + + internal static IPythonProject2 GetPythonProject(this EnvDTE.Project project) { + return project.GetCommonProject() as IPythonProject2; + } + + internal static EnvDTE.Project GetProject(this IVsHierarchy hierarchy) { + object project; + + ErrorHandler.ThrowOnFailure( + hierarchy.GetProperty( + VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ExtObject, + out project + ) + ); + + return (project as EnvDTE.Project); + } + + internal static object GetCommonProject(this EnvDTE.Project project) { + OAProject oaProj = project as OAProject; + if (oaProj != null) { + var common = oaProj.Project; + if (common != null) { + return common; + } + } + return null; + } + + internal static Guid GetItemType(this VSITEMSELECTION vsItemSelection) { + Guid typeGuid; + try { + ErrorHandler.ThrowOnFailure( + vsItemSelection.pHier.GetGuidProperty( + vsItemSelection.itemid, + (int)__VSHPROPID.VSHPROPID_TypeGuid, + out typeGuid + ) + ); + } catch (System.Runtime.InteropServices.COMException) { + return Guid.Empty; + } + return typeGuid; + } + + internal static bool IsFolder(this VSITEMSELECTION item) { + return item.GetItemType() == VSConstants.GUID_ItemType_PhysicalFolder || + item.itemid == VSConstants.VSITEMID_ROOT; + } + + internal static bool IsNonMemberItem(this VSITEMSELECTION item) { + object obj; + try { + ErrorHandler.ThrowOnFailure( + item.pHier.GetProperty( + item.itemid, + (int)__VSHPROPID.VSHPROPID_IsNonMemberItem, + out obj + ) + ); + } catch (System.Runtime.InteropServices.COMException) { + return false; + } + return (obj as bool?) ?? false; + } + + internal static string Name(this VSITEMSELECTION item) { + return item.pHier.GetItemName(item.itemid); + } + + internal static string GetItemName(this IVsHierarchy hier, uint itemid) { + object name; + ErrorHandler.ThrowOnFailure(hier.GetProperty(itemid, (int)__VSHPROPID.VSHPROPID_Name, out name)); + return (string)name; + } + + internal static VSITEMSELECTION GetParent(this VSITEMSELECTION vsItemSelection) { + object parent; + ErrorHandler.ThrowOnFailure( + vsItemSelection.pHier.GetProperty( + vsItemSelection.itemid, + (int)__VSHPROPID.VSHPROPID_Parent, + out parent + ) + ); + + var res = new VSITEMSELECTION(); + var i = parent as int?; + if (i.HasValue) { + res.itemid = (uint)i.GetValueOrDefault(); + } else { + var ip = parent as IntPtr?; + res.itemid = (uint)ip.GetValueOrDefault().ToInt32(); + } + + res.pHier = vsItemSelection.pHier; + return res; + } + + internal static VSITEMSELECTION GetParentFolder(this VSITEMSELECTION vsItemSelection) { + var parent = vsItemSelection.GetParent(); + while (!parent.IsFolder()) { + parent = parent.GetParent(); + } + return parent; + } + } +} diff --git a/Python/Product/Uap/Guids.cs b/Python/Product/Uap/Guids.cs new file mode 100644 index 0000000000..815e0e5dc1 --- /dev/null +++ b/Python/Product/Uap/Guids.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// Guids.cs +// MUST match guids.h +using System; + +namespace Microsoft.PythonTools.Uap { + static class GuidList { + public const string guidUapPkgString = "0a078d3c-15a9-47f5-8418-9ee5db43993d"; + public const string guidUapFactoryString = "c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e"; + public const string guidUapPropertyPageString = "700c8e09-f81c-4fb8-a386-508fb48c372d"; + public static readonly Guid guidOfficeSharePointCmdSet = new Guid("d26c976c-8ee8-4ec4-8746-f5f7702a17c5"); + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs new file mode 100644 index 0000000000..c275773806 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs @@ -0,0 +1,237 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PythonTools.Analysis; +using Microsoft.PythonTools.Interpreter; +using Microsoft.VisualStudioTools; + +namespace Microsoft.PythonTools.Uap.Interpreter { + class PythonUapInterpreter : IPythonInterpreter, IPythonInterpreterWithProjectReferences2, IDisposable { + readonly Version _langVersion; + private PythonInterpreterFactoryWithDatabase _factory; + private PythonTypeDatabase _typeDb; + private HashSet _references; + + public PythonUapInterpreter(PythonInterpreterFactoryWithDatabase factory) { + _langVersion = factory.Configuration.Version; + _factory = factory; + _typeDb = _factory.GetCurrentDatabase(); + _factory.NewDatabaseAvailable += OnNewDatabaseAvailable; + } + + private async void OnNewDatabaseAvailable(object sender, EventArgs e) { + var factory = _factory; + if (factory == null) { + // We have been disposed already, so ignore this event + return; + } + + _typeDb = factory.GetCurrentDatabase(); + + if (_references != null) { + _typeDb = _typeDb.Clone(); + foreach (var reference in _references) { + string modName; + try { + modName = Path.GetFileNameWithoutExtension(reference.Name); + } catch (ArgumentException) { + continue; + } + + await Task.Yield(); + } + } + + var evt = ModuleNamesChanged; + if (evt != null) { + evt(this, EventArgs.Empty); + } + } + + #region IPythonInterpreter Members + + public IPythonType GetBuiltinType(BuiltinTypeId id) { + if (id == BuiltinTypeId.Unknown) { + return null; + } + + if (_typeDb == null) { + throw new KeyNotFoundException(string.Format("{0} ({1})", id, (int)id)); + } + + var name = GetBuiltinTypeName(id, _typeDb.LanguageVersion); + var res = _typeDb.BuiltinModule.GetAnyMember(name) as IPythonType; + if (res == null) { + throw new KeyNotFoundException(string.Format("{0} ({1})", id, (int)id)); + } + return res; + } + + + public IList GetModuleNames() { + if (_typeDb == null) { + return new string[0]; + } + return new List(_typeDb.GetModuleNames()); + } + + public IPythonModule ImportModule(string name) { + if (_typeDb == null) { + return null; + } + return _typeDb.GetModule(name); + } + + public IModuleContext CreateModuleContext() { + return null; + } + + public void Initialize(PythonAnalyzer state) { + } + + public event EventHandler ModuleNamesChanged; + + public Task AddReferenceAsync(ProjectReference reference, CancellationToken cancellationToken = default(CancellationToken)) { + if (reference == null) { + return MakeExceptionTask(new ArgumentNullException("reference")); + } + + if (_references == null) { + _references = new HashSet(); + // If we needed to set _references, then we also need to clone + // _typeDb to avoid adding modules to the shared database. + if (_typeDb != null) { + _typeDb = _typeDb.Clone(); + } + } + + switch (reference.Kind) { + case ProjectReferenceKind.ExtensionModule: + _references.Add(reference); + string filename; + try { + filename = Path.GetFileNameWithoutExtension(reference.Name); + } catch (Exception e) { + return MakeExceptionTask(e); + } + + if (_typeDb != null) { + return Task.Factory.StartNew(EmptyTask); + } + break; + } + + return Task.Factory.StartNew(EmptyTask); + } + + public void RemoveReference(ProjectReference reference) { + switch (reference.Kind) { + case ProjectReferenceKind.ExtensionModule: + if (_references != null && _references.Remove(reference) && _typeDb != null) { + RaiseModulesChanged(null); + } + break; + } + } + + public IEnumerable GetReferences() { + return _references != null ? _references : Enumerable.Empty(); + } + + private static Task MakeExceptionTask(Exception e) { + var res = new TaskCompletionSource(); + res.SetException(e); + return res.Task; + } + + private static void EmptyTask() { + } + + private void RaiseModulesChanged(Task task) { + if (task != null && task.Exception != null) { + throw task.Exception; + } + var modNamesChanged = ModuleNamesChanged; + if (modNamesChanged != null) { + modNamesChanged(this, EventArgs.Empty); + } + } + + public static string GetBuiltinTypeName(BuiltinTypeId id, Version languageVersion) { + string name; + switch (id) { + case BuiltinTypeId.Bool: name = "bool"; break; + case BuiltinTypeId.Complex: name = "complex"; break; + case BuiltinTypeId.Dict: name = "dict"; break; + case BuiltinTypeId.Float: name = "float"; break; + case BuiltinTypeId.Int: name = "int"; break; + case BuiltinTypeId.List: name = "list"; break; + case BuiltinTypeId.Long: name = languageVersion.Major == 3 ? "int" : "long"; break; + case BuiltinTypeId.Object: name = "object"; break; + case BuiltinTypeId.Set: name = "set"; break; + case BuiltinTypeId.Str: name = "str"; break; + case BuiltinTypeId.Unicode: name = languageVersion.Major == 3 ? "str" : "unicode"; break; + case BuiltinTypeId.Bytes: name = languageVersion.Major == 3 ? "bytes" : "str"; break; + case BuiltinTypeId.Tuple: name = "tuple"; break; + case BuiltinTypeId.Type: name = "type"; break; + + case BuiltinTypeId.BuiltinFunction: name = "builtin_function"; break; + case BuiltinTypeId.BuiltinMethodDescriptor: name = "builtin_method_descriptor"; break; + case BuiltinTypeId.DictKeys: name = "dict_keys"; break; + case BuiltinTypeId.DictValues: name = "dict_values"; break; + case BuiltinTypeId.DictItems: name = "dict_items"; break; + case BuiltinTypeId.Function: name = "function"; break; + case BuiltinTypeId.Generator: name = "generator"; break; + case BuiltinTypeId.NoneType: name = "NoneType"; break; + case BuiltinTypeId.Ellipsis: name = "ellipsis"; break; + case BuiltinTypeId.Module: name = "module_type"; break; + case BuiltinTypeId.ListIterator: name = "list_iterator"; break; + case BuiltinTypeId.TupleIterator: name = "tuple_iterator"; break; + case BuiltinTypeId.SetIterator: name = "set_iterator"; break; + case BuiltinTypeId.StrIterator: name = "str_iterator"; break; + case BuiltinTypeId.UnicodeIterator: name = languageVersion.Major == 3 ? "str_iterator" : "unicode_iterator"; break; + case BuiltinTypeId.BytesIterator: name = languageVersion.Major == 3 ? "bytes_iterator" : "str_iterator"; break; + case BuiltinTypeId.CallableIterator: name = "callable_iterator"; break; + + case BuiltinTypeId.Property: name = "property"; break; + case BuiltinTypeId.ClassMethod: name = "classmethod"; break; + case BuiltinTypeId.StaticMethod: name = "staticmethod"; break; + case BuiltinTypeId.FrozenSet: name = "frozenset"; break; + + case BuiltinTypeId.Unknown: + default: + return null; + } + return name; + } + #endregion + + + public void Dispose() { + _typeDb = null; + + var factory = _factory; + _factory = null; + if (factory != null) { + factory.NewDatabaseAvailable -= OnNewDatabaseAvailable; + } + } + } +} diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs new file mode 100644 index 0000000000..c29800ad48 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.PythonTools.Interpreter; + +namespace Microsoft.PythonTools.Uap.Interpreter { + class PythonUapInterpreterFactory : PythonInterpreterFactoryWithDatabase { + public const string InterpreterGuidString = "{86767848-40B4-4007-8BCC-A3835EDF0E69}"; + public static readonly Guid InterpreterGuid = new Guid(InterpreterGuidString); + + public PythonUapInterpreterFactory() + : base( + InterpreterGuid, + "Python UAP", + new InterpreterConfiguration(new Version(3, 5)), + true) { + } + + public override string Description { + get { + return base.Description; + } + } + public override IPythonInterpreter MakeInterpreter(PythonInterpreterFactoryWithDatabase factory) { + return new PythonUapInterpreter(factory); + } + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs new file mode 100644 index 0000000000..aca47017f9 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.PythonTools.Interpreter; + +namespace Microsoft.PythonTools.Uap.Interpreter { + [Export(typeof(IPythonInterpreterFactoryProvider))] + class PythonUapInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { + private HashSet _factories = null; + + public event EventHandler InterpreterFactoriesChanged; + + public PythonUapInterpreterFactoryProvider() { + } + + private void DiscoverFactories() { + if (_factories == null) { + _factories = new HashSet(); + + _factories.Add(new PythonUapInterpreterFactory()); + + if (InterpreterFactoriesChanged != null) { + InterpreterFactoriesChanged(this, new EventArgs()); + } + } + } + + public IEnumerable GetInterpreterFactories() { + DiscoverFactories(); + return _factories; + } + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Microsoft.PythonTools.Uap.targets b/Python/Product/Uap/Microsoft.PythonTools.Uap.targets new file mode 100644 index 0000000000..5480c6c45d --- /dev/null +++ b/Python/Product/Uap/Microsoft.PythonTools.Uap.targets @@ -0,0 +1,66 @@ + + + + + + ARM,x86,x64 + $(MSBuildProjectDirectory) + $([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(ProjectHome))))) + $(QualifiedProjectHome)\ + + + + + BuiltProjectOutputGroupFast; + $(BuiltProjectOutputGroupDependsOn) + + + + + + + <_BuiltProjectOutputGroupFastOutput Remove="@(_BuiltProjectOutputGroupFastOutput)" /> + + + + + + + + + + + + + + + + + + + + + + <_SourceFilesProjectOutputGroupOutput Remove="@(_SourceFilesProjectOutputGroupOutput)" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/Project/PythonUapProject.cs b/Python/Product/Uap/Project/PythonUapProject.cs new file mode 100644 index 0000000000..b09136470f --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProject.cs @@ -0,0 +1,299 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Flavor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudioTools; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid("27BB1268-135A-4409-914F-7AA64AD8195D")] + partial class PythonUapProject : + FlavoredProjectBase, + IOleCommandTarget, + IVsProjectFlavorCfgProvider, + IVsProject, + IVsFilterAddProjectItemDlg { + private PythonUapPackage _package; + internal IVsProject _innerProject; + internal IVsProject3 _innerProject3; + private IVsProjectFlavorCfgProvider _innerVsProjectFlavorCfgProvider; + private static Guid PythonProjectGuid = new Guid(PythonConstants.ProjectFactoryGuid); + private IOleCommandTarget _menuService; + + public PythonUapProject() { + } + + internal PythonUapPackage Package { + get { return _package; } + set { + Debug.Assert(_package == null); + if (_package != null) { + throw new InvalidOperationException("PythonUapProject.Package must only be set once"); + } + _package = value; + } + } + + #region IVsAggregatableProject + + /// + /// Do the initialization here (such as loading flavor specific + /// information from the project) + /// + protected override void InitializeForOuter(string fileName, string location, string name, uint flags, ref Guid guidProject, out bool cancel) { + base.InitializeForOuter(fileName, location, name, flags, ref guidProject, out cancel); + } + + #endregion + + protected override int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds, VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText) { + if (pguidCmdGroup == GuidList.guidOfficeSharePointCmdSet) { + for (int i = 0; i < prgCmds.Length; i++) { + // Report it as supported so that it's not routed any + // further, but disable it and make it invisible. + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_INVISIBLE); + } + return VSConstants.S_OK; + } + + return base.QueryStatusCommand(itemid, ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + protected override void SetInnerProject(IntPtr innerIUnknown) { + var inner = Marshal.GetObjectForIUnknown(innerIUnknown); + + // The reason why we keep a reference to those is that doing a QI after being + // aggregated would do the AddRef on the outer object. + _innerVsProjectFlavorCfgProvider = inner as IVsProjectFlavorCfgProvider; + _innerProject = inner as IVsProject; + _innerProject3 = inner as IVsProject3; + _innerVsHierarchy = inner as IVsHierarchy; + + // Ensure we have a service provider as this is required for menu items to work + if (this.serviceProvider == null) { + this.serviceProvider = (IServiceProvider)Package; + } + + // Now let the base implementation set the inner object + base.SetInnerProject(innerIUnknown); + + // Get access to the menu service used by FlavoredProjectBase. We + // need to forward IOleCommandTarget functions to this object, since + // we override the FlavoredProjectBase implementation with no way to + // call it directory. + // (This must run after we called base.SetInnerProject) + _menuService = (IOleCommandTarget)((IServiceProvider)this).GetService(typeof(IMenuCommandService)); + if (_menuService == null) { + throw new InvalidOperationException("Cannot initialize Uap project"); + } + } + + protected override int GetProperty(uint itemId, int propId, out object property) { + switch ((__VSHPROPID2)propId) { + case __VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList: + { + var res = base.GetProperty(itemId, propId, out property); + if (ErrorHandler.Succeeded(res)) { + var guids = GetGuidsFromList(property as string); + guids.RemoveAll(g => CfgSpecificPropertyPagesToRemove.Contains(g)); + guids.AddRange(CfgSpecificPropertyPagesToAdd); + property = MakeListFromGuids(guids); + } + return res; + } + case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList: + { + var res = base.GetProperty(itemId, propId, out property); + if (ErrorHandler.Succeeded(res)) { + var guids = GetGuidsFromList(property as string); + guids.RemoveAll(g => PropertyPagesToRemove.Contains(g)); + guids.AddRange(PropertyPagesToAdd); + property = MakeListFromGuids(guids); + } + return res; + } + } + + switch ((__VSHPROPID6)propId) { + case __VSHPROPID6.VSHPROPID_Subcaption: + { + var bps = this._innerProject as IVsBuildPropertyStorage; + string descriptor = null; + + if (bps != null) { + var res = bps.GetPropertyValue("TargetOsAndVersion", null, (uint)_PersistStorageType.PST_PROJECT_FILE, out descriptor); + property = descriptor; + return res; + } + break; + } + } + + return base.GetProperty(itemId, propId, out property); + } + + private static Guid[] PropertyPagesToAdd = new Guid[0]; + + private static Guid[] CfgSpecificPropertyPagesToAdd = new Guid[] { + new Guid(GuidList.guidUapPropertyPageString) + }; + + private static HashSet PropertyPagesToRemove = new HashSet { + new Guid("{8C0201FE-8ECA-403C-92A3-1BC55F031979}"), // typeof(DeployPropertyPageComClass) + new Guid("{ED3B544C-26D8-4348-877B-A1F7BD505ED9}"), // typeof(DatabaseDeployPropertyPageComClass) + new Guid("{909D16B3-C8E8-43D1-A2B8-26EA0D4B6B57}"), // Microsoft.VisualStudio.Web.Application.WebPropertyPage + new Guid("{379354F2-BBB3-4BA9-AA71-FBE7B0E5EA94}"), // Microsoft.VisualStudio.Web.Application.SilverlightLinksPage + new Guid("{A553AD0B-2F9E-4BCE-95B3-9A1F7074BC27}"), // Package/Publish Web + new Guid("{9AB2347D-948D-4CD2-8DBE-F15F0EF78ED3}"), // Package/Publish SQL + new Guid(PythonConstants.DebugPropertyPageGuid), + new Guid(PythonConstants.GeneralPropertyPageGuid), + new Guid(PythonConstants.PublishPropertyPageGuid) + }; + + internal static HashSet CfgSpecificPropertyPagesToRemove = new HashSet(new Guid[] { Guid.Empty }); + + private static List GetGuidsFromList(string guidList) { + if (string.IsNullOrEmpty(guidList)) { + return new List(); + } + + Guid value; + return guidList.Split(';') + .Select(str => Guid.TryParse(str, out value) ? (Guid?)value : null) + .Where(g => g.HasValue) + .Select(g => g.Value) + .ToList(); + } + + private static string MakeListFromGuids(IEnumerable guidList) { + return string.Join(";", guidList.Select(g => g.ToString("B"))); + } + + internal string RemovePropertyPagesFromList(string propertyPagesList, string[] pagesToRemove) { + if (pagesToRemove == null || !pagesToRemove.Any()) { + return propertyPagesList; + } + + var guidsToRemove = new HashSet( + pagesToRemove.Select(str => { Guid guid; return Guid.TryParse(str, out guid) ? guid : Guid.Empty; }) + ); + guidsToRemove.Add(Guid.Empty); + + return string.Join( + ";", + propertyPagesList.Split(';') + .Where(str => !string.IsNullOrEmpty(str)) + .Select(str => { Guid guid; return Guid.TryParse(str, out guid) ? guid : Guid.Empty; }) + .Except(guidsToRemove) + .Select(guid => guid.ToString("B")) + ); + } + + int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + return _menuService.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + return _menuService.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #region IVsProjectFlavorCfgProvider Members + + public int CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg) { + // We're flavored with a Windows Store Application project and our normal + // project... But we don't want the web application project to + // influence our config as that alters our debug launch story. We + // control that w/ the web project which is actually just letting + // the base Python project handle it. So we keep the base Python + // project config here. + IVsProjectFlavorCfg uapCfg; + ErrorHandler.ThrowOnFailure( + _innerVsProjectFlavorCfgProvider.CreateProjectFlavorCfg( + pBaseProjectCfg, + out uapCfg + ) + ); + ppFlavorCfg = new PythonUapProjectConfig(pBaseProjectCfg, uapCfg); + return VSConstants.S_OK; + } + + #endregion + + #region IVsProject Members + + int IVsProject.AddItem(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, VSADDRESULT[] pResult) { + return _innerProject.AddItem(itemidLoc, dwAddItemOperation, pszItemName, cFilesToOpen, rgpszFilesToOpen, hwndDlgOwner, pResult); + } + + int IVsProject.GenerateUniqueItemName(uint itemidLoc, string pszExt, string pszSuggestedRoot, out string pbstrItemName) { + return _innerProject.GenerateUniqueItemName(itemidLoc, pszExt, pszSuggestedRoot, out pbstrItemName); + } + + int IVsProject.GetItemContext(uint itemid, out VisualStudio.OLE.Interop.IServiceProvider ppSP) { + return _innerProject.GetItemContext(itemid, out ppSP); + } + + int IVsProject.GetMkDocument(uint itemid, out string pbstrMkDocument) { + return _innerProject.GetMkDocument(itemid, out pbstrMkDocument); + } + + int IVsProject.IsDocumentInProject(string pszMkDocument, out int pfFound, VSDOCUMENTPRIORITY[] pdwPriority, out uint pitemid) { + return _innerProject.IsDocumentInProject(pszMkDocument, out pfFound, pdwPriority, out pitemid); + } + + int IVsProject.OpenItem(uint itemid, ref Guid rguidLogicalView, IntPtr punkDocDataExisting, out IVsWindowFrame ppWindowFrame) { + return _innerProject.OpenItem(itemid, rguidLogicalView, punkDocDataExisting, out ppWindowFrame); + } + + #endregion + + #region IVsFilterAddProjectItemDlg Members + + int IVsFilterAddProjectItemDlg.FilterListItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterListItemByTemplateFile(ref Guid rguidProjectItemTemplates, string pszTemplateFile, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterTreeItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterTreeItemByTemplateDir(ref Guid rguidProjectItemTemplates, string pszTemplateDir, out int pfFilter) { + // https://pytools.codeplex.com/workitem/1313 + // ASP.NET will filter some things out, including .css files, which we don't want it to do. + // So we shut that down by not forwarding this to any inner projects, which is fine, because + // Python projects don't implement this interface either. + pfFilter = 0; + return VSConstants.S_OK; + } + + #endregion + } +} diff --git a/Python/Product/Uap/Project/PythonUapProjectConfig.cs b/Python/Product/Uap/Project/PythonUapProjectConfig.cs new file mode 100644 index 0000000000..583c6ad352 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProjectConfig.cs @@ -0,0 +1,961 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Web; +using System.Windows.Forms; +using System.Xml.Linq; +using Microsoft.PythonTools.Debugger.DebugEngine; +using Microsoft.PythonTools.Debugger.Remote; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Project { + /// + /// Merges the PTVS IVsCfg object with the Venus IVsCfg implementation redirecting + /// things appropriately to either one. + /// + class PythonUapProjectConfig : + IVsCfg, + IVsProjectCfg, + IVsProjectCfg2, + IVsProjectFlavorCfg, + IVsDebuggableProjectCfg, + ISpecifyPropertyPages, + IVsSpecifyProjectDesignerPages, + IVsCfgBrowseObject, + IVsDeployableProjectCfg, + IVsProjectCfgDebugTargetSelection, + IVsQueryDebuggableProjectCfg, + IVsQueryDebuggableProjectCfg2, + IVsAppContainerProjectDeployCallback { + private readonly IVsCfg _pythonCfg; + private readonly IVsProjectFlavorCfg _uapCfg; + private readonly object syncObject = new object(); + private EventSinkCollection deployCallbackCollection = new EventSinkCollection(); + private IVsAppContainerProjectDeployOperation deployOp; + private IVsTask appContainerBootstrapperOperation; + private IVsOutputWindowPane outputWindow = null; + private IVsDebuggerDeployConnection connection = null; + private string deployPackageMoniker; + private string deployAppUserModelID; + + private const string RemoteTarget = "Remote Machine"; + + public PythonUapProjectConfig(IVsCfg pythonCfg, IVsProjectFlavorCfg uapConfig) { + _pythonCfg = pythonCfg; + _uapCfg = uapConfig; + } + + internal IVsHierarchy PythonConfig { + get { + IVsHierarchy proj = null; + + var browseObj = _pythonCfg as IVsCfgBrowseObject; + + if (browseObj != null) { + uint itemId = 0; + + browseObj.GetProjectItem(out proj, out itemId); + } + return proj; + } + } + + internal string LayoutDir { + get; private set; + } + + internal string DeployPackageMoniker { + get { return this.deployPackageMoniker; } + } + + internal string DeployAppUserModelID { + get { return this.deployAppUserModelID; } + } + + private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remoteMachine, string secret, string port, string sourceDir, string targetDir) { + bool attached = false; + bool stoppedDebugging = false; + const int attachRetryLimit = 10; + int attachRetryCount = 0; + + var qualifierString = string.Format( + "tcp://{0}@{1}:{2}?{3}={4}&{5}={6}&{7}={8}", + secret, + remoteMachine, + port, + AD7Engine.SourceDirectoryKey, + HttpUtility.UrlEncode(sourceDir), + AD7Engine.TargetDirectoryKey, + HttpUtility.UrlEncode(targetDir), + AD7Engine.TargetHostType, + HttpUtility.UrlEncode(AD7Engine.TargetUap)); + + var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); + var debugger = (EnvDTE90.Debugger3)dte.Debugger; + + var transport = default(EnvDTE80.Transport); + var transports = debugger.Transports; + + for (int i = 1; i <= transports.Count; ++i) { + var t = transports.Item(i); + Guid tid; + if (Guid.TryParse(t.ID, out tid) && tid == PythonRemoteDebugPortSupplier.PortSupplierGuid) { + transport = t; + } + } + + System.Diagnostics.Debug.Assert(transport != null, "Python remote debugging transport is missing."); + + if (transport == null) { + return; + } + + while (!attached && attachRetryCount++ < attachRetryLimit) { + try { + if (debugger.DebuggedProcesses == null || debugger.DebuggedProcesses.Count == 0) { + // We are no longer debugging, so just bail + stoppedDebugging = true; + break; + } else if (debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode) { + attachRetryCount--; + await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)); + continue; + } + + var processes = debugger.GetProcesses(transport, qualifierString); + + if (processes.Count > 0) { + foreach (EnvDTE.Process process in processes) { + process.Attach(); + attached = true; + System.Diagnostics.Debug.WriteLine("Successfully attached to a python remote process"); + break; + } + + if (attached) { + IVsDebugger vsDebugger = Package.GetGlobalService(typeof(SVsShellDebugger)) as IVsDebugger; + + if (vsDebugger != null) { + vsDebugger.UnadviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); + } + + break; + } + } + } catch (COMException comException) { + // In the case where the debug client does not setup the PTVSD server in time for the Attach to work, we will + // get this exception. We retry a few times to ensure client has time to start the Python debug server + System.Diagnostics.Debug.WriteLine("Failure during attach to remote Python process:\r\n{0}", comException); + } + + await System.Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(100)); + } + + if (!attached && !stoppedDebugging) { + MessageBox.Show("Could not attach to remote Python debug session.", null, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + #region IVsCfg Members + + public int get_DisplayName(out string pbstrDisplayName) { + int ret = _pythonCfg.get_DisplayName(out pbstrDisplayName); + + return ret; + } + + public int get_IsDebugOnly(out int pfIsDebugOnly) { + return _pythonCfg.get_IsDebugOnly(out pfIsDebugOnly); + } + + public int get_IsReleaseOnly(out int pfIsReleaseOnly) { + return _pythonCfg.get_IsReleaseOnly(out pfIsReleaseOnly); + } + + #endregion + + #region IVsProjectCfg Members + + public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.EnumOutputs(out ppIVsEnumOutputs); + } + ppIVsEnumOutputs = null; + return VSConstants.E_NOTIMPL; + } + + public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.OpenOutput(szOutputCanonicalName, out ppIVsOutput); + } + ppIVsOutput = null; + return VSConstants.E_NOTIMPL; + } + + public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProjectCfg) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_BuildableProjectCfg(out ppIVsBuildableProjectCfg); + } + ppIVsBuildableProjectCfg = null; + return VSConstants.E_NOTIMPL; + } + + public int get_CanonicalName(out string pbstrCanonicalName) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_CanonicalName(out pbstrCanonicalName); + } + pbstrCanonicalName = null; + return VSConstants.E_NOTIMPL; + } + + public int get_IsPackaged(out int pfIsPackaged) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_IsPackaged(out pfIsPackaged); + } + pfIsPackaged = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_IsSpecifyingOutputSupported(out pfIsSpecifyingOutputSupported); + } + pfIsSpecifyingOutputSupported = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_Platform(out Guid pguidPlatform) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_Platform(out pguidPlatform); + } + pguidPlatform = Guid.Empty; + return VSConstants.E_NOTIMPL; + } + + public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvider) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_ProjectCfgProvider(out ppIVsProjectCfgProvider); + } + ppIVsProjectCfgProvider = null; + return VSConstants.E_NOTIMPL; + } + + public int get_RootURL(out string pbstrRootURL) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_RootURL(out pbstrRootURL); + } + pbstrRootURL = null; + return VSConstants.E_NOTIMPL; + } + + public int get_TargetCodePage(out uint puiTargetCodePage) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_TargetCodePage(out puiTargetCodePage); + } + puiTargetCodePage = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_UpdateSequenceNumber(ULARGE_INTEGER[] puliUSN) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_UpdateSequenceNumber(puliUSN); + } + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectCfg2 Members + + public int OpenOutputGroup(string szCanonicalName, out IVsOutputGroup ppIVsOutputGroup) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.OpenOutputGroup(szCanonicalName, out ppIVsOutputGroup); + } + ppIVsOutputGroup = null; + return VSConstants.E_NOTIMPL; + } + + public int OutputsRequireAppRoot(out int pfRequiresAppRoot) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.OutputsRequireAppRoot(out pfRequiresAppRoot); + } + pfRequiresAppRoot = 1; + return VSConstants.E_NOTIMPL; + } + + public int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) { + if (iidCfg == typeof(IVsDebuggableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg)); + return VSConstants.S_OK; + } + + if (iidCfg == typeof(IVsDeployableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDeployableProjectCfg)); + return VSConstants.S_OK; + } + + if (iidCfg == typeof(IVsQueryDebuggableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsQueryDebuggableProjectCfg)); + return VSConstants.S_OK; + } + + var projCfg = _uapCfg as IVsProjectFlavorCfg; + if (projCfg != null) { + return projCfg.get_CfgType(ref iidCfg, out ppCfg); + } + ppCfg = IntPtr.Zero; + return VSConstants.E_NOTIMPL; + } + + public int get_IsPrivate(out int pfPrivate) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_IsPrivate(out pfPrivate); + } + pfPrivate = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_OutputGroups(uint celt, IVsOutputGroup[] rgpcfg, uint[] pcActual = null) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_OutputGroups(celt, rgpcfg, pcActual); + } + return VSConstants.E_NOTIMPL; + } + + public int get_VirtualRoot(out string pbstrVRoot) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_VirtualRoot(out pbstrVRoot); + } + pbstrVRoot = null; + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectFlavorCfg Members + + public int Close() { + IVsProjectFlavorCfg cfg = _uapCfg as IVsProjectFlavorCfg; + if (cfg != null) { + return cfg.Close(); + } + return VSConstants.S_OK; + } + + #endregion + + #region IVsDebuggableProjectCfg Members + + public int DebugLaunch(uint grfLaunch) { + if (PythonConfig.IsAppxPackageableProject()) { + VsDebugTargetInfo2[] targets; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) + return hr; + + VsDebugTargetInfo4[] appPackageDebugTarget = new VsDebugTargetInfo4[1]; + int targetLength = (int)Marshal.SizeOf(typeof(VsDebugTargetInfo4)); + + // Setup the app-specific parameters + appPackageDebugTarget[0].AppPackageLaunchInfo.AppUserModelID = DeployAppUserModelID; + appPackageDebugTarget[0].AppPackageLaunchInfo.PackageMoniker = DeployPackageMoniker; + appPackageDebugTarget[0].AppPackageLaunchInfo.AppPlatform = VsAppPackagePlatform.APPPLAT_WindowsAppx; + +#if DEV14_OR_LATER + // Check if this project contains a startup task and set launch flag appropriately + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)this.PythonConfig; + string canonicalName; + string containsStartupTaskValue = null; + bool containsStartupTask = false; + + get_CanonicalName(out canonicalName); + + bps.GetPropertyValue("ContainsStartupTask", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out containsStartupTaskValue); + + if (containsStartupTaskValue != null && bool.TryParse(containsStartupTaskValue, out containsStartupTask) && containsStartupTask) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS140.DBGLAUNCH_ContainsStartupTask; + } +#endif + + appPackageDebugTarget[0].dlo = (uint)_DEBUG_LAUNCH_OPERATION4.DLO_AppPackageDebug; + appPackageDebugTarget[0].LaunchFlags = grfLaunch; + appPackageDebugTarget[0].bstrRemoteMachine = targets[0].bstrRemoteMachine; + appPackageDebugTarget[0].bstrExe = targets[0].bstrExe; + appPackageDebugTarget[0].bstrArg = targets[0].bstrArg; + appPackageDebugTarget[0].bstrCurDir = targets[0].bstrCurDir; + appPackageDebugTarget[0].bstrEnv = targets[0].bstrEnv; + appPackageDebugTarget[0].dwProcessId = targets[0].dwProcessId; + appPackageDebugTarget[0].pStartupInfo = IntPtr.Zero; + appPackageDebugTarget[0].guidLaunchDebugEngine = targets[0].guidLaunchDebugEngine; + appPackageDebugTarget[0].dwDebugEngineCount = targets[0].dwDebugEngineCount; + appPackageDebugTarget[0].pDebugEngines = targets[0].pDebugEngines; + appPackageDebugTarget[0].guidPortSupplier = targets[0].guidPortSupplier; + + appPackageDebugTarget[0].bstrPortName = targets[0].bstrPortName; + appPackageDebugTarget[0].bstrOptions = targets[0].bstrOptions; + appPackageDebugTarget[0].fSendToOutputWindow = targets[0].fSendToOutputWindow; + appPackageDebugTarget[0].pUnknown = targets[0].pUnknown; + appPackageDebugTarget[0].guidProcessLanguage = targets[0].guidProcessLanguage; + appPackageDebugTarget[0].project = PythonConfig; + + // Pass the debug launch targets to the debugger + IVsDebugger4 debugger4 = (IVsDebugger4)Package.GetGlobalService(typeof(SVsShellDebugger)); + + VsDebugTargetProcessInfo[] results = new VsDebugTargetProcessInfo[1]; + + IVsDebugger debugger = (IVsDebugger)debugger4; + + // Launch task to monitor to attach to Python remote process + var sourceDir = System.IO.Path.GetFullPath(PythonConfig.GetProjectProperty("ProjectDir")).Trim('\\'); + var targetDir = System.IO.Path.GetFullPath(this.LayoutDir).Trim('\\'); + var debugPort = "5678"; + var debugId = Guid.NewGuid(); + var debugXml = new XDocument(new XElement("dbg", + new XElement("arg", @"visualstudio_py_remote_launcher.py"), + new XElement("arg", debugPort), + new XElement("arg", debugId), + new XElement("arg", "--redirect-output"))); + + Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteDebugXml = debugXml.ToString(); + Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteProcessFunction = () => { + return RemoteProcessAttachAsync( + appPackageDebugTarget[0].bstrRemoteMachine, + debugId.ToString(), + debugPort, + sourceDir, + targetDir); + }; + + int result = debugger.AdviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); + System.Diagnostics.Debug.Assert(result == VSConstants.S_OK, string.Format(System.Globalization.CultureInfo.CurrentCulture, "Failure {0}", result)); + + debugger4.LaunchDebugTargets4(1, appPackageDebugTarget, results); + + return VSConstants.S_OK; + } else { + IVsDebuggableProjectCfg cfg = _pythonCfg as IVsDebuggableProjectCfg; + if (cfg != null) { + return cfg.DebugLaunch(grfLaunch); + } + return VSConstants.E_NOTIMPL; + } + } + + public int QueryDebugLaunch(uint grfLaunch, out int pfCanLaunch) { + pfCanLaunch = this.PythonConfig.IsAppxPackageableProject() ? 1 : 0; + return VSConstants.S_OK; + } + + #endregion + + #region ISpecifyPropertyPages Members + + public void GetPages(CAUUID[] pPages) { + var cfg = _pythonCfg as ISpecifyPropertyPages; + if (cfg != null) { + cfg.GetPages(pPages); + } + } + + #endregion + + #region IVsSpecifyProjectDesignerPages Members + + public int GetProjectDesignerPages(CAUUID[] pPages) { + var cfg = _pythonCfg as IVsSpecifyProjectDesignerPages; + if (cfg != null) { + return cfg.GetProjectDesignerPages(pPages); + } + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsCfgBrowseObject Members + + public int GetCfg(out IVsCfg ppCfg) { + ppCfg = this; + return VSConstants.S_OK; + } + + public int GetProjectItem(out IVsHierarchy pHier, out uint pItemid) { + var cfg = _pythonCfg as IVsCfgBrowseObject; + if (cfg != null) { + return cfg.GetProjectItem(out pHier, out pItemid); + } + pHier = null; + pItemid = 0; + return VSConstants.E_NOTIMPL; + } + + #endregion + + + #region IVsDeployableProjectCfg Members + + public int AdviseDeployStatusCallback(IVsDeployStatusCallback pIVsDeployStatusCallback, out uint pdwCookie) { + if (pIVsDeployStatusCallback == null) { + pdwCookie = 0; + return VSConstants.E_UNEXPECTED; + } + + lock (syncObject) { + pdwCookie = deployCallbackCollection.Add(pIVsDeployStatusCallback); + } + + return VSConstants.S_OK; + } + + public int Commit(uint dwReserved) { + return VSConstants.S_OK; + } + + public int QueryStartDeploy(uint dwOptions, int[] pfSupported, int[] pfReady) { + if (pfSupported.Length > 0) { + // Only Appx package producing appcontainer projects should support deployment + pfSupported[0] = 1; + } + + if (pfReady.Length > 0) { + lock (syncObject) { + pfReady[0] = (deployOp == null && appContainerBootstrapperOperation == null) ? 1 : 0; + } + } + + return VSConstants.S_OK; + } + + public int QueryStatusDeploy(out int pfDeployDone) { + lock (syncObject) { + pfDeployDone = (deployOp == null && appContainerBootstrapperOperation == null) ? 1 : 0; + } + + return VSConstants.S_OK; + } + + public int Rollback(uint dwReserved) { + return VSConstants.S_OK; + } + + public int StartDeploy(IVsOutputWindowPane pIVsOutputWindowPane, uint dwOptions) { + int continueOn = 1; + + outputWindow = pIVsOutputWindowPane; + + if (connection != null) { + lock (syncObject) { + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + + // Loop through deploy status callbacks + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + if (ErrorHandler.Failed(callback.OnStartDeploy(ref continueOn))) { + continueOn = 0; + } + + if (continueOn == 0) { + outputWindow = null; + return VSConstants.E_ABORT; + } + } + + try { + VsDebugTargetInfo2[] targets; + uint deployFlags = (uint)(_AppContainerDeployOptions.ACDO_NetworkLoopbackEnable | _AppContainerDeployOptions.ACDO_SetNetworkLoopback | _AppContainerDeployOptions.ACDO_ForceCleanLayout); + string recipeFile = null; + string layoutDir = null; + var pythonProject = PythonConfig; + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)pythonProject; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) { + NotifyEndDeploy(0); + return hr; + } + + string canonicalName; + + get_CanonicalName(out canonicalName); + + bps.GetPropertyValue("AppxPackageRecipe", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out recipeFile); + bps.GetPropertyValue("LayoutDir", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out layoutDir); + + string projectUniqueName = null; + IVsSolution vsSolution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution; + if (vsSolution != null) { + hr = vsSolution.GetUniqueNameOfProject(pythonProject, out projectUniqueName); + } + + IVsAppContainerProjectDeploy deployHelper = (IVsAppContainerProjectDeploy)Package.GetGlobalService(typeof(SVsAppContainerProjectDeploy)); + if (String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + deployOp = deployHelper.StartDeployAsync(deployFlags, recipeFile, layoutDir, projectUniqueName, this); + } else { + IVsDebuggerDeploy deploy = (IVsDebuggerDeploy)Package.GetGlobalService(typeof(SVsShellDebugger)); + IVsDebuggerDeployConnection deployConnection; + + hr = deploy.ConnectToTargetComputer(targets[0].bstrRemoteMachine, VsDebugRemoteAuthenticationMode.VSAUTHMODE_None, out deployConnection); + if (ErrorHandler.Failed(hr)) { + NotifyEndDeploy(0); + return hr; + } + + connection = deployConnection; + + deployOp = deployHelper.StartRemoteDeployAsync(deployFlags, connection, recipeFile, projectUniqueName, this); + } + } catch (Exception) { + if (connection != null) { + lock (syncObject) { + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + connection = null; + + NotifyEndDeploy(0); + + // Rethrow exception + throw; + } + + return VSConstants.S_OK; + } + + public int StopDeploy(int fSync) { + IVsTask bootstrapOp = null; + IVsAppContainerProjectDeployOperation deployOp = null; + int result = VSConstants.S_OK; + + lock (syncObject) { + bootstrapOp = appContainerBootstrapperOperation; + deployOp = this.deployOp; + appContainerBootstrapperOperation = null; + this.deployOp = null; + } + + if (bootstrapOp != null) { + bootstrapOp.Cancel(); + if (fSync != 0) { + try { + bootstrapOp.Wait(); + } catch (Exception e) { + if (outputWindow != null) { + outputWindow.OutputString(e.ToString()); + } + result = VSConstants.E_FAIL; + } + } + } + + if (deployOp != null) { + deployOp.StopDeploy(fSync != 0); + } + + return result; + } + + int IVsDeployableProjectCfg.UnadviseDeployStatusCallback(uint dwCookie) { + lock (syncObject) { + deployCallbackCollection.RemoveAt(dwCookie); + } + + return VSConstants.S_OK; + } + + int IVsDeployableProjectCfg.WaitDeploy(uint dwMilliseconds, int fTickWhenMessageQNotEmpty) { + return VSConstants.S_OK; + } + + #endregion + + #region IVsAppContainerProjectDeployCallback Members + + void IVsAppContainerProjectDeployCallback.OnEndDeploy(bool successful, string deployedPackageMoniker, string deployedAppUserModelID) { + try { + if (successful) { + deployPackageMoniker = deployedPackageMoniker; + deployAppUserModelID = deployedAppUserModelID; + NotifyEndDeploy(1); + + var result = deployOp.GetDeployResult(); + + this.LayoutDir = result.LayoutFolder; + } else { + deployPackageMoniker = null; + deployAppUserModelID = null; + NotifyEndDeploy(0); + } + } finally { + lock (syncObject) { + deployOp = null; + + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + } + + void IVsAppContainerProjectDeployCallback.OutputMessage(string message) { + if (null != outputWindow) { + outputWindow.OutputString(message); + } + } + + #endregion + + #region IVsQueryDebuggableProjectCfg2 Members + void IVsQueryDebuggableProjectCfg2.QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTargetInfo4[] rgDebugTargetInfo, uint[] pcActual) { + if (cTargets <= 0) { + if (pcActual == null) { + Marshal.ThrowExceptionForHR(VSConstants.E_POINTER); + } + + pcActual[0] = 1; + return; + } + + if (pcActual != null) { + pcActual[0] = 0; + } + + VsDebugTargetInfo2[] targets; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) + Marshal.ThrowExceptionForHR(hr); + + int targetLength = (int)Marshal.SizeOf(typeof(VsDebugTargetInfo4)); + + rgDebugTargetInfo[0].AppPackageLaunchInfo.AppUserModelID = deployAppUserModelID; + rgDebugTargetInfo[0].AppPackageLaunchInfo.PackageMoniker = deployPackageMoniker; + + bool isSimulator = GetDebugFlag("UseSimulator", false); + + if (isSimulator && String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS6.DBGLAUNCH_StartInSimulator; + } + + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_DetachOnStop; + + bool containsStartupTask = GetDebugFlag("ContainsStartupTask", false); + +#if DEV14_OR_LATER + if (containsStartupTask) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS140.DBGLAUNCH_ContainsStartupTask; + } +#endif + + rgDebugTargetInfo[0].dlo = (uint)_DEBUG_LAUNCH_OPERATION4.DLO_AppPackageDebug; + rgDebugTargetInfo[0].LaunchFlags = grfLaunch; + rgDebugTargetInfo[0].bstrRemoteMachine = targets[0].bstrRemoteMachine; + rgDebugTargetInfo[0].bstrExe = targets[0].bstrExe; + rgDebugTargetInfo[0].bstrArg = targets[0].bstrArg; + rgDebugTargetInfo[0].bstrCurDir = targets[0].bstrCurDir; + rgDebugTargetInfo[0].bstrEnv = targets[0].bstrEnv; + rgDebugTargetInfo[0].dwProcessId = targets[0].dwProcessId; + rgDebugTargetInfo[0].pStartupInfo = IntPtr.Zero; + rgDebugTargetInfo[0].guidLaunchDebugEngine = targets[0].guidLaunchDebugEngine; + rgDebugTargetInfo[0].dwDebugEngineCount = targets[0].dwDebugEngineCount; + rgDebugTargetInfo[0].pDebugEngines = targets[0].pDebugEngines; + rgDebugTargetInfo[0].guidPortSupplier = targets[0].guidPortSupplier; + rgDebugTargetInfo[0].bstrPortName = targets[0].bstrPortName; + rgDebugTargetInfo[0].bstrOptions = targets[0].bstrOptions; + rgDebugTargetInfo[0].fSendToOutputWindow = targets[0].fSendToOutputWindow; + rgDebugTargetInfo[0].pUnknown = targets[0].pUnknown; + rgDebugTargetInfo[0].guidProcessLanguage = targets[0].guidProcessLanguage; + + if (pcActual != null) { + pcActual[0] = 1; + } + } + #endregion + + #region IVsProjectCfgDebugTargetSelection Members + void IVsProjectCfgDebugTargetSelection.GetCurrentDebugTarget(out Guid pguidDebugTargetType, out uint pDebugTargetTypeId, out string pbstrCurrentDebugTarget) { + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)PythonConfig; + + pguidDebugTargetType = VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet; + pDebugTargetTypeId = VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine; + pbstrCurrentDebugTarget = RemoteTarget; + } + + Array IVsProjectCfgDebugTargetSelection.GetDebugTargetListOfType(Guid guidDebugTargetType, uint debugTargetTypeId) { + string[] result = new string[1]; + if (guidDebugTargetType != VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet) { + return new string[0]; + } + + switch (debugTargetTypeId) { + case VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine: + result[0] = RemoteTarget; + break; + default: + return new string[0]; + } + + return result; + } + + bool IVsProjectCfgDebugTargetSelection.HasDebugTargets(IVsDebugTargetSelectionService pDebugTargetSelectionService, out Array pbstrSupportedTargetCommandIDs) { + pbstrSupportedTargetCommandIDs = new string[] { + String.Join(":", VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet, VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine) + }; + + return true; + } + + void IVsProjectCfgDebugTargetSelection.SetCurrentDebugTarget(Guid guidDebugTargetType, uint debugTargetTypeId, string bstrCurrentDebugTarget) { + if (guidDebugTargetType == VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet) { + } + } + + #endregion + + internal int QueryDebugTargets(out VsDebugTargetInfo2[] targets) { + IntPtr queryDebuggableProjectCfgPtr = IntPtr.Zero; + targets = null; + + Guid guid = typeof(IVsQueryDebuggableProjectCfg).GUID; + int hr = get_CfgType(ref guid, out queryDebuggableProjectCfgPtr); + if (ErrorHandler.Failed(hr)) + return hr; + + object queryDebuggableProjectCfgObject = Marshal.GetObjectForIUnknown(queryDebuggableProjectCfgPtr); + if (queryDebuggableProjectCfgObject == null) + return VSConstants.E_UNEXPECTED; + + IVsQueryDebuggableProjectCfg baseQueryDebugbableCfg = queryDebuggableProjectCfgObject as IVsQueryDebuggableProjectCfg; + if (baseQueryDebugbableCfg == null) + return VSConstants.E_UNEXPECTED; + + uint[] targetsCountOutput = new uint[1]; + hr = baseQueryDebugbableCfg.QueryDebugTargets(0, 0, null, targetsCountOutput); + if (ErrorHandler.Failed(hr)) + return hr; + uint numberOfDebugTargets = targetsCountOutput[0]; + + targets = new VsDebugTargetInfo2[numberOfDebugTargets]; + hr = baseQueryDebugbableCfg.QueryDebugTargets(0, numberOfDebugTargets, targets, null); + if (ErrorHandler.Failed(hr)) + return hr; + + if (string.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + MessageBox.Show( + "The project cannot be deployed or debugged because there is not a remote machine specified in Debug settings.", + "Python Tools for Visual Studio", MessageBoxButtons.OK, MessageBoxIcon.Error); + return VSConstants.E_ABORT; + } + + return hr; + } + + private bool GetDebugFlag(string name, bool defaultValue) { + string value; + string canonicalName; + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)PythonConfig; + + get_CanonicalName(out canonicalName); + + int hr = bps.GetPropertyValue(name, canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out value); + + if (Microsoft.VisualStudio.ErrorHandler.Failed(hr)) + return defaultValue; + + return value.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + private int Deploy() { + try { + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + callback.OnEndDeploy(1); + } + } finally { + lock (syncObject) { + appContainerBootstrapperOperation = null; + deployOp = null; + } + } + + this.outputWindow = null; + + return VSConstants.S_OK; + } + + private void NotifyEndDeploy(int success) { + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + callback.OnEndDeploy(success); + } + + outputWindow = null; + } + + int IVsQueryDebuggableProjectCfg.QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTargetInfo2[] rgDebugTargetInfo, uint[] pcActual) { + var project = PythonConfig; + + if (pcActual != null && pcActual.Length > 0) { + pcActual[0] = 1; + } + + if (rgDebugTargetInfo != null && rgDebugTargetInfo.Length > 0) { + IList debugEngineGuids = new Guid[] { VSConstants.DebugEnginesGuids.NativeOnly_guid }; + + rgDebugTargetInfo[0] = new VsDebugTargetInfo2(); + + rgDebugTargetInfo[0].bstrExe = project.GetProjectProperty("Name"); + rgDebugTargetInfo[0].bstrRemoteMachine = project.GetProjectProperty("RemoteDebugMachine"); + rgDebugTargetInfo[0].guidPortSupplier = VSConstants.DebugPortSupplierGuids.NoAuth_guid; + rgDebugTargetInfo[0].guidLaunchDebugEngine = debugEngineGuids[0]; + rgDebugTargetInfo[0].dwDebugEngineCount = (uint)debugEngineGuids.Count; + rgDebugTargetInfo[0].pDebugEngines = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * debugEngineGuids.Count); + + for (var i = 0; i < debugEngineGuids.Count; i++) { + Marshal.StructureToPtr(debugEngineGuids[i], + IntPtr.Add(rgDebugTargetInfo[0].pDebugEngines, i * Marshal.SizeOf(typeof(Guid))), + false); + } + } + + return VSConstants.S_OK; + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapProjectFactory.cs b/Python/Product/Uap/Project/PythonUapProjectFactory.cs new file mode 100644 index 0000000000..60a9243890 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProjectFactory.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Flavor; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid(GuidList.guidUapFactoryString)] + public class PythonUapProjectFactory : FlavoredProjectFactoryBase { + private PythonUapPackage _package; + + public PythonUapProjectFactory(PythonUapPackage package) { + _package = package; + } + + protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) { + return new PythonUapProject { Package = _package }; + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPage.cs b/Python/Product/Uap/Project/PythonUapPropertyPage.cs new file mode 100644 index 0000000000..85aed0126e --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPage.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.VisualStudioTools.Project; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid(GuidList.guidUapPropertyPageString)] + class PythonUapPropertyPage : CommonPropertyPage { + private readonly PythonUapPropertyPageControl _control; + + public const string RemoteMachineSetting = "RemoteDebugMachine"; + public const string UapUserSetting = "UserSettingsChanged"; + + public PythonUapPropertyPage() { + _control = new PythonUapPropertyPageControl(this); + } + + public override Control Control { + get { return _control; } + } + + public override void Apply() { + SetConfigUserProjectProperty(RemoteMachineSetting, _control.RemoteDebugMachine); + + // Workaround to reload user project file + SetProjectProperty(UapUserSetting, DateTime.UtcNow.ToString()); + SetProjectProperty(UapUserSetting, string.Empty); + + IsDirty = false; + } + + public override void LoadSettings() { + Loading = true; + try { + _control.RemoteDebugMachine = GetConfigUserProjectProperty(RemoteMachineSetting); + IsDirty = false; + } finally { + Loading = false; + } + } + + public override string Name { + get { return Resources.UapPropertyPageTitle; } + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs new file mode 100644 index 0000000000..d5bf870632 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs @@ -0,0 +1,147 @@ +namespace Microsoft.PythonTools.Uap.Project { + partial class PythonUapPropertyPageControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + this._remoteMachineLabel = new System.Windows.Forms.Label(); + this._remoteMachine = new System.Windows.Forms.TextBox(); + this._uapGroup = new System.Windows.Forms.GroupBox(); + this._toolTip = new System.Windows.Forms.ToolTip(this.components); + tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + tableLayoutPanel2.SuspendLayout(); + tableLayoutPanel1.SuspendLayout(); + this._uapGroup.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + tableLayoutPanel2.AutoSize = true; + tableLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + tableLayoutPanel2.ColumnCount = 2; + tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel2.Controls.Add(this._remoteMachineLabel, 0, 1); + tableLayoutPanel2.Controls.Add(this._remoteMachine, 1, 1); + tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel2.Location = new System.Drawing.Point(6, 21); + tableLayoutPanel2.Name = "tableLayoutPanel2"; + tableLayoutPanel2.RowCount = 2; + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.Size = new System.Drawing.Size(406, 26); + tableLayoutPanel2.TabIndex = 0; + // + // _remoteMachineLabel + // + this._remoteMachineLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._remoteMachineLabel.AutoSize = true; + this._remoteMachineLabel.Location = new System.Drawing.Point(6, 6); + this._remoteMachineLabel.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this._remoteMachineLabel.Name = "_remoteMachineLabel"; + this._remoteMachineLabel.Size = new System.Drawing.Size(91, 13); + this._remoteMachineLabel.TabIndex = 2; + this._remoteMachineLabel.Text = "&Remote Machine:"; + this._remoteMachineLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // _remoteMachine + // + this._remoteMachine.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._remoteMachine.Location = new System.Drawing.Point(109, 3); + this._remoteMachine.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); + this._remoteMachine.MinimumSize = new System.Drawing.Size(50, 4); + this._remoteMachine.Name = "_remoteMachine"; + this._remoteMachine.Size = new System.Drawing.Size(291, 20); + this._remoteMachine.TabIndex = 3; + this._remoteMachine.TextChanged += new System.EventHandler(this.Setting_TextChanged); + // + // tableLayoutPanel1 + // + tableLayoutPanel1.AutoSize = true; + tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + tableLayoutPanel1.ColumnCount = 1; + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel1.Controls.Add(this._uapGroup, 0, 0); + tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 2; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + tableLayoutPanel1.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel1.TabIndex = 0; + // + // _uapGroup + // + this._uapGroup.AutoSize = true; + this._uapGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._uapGroup.Controls.Add(tableLayoutPanel2); + this._uapGroup.Dock = System.Windows.Forms.DockStyle.Fill; + this._uapGroup.Location = new System.Drawing.Point(6, 8); + this._uapGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapGroup.Name = "_uapGroup"; + this._uapGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapGroup.Size = new System.Drawing.Size(418, 55); + this._uapGroup.TabIndex = 0; + this._uapGroup.TabStop = false; + this._uapGroup.Text = "Debug Settings"; + // + // PythonUapPropertyPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.Controls.Add(tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this.Name = "PythonUapPropertyPageControl"; + this.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel2.ResumeLayout(false); + tableLayoutPanel2.PerformLayout(); + tableLayoutPanel1.ResumeLayout(false); + tableLayoutPanel1.PerformLayout(); + this._uapGroup.ResumeLayout(false); + this._uapGroup.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox _uapGroup; + private System.Windows.Forms.ToolTip _toolTip; + private System.Windows.Forms.Label _remoteMachineLabel; + private System.Windows.Forms.TextBox _remoteMachine; + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs new file mode 100644 index 0000000000..1651140c25 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using Microsoft.PythonTools; +using Microsoft.PythonTools.Project; +using Microsoft.VisualStudioTools.Project; + +namespace Microsoft.PythonTools.Uap.Project { + public partial class PythonUapPropertyPageControl : UserControl { + private readonly PythonUapPropertyPage _properties; + + private PythonUapPropertyPageControl() { + InitializeComponent(); + + _toolTip.SetToolTip(_remoteMachine, Resources.UapRemoteMachineHelp); + _toolTip.SetToolTip(_remoteMachineLabel, Resources.UapRemoteMachineHelp); + } + + internal PythonUapPropertyPageControl(PythonUapPropertyPage properties) + : this() { + _properties = properties; + } + + public string RemoteDebugMachine { + get { return _remoteMachine.Text; } + set { _remoteMachine.Text = value; } + } + + private void Setting_TextChanged(object sender, EventArgs e) { + if (_properties != null) { + _properties.IsDirty = true; + } + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx new file mode 100644 index 0000000000..1f83e4cc41 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + 17, 17 + + \ No newline at end of file diff --git a/Python/Product/Uap/Properties/AssemblyInfo.cs b/Python/Product/Uap/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0e203b3dd7 --- /dev/null +++ b/Python/Product/Uap/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Python Tools for Visual Studio - UAP Integration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en-US")] + diff --git a/Python/Product/Uap/PythonUapPackage.cs b/Python/Product/Uap/PythonUapPackage.cs new file mode 100644 index 0000000000..50cf58e2a5 --- /dev/null +++ b/Python/Product/Uap/PythonUapPackage.cs @@ -0,0 +1,125 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using EnvDTE90a; +using Microsoft.PythonTools.Debugger.DebugEngine; +using Microsoft.PythonTools.Uap.Interpreter; +using Microsoft.PythonTools.Uap.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.PythonTools.Uap { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. + /// + // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is + // a package. + [PackageRegistration(UseManagedResourcesOnly = true)] + + // This attribute is needed to let the shell know that this package exposes some menus. + [Guid(GuidList.guidUapPkgString)] + + [ProvideObject(typeof(PythonUapPropertyPage))] + [ProvideObject(typeof(PythonUapProject))] + [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasAppContainerProject_string)] + [Description("Python Tools UAP Interpreter")] + [ProvideProjectFactory(typeof(PythonUapProjectFactory), null, null, null, null, ".\\NullPath", LanguageVsTemplate = PythonConstants.LanguageName)] + [ProvidePythonInterpreterFactoryProvider(PythonUapInterpreterFactory.InterpreterGuidString, typeof(PythonUapInterpreterFactoryProvider))] + public sealed class PythonUapPackage : Package { + internal static PythonUapPackage Instance; + + /// + /// Default constructor of the package. + /// Inside this method you can place any initialization code that does not require + /// any Visual Studio service because at this point the package object is created but + /// not sited yet inside Visual Studio environment. The place to do all the other + /// initialization is the Initialize method. + /// + public PythonUapPackage() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this)); + Instance = this; + } + + ///////////////////////////////////////////////////////////////////////////// + // Overridden Package Implementation + #region Package Members + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initialization code that rely on services provided by VisualStudio. + /// + protected override void Initialize() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); + base.Initialize(); + + RegisterProjectFactory(new PythonUapProjectFactory(this)); + } + + #endregion + + internal new object GetService(Type serviceType) { + return base.GetService(serviceType); + } + + public EnvDTE.DTE DTE { + get { + return (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE)); + } + } + + internal static PythonUapProject GetProject(IServiceProvider serviceProvider, string filename) { + IVsHierarchy hierarchy; + IVsRunningDocumentTable rdt = serviceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + uint itemid; + IntPtr docData = IntPtr.Zero; + uint cookie; + try { + int hr = rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, + filename, + out hierarchy, + out itemid, + out docData, + out cookie); + + if (ErrorHandler.Succeeded(hr)) { + rdt.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, cookie); + } + var res = hierarchy as PythonUapProject; + if (res != null) { + return res; + } + return null; + } finally { + if (docData != IntPtr.Zero) { + Marshal.Release(docData); + } + } + } + } +} diff --git a/Python/Product/Uap/Resources.Designer.cs b/Python/Product/Uap/Resources.Designer.cs new file mode 100644 index 0000000000..6dc644c56b --- /dev/null +++ b/Python/Product/Uap/Resources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.PythonTools.Uap { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PythonTools.Uap.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to UAP Project Settings. + /// + internal static string UapPropertyPageTitle { + get { + return ResourceManager.GetString("UapPropertyPageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remote machine for deployment. + /// + internal static string UapRemoteMachineHelp { + get { + return ResourceManager.GetString("UapRemoteMachineHelp", resourceCulture); + } + } + } +} diff --git a/Python/Product/Uap/Resources.resx b/Python/Product/Uap/Resources.resx new file mode 100644 index 0000000000..8ae2e557a5 --- /dev/null +++ b/Python/Product/Uap/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UAP Project Settings + + + Remote machine for deployment + + \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx b/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest b/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest new file mode 100644 index 0000000000..e8175443d1 --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest @@ -0,0 +1,67 @@ + + + + + + + + + + $projectname$ + $XmlEscapedPublisher$ + StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pyuapbackgroundservice.dll + + + + + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj new file mode 100644 index 0000000000..773e701a54 --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj @@ -0,0 +1,61 @@ + + + + $safeprojectname$ + Debug + x86 + x86 + 2.0 + 8.2 + Windows Store + true + 14.0 + $guid1$ + . + . + StartupTask.py + true + + $projectname$_TemporaryKey.pfx + $currentuiculturename$ + {c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e};{888888A0-9F3D-457C-B088-3A5042F75D52} + {86767848-40B4-4007-8BCC-A3835EDF0E69} + 3.5 + true + + + + true + false + bin\Debug\$(Platform)\ + + + true + false + bin\Release\$(Platform)\ + + + + + + + Designer + + + + + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + + + + 14.0 + 2.2 + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + $(MSBuildProgramFiles32)\Microsoft Visual Studio $(VisualStudioVersion)\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate new file mode 100644 index 0000000000..406eb323ac --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate @@ -0,0 +1,31 @@ + + + Background Application (IoT) + A project for creating an Python IoT Background Application + + Python + 20 + 2 + true + Microsoft.Ptvs.WinRT.UAP.IOT.BackgroundApplication + BackgroundApplication + true + Windows + 6.1.0 + + + + Package.appxmanifest + StartupTask.py + Application_TemporaryKey.pfx + + + + Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Microsoft.VisualStudio.WinRT.TemplateWizards.CreateProjectCertificate.Wizard + + + Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Microsoft.VisualStudio.WinRT.TemplateWizards.PlatformVersion.Wizard + + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py new file mode 100644 index 0000000000..df1dc6821e --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py @@ -0,0 +1 @@ +print('Hello World') diff --git a/Python/Product/Uap/Uap.csproj b/Python/Product/Uap/Uap.csproj new file mode 100644 index 0000000000..3baed11995 --- /dev/null +++ b/Python/Product/Uap/Uap.csproj @@ -0,0 +1,323 @@ + + + + + + 12.0 + + + + + 4.0 + + + + + 14.0 + + + + + 4.0 + + + + + + Debug + AnyCPU + 2.0 + {D28045D6-232C-4101-A5CB-AC4A68E92211} + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + Microsoft.PythonTools.Uap + Microsoft.PythonTools.Uap + true + SAK + SAK + SAK + SAK + 1762 + + + AnyCPU + + + + True + + + True + + + + + + + + + True + + + + + + + True + + + + + + + + + + + + + + + false + + + + True + + + + + + + + + + + + + + + + + + + + + + True + + + + $(VSSDKDir)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Debugger.Engine.dll + + + + + $(DevEnvDir)Microsoft.VisualStudio.Web.Html.dll + + + + + {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2} + 8 + 0 + 0 + primary + False + False + + + {26AD1324-4B7C-44BC-84F8-B86AED45729F} + 10 + 0 + 0 + primary + False + False + + + {1A31287A-4D7D-413E-8E32-3B374931BD89} + 8 + 0 + 0 + primary + False + False + + + {2CE2370E-D744-4936-A090-3FFFE667B0E1} + 9 + 0 + 0 + primary + False + False + + + {1CBA492E-7263-47BB-87FE-639000619B15} + 8 + 0 + 0 + primary + False + False + + + {00020430-0000-0000-C000-000000000046} + 2 + 0 + 0 + primary + False + False + + + + + Python + Windows IoT Core + + + Python + + + + + + + + + Project\SolutionListener.cs + + + Project\CommonUtils.cs + + + ExceptionExtensions.cs + + + + + + + + + + + + usercontrol + + + PythonUapPropertyPageControl.cs + + + + + True + True + Resources.resx + + + + Designer + + + Designer + + + + Designer + + + + + true + Microsoft.VSPackage + Designer + + + + + true + PreserveNewest + Designer + + + Designer + + + + Designer + + + + + PythonUapPropertyPageControl.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + PreserveNewest + true + . + + + PreserveNewest + true + . + + + + + {A85D479D-67A9-4BDB-904A-7D86DAF68A6F} + Analysis + + + {DECC7971-FA58-4DB0-9561-BFFADD393BBD} + Debugger + + + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + PythonTools + False + + + + + true + + + + $(IntermediateOutputPath)\RemoteDebugger.vsdconfig + + + + <_Arguments>@(VsdConfigXml, ' ') @(IntermediateAssembly->'"%(FullPath)"', ' ') "$(VsdConfigOutput)" + + + + + + + true + . + PreserveNewest + $([System.IO.Path]::GetFileName($(VsdConfigOutput))) + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/VSPackage.resx b/Python/Product/Uap/VSPackage.resx new file mode 100644 index 0000000000..25ba4c515b --- /dev/null +++ b/Python/Product/Uap/VSPackage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Python Tools for Visual Studio - UAP Integration + + + Provides templates and integration for the UAP framework. + + \ No newline at end of file diff --git a/Python/Product/Uap/source.extension.vsixmanifest b/Python/Product/Uap/source.extension.vsixmanifest new file mode 100644 index 0000000000..b42b5127d1 --- /dev/null +++ b/Python/Product/Uap/source.extension.vsixmanifest @@ -0,0 +1,47 @@ + + + + Python Tools for Visual Studio - UAP + Microsoft + 2.2 + Provides templates and integration for the UAP framework. + http://pytools.codeplex.com + http://pytools.codeplex.com + PythonProject.ico + PythonProjectBig.ico + 1033 + true + + + + Pro + Ultimate + IntegratedShell + WDExpress + VWDExpress + + + + + + + Visual Studio MPF + + + Python Tools for Visual Studio + http://pytools.codeplex.com/ + + + Python Tools for Visual Studio - Interactive + http://pytools.codeplex.com/ + + + + |%CurrentProject%;PkgdefProjectOutputGroup| + |%CurrentProject%| + ProjectTemplates + ItemTemplates + RemoteDebugger.vsdconfig + + diff --git a/Python/PythonTools.sln b/Python/PythonTools.sln index 38c920f204d15805df8a9849fb767b1d94ec817b..6203b53bbce35a1f1aeba196dd2d33db7ab4eeeb 100644 GIT binary patch literal 222278 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 aJTG&ii*%+TwvW@vR!EsfD`eIibN>gQ^A+C! diff --git a/Python/Setup/BuildRelease.ps1 b/Python/Setup/BuildRelease.ps1 index bfcb2ace14..2cc4b5b279 100644 --- a/Python/Setup/BuildRelease.ps1 +++ b/Python/Setup/BuildRelease.ps1 @@ -261,6 +261,7 @@ $managed_files = ( "Microsoft.PythonTools.WebRole.dll", "Microsoft.PythonTools.Django.dll", "Microsoft.PythonTools.VsLogger.dll", + "Microsoft.PythonTools.Uap.dll", "Microsoft.PythonTools.AzureSetup.exe", "Microsoft.IronPythonTools.Resolver.dll" ) diff --git a/Python/Setup/MergeModule.wxi b/Python/Setup/MergeModule.wxi index 412e54b73d..19a5beaa17 100644 --- a/Python/Setup/MergeModule.wxi +++ b/Python/Setup/MergeModule.wxi @@ -47,6 +47,9 @@ + + + diff --git a/Python/Setup/PythonTools/PythonToolsFiles.proj b/Python/Setup/PythonTools/PythonToolsFiles.proj index e8d3d6960e..4e08c420f3 100644 --- a/Python/Setup/PythonTools/PythonToolsFiles.proj +++ b/Python/Setup/PythonTools/PythonToolsFiles.proj @@ -44,6 +44,7 @@ !(bindpath.bin)\PyDebugAttachX86.dll; visualstudio_py_repl.py; visualstudio_ipython_repl.py; + visualstudio_py_remote_launcher.py; visualstudio_py_launcher.py; visualstudio_py_debugger.py; visualstudio_py_util.py" /> @@ -135,7 +136,11 @@ CompletionDB - + + + CompletionDB + + $(DefineConstants); @@ -85,6 +86,10 @@ DjangoMsm {16671BE6-DD23-41D9-841A-0B80D47A090D} + + UapMsm + {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} + VsLoggerMsm {639BFC80-B31B-4D3F-9DF3-A65913000B4B} diff --git a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs index eda5726c0c..e9c9a3ede2 100644 --- a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs +++ b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs @@ -197,6 +197,15 @@ + + + + + + + + + @@ -252,6 +261,12 @@ + + + + + + diff --git a/Python/Setup/Uap/Uap.wixproj b/Python/Setup/Uap/Uap.wixproj new file mode 100644 index 0000000000..5213c9c543 --- /dev/null +++ b/Python/Setup/Uap/Uap.wixproj @@ -0,0 +1,33 @@ + + + + + 3.5 + {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} + 2.0 + Uap + Module + false + $(DefineConstants);ProductSuffix=Uap + SAK + SAK + SAK + SAK + + + + + + + Microsoft.PythonTools.Uap + {D28045D6-232C-4101-A5CB-AC4A68E92211} + + + + + MergeModule.wxi + + + + + \ No newline at end of file diff --git a/Python/Setup/Uap/Uap.wxs b/Python/Setup/Uap/Uap.wxs new file mode 100644 index 0000000000..d65abdcad1 --- /dev/null +++ b/Python/Setup/Uap/Uap.wxs @@ -0,0 +1,20 @@ + + + + + + + + + NOT ALLUSERS = 1 + + + + + + + + + + + diff --git a/Python/Setup/Uap/UapFiles.proj b/Python/Setup/Uap/UapFiles.proj new file mode 100644 index 0000000000..4060e61055 --- /dev/null +++ b/Python/Setup/Uap/UapFiles.proj @@ -0,0 +1,42 @@ + + + + + UapFiles + + + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + UapProjectTemplates + + + + + + diff --git a/Python/Setup/setup.proj b/Python/Setup/setup.proj index edae64f88f..80bf0fc743 100644 --- a/Python/Setup/setup.proj +++ b/Python/Setup/setup.proj @@ -17,6 +17,7 @@ + diff --git a/Python/products.settings b/Python/products.settings index 4bfac28cc0..564ffddf21 100644 --- a/Python/products.settings +++ b/Python/products.settings @@ -1,5 +1,9 @@ + + true + false + false true From 928027a58b0fdcf6cafbfa1bc0f73d050a709e2b Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Mon, 4 May 2015 13:52:01 -0700 Subject: [PATCH 02/30] Adding support for UAP based Python applications --- .../SharedProject/CommonConfigProvider.cs | 112 +- .../SharedProject/CommonProjectConfig.cs | 10 + .../SharedProject/CommonPropertyPage.cs | 165 ++- .../Product/SharedProject/ConfigProvider.cs | 11 +- Common/Product/SharedProject/ProjectConfig.cs | 35 +- .../SharedProject/ProjectFileConstants.cs | 3 + Common/Product/SharedProject/ProjectNode.cs | 31 +- Python/Product/Debugger/Debugger.csproj | 1 + .../DebugEngine/AD7BoundBreakpoint.cs | 11 +- .../Debugger/DebugEngine/AD7Engine.cs | 6 + .../DebugEngine/Remote/PythonRemoteProcess.cs | 21 +- .../Debugger/Debugger/PythonProcess.cs | 25 +- .../Debugger/Debugger/PythonStackFrame.cs | 2 +- .../Debugger/Transports/TcpTransport.cs | 2 +- Python/Product/PythonTools/PythonConstants.cs | 6 +- Python/Product/PythonTools/PythonTools.csproj | 4 + .../PythonTools/PythonTools/Extensions.cs | 24 +- .../Project/ImportWizard/ImportSettings.cs | 3 +- .../ImportWizard/ProjectCustomizations.cs | 24 + .../Project/PythonProjectConfig.cs | 23 + .../PythonTools/Project/PythonProjectNode.cs | 45 +- .../PythonTools/visualstudio_py_debugger.py | 142 +-- .../visualstudio_py_remote_launcher.py | 114 +++ .../Uap/Debugger/PythonRemoteDebugEvents.cs | 118 +++ .../RemoteDebugServerComponent.vsdconfigxml | 36 + Python/Product/Uap/Extensions.cs | 144 +++ Python/Product/Uap/Guids.cs | 26 + .../Uap/Interpreter/PythonUapInterpreter.cs | 237 +++++ .../PythonUapInterpreterFactory.cs | 40 + .../PythonUapInterpreterFactoryProvider.cs | 47 + .../Uap/Microsoft.PythonTools.Uap.targets | 66 ++ .../Product/Uap/Project/PythonUapProject.cs | 299 ++++++ .../Uap/Project/PythonUapProjectConfig.cs | 961 ++++++++++++++++++ .../Uap/Project/PythonUapProjectFactory.cs | 34 + .../Uap/Project/PythonUapPropertyPage.cs | 60 ++ .../PythonUapPropertyPageControl.Designer.cs | 147 +++ .../Project/PythonUapPropertyPageControl.cs | 49 + .../Project/PythonUapPropertyPageControl.resx | 129 +++ Python/Product/Uap/Properties/AssemblyInfo.cs | 30 + Python/Product/Uap/PythonUapPackage.cs | 125 +++ Python/Product/Uap/Resources.Designer.cs | 81 ++ Python/Product/Uap/Resources.resx | 126 +++ .../Application_TemporaryKey.pfx | 0 .../BackgroundService/Package.appxmanifest | 67 ++ .../PythonBackgroundService.pyproj | 61 ++ .../PythonBackgroundService.vstemplate | 31 + .../Projects/BackgroundService/StartupTask.py | 1 + Python/Product/Uap/Uap.csproj | 323 ++++++ Python/Product/Uap/VSPackage.resx | 126 +++ .../Product/Uap/source.extension.vsixmanifest | 47 + Python/PythonTools.sln | Bin 168906 -> 222278 bytes Python/Setup/BuildRelease.ps1 | 1 + Python/Setup/MergeModule.wxi | 3 + .../Setup/PythonTools/PythonToolsFiles.proj | 7 +- Python/Setup/PythonTools/_wingpio.idb | Bin 0 -> 4525 bytes Python/Setup/PythonTools/_wini2c.idb | Bin 0 -> 6744 bytes Python/Setup/PythonTools/_winspi.idb | Bin 0 -> 12954 bytes .../PythonToolsInstaller.wixproj | 5 + .../PythonToolsInstaller.wxs | 15 + Python/Setup/Uap/Uap.wixproj | 33 + Python/Setup/Uap/Uap.wxs | 20 + Python/Setup/Uap/UapFiles.proj | 42 + Python/Setup/setup.proj | 1 + Python/products.settings | 4 + 64 files changed, 4229 insertions(+), 133 deletions(-) create mode 100644 Python/Product/PythonTools/visualstudio_py_remote_launcher.py create mode 100644 Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs create mode 100644 Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml create mode 100644 Python/Product/Uap/Extensions.cs create mode 100644 Python/Product/Uap/Guids.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreter.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs create mode 100644 Python/Product/Uap/Microsoft.PythonTools.Uap.targets create mode 100644 Python/Product/Uap/Project/PythonUapProject.cs create mode 100644 Python/Product/Uap/Project/PythonUapProjectConfig.cs create mode 100644 Python/Product/Uap/Project/PythonUapProjectFactory.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPage.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.resx create mode 100644 Python/Product/Uap/Properties/AssemblyInfo.cs create mode 100644 Python/Product/Uap/PythonUapPackage.cs create mode 100644 Python/Product/Uap/Resources.Designer.cs create mode 100644 Python/Product/Uap/Resources.resx create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py create mode 100644 Python/Product/Uap/Uap.csproj create mode 100644 Python/Product/Uap/VSPackage.resx create mode 100644 Python/Product/Uap/source.extension.vsixmanifest create mode 100644 Python/Setup/PythonTools/_wingpio.idb create mode 100644 Python/Setup/PythonTools/_wini2c.idb create mode 100644 Python/Setup/PythonTools/_winspi.idb create mode 100644 Python/Setup/Uap/Uap.wixproj create mode 100644 Python/Setup/Uap/Uap.wxs create mode 100644 Python/Setup/Uap/UapFiles.proj diff --git a/Common/Product/SharedProject/CommonConfigProvider.cs b/Common/Product/SharedProject/CommonConfigProvider.cs index 60879b8b55..164a43fa82 100644 --- a/Common/Product/SharedProject/CommonConfigProvider.cs +++ b/Common/Product/SharedProject/CommonConfigProvider.cs @@ -13,46 +13,134 @@ * ***************************************************************************/ using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using System.Collections.Generic; namespace Microsoft.VisualStudioTools.Project { /// - /// Enables the Any CPU Platform form name for Dynamic Projects. + /// Enables the Any CPU, x86, x64, and ARM Platform form names for Dynamic Projects. /// Hooks language specific project config. + /// Projects that are platform aware should have a PlatformAware and AvailablePlatforms + /// property for configuration handling to function correctly. + /// PlatformAware value can be true or false. AvailablePlatforms is a comma separated string of supported platforms (e.g. "x86, X64") + /// If the PlatformAware property is ommited then this provider will only provide "Any CPU" platform. /// internal class CommonConfigProvider : ConfigProvider { private CommonProjectNode _project; + private bool _isPlatformAware; public CommonConfigProvider(CommonProjectNode project) : base(project) { + bool appxPackage, windowsAppContainer; _project = project; + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.PlatformAware), out _isPlatformAware); + + if (!_isPlatformAware) { + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AppxPackage), out appxPackage); + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.WindowsAppContainer), out windowsAppContainer); + _isPlatformAware = appxPackage && windowsAppContainer; + } } #region overridden methods - protected override ProjectConfig CreateProjectConfiguration(string configName) { + protected override ProjectConfig CreateProjectConfiguration(string configName) { + if (_isPlatformAware) { + if (configName != null) { + var configParts = configName.Split('|'); + + if (configParts.Length == 2) { + var config = _project.MakeConfiguration(configName); + config.PlatformName = configParts[1]; + return config; + } + } + } + return _project.MakeConfiguration(configName); } public override int GetPlatformNames(uint celt, string[] names, uint[] actual) { - if (names != null) { - names[0] = "Any CPU"; + if (_isPlatformAware) { + var platforms = GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + else { + if (names != null) { + names[0] = AnyCPUPlatform; } - if (actual != null) { - actual[0] = 1; + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } } - return VSConstants.S_OK; + public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { + if (_isPlatformAware) { + var platforms = GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + else { + if (names != null) { + names[0] = AnyCPUPlatform; + } + + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } } - public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { - if (names != null) { - names[0] = "Any CPU"; + public override int GetCfgs(uint celt, IVsCfg[] a, uint[] actual, uint[] flags) { + if (_isPlatformAware) { + if (flags != null) { + flags[0] = 0; + } + + int i = 0; + string[] configList = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + string[] platformList = GetSupportedPlatformsFromProject(); + + if (a != null) { + foreach (string platformName in platformList) { + foreach (string configName in configList) { + a[i] = this.GetProjectConfiguration(configName + "|" + platformName); + i++; + if (i == celt) { + break; + } + } + if(i == celt) { + break; + } + } + } + else { + i = configList.Length * platformList.Length; + } + + if (actual != null) { + actual[0] = (uint)i; + } + + return VSConstants.S_OK; } - if (actual != null) { - actual[0] = 1; + return base.GetCfgs(celt, a, actual, flags); + } + + public override int GetCfgOfName(string name, string platName, out IVsCfg cfg) { + if (!string.IsNullOrEmpty(platName)) { + cfg = this.GetProjectConfiguration(name + "|" + platName); + + return VSConstants.S_OK; } + cfg = this.GetProjectConfiguration(name); return VSConstants.S_OK; } diff --git a/Common/Product/SharedProject/CommonProjectConfig.cs b/Common/Product/SharedProject/CommonProjectConfig.cs index 83b90daf55..7e2b28f645 100644 --- a/Common/Product/SharedProject/CommonProjectConfig.cs +++ b/Common/Product/SharedProject/CommonProjectConfig.cs @@ -28,6 +28,16 @@ public CommonProjectConfig(CommonProjectNode/*!*/ project, string configuration) _project = project; } + public virtual string Platform { + get { + return GetConfigurationProperty("Platform", false); + } + + set { + SetConfigurationProperty("Platform", value); + } + } + public override int DebugLaunch(uint flags) { IProjectLauncher starter = _project.GetLauncher(); diff --git a/Common/Product/SharedProject/CommonPropertyPage.cs b/Common/Product/SharedProject/CommonPropertyPage.cs index 06f53b33bc..168943a862 100644 --- a/Common/Product/SharedProject/CommonPropertyPage.cs +++ b/Common/Product/SharedProject/CommonPropertyPage.cs @@ -14,9 +14,13 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Drawing; +using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; +using Microsoft.Build.Construction; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; @@ -49,6 +53,10 @@ internal virtual CommonProjectNode Project { } } + internal virtual IEnumerable SelectedConfigs { + get; set; + } + protected void SetProjectProperty(string propertyName, string propertyValue) { // SetProjectProperty's implementation will check whether the value // has changed. @@ -59,6 +67,150 @@ protected string GetProjectProperty(string propertyName) { return Project.GetUnevaluatedProperty(propertyName); } + protected void SetUserProjectProperty(string propertyName, string propertyValue) { + // SetUserProjectProperty's implementation will check whether the value + // has changed. + Project.SetUserProjectProperty(propertyName, propertyValue); + } + + protected string GetUserProjectProperty(string propertyName) { + return Project.GetUserProjectProperty(propertyName); + } + + protected string GetConfigUserProjectProperty(string propertyName) { + if (SelectedConfigs == null) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + Project.CurrentConfig.GetPropertyValue("Configuration"), + Project.CurrentConfig.GetPropertyValue("Platform")); + + return GetUserPropertyUnderCondition(propertyName, condition); + } else { + StringCollection values = new StringCollection(); + + foreach (var config in SelectedConfigs) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + config.ConfigName, + config.Platform); + + values.Add(GetUserPropertyUnderCondition(propertyName, condition)); + } + + switch (values.Count) { + case 0: + return null; + case 1: + return values[0]; + default: + return ""; + } + } + } + + protected void SetConfigUserProjectProperty(string propertyName, string propertyValue) { + if (SelectedConfigs == null) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + Project.CurrentConfig.GetPropertyValue("Configuration"), + Project.CurrentConfig.GetPropertyValue("Platform")); + + SetUserPropertyUnderCondition(propertyName, propertyValue, condition); + } else { + foreach (var config in SelectedConfigs) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + config.ConfigName, + config.GetConfigurationProperty("Platform", false)); + + SetUserPropertyUnderCondition(propertyName, propertyValue, condition); + } + } + } + + private string GetUserPropertyUnderCondition(string propertyName, string condition) { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + + if (Project.UserBuildProject != null) { + if (conditionTrimmed.Length == 0) { + return Project.UserBuildProject.GetProperty(propertyName).UnevaluatedValue; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + ProjectPropertyGroupElement matchingGroup = null; + + foreach (ProjectPropertyGroupElement group in Project.UserBuildProject.Xml.PropertyGroups) { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) { + matchingGroup = group; + break; + } + } + + if (matchingGroup != null) { + foreach (ProjectPropertyElement property in matchingGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && (property.Condition == null || property.Condition.Length == 0)) { + return property.Value; + } + } + } + + } + + return null; + } + + /// + /// Emulates the behavior of SetProperty(name, value, condition) on the old MSBuild object model. + /// This finds a property group with the specified condition (or creates one if necessary) then sets the property in there. + /// + private void SetUserPropertyUnderCondition(string propertyName, string propertyValue, string condition) { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + const string userProjectCreateProperty = "UserProject"; + + if (Project.UserBuildProject == null) { + Project.SetUserProjectProperty(userProjectCreateProperty, null); + } + + if (conditionTrimmed.Length == 0) { + var userProp = Project.UserBuildProject.GetProperty(userProjectCreateProperty); + if (userProp != null) { + Project.UserBuildProject.RemoveProperty(userProp); + } + Project.UserBuildProject.SetProperty(propertyName, propertyValue); + return; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + ProjectPropertyGroupElement newGroup = null; + + foreach (ProjectPropertyGroupElement group in Project.UserBuildProject.Xml.PropertyGroups) { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) { + newGroup = group; + break; + } + } + + if (newGroup == null) { + newGroup = Project.UserBuildProject.Xml.AddPropertyGroup(); // Adds after last existing PG, else at start of project + newGroup.Condition = condition; + } + + foreach (ProjectPropertyElement property in newGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && (property.Condition == null || property.Condition.Length == 0)) { + property.Value = propertyValue; + return; + } + } + + newGroup.AddProperty(propertyName, propertyValue); + } + public bool Loading { get { return _loading; @@ -138,18 +290,19 @@ void IPropertyPage.SetObjects(uint count, object[] punk) { if (count > 0) { if (punk[0] is ProjectConfig) { - ArrayList configs = new ArrayList(); + if (_project == null) { + _project = (CommonProjectNode)((CommonProjectConfig)punk.First()).ProjectMgr; + } + + var configs = new List(); for (int i = 0; i < count; i++) { CommonProjectConfig config = (CommonProjectConfig)punk[i]; - if (_project == null) { - Project = (CommonProjectNode)config.ProjectMgr; - break; - } - configs.Add(config); } + + SelectedConfigs = configs; } else if (punk[0] is NodeProperties) { if (_project == null) { Project = (CommonProjectNode)(punk[0] as NodeProperties).HierarchyNode.ProjectMgr; diff --git a/Common/Product/SharedProject/ConfigProvider.cs b/Common/Product/SharedProject/ConfigProvider.cs index d6c028f245..1e1bdc0857 100644 --- a/Common/Product/SharedProject/ConfigProvider.cs +++ b/Common/Product/SharedProject/ConfigProvider.cs @@ -37,8 +37,11 @@ namespace Microsoft.VisualStudioTools.Project { [ComVisible(true)] internal abstract class ConfigProvider : IVsCfgProvider2 { internal const string configString = " '$(Configuration)' == '{0}' "; + internal const string configPlatformString = " '$(Configuration)|$(Platform)' == '{0}|{1}' "; internal const string AnyCPUPlatform = "Any CPU"; internal const string x86Platform = "x86"; + internal const string x64Platform = "x64"; + internal const string ARMPlatform = "ARM"; private ProjectNode project; private EventSinkCollection cfgEventSinks = new EventSinkCollection(); @@ -473,7 +476,7 @@ private string[] GetPlatformsFromProject() { string[] platforms = GetPropertiesConditionedOn(ProjectFileConstants.Platform); if (platforms == null || platforms.Length == 0) { - return new string[] { x86Platform, AnyCPUPlatform }; + return new string[] { x86Platform, AnyCPUPlatform, x64Platform, ARMPlatform }; } for (int i = 0; i < platforms.Length; i++) { @@ -487,7 +490,7 @@ private string[] GetPlatformsFromProject() { /// Return the supported platform names. /// /// An array of supported platform names. - private string[] GetSupportedPlatformsFromProject() { + internal string[] GetSupportedPlatformsFromProject() { string platforms = this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AvailablePlatforms); if (platforms == null) { @@ -523,7 +526,7 @@ private static string ConvertPlatformToVsProject(string oldPlatformName) { /// An array of available platform names /// A count of the actual number of platform names returned. /// The platforms array is never null. It is assured by the callers. - private static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) { + internal static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) { Utilities.ArgumentNotNull("platforms", platforms); if (names == null) { if (actual == null || actual.Length == 0) { @@ -564,7 +567,7 @@ private static int GetPlatforms(uint celt, string[] names, uint[] actual, string /// /// Get all the configurations in the project. /// - private string[] GetPropertiesConditionedOn(string constant) { + internal string[] GetPropertiesConditionedOn(string constant) { List configurations = null; this.project.BuildProject.ReevaluateIfNecessary(); this.project.BuildProject.ConditionedProperties.TryGetValue(constant, out configurations); diff --git a/Common/Product/SharedProject/ProjectConfig.cs b/Common/Product/SharedProject/ProjectConfig.cs index 187451f05d..6583008e4f 100644 --- a/Common/Product/SharedProject/ProjectConfig.cs +++ b/Common/Product/SharedProject/ProjectConfig.cs @@ -48,8 +48,19 @@ internal abstract class ProjectConfig : private IVsProjectFlavorCfg flavoredCfg; private List outputGroups; private BuildableProjectConfig buildableCfg; + private string platformName; #region properties + + public string PlatformName { + get { + return platformName; + } + set { + platformName = value; + } + } + internal ProjectNode ProjectMgr { get { return this.project; @@ -97,7 +108,20 @@ internal IList OutputGroups { #region ctors internal ProjectConfig(ProjectNode project, string configuration) { this.project = project; - this.configName = configuration; + if (configuration.Contains("|")) { // If configuration is in the form "|" + + string[] configStrArray = configuration.Split('|'); + if (2 == configStrArray.Length) { + this.configName = configStrArray[0]; + this.platformName = configStrArray[1]; + } + else { + throw new Exception(string.Format(CultureInfo.InvariantCulture, "Invalid configuration format: {0}", configuration)); + } + } + else { // If configuration is in the form "" + this.configName = configuration; + } var flavoredCfgProvider = ProjectMgr.GetOuterInterface(); Utilities.ArgumentNotNull("flavoredCfgProvider", flavoredCfgProvider); @@ -107,7 +131,7 @@ internal ProjectConfig(ProjectNode project, string configuration) { // if the flavored object support XML fragment, initialize it IPersistXMLFragment persistXML = flavoredCfg as IPersistXMLFragment; if (null != persistXML) { - this.project.LoadXmlFragment(persistXML, configName); + this.project.LoadXmlFragment(persistXML, configName, platformName); } } #endregion @@ -240,7 +264,12 @@ public virtual int GetProjectDesignerPages(CAUUID[] pages) { /// first part is the config name, 2nd part is the platform name /// public virtual int get_DisplayName(out string name) { - name = DisplayName; + if (!string.IsNullOrEmpty(PlatformName)) { + name = ConfigName + "|" + PlatformName; + + } else { + name = DisplayName; + } return VSConstants.S_OK; } diff --git a/Common/Product/SharedProject/ProjectFileConstants.cs b/Common/Product/SharedProject/ProjectFileConstants.cs index efd04b18e2..ff70be332b 100644 --- a/Common/Product/SharedProject/ProjectFileConstants.cs +++ b/Common/Product/SharedProject/ProjectFileConstants.cs @@ -89,6 +89,9 @@ public static class ProjectFileConstants { public const string FlavorProperties = "FlavorProperties"; public const string VisualStudio = "VisualStudio"; public const string User = "User"; + public const string PlatformAware = "PlatformAware"; + public const string AppxPackage = "AppxPackage"; + public const string WindowsAppContainer = "WindowsAppContainer"; } public static class ProjectFileAttributeValue { diff --git a/Common/Product/SharedProject/ProjectNode.cs b/Common/Product/SharedProject/ProjectNode.cs index 7c6cae1b8a..ebafc9d6ed 100644 --- a/Common/Product/SharedProject/ProjectNode.cs +++ b/Common/Product/SharedProject/ProjectNode.cs @@ -949,6 +949,12 @@ public override object GetProperty(int propId) { case __VSHPROPID.VSHPROPID_ExpandByDefault: return true; + case __VSHPROPID.VSHPROPID_DefaultEnableDeployProjectCfg: + return true; + + case __VSHPROPID.VSHPROPID_DefaultEnableBuildProjectCfg: + return true; + // Use the same icon as if the folder was closed case __VSHPROPID.VSHPROPID_OpenFolderIconIndex: return GetProperty((int)__VSHPROPID.VSHPROPID_IconIndex); @@ -2003,7 +2009,7 @@ protected virtual Guid[] GetConfigurationIndependentPropertyPages() { /// /// protected virtual Guid[] GetConfigurationDependentPropertyPages() { - return new Guid[0]; + return new Guid[] { Guid.Empty }; } /// @@ -2956,7 +2962,7 @@ protected internal virtual void ProcessDependentFileNodes(IList subitems protected internal virtual void LoadNonBuildInformation() { IPersistXMLFragment outerHierarchy = GetOuterInterface(); if (outerHierarchy != null) { - this.LoadXmlFragment(outerHierarchy, null); + this.LoadXmlFragment(outerHierarchy, null, null); } } @@ -3371,7 +3377,8 @@ protected void AddCATIDMapping(Type type, Guid catid) { /// /// Object that support being initialized with an XML fragment /// Name of the configuration being initialized, null if it is the project - protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName) { + /// Name of the platform being initialized, null is ok + protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName, string platformName) { Utilities.ArgumentNotNull("persistXmlFragment", persistXmlFragment); if (xmlFragments == null) { @@ -3395,15 +3402,20 @@ protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, if (child.Attributes.Count > 0) { string guid = String.Empty; string configuration = String.Empty; + string platform = String.Empty; if (child.Attributes[ProjectFileConstants.Guid] != null) guid = child.Attributes[ProjectFileConstants.Guid].Value; if (child.Attributes[ProjectFileConstants.Configuration] != null) configuration = child.Attributes[ProjectFileConstants.Configuration].Value; + if (child.Attributes[ProjectFileConstants.Platform] != null) + platform = child.Attributes[ProjectFileConstants.Platform].Value; if (String.Compare(child.Name, ProjectFileConstants.FlavorProperties, StringComparison.OrdinalIgnoreCase) == 0 && String.Compare(guid, flavorGuidString, StringComparison.OrdinalIgnoreCase) == 0 && ((String.IsNullOrEmpty(configName) && String.IsNullOrEmpty(configuration)) - || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0))) { + || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0)) + && ((String.IsNullOrEmpty(platformName) && String.IsNullOrEmpty(platform)) + || (String.Compare(platform, platformName, StringComparison.OrdinalIgnoreCase) == 0))) { // we found the matching fragment fragment = child.InnerXml; node = child; @@ -3463,7 +3475,7 @@ protected void PersistXMLFragments() { ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, out fragment, 1)); if (!String.IsNullOrEmpty(fragment)) { // Add the fragment to our XML - WrapXmlFragment(doc, root, flavor, null, fragment); + WrapXmlFragment(doc, root, flavor, null, null, fragment); } // While we don't yet support user files, our flavors might, so we will store that in the project file until then // TODO: Refactor this code when we support user files @@ -3471,7 +3483,7 @@ protected void PersistXMLFragments() { ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, out fragment, 1)); if (!String.IsNullOrEmpty(fragment)) { // Add the fragment to our XML - XmlElement node = WrapXmlFragment(doc, root, flavor, null, fragment); + XmlElement node = WrapXmlFragment(doc, root, flavor, null, null, fragment); node.Attributes.Append(doc.CreateAttribute(ProjectFileConstants.User)); } } @@ -3482,7 +3494,7 @@ protected void PersistXMLFragments() { string fragment; ErrorHandler.ThrowOnFailure(((ProjectConfig)config).GetXmlFragment(flavor, _PersistStorageType.PST_PROJECT_FILE, out fragment)); if (!String.IsNullOrEmpty(fragment)) { - WrapXmlFragment(doc, root, flavor, ((ProjectConfig)config).ConfigName, fragment); + WrapXmlFragment(doc, root, flavor, ((ProjectConfig)config).ConfigName, ((ProjectConfig)config).PlatformName, fragment); } } } @@ -5091,7 +5103,7 @@ internal void OnAfterProjectOpen() { this.projectOpened = true; } - private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string fragment) { + private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string platform, string fragment) { XmlElement node = document.CreateElement(ProjectFileConstants.FlavorProperties); XmlAttribute attribute = document.CreateAttribute(ProjectFileConstants.Guid); attribute.Value = flavor.ToString("B"); @@ -5100,6 +5112,9 @@ private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, attribute = document.CreateAttribute(ProjectFileConstants.Configuration); attribute.Value = configuration; node.Attributes.Append(attribute); + attribute = document.CreateAttribute(ProjectFileConstants.Platform); + attribute.Value = platform; + node.Attributes.Append(attribute); } node.InnerXml = fragment; root.AppendChild(node); diff --git a/Python/Product/Debugger/Debugger.csproj b/Python/Product/Debugger/Debugger.csproj index 78c71f2fd9..f80f6e6fde 100644 --- a/Python/Product/Debugger/Debugger.csproj +++ b/Python/Product/Debugger/Debugger.csproj @@ -82,6 +82,7 @@ global + diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs index 61332b3521..4a02742539 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs @@ -108,7 +108,16 @@ int IDebugBoundBreakpoint2.SetCondition(BP_CONDITION bpCondition) { } int IDebugBoundBreakpoint2.GetHitCount(out uint pdwHitCount) { - pdwHitCount = (uint)_breakpoint.GetHitCountAsync().GetAwaiter().GetResult(); + var remoteProcess = _engine.Process as Remote.PythonRemoteProcess; + if (remoteProcess != null && remoteProcess.TargetHostType == AD7Engine.TargetUap) { + // Target is UAP host and we will just assume breakpoint hit count is 1 from this + // remote debug type due to issues with communicating this command + pdwHitCount = 1; + } else { + var task = _breakpoint.GetHitCountAsync(); + pdwHitCount = (uint)task.GetAwaiter().GetResult(); + } + return VSConstants.S_OK; } diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs index 626412d044..73c39f03e6 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs @@ -89,6 +89,12 @@ public sealed class AD7Engine : IDebugEngine2, IDebugEngineLaunch2, IDebugProgra public const string DebugEngineId = "{EC1375B7-E2CE-43E8-BF75-DC638DE1F1F9}"; public const string DebugEngineName = "Python"; public static Guid DebugEngineGuid = new Guid(DebugEngineId); + public const string SourceDirectoryKey = "sd"; + public const string TargetDirectoryKey = "td"; + public const string TargetHostType = "host"; + + public const string TargetUap = "uap"; + /// /// Specifies the version of the language which is being debugged. One of /// V24, V25, V26, V27, V30, V31 or V32. diff --git a/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs b/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs index 8378c6473d..c1e24a034e 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs @@ -13,13 +13,13 @@ * ***************************************************************************/ using System; -using System.Diagnostics; using System.IO; -using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Text; +using System.Web; using System.Windows.Forms; +using Microsoft.PythonTools.Debugger.DebugEngine; using Microsoft.PythonTools.Debugger.Transports; using Microsoft.PythonTools.Parsing; @@ -37,10 +37,27 @@ internal class PythonRemoteProcess : PythonProcess { private PythonRemoteProcess(int pid, Uri uri, PythonLanguageVersion langVer) : base(pid, langVer) { Uri = uri; + ParseQueryString(); } public Uri Uri { get; private set; } + internal string TargetHostType { get; private set; } + + internal void ParseQueryString() { + if (Uri != null && Uri.Query != null) { + var queryParts = HttpUtility.ParseQueryString(Uri.Query); + + var sourceDir = queryParts[AD7Engine.SourceDirectoryKey]; + var targetDir = queryParts[AD7Engine.TargetDirectoryKey]; + + TargetHostType = queryParts[AD7Engine.TargetHostType]; + + if (!string.IsNullOrWhiteSpace(sourceDir) && !string.IsNullOrWhiteSpace(targetDir)) + AddDirMapping(new string[] { sourceDir, targetDir }); + } + } + /// /// Connects to and performs the initial handshake with the remote debugging server, verifying protocol signature and version number, /// and returns the opened stream in a state ready to receive further ptvsd commands (e.g. attach). diff --git a/Python/Product/Debugger/Debugger/PythonProcess.cs b/Python/Product/Debugger/Debugger/PythonProcess.cs index ad7cb7d06c..c1d8bf93dc 100644 --- a/Python/Product/Debugger/Debugger/PythonProcess.cs +++ b/Python/Product/Debugger/Debugger/PythonProcess.cs @@ -62,6 +62,7 @@ class PythonProcess : IDisposable { protected PythonProcess(int pid, PythonLanguageVersion languageVersion) { _pid = pid; _langVersion = languageVersion; + _dirMapping = new List(); } private PythonProcess(int pid) { @@ -185,6 +186,12 @@ public void Dispose() { GC.SuppressFinalize(this); } + internal void AddDirMapping(string[] mapping) { + if (mapping != null) { + _dirMapping.Add(mapping); + } + } + protected virtual void Dispose(bool disposing) { DebugConnectionListener.UnregisterProcess(_processGuid); @@ -627,6 +634,8 @@ private void HandleDebuggerOutput(Stream stream) { long tid = stream.ReadInt64(); string output = stream.ReadString(); + System.Diagnostics.Debug.WriteLine(output); + PythonThread thread; if (_threads.TryGetValue(tid, out thread)) { var outputEvent = DebuggerOutput; @@ -1043,16 +1052,14 @@ internal string MapFile(string file, bool toDebuggee = true) { string mapTo = mappingInfo[toDebuggee ? 1 : 0]; if (file.StartsWith(mapFrom, StringComparison.OrdinalIgnoreCase)) { - if (file.StartsWith(mapFrom, StringComparison.OrdinalIgnoreCase)) { - int len = mapFrom.Length; - if (!mappingInfo[0].EndsWith("\\")) { - len++; - } - - string newFile = Path.Combine(mapTo, file.Substring(len)); - Debug.WriteLine(String.Format("Filename mapped from {0} to {1}", file, newFile)); - return newFile; + int len = mapFrom.Length; + if (!mappingInfo[0].EndsWith("\\")) { + len++; } + + string newFile = Path.Combine(mapTo, file.Substring(len)); + Debug.WriteLine("Filename mapped from {0} to {1}", file, newFile); + return newFile; } } } diff --git a/Python/Product/Debugger/Debugger/PythonStackFrame.cs b/Python/Product/Debugger/Debugger/PythonStackFrame.cs index ceefa0e3f0..b9a40259df 100644 --- a/Python/Product/Debugger/Debugger/PythonStackFrame.cs +++ b/Python/Product/Debugger/Debugger/PythonStackFrame.cs @@ -204,7 +204,7 @@ public bool SetLineNumber(int lineNo) { /// And with the current statement being pass, the qualified name is "D.e in c in A.b". /// public string GetQualifiedFunctionName() { - var ast = _thread.Process.GetAst(_filename); + var ast = _thread.Process.GetAst(FileName); if (ast == null) { return FunctionName; } diff --git a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs index c17f8f1a48..e1fba47486 100644 --- a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs +++ b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs @@ -21,7 +21,7 @@ internal class TcpTransport : IDebuggerTransport { public const ushort DefaultPort = 5678; public Exception Validate(Uri uri) { - if (uri.PathAndQuery != "/") { + if (uri.AbsolutePath != "/") { return new FormatException("tcp:// URI cannot contain a path"); } return null; diff --git a/Python/Product/PythonTools/PythonConstants.cs b/Python/Product/PythonTools/PythonConstants.cs index 8c03ce41cd..825508593e 100644 --- a/Python/Product/PythonTools/PythonConstants.cs +++ b/Python/Product/PythonTools/PythonConstants.cs @@ -40,9 +40,9 @@ public static class PythonConstants { internal const string WebProjectFactoryGuid = "1b580a1a-fdb3-4b32-83e1-6407eb2722e6"; internal const string EditorFactoryGuid = "888888c4-36f9-4453-90aa-29fa4d2e5706"; internal const string ProjectNodeGuid = "8888881a-afb8-42b1-8398-e60d69ee864d"; - internal const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; - internal const string DebugPropertyPageGuid = "9A46BC86-34CB-4597-83E5-498E3BDBA20A"; - internal const string PublishPropertyPageGuid = "63DF0877-CF53-4975-B200-2B11D669AB00"; + public const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; + public const string DebugPropertyPageGuid = "9A46BC86-34CB-4597-83E5-498E3BDBA20A"; + public const string PublishPropertyPageGuid = "63DF0877-CF53-4975-B200-2B11D669AB00"; internal const string WebPropertyPageGuid = "76EED3B5-14B1-413B-937A-F6F79AC1F8C8"; internal const string EditorFactoryPromptForEncodingGuid = "CA887E0B-55C6-4AE9-B5CF-A2EEFBA90A3E"; diff --git a/Python/Product/PythonTools/PythonTools.csproj b/Python/Product/PythonTools/PythonTools.csproj index f99d53b296..3e7ec34a19 100644 --- a/Python/Product/PythonTools/PythonTools.csproj +++ b/Python/Product/PythonTools/PythonTools.csproj @@ -1153,6 +1153,10 @@ true PreserveNewest + + true + PreserveNewest + diff --git a/Python/Product/PythonTools/PythonTools/Extensions.cs b/Python/Product/PythonTools/PythonTools/Extensions.cs index 2347460773..2a783b45b0 100644 --- a/Python/Product/PythonTools/PythonTools/Extensions.cs +++ b/Python/Product/PythonTools/PythonTools/Extensions.cs @@ -28,6 +28,7 @@ using Microsoft.PythonTools.Editor.Core; using Microsoft.PythonTools.Intellisense; using Microsoft.PythonTools.Interpreter; +using Microsoft.PythonTools.InterpreterList; using Microsoft.PythonTools.Parsing.Ast; using Microsoft.PythonTools.Project; using Microsoft.PythonTools.Repl; @@ -43,15 +44,30 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudioTools; +using Microsoft.VisualStudioTools.Project; + +// Disables obsolete warning for ISmartTag* interfaces - http://go.microsoft.com/fwlink/?LinkId=394601 +#pragma warning disable 618 namespace Microsoft.PythonTools { - using Microsoft.PythonTools.InterpreterList; - using Task = System.Threading.Tasks.Task; #if INTERACTIVE_WINDOW using IReplEvaluator = IInteractiveEngine; #endif public static class Extensions { + internal static bool IsAppxPackageableProject(this ProjectNode projectNode) { + var appxProp = projectNode.BuildProject.GetPropertyValue(ProjectFileConstants.AppxPackage); + var containerProp = projectNode.BuildProject.GetPropertyValue(ProjectFileConstants.WindowsAppContainer); + var appxFlag = false; + var containerFlag = false; + + if (bool.TryParse(appxProp, out appxFlag) && bool.TryParse(containerProp, out containerFlag)) { + return appxFlag && containerFlag; + } else { + return false; + } + } + public static StandardGlyphGroup ToGlyphGroup(this PythonMemberType objectType) { StandardGlyphGroup group; switch (objectType) { @@ -702,8 +718,8 @@ internal static T Peek(this List list) { return list[list.Count - 1]; } - internal static Task StartNew(this TaskScheduler scheduler, Action func) { - return Task.Factory.StartNew(func, default(CancellationToken), TaskCreationOptions.None, scheduler); + internal static System.Threading.Tasks.Task StartNew(this TaskScheduler scheduler, Action func) { + return System.Threading.Tasks.Task.Factory.StartNew(func, default(CancellationToken), TaskCreationOptions.None, scheduler); } internal static int GetStartIncludingIndentation(this Node self, PythonAst ast) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs index 3f7bb01d1f..0db9e064df 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs @@ -45,7 +45,8 @@ internal class ImportSettings : DependencyObject { BottleProjectCustomization.Instance, DjangoProjectCustomization.Instance, FlaskProjectCustomization.Instance, - GenericWebProjectCustomization.Instance + GenericWebProjectCustomization.Instance, + UapProjectCustomization.Instance }; public ImportSettings(IInterpreterOptionsService service) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs index b70302caa8..4d527a928a 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs @@ -193,4 +193,28 @@ Dictionary groups project.AddImport(@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets"); } } + + class UapProjectCustomization : ProjectCustomization { + public static readonly ProjectCustomization Instance = new UapProjectCustomization(); + + private UapProjectCustomization() { } + + public override string DisplayName { + get { + return SR.GetString(SR.ImportWizardGenericWebProjectCustomization); + } + } + + public override void Process( + ProjectRootElement project, + Dictionary groups + ) { + ProjectPropertyGroupElement globals; + if (!groups.TryGetValue("Globals", out globals)) { + globals = project.AddPropertyGroup(); + } + + AddOrSetProperty(globals, "ProjectTypeGuids", "{c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e};{888888A0-9F3D-457C-B088-3A5042F75D52}"); + } + } } diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs index 4dcb084e1a..a39467aa4f 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs @@ -21,12 +21,35 @@ namespace Microsoft.PythonTools.Project { class PythonProjectConfig : CommonProjectConfig { private readonly PythonProjectNode _project; + private string _platform; public PythonProjectConfig(PythonProjectNode project, string configuration) : base(project, configuration) { _project = project; } + public override string Platform { + get { + return _platform; + } + set { + _platform = value; + } + } + + /// + /// The display name is a two part item + /// first part is the config name, 2nd part is the platform name + /// + public override int get_DisplayName(out string name) { + if (!string.IsNullOrEmpty(Platform)) { + name = ConfigName + "|" + Platform; + return VSConstants.S_OK; + } else { + return base.get_DisplayName(out name); + } + } + public override int DebugLaunch(uint flags) { if (_project.ShouldWarnOnLaunch) { var pyService = ProjectMgr.Site.GetPythonToolsService(); diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs index f1e1ca851c..737b03adb0 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs @@ -167,6 +167,19 @@ protected override Stream ProjectIconsImageStripStream { } } + protected internal override void SetCurrentConfiguration() { + base.SetCurrentConfiguration(); + + if (!IsProjectOpened) + return; + + if (this.IsAppxPackageableProject()) { + EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project; + + this.BuildProject.SetGlobalProperty(ProjectFileConstants.Platform, automationObject.ConfigurationManager.ActiveConfiguration.PlatformName); + } + } + internal int GetIconIndex(PythonProjectImageName name) { return ImageOffset + (int)name; } @@ -197,6 +210,9 @@ public override CommonFileNode CreateNonCodeFileNode(ProjectElement item) { return new PythonNonCodeFileNode(this, item); } + protected override ConfigProvider CreateConfigProvider() { + return new CommonConfigProvider(this); + } protected override ReferenceContainerNode CreateReferenceContainerNode() { return new PythonReferenceContainerNode(this); } @@ -307,14 +323,29 @@ public override int GenerateUniqueItemName(uint itemIdLoc, string ext, string su return base.GenerateUniqueItemName(itemIdLoc, ext, suggestedRoot, out itemName); } + public override MSBuildResult Build(string config, string target) { + if (this.IsAppxPackageableProject()) { + // Ensure that AnyCPU is not the default Platform if this is an AppX project + // Use x86 instead + var platform = this.BuildProject.GetPropertyValue(GlobalProperty.Platform.ToString()); + + if (platform == ProjectConfig.AnyCPU) { + this.BuildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), ConfigProvider.x86Platform); + } + } + return base.Build(config, target); + } + protected override void Reload() { - _searchPathContainer = new CommonSearchPathContainerNode(this); - this.AddChild(_searchPathContainer); - RefreshCurrentWorkingDirectory(); - RefreshSearchPaths(); - _interpretersContainer = new InterpretersContainerNode(this); - this.AddChild(_interpretersContainer); - RefreshInterpreters(alwaysCollapse: true); + if (!this.IsAppxPackageableProject()) { + _searchPathContainer = new CommonSearchPathContainerNode(this); + this.AddChild(_searchPathContainer); + RefreshCurrentWorkingDirectory(); + RefreshSearchPaths(); + _interpretersContainer = new InterpretersContainerNode(this); + this.AddChild(_interpretersContainer); + RefreshInterpreters(alwaysCollapse: true); + } OnProjectPropertyChanged += PythonProjectNode_OnProjectPropertyChanged; base.Reload(); diff --git a/Python/Product/PythonTools/visualstudio_py_debugger.py b/Python/Product/PythonTools/visualstudio_py_debugger.py index 5470c07cd9..485882081f 100644 --- a/Python/Product/PythonTools/visualstudio_py_debugger.py +++ b/Python/Product/PythonTools/visualstudio_py_debugger.py @@ -28,6 +28,7 @@ from os import path import ntpath import runpy +import datetime from codecs import BOM_UTF8 try: @@ -39,6 +40,7 @@ import visualstudio_py_util as _vspu except ImportError: import ptvsd.visualstudio_py_util as _vspu + to_bytes = _vspu.to_bytes exec_file = _vspu.exec_file exec_module = _vspu.exec_module @@ -207,7 +209,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, tb): send_lock.release() - + # start sending debug events again cur_thread = get_thread_from_id(thread.get_ident()) if cur_thread is not None: @@ -218,7 +220,7 @@ def __exit__(self, exc_type, exc_value, tb): detach_process() # swallow the exception, we're no longer debugging return True - + _SendLockCtx = _SendLockContextManager() SEND_BREAK_COMPLETE = False @@ -309,7 +311,7 @@ def is_repr_round_tripping(x): class StackOverflowException(Exception): pass else: StackOverflowException = RuntimeError - + ASBR = to_bytes('ASBR') SETL = to_bytes('SETL') THRF = to_bytes('THRF') @@ -349,7 +351,7 @@ def should_send_frame(frame): KNOWN_ZIPS = set() def is_file_in_zip(filename): - parent, name = path.split(filename) + parent, name = path.split(path.abspath(filename)) if parent in KNOWN_DIRECTORIES: return False elif parent in KNOWN_ZIPS: @@ -378,7 +380,7 @@ def lookup_local(frame, name): while bits and obj is not None and type(obj) is types.ModuleType: obj = getattr(obj, bits.pop(0), None) return obj - + if sys.version_info[0] >= 3: _EXCEPTIONS_MODULE = 'builtins' else: @@ -448,7 +450,7 @@ def should_break(self, thread, ex_type, ex_value, trace): break_type = BREAK_TYPE_NONE return break_type - + def is_handled(self, thread, ex_type, ex_value, trace): if trace is None: # get out if we didn't get a traceback @@ -459,9 +461,9 @@ def is_handled(self, thread, ex_type, ex_value, trace): # don't break if this is not the top of the traceback, # unless the previous frame was not debuggable return True - + cur_frame = trace.tb_frame - + while should_send_frame(cur_frame) and cur_frame.f_code is not None and cur_frame.f_code.co_filename is not None: filename = path.normcase(cur_frame.f_code.co_filename) if is_file_in_zip(filename): @@ -470,11 +472,11 @@ def is_handled(self, thread, ex_type, ex_value, trace): if not is_same_py_file(filename, __file__): handlers = self.handler_cache.get(filename) - + if handlers is None: # req handlers for this file from the debug engine self.handler_lock.acquire() - + with _SendLockCtx: write_bytes(conn, REQH) write_string(conn, filename) @@ -506,7 +508,7 @@ def is_handled(self, thread, ex_type, ex_value, trace): cur_frame = cur_frame.f_back return False - + def add_exception(self, name, mode=BREAK_MODE_UNHANDLED): if name.startswith(_EXCEPTIONS_MODULE + '.'): name = name[len(_EXCEPTIONS_MODULE) + 1:] @@ -535,7 +537,7 @@ def should_debug_code(code): filename = path.normcase(code.co_filename) if not DEBUG_STDLIB: for prefix in PREFIXES: - if filename.startswith(prefix): + if prefix != '' and filename.startswith(prefix): return False for dont_debug_file in DONT_DEBUG: @@ -558,7 +560,7 @@ def breakpoint_path_match(vs_path, local_path): local_path_norm = path.normcase(local_path) if local_path_to_vs_path.get(local_path_norm) == vs_path_norm: return True - + # Walk the local filesystem from local_path up, matching agains win_path component by component, # and stop when we no longer see an __init__.py. This should give a reasonably close approximation # of matching the package name. @@ -574,7 +576,7 @@ def breakpoint_path_match(vs_path, local_path): # needed to, and matched all names on our way, so this is a match. if not path.exists(path.join(local_path, '__init__.py')): break - + local_path_to_vs_path[local_path_norm] = vs_path_norm return True @@ -582,26 +584,26 @@ def update_all_thread_stacks(blocking_thread = None, check_is_blocked = True): THREADS_LOCK.acquire() all_threads = list(THREADS.values()) THREADS_LOCK.release() - + for cur_thread in all_threads: if cur_thread is blocking_thread: continue - + cur_thread._block_starting_lock.acquire() if not check_is_blocked or not cur_thread._is_blocked: # release the lock, we're going to run user code to evaluate the frames cur_thread._block_starting_lock.release() - + frames = cur_thread.get_frame_list() - + # re-acquire the lock and make sure we're still not blocked. If so send # the frame list. cur_thread._block_starting_lock.acquire() if not check_is_blocked or not cur_thread._is_blocked: cur_thread.send_frame_list(frames) - + cur_thread._block_starting_lock.release() - + DJANGO_BREAKPOINTS = {} class DjangoBreakpointInfo(object): @@ -609,13 +611,13 @@ def __init__(self, filename): self._line_locations = None self.filename = filename self.breakpoints = {} - + def add_breakpoint(self, lineno, brkpt_id): self.breakpoints[lineno] = brkpt_id def remove_breakpoint(self, lineno): del self.breakpoints[lineno] - + @property def line_locations(self): if self._line_locations is None: @@ -752,7 +754,7 @@ def new_f(old_f, args, kwargs): tsk.tempval = new_f stackless.tasklet.setup(tsk, f, args, kwargs) return tsk - + def settrace(tsk, tb): if hasattr(tsk.frame, "f_trace"): tsk.frame.f_trace = tb @@ -764,7 +766,7 @@ def settrace(tsk, tb): stackless.set_schedule_callback(self._legacy_stackless_schedule_cb) else: stackless.set_schedule_callback(self._stackless_schedule_cb) - + def _stackless_legacy_schedule_cb(self, old, new): self.stepping = STEPPING_NONE # for those tasklets that started before we started tracing @@ -843,7 +845,7 @@ def trace_func(self, frame, event, arg): except (StackOverflowException, KeyboardInterrupt): # stack overflow, disable tracing return self.trace_func - + def handle_call(self, frame, arg): self.push_frame(frame) @@ -851,7 +853,7 @@ def handle_call(self, frame, arg): source_obj = get_django_frame_source(frame) if source_obj is not None: origin, (start, end) = source_obj - + active_bps = DJANGO_BREAKPOINTS.get(origin.name.lower()) should_break = False if active_bps is not None: @@ -906,7 +908,7 @@ def handle_call(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'call', arg) return self.trace_func - + def should_block_on_frame(self, frame): if not should_debug_code(frame.f_code): return False @@ -942,7 +944,7 @@ def handle_line(self, frame, arg): if self.should_block_on_frame(frame): # don't step complete in our own debugger / non-user code step_complete = True elif stepping == STEPPING_LAUNCH_BREAK or stepping == STEPPING_ATTACH_BREAK: - # If launching rather than attaching, don't break into inital Python code needed to set things up + # If launching rather than attaching, don't break into initial Python code needed to set things up if stepping == STEPPING_LAUNCH_BREAK and (not MODULES or not self.should_block_on_frame(frame)): handle_breakpoints = False else: @@ -1027,7 +1029,7 @@ def handle_line(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'line', arg) return self.trace_func - + def handle_return(self, frame, arg): self.pop_frame() @@ -1064,7 +1066,7 @@ def handle_return(self, frame, arg): # restore previous frames trace function if there is one if self.trace_func_stack: self.prev_trace_func = self.trace_func_stack.pop() - + def handle_exception(self, frame, arg): if self.stepping == STEPPING_ATTACH_BREAK: self.block_maybe_attach() @@ -1082,15 +1084,15 @@ def handle_exception(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'exception', arg) return self.trace_func - + def handle_c_call(self, frame, arg): # break points? pass - + def handle_c_return(self, frame, arg): # step out of ? pass - + def handle_c_exception(self, frame, arg): pass @@ -1104,7 +1106,7 @@ def block_maybe_attach(self): will_block_now = False attach_sent_break = True attach_lock.release() - + probe_stack() stepping = self.stepping self.stepping = STEPPING_NONE @@ -1120,7 +1122,7 @@ def block_cond(): return report_process_loaded(self.id) update_all_thread_stacks(self) self.block(block_cond) - + def async_break(self): def async_break_send(): with _SendLockCtx: @@ -1146,10 +1148,10 @@ def block(self, block_lambda, keep_stopped_on_line = False): """blocks the current thread until the debugger resumes it""" assert not self._is_blocked #assert self.id == thread.get_ident(), 'wrong thread identity' + str(self.id) + ' ' + str(thread.get_ident()) # we should only ever block ourselves - + # send thread frames before we block self.enum_thread_frames_locally() - + if not keep_stopped_on_line: self.stopped_on_line = self.cur_frame.f_lineno @@ -1169,7 +1171,7 @@ def block(self, block_lambda, keep_stopped_on_line = False): self.unblock_work() self.unblock_work = None self._is_working = False - + self._block_starting_lock.acquire() assert self._is_blocked self._is_blocked = False @@ -1179,7 +1181,7 @@ def unblock(self): """unblocks the current thread allowing it to continue to run""" assert self._is_blocked assert self.id != thread.get_ident() # only someone else should unblock us - + self._block_lock.release() def schedule_work(self, work): @@ -1188,26 +1190,26 @@ def schedule_work(self, work): def run_on_thread(self, text, cur_frame, execution_id, frame_kind, repr_kind = PYTHON_EVALUATION_RESULT_REPR_KIND_NORMAL): self._block_starting_lock.acquire() - + if not self._is_blocked: report_execution_error('', execution_id) elif not self._is_working: self.schedule_work(lambda : self.run_locally(text, cur_frame, execution_id, frame_kind, repr_kind)) else: report_execution_error('', execution_id) - + self._block_starting_lock.release() def run_on_thread_no_report(self, text, cur_frame, frame_kind): self._block_starting_lock.acquire() - + if not self._is_blocked: pass elif not self._is_working: self.schedule_work(lambda : self.run_locally_no_report(text, cur_frame, frame_kind)) else: pass - + self._block_starting_lock.release() def enum_child_on_thread(self, text, cur_frame, execution_id, frame_kind): @@ -1331,7 +1333,7 @@ def enum_child_locally(self, expr, cur_frame, execution_id, frame_kind): break key_repr = safe_repr(key) - + # Some objects are enumerable but not indexable, or repr(key) is not a valid Python expression. For those, we # cannot use obj[key] to get the item by its key, and have to retrieve it by index from enumerate() instead. try: @@ -1362,7 +1364,7 @@ def enum_child_locally(self, expr, cur_frame, execution_id, frame_kind): def get_frame_list(self): frames = [] cur_frame = self.cur_frame - + while should_send_frame(cur_frame): # calculate the ending line number lineno = cur_frame.f_code.co_firstlineno @@ -1409,7 +1411,7 @@ def get_frame_list(self): f_globals = cur_frame.f_globals if f_globals: # ensure globals to work with (IPy may have None for cur_frame.f_globals for frames within stdlib) self.collect_variables(vars, f_globals, cur_frame.f_code.co_names, treated, skip_unknown = True) - + frame_info = None if source_obj is not None: @@ -1451,9 +1453,9 @@ def get_frame_list(self): ) frames.append(frame_info) - + cur_frame = cur_frame.f_back - + return frames def collect_variables(self, vars, objects, names, treated, skip_unknown = False): @@ -1481,7 +1483,7 @@ def send_frame_list(self, frames, thread_name = None): write_bytes(conn, THRF) write_int(conn, self.id) write_string(conn, thread_name) - + # send the frame count write_int(conn, len(frames)) for firstlineno, lineno, curlineno, name, filename, argcount, variables, frameKind, sourceFile, sourceLine in frames: @@ -1489,16 +1491,16 @@ def send_frame_list(self, frames, thread_name = None): write_int(conn, firstlineno) write_int(conn, lineno) write_int(conn, curlineno) - + write_string(conn, name) write_string(conn, filename) write_int(conn, argcount) - + write_int(conn, frameKind) if frameKind == FRAME_KIND_DJANGO: write_string(conn, sourceFile) write_int(conn, sourceLine) - + write_int(conn, len(variables)) for name, type_obj, safe_repr_obj, hex_repr_obj, type_name, obj_len in variables: write_string(conn, name) @@ -1604,7 +1606,7 @@ def loop(self): pass except: traceback.print_exc() - + def command_step_into(self): tid = read_int(self.conn) thread = get_thread_from_id(tid) @@ -1620,7 +1622,7 @@ def command_step_out(self): assert thread._is_blocked thread.stepping = STEPPING_OUT self.command_resume_all() - + def command_step_over(self): # set step over tid = read_int(self.conn) @@ -1660,7 +1662,7 @@ def command_set_breakpoint_condition(self): breakpoint_id = read_int(self.conn) kind = read_int(self.conn) condition = read_string(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) if bp is not None: bp.condition_kind = kind @@ -1679,7 +1681,7 @@ def command_set_breakpoint_pass_count(self): def command_set_breakpoint_hit_count(self): breakpoint_id = read_int(self.conn) count = read_int(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) if bp is not None: bp.hit_count = count @@ -1687,7 +1689,7 @@ def command_set_breakpoint_hit_count(self): def command_get_breakpoint_hit_count(self): req_id = read_int(self.conn) breakpoint_id = read_int(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) count = 0 if bp is not None: @@ -1767,7 +1769,7 @@ def command_resume_all(self): if thread._is_blocked: thread.unblock() thread._block_starting_lock.release() - + def command_resume_thread(self): tid = read_int(self.conn) THREADS_LOCK.acquire() @@ -1880,11 +1882,11 @@ def command_enum_children(self): fid = read_int(self.conn) # frame id eid = read_int(self.conn) # execution id frame_kind = read_int(self.conn) # frame kind - + thread, cur_frame = self.get_thread_and_frame(tid, fid, frame_kind) if thread is not None and cur_frame is not None: thread.enum_child_on_thread(text, cur_frame, eid, frame_kind) - + def get_thread_and_frame(self, tid, fid, frame_kind): thread = get_thread_from_id(tid) cur_frame = None @@ -1913,7 +1915,7 @@ def command_detach(self): for callback in DETACH_CALLBACKS: callback() - + raise DebuggerExitException() def command_last_ack(self): @@ -1959,12 +1961,12 @@ def report_exception(frame, exc_info, tid, break_type): exc_name = get_exception_name(exc_type) exc_value = exc_info[1] tb_value = exc_info[2] - + if type(exc_value) is tuple: # exception object hasn't been created yet, create it now # so we can get the correct msg. exc_value = exc_type(*exc_value) - + excp_text = str(exc_value) with _SendLockCtx: @@ -2220,7 +2222,7 @@ def detach_threads(): THREADS_LOCK.acquire() THREADS.clear() THREADS_LOCK.release() - + BREAKPOINTS.clear() def new_thread(tid = None, set_break = False, frame = None): @@ -2282,11 +2284,11 @@ def __init__(self, old_out, is_stdout): def flush(self): if self.old_out: self.old_out.flush() - + def writelines(self, lines): for line in lines: self.write(line) - + @property def encoding(self): return 'utf8' @@ -2300,13 +2302,13 @@ def write(self, value): write_string(conn, value) if self.old_out: self.old_out.write(value) - + def isatty(self): return True def next(self): pass - + @property def name(self): if self.is_stdout: @@ -2366,8 +2368,8 @@ def print_exception(exc_type, exc_value, exc_tb): if tb: print('Traceback (most recent call last):') for out in traceback.format_list(tb): - sys.stdout.write(out) - + sys.stderr.write(out) + # print the exception for out in traceback.format_exception_only(exc_type, exc_value): sys.stdout.write(out) diff --git a/Python/Product/PythonTools/visualstudio_py_remote_launcher.py b/Python/Product/PythonTools/visualstudio_py_remote_launcher.py new file mode 100644 index 0000000000..1ded935b52 --- /dev/null +++ b/Python/Product/PythonTools/visualstudio_py_remote_launcher.py @@ -0,0 +1,114 @@ + # ############################################################################ + # + # Copyright (c) Microsoft Corporation. + # + # This source code is subject to terms and conditions of the Apache License, Version 2.0. A + # copy of the license can be found in the License.html file at the root of this distribution. If + # you cannot locate the Apache License, Version 2.0, please send an email to + # vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + # by the terms of the Apache License, Version 2.0. + # + # You must not remove this notice, or any other, from this software. + # + # ########################################################################### + +""" +Starts Debugging, expected to start with normal program +to start as first argument and directory to run from as +the second argument. +""" +import os +import sys +import ptvsd +import visualstudio_py_debugger as vspd + +def debug_remote( + file, + port_num, + debug_id, + wait_on_exception, + redirect_output, + wait_on_exit, + break_on_systemexit_zero, + debug_stdlib, + run_as +): + global BREAK_ON_SYSTEMEXIT_ZERO, DEBUG_STDLIB + BREAK_ON_SYSTEMEXIT_ZERO = break_on_systemexit_zero + DEBUG_STDLIB = debug_stdlib + + print('Remote launcher starting ptvsd attach wait with File: %s, Port: %d, Id: %s\n' % (file, port_num, debug_id)) + + ptvsd.enable_attach(debug_id, address = ('0.0.0.0', port_num), redirect_output = redirect_output) + ptvsd.wait_for_attach() + + # now execute main file + globals_obj = {'__name__': '__main__'} + if run_as == 'module': + vspd.exec_module(file, globals_obj) + elif run_as == 'code': + vspd.exec_code(file, '', globals_obj) + else: + vspd.exec_file(file, globals_obj) + +# arguments are port, debug id, normal arguments which should include a filename to execute + +# change to directory we expected to start from +port_num = int(sys.argv[1]) +debug_id = sys.argv[2] + +del sys.argv[0:3] + +wait_on_exception = False +redirect_output = False +wait_on_exit = False +break_on_systemexit_zero = False +debug_stdlib = False +run_as = 'script' + +for opt in [ + # Order is important for these options. + 'redirect_output', + 'wait_on_exception', + 'wait_on_exit', + 'break_on_systemexit_zero', + 'debug_stdlib' +]: + if sys.argv and sys.argv[0] == '--' + opt.replace('_', '-'): + globals()[opt] = True + del sys.argv[0] + +# set run_as mode appropriately +if sys.argv and sys.argv[0] == '-m': + run_as = 'module' + del sys.argv[0] + +if sys.argv and sys.argv[0] == '-c': + run_as = 'code' + del sys.argv[0] + +# preserve filename before we del sys +file = sys.argv[0] + +# fix sys.path to be the script file dir +sys.path[0] = '' + +# exclude ourselves from being debugged +vspd.DONT_DEBUG.append(os.path.normcase(__file__)) + +# remove all state we imported +del sys, os + +import sys + +debug_remote( + file, + port_num, + debug_id, + wait_on_exception, + redirect_output, + wait_on_exit, + break_on_systemexit_zero, + debug_stdlib, + run_as +) diff --git a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs new file mode 100644 index 0000000000..8c68c53ab9 --- /dev/null +++ b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs @@ -0,0 +1,118 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Xml.Linq; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger; +using Microsoft.VisualStudio.Debugger.ComponentInterfaces; +using Microsoft.VisualStudio.Debugger.Exceptions; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Debugger { + internal class PythonRemoteDebugEvents : IVsDebuggerEvents, IDebugEventCallback2, IDkmExceptionTriggerHitNotification { + private static readonly Lazy instance = new Lazy(); + + // This exception code is a Win32 exception that is expected to be thrown by the debug client to trigger this code flow + // i.e. whatever remote process is launching the debug server, should throw and catch this exception code before starting + // remote Python debug server to have automatic attach work + internal const uint RemoteDebugStartExceptionCode = 0xEDCBA987; + + public const string RemoteDebugExceptionId = "E7DD0845-FB1A-4A45-8192-44953C0ACC51"; + public static readonly Guid RemoteDebugExceptionGuid = new Guid(RemoteDebugExceptionId); + + public static PythonRemoteDebugEvents Instance { + get { return instance.Value; } + } + + public Func AttachRemoteProcessFunction { get; set; } + + public string AttachRemoteDebugXml { get; set; } + + public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { + try { + if (riidEvent == typeof(IDebugProgramCreateEvent2).GUID) { + Guid processId; + + // A program was created and attached + if (pProcess != null) { + if (VSConstants.S_OK == pProcess.GetProcessId(out processId)) { + DkmProcess dkmProcess = DkmProcess.FindProcess(processId); + + if (dkmProcess != null) { + var debugTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugStartExceptionCode); + + // Try to add exception trigger for when a remote debugger server is started for Python + dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, debugTrigger); + } + } + } + } + + return VSConstants.S_OK; + } finally { + if (pEngine != null && Marshal.IsComObject(pEngine)) { + Marshal.ReleaseComObject(pEngine); + } + + if (pProcess != null && Marshal.IsComObject(pProcess)) { + Marshal.ReleaseComObject(pProcess); + } + + if (pProgram != null && Marshal.IsComObject(pProgram)) { + Marshal.ReleaseComObject(pProgram); + } + + if (pThread != null && Marshal.IsComObject(pThread)) { + Marshal.ReleaseComObject(pThread); + } + + if (pEvent != null && Marshal.IsComObject(pEvent)) { + Marshal.ReleaseComObject(pEvent); + } + } + } + + void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTriggerHit hit, DkmEventDescriptorS eventDescriptor) { + var remoteProcessTask = default(System.Threading.Tasks.Task); + + using (var evt = new System.Threading.ManualResetEvent(false)) { + + ThreadHelper.Generic.Invoke(() => { + try { + var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; + + // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration + // of the debugger + const int exceptionParameterCount = 2; + + if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { + // If we have a port and debug id, we'll go ahead and tell the client we are present + if (Instance.AttachRemoteProcessFunction != null && Instance.AttachRemoteDebugXml != null) { + // Write back that debugger is present + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); + + // Write back the details of the debugger arguments + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Encoding.Unicode.GetBytes(Instance.AttachRemoteDebugXml)); + } + } + } finally { + evt.Set(); + } + }); + + evt.WaitOne(); + + eventDescriptor.Suppress(); + + // Start the task to attach to the remote Python debugger session + remoteProcessTask = System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); + } + } + + public int OnModeChange(DBGMODE dbgmodeNew) { + return VSConstants.S_OK; + } + } +} diff --git a/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml b/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml new file mode 100644 index 0000000000..1880da70bd --- /dev/null +++ b/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Python/Product/Uap/Extensions.cs b/Python/Product/Uap/Extensions.cs new file mode 100644 index 0000000000..576c1f360d --- /dev/null +++ b/Python/Product/Uap/Extensions.cs @@ -0,0 +1,144 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.PythonTools.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudioTools.Project; +using Microsoft.VisualStudioTools.Project.Automation; + +namespace Microsoft.PythonTools.Uap { + static class Extensions { + + internal static string GetProjectProperty(this IVsHierarchy projectNode, string name) { + return projectNode.GetProject().GetPythonProject().GetProperty(name); + } + + internal static bool IsAppxPackageableProject(this IVsHierarchy projectNode) { + var appxProp = projectNode.GetProjectProperty(ProjectFileConstants.AppxPackage); + var containerProp = projectNode.GetProjectProperty(ProjectFileConstants.WindowsAppContainer); + + return Convert.ToBoolean(appxProp) && Convert.ToBoolean(containerProp); + } + + internal static IPythonProject2 GetPythonProject(this EnvDTE.Project project) { + return project.GetCommonProject() as IPythonProject2; + } + + internal static EnvDTE.Project GetProject(this IVsHierarchy hierarchy) { + object project; + + ErrorHandler.ThrowOnFailure( + hierarchy.GetProperty( + VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ExtObject, + out project + ) + ); + + return (project as EnvDTE.Project); + } + + internal static object GetCommonProject(this EnvDTE.Project project) { + OAProject oaProj = project as OAProject; + if (oaProj != null) { + var common = oaProj.Project; + if (common != null) { + return common; + } + } + return null; + } + + internal static Guid GetItemType(this VSITEMSELECTION vsItemSelection) { + Guid typeGuid; + try { + ErrorHandler.ThrowOnFailure( + vsItemSelection.pHier.GetGuidProperty( + vsItemSelection.itemid, + (int)__VSHPROPID.VSHPROPID_TypeGuid, + out typeGuid + ) + ); + } catch (System.Runtime.InteropServices.COMException) { + return Guid.Empty; + } + return typeGuid; + } + + internal static bool IsFolder(this VSITEMSELECTION item) { + return item.GetItemType() == VSConstants.GUID_ItemType_PhysicalFolder || + item.itemid == VSConstants.VSITEMID_ROOT; + } + + internal static bool IsNonMemberItem(this VSITEMSELECTION item) { + object obj; + try { + ErrorHandler.ThrowOnFailure( + item.pHier.GetProperty( + item.itemid, + (int)__VSHPROPID.VSHPROPID_IsNonMemberItem, + out obj + ) + ); + } catch (System.Runtime.InteropServices.COMException) { + return false; + } + return (obj as bool?) ?? false; + } + + internal static string Name(this VSITEMSELECTION item) { + return item.pHier.GetItemName(item.itemid); + } + + internal static string GetItemName(this IVsHierarchy hier, uint itemid) { + object name; + ErrorHandler.ThrowOnFailure(hier.GetProperty(itemid, (int)__VSHPROPID.VSHPROPID_Name, out name)); + return (string)name; + } + + internal static VSITEMSELECTION GetParent(this VSITEMSELECTION vsItemSelection) { + object parent; + ErrorHandler.ThrowOnFailure( + vsItemSelection.pHier.GetProperty( + vsItemSelection.itemid, + (int)__VSHPROPID.VSHPROPID_Parent, + out parent + ) + ); + + var res = new VSITEMSELECTION(); + var i = parent as int?; + if (i.HasValue) { + res.itemid = (uint)i.GetValueOrDefault(); + } else { + var ip = parent as IntPtr?; + res.itemid = (uint)ip.GetValueOrDefault().ToInt32(); + } + + res.pHier = vsItemSelection.pHier; + return res; + } + + internal static VSITEMSELECTION GetParentFolder(this VSITEMSELECTION vsItemSelection) { + var parent = vsItemSelection.GetParent(); + while (!parent.IsFolder()) { + parent = parent.GetParent(); + } + return parent; + } + } +} diff --git a/Python/Product/Uap/Guids.cs b/Python/Product/Uap/Guids.cs new file mode 100644 index 0000000000..815e0e5dc1 --- /dev/null +++ b/Python/Product/Uap/Guids.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// Guids.cs +// MUST match guids.h +using System; + +namespace Microsoft.PythonTools.Uap { + static class GuidList { + public const string guidUapPkgString = "0a078d3c-15a9-47f5-8418-9ee5db43993d"; + public const string guidUapFactoryString = "c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e"; + public const string guidUapPropertyPageString = "700c8e09-f81c-4fb8-a386-508fb48c372d"; + public static readonly Guid guidOfficeSharePointCmdSet = new Guid("d26c976c-8ee8-4ec4-8746-f5f7702a17c5"); + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs new file mode 100644 index 0000000000..c275773806 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs @@ -0,0 +1,237 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PythonTools.Analysis; +using Microsoft.PythonTools.Interpreter; +using Microsoft.VisualStudioTools; + +namespace Microsoft.PythonTools.Uap.Interpreter { + class PythonUapInterpreter : IPythonInterpreter, IPythonInterpreterWithProjectReferences2, IDisposable { + readonly Version _langVersion; + private PythonInterpreterFactoryWithDatabase _factory; + private PythonTypeDatabase _typeDb; + private HashSet _references; + + public PythonUapInterpreter(PythonInterpreterFactoryWithDatabase factory) { + _langVersion = factory.Configuration.Version; + _factory = factory; + _typeDb = _factory.GetCurrentDatabase(); + _factory.NewDatabaseAvailable += OnNewDatabaseAvailable; + } + + private async void OnNewDatabaseAvailable(object sender, EventArgs e) { + var factory = _factory; + if (factory == null) { + // We have been disposed already, so ignore this event + return; + } + + _typeDb = factory.GetCurrentDatabase(); + + if (_references != null) { + _typeDb = _typeDb.Clone(); + foreach (var reference in _references) { + string modName; + try { + modName = Path.GetFileNameWithoutExtension(reference.Name); + } catch (ArgumentException) { + continue; + } + + await Task.Yield(); + } + } + + var evt = ModuleNamesChanged; + if (evt != null) { + evt(this, EventArgs.Empty); + } + } + + #region IPythonInterpreter Members + + public IPythonType GetBuiltinType(BuiltinTypeId id) { + if (id == BuiltinTypeId.Unknown) { + return null; + } + + if (_typeDb == null) { + throw new KeyNotFoundException(string.Format("{0} ({1})", id, (int)id)); + } + + var name = GetBuiltinTypeName(id, _typeDb.LanguageVersion); + var res = _typeDb.BuiltinModule.GetAnyMember(name) as IPythonType; + if (res == null) { + throw new KeyNotFoundException(string.Format("{0} ({1})", id, (int)id)); + } + return res; + } + + + public IList GetModuleNames() { + if (_typeDb == null) { + return new string[0]; + } + return new List(_typeDb.GetModuleNames()); + } + + public IPythonModule ImportModule(string name) { + if (_typeDb == null) { + return null; + } + return _typeDb.GetModule(name); + } + + public IModuleContext CreateModuleContext() { + return null; + } + + public void Initialize(PythonAnalyzer state) { + } + + public event EventHandler ModuleNamesChanged; + + public Task AddReferenceAsync(ProjectReference reference, CancellationToken cancellationToken = default(CancellationToken)) { + if (reference == null) { + return MakeExceptionTask(new ArgumentNullException("reference")); + } + + if (_references == null) { + _references = new HashSet(); + // If we needed to set _references, then we also need to clone + // _typeDb to avoid adding modules to the shared database. + if (_typeDb != null) { + _typeDb = _typeDb.Clone(); + } + } + + switch (reference.Kind) { + case ProjectReferenceKind.ExtensionModule: + _references.Add(reference); + string filename; + try { + filename = Path.GetFileNameWithoutExtension(reference.Name); + } catch (Exception e) { + return MakeExceptionTask(e); + } + + if (_typeDb != null) { + return Task.Factory.StartNew(EmptyTask); + } + break; + } + + return Task.Factory.StartNew(EmptyTask); + } + + public void RemoveReference(ProjectReference reference) { + switch (reference.Kind) { + case ProjectReferenceKind.ExtensionModule: + if (_references != null && _references.Remove(reference) && _typeDb != null) { + RaiseModulesChanged(null); + } + break; + } + } + + public IEnumerable GetReferences() { + return _references != null ? _references : Enumerable.Empty(); + } + + private static Task MakeExceptionTask(Exception e) { + var res = new TaskCompletionSource(); + res.SetException(e); + return res.Task; + } + + private static void EmptyTask() { + } + + private void RaiseModulesChanged(Task task) { + if (task != null && task.Exception != null) { + throw task.Exception; + } + var modNamesChanged = ModuleNamesChanged; + if (modNamesChanged != null) { + modNamesChanged(this, EventArgs.Empty); + } + } + + public static string GetBuiltinTypeName(BuiltinTypeId id, Version languageVersion) { + string name; + switch (id) { + case BuiltinTypeId.Bool: name = "bool"; break; + case BuiltinTypeId.Complex: name = "complex"; break; + case BuiltinTypeId.Dict: name = "dict"; break; + case BuiltinTypeId.Float: name = "float"; break; + case BuiltinTypeId.Int: name = "int"; break; + case BuiltinTypeId.List: name = "list"; break; + case BuiltinTypeId.Long: name = languageVersion.Major == 3 ? "int" : "long"; break; + case BuiltinTypeId.Object: name = "object"; break; + case BuiltinTypeId.Set: name = "set"; break; + case BuiltinTypeId.Str: name = "str"; break; + case BuiltinTypeId.Unicode: name = languageVersion.Major == 3 ? "str" : "unicode"; break; + case BuiltinTypeId.Bytes: name = languageVersion.Major == 3 ? "bytes" : "str"; break; + case BuiltinTypeId.Tuple: name = "tuple"; break; + case BuiltinTypeId.Type: name = "type"; break; + + case BuiltinTypeId.BuiltinFunction: name = "builtin_function"; break; + case BuiltinTypeId.BuiltinMethodDescriptor: name = "builtin_method_descriptor"; break; + case BuiltinTypeId.DictKeys: name = "dict_keys"; break; + case BuiltinTypeId.DictValues: name = "dict_values"; break; + case BuiltinTypeId.DictItems: name = "dict_items"; break; + case BuiltinTypeId.Function: name = "function"; break; + case BuiltinTypeId.Generator: name = "generator"; break; + case BuiltinTypeId.NoneType: name = "NoneType"; break; + case BuiltinTypeId.Ellipsis: name = "ellipsis"; break; + case BuiltinTypeId.Module: name = "module_type"; break; + case BuiltinTypeId.ListIterator: name = "list_iterator"; break; + case BuiltinTypeId.TupleIterator: name = "tuple_iterator"; break; + case BuiltinTypeId.SetIterator: name = "set_iterator"; break; + case BuiltinTypeId.StrIterator: name = "str_iterator"; break; + case BuiltinTypeId.UnicodeIterator: name = languageVersion.Major == 3 ? "str_iterator" : "unicode_iterator"; break; + case BuiltinTypeId.BytesIterator: name = languageVersion.Major == 3 ? "bytes_iterator" : "str_iterator"; break; + case BuiltinTypeId.CallableIterator: name = "callable_iterator"; break; + + case BuiltinTypeId.Property: name = "property"; break; + case BuiltinTypeId.ClassMethod: name = "classmethod"; break; + case BuiltinTypeId.StaticMethod: name = "staticmethod"; break; + case BuiltinTypeId.FrozenSet: name = "frozenset"; break; + + case BuiltinTypeId.Unknown: + default: + return null; + } + return name; + } + #endregion + + + public void Dispose() { + _typeDb = null; + + var factory = _factory; + _factory = null; + if (factory != null) { + factory.NewDatabaseAvailable -= OnNewDatabaseAvailable; + } + } + } +} diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs new file mode 100644 index 0000000000..c29800ad48 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.PythonTools.Interpreter; + +namespace Microsoft.PythonTools.Uap.Interpreter { + class PythonUapInterpreterFactory : PythonInterpreterFactoryWithDatabase { + public const string InterpreterGuidString = "{86767848-40B4-4007-8BCC-A3835EDF0E69}"; + public static readonly Guid InterpreterGuid = new Guid(InterpreterGuidString); + + public PythonUapInterpreterFactory() + : base( + InterpreterGuid, + "Python UAP", + new InterpreterConfiguration(new Version(3, 5)), + true) { + } + + public override string Description { + get { + return base.Description; + } + } + public override IPythonInterpreter MakeInterpreter(PythonInterpreterFactoryWithDatabase factory) { + return new PythonUapInterpreter(factory); + } + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs new file mode 100644 index 0000000000..aca47017f9 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.PythonTools.Interpreter; + +namespace Microsoft.PythonTools.Uap.Interpreter { + [Export(typeof(IPythonInterpreterFactoryProvider))] + class PythonUapInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { + private HashSet _factories = null; + + public event EventHandler InterpreterFactoriesChanged; + + public PythonUapInterpreterFactoryProvider() { + } + + private void DiscoverFactories() { + if (_factories == null) { + _factories = new HashSet(); + + _factories.Add(new PythonUapInterpreterFactory()); + + if (InterpreterFactoriesChanged != null) { + InterpreterFactoriesChanged(this, new EventArgs()); + } + } + } + + public IEnumerable GetInterpreterFactories() { + DiscoverFactories(); + return _factories; + } + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Microsoft.PythonTools.Uap.targets b/Python/Product/Uap/Microsoft.PythonTools.Uap.targets new file mode 100644 index 0000000000..5480c6c45d --- /dev/null +++ b/Python/Product/Uap/Microsoft.PythonTools.Uap.targets @@ -0,0 +1,66 @@ + + + + + + ARM,x86,x64 + $(MSBuildProjectDirectory) + $([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(ProjectHome))))) + $(QualifiedProjectHome)\ + + + + + BuiltProjectOutputGroupFast; + $(BuiltProjectOutputGroupDependsOn) + + + + + + + <_BuiltProjectOutputGroupFastOutput Remove="@(_BuiltProjectOutputGroupFastOutput)" /> + + + + + + + + + + + + + + + + + + + + + + <_SourceFilesProjectOutputGroupOutput Remove="@(_SourceFilesProjectOutputGroupOutput)" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/Project/PythonUapProject.cs b/Python/Product/Uap/Project/PythonUapProject.cs new file mode 100644 index 0000000000..b09136470f --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProject.cs @@ -0,0 +1,299 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Flavor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudioTools; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid("27BB1268-135A-4409-914F-7AA64AD8195D")] + partial class PythonUapProject : + FlavoredProjectBase, + IOleCommandTarget, + IVsProjectFlavorCfgProvider, + IVsProject, + IVsFilterAddProjectItemDlg { + private PythonUapPackage _package; + internal IVsProject _innerProject; + internal IVsProject3 _innerProject3; + private IVsProjectFlavorCfgProvider _innerVsProjectFlavorCfgProvider; + private static Guid PythonProjectGuid = new Guid(PythonConstants.ProjectFactoryGuid); + private IOleCommandTarget _menuService; + + public PythonUapProject() { + } + + internal PythonUapPackage Package { + get { return _package; } + set { + Debug.Assert(_package == null); + if (_package != null) { + throw new InvalidOperationException("PythonUapProject.Package must only be set once"); + } + _package = value; + } + } + + #region IVsAggregatableProject + + /// + /// Do the initialization here (such as loading flavor specific + /// information from the project) + /// + protected override void InitializeForOuter(string fileName, string location, string name, uint flags, ref Guid guidProject, out bool cancel) { + base.InitializeForOuter(fileName, location, name, flags, ref guidProject, out cancel); + } + + #endregion + + protected override int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds, VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText) { + if (pguidCmdGroup == GuidList.guidOfficeSharePointCmdSet) { + for (int i = 0; i < prgCmds.Length; i++) { + // Report it as supported so that it's not routed any + // further, but disable it and make it invisible. + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_INVISIBLE); + } + return VSConstants.S_OK; + } + + return base.QueryStatusCommand(itemid, ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + protected override void SetInnerProject(IntPtr innerIUnknown) { + var inner = Marshal.GetObjectForIUnknown(innerIUnknown); + + // The reason why we keep a reference to those is that doing a QI after being + // aggregated would do the AddRef on the outer object. + _innerVsProjectFlavorCfgProvider = inner as IVsProjectFlavorCfgProvider; + _innerProject = inner as IVsProject; + _innerProject3 = inner as IVsProject3; + _innerVsHierarchy = inner as IVsHierarchy; + + // Ensure we have a service provider as this is required for menu items to work + if (this.serviceProvider == null) { + this.serviceProvider = (IServiceProvider)Package; + } + + // Now let the base implementation set the inner object + base.SetInnerProject(innerIUnknown); + + // Get access to the menu service used by FlavoredProjectBase. We + // need to forward IOleCommandTarget functions to this object, since + // we override the FlavoredProjectBase implementation with no way to + // call it directory. + // (This must run after we called base.SetInnerProject) + _menuService = (IOleCommandTarget)((IServiceProvider)this).GetService(typeof(IMenuCommandService)); + if (_menuService == null) { + throw new InvalidOperationException("Cannot initialize Uap project"); + } + } + + protected override int GetProperty(uint itemId, int propId, out object property) { + switch ((__VSHPROPID2)propId) { + case __VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList: + { + var res = base.GetProperty(itemId, propId, out property); + if (ErrorHandler.Succeeded(res)) { + var guids = GetGuidsFromList(property as string); + guids.RemoveAll(g => CfgSpecificPropertyPagesToRemove.Contains(g)); + guids.AddRange(CfgSpecificPropertyPagesToAdd); + property = MakeListFromGuids(guids); + } + return res; + } + case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList: + { + var res = base.GetProperty(itemId, propId, out property); + if (ErrorHandler.Succeeded(res)) { + var guids = GetGuidsFromList(property as string); + guids.RemoveAll(g => PropertyPagesToRemove.Contains(g)); + guids.AddRange(PropertyPagesToAdd); + property = MakeListFromGuids(guids); + } + return res; + } + } + + switch ((__VSHPROPID6)propId) { + case __VSHPROPID6.VSHPROPID_Subcaption: + { + var bps = this._innerProject as IVsBuildPropertyStorage; + string descriptor = null; + + if (bps != null) { + var res = bps.GetPropertyValue("TargetOsAndVersion", null, (uint)_PersistStorageType.PST_PROJECT_FILE, out descriptor); + property = descriptor; + return res; + } + break; + } + } + + return base.GetProperty(itemId, propId, out property); + } + + private static Guid[] PropertyPagesToAdd = new Guid[0]; + + private static Guid[] CfgSpecificPropertyPagesToAdd = new Guid[] { + new Guid(GuidList.guidUapPropertyPageString) + }; + + private static HashSet PropertyPagesToRemove = new HashSet { + new Guid("{8C0201FE-8ECA-403C-92A3-1BC55F031979}"), // typeof(DeployPropertyPageComClass) + new Guid("{ED3B544C-26D8-4348-877B-A1F7BD505ED9}"), // typeof(DatabaseDeployPropertyPageComClass) + new Guid("{909D16B3-C8E8-43D1-A2B8-26EA0D4B6B57}"), // Microsoft.VisualStudio.Web.Application.WebPropertyPage + new Guid("{379354F2-BBB3-4BA9-AA71-FBE7B0E5EA94}"), // Microsoft.VisualStudio.Web.Application.SilverlightLinksPage + new Guid("{A553AD0B-2F9E-4BCE-95B3-9A1F7074BC27}"), // Package/Publish Web + new Guid("{9AB2347D-948D-4CD2-8DBE-F15F0EF78ED3}"), // Package/Publish SQL + new Guid(PythonConstants.DebugPropertyPageGuid), + new Guid(PythonConstants.GeneralPropertyPageGuid), + new Guid(PythonConstants.PublishPropertyPageGuid) + }; + + internal static HashSet CfgSpecificPropertyPagesToRemove = new HashSet(new Guid[] { Guid.Empty }); + + private static List GetGuidsFromList(string guidList) { + if (string.IsNullOrEmpty(guidList)) { + return new List(); + } + + Guid value; + return guidList.Split(';') + .Select(str => Guid.TryParse(str, out value) ? (Guid?)value : null) + .Where(g => g.HasValue) + .Select(g => g.Value) + .ToList(); + } + + private static string MakeListFromGuids(IEnumerable guidList) { + return string.Join(";", guidList.Select(g => g.ToString("B"))); + } + + internal string RemovePropertyPagesFromList(string propertyPagesList, string[] pagesToRemove) { + if (pagesToRemove == null || !pagesToRemove.Any()) { + return propertyPagesList; + } + + var guidsToRemove = new HashSet( + pagesToRemove.Select(str => { Guid guid; return Guid.TryParse(str, out guid) ? guid : Guid.Empty; }) + ); + guidsToRemove.Add(Guid.Empty); + + return string.Join( + ";", + propertyPagesList.Split(';') + .Where(str => !string.IsNullOrEmpty(str)) + .Select(str => { Guid guid; return Guid.TryParse(str, out guid) ? guid : Guid.Empty; }) + .Except(guidsToRemove) + .Select(guid => guid.ToString("B")) + ); + } + + int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + return _menuService.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + return _menuService.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #region IVsProjectFlavorCfgProvider Members + + public int CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg) { + // We're flavored with a Windows Store Application project and our normal + // project... But we don't want the web application project to + // influence our config as that alters our debug launch story. We + // control that w/ the web project which is actually just letting + // the base Python project handle it. So we keep the base Python + // project config here. + IVsProjectFlavorCfg uapCfg; + ErrorHandler.ThrowOnFailure( + _innerVsProjectFlavorCfgProvider.CreateProjectFlavorCfg( + pBaseProjectCfg, + out uapCfg + ) + ); + ppFlavorCfg = new PythonUapProjectConfig(pBaseProjectCfg, uapCfg); + return VSConstants.S_OK; + } + + #endregion + + #region IVsProject Members + + int IVsProject.AddItem(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, VSADDRESULT[] pResult) { + return _innerProject.AddItem(itemidLoc, dwAddItemOperation, pszItemName, cFilesToOpen, rgpszFilesToOpen, hwndDlgOwner, pResult); + } + + int IVsProject.GenerateUniqueItemName(uint itemidLoc, string pszExt, string pszSuggestedRoot, out string pbstrItemName) { + return _innerProject.GenerateUniqueItemName(itemidLoc, pszExt, pszSuggestedRoot, out pbstrItemName); + } + + int IVsProject.GetItemContext(uint itemid, out VisualStudio.OLE.Interop.IServiceProvider ppSP) { + return _innerProject.GetItemContext(itemid, out ppSP); + } + + int IVsProject.GetMkDocument(uint itemid, out string pbstrMkDocument) { + return _innerProject.GetMkDocument(itemid, out pbstrMkDocument); + } + + int IVsProject.IsDocumentInProject(string pszMkDocument, out int pfFound, VSDOCUMENTPRIORITY[] pdwPriority, out uint pitemid) { + return _innerProject.IsDocumentInProject(pszMkDocument, out pfFound, pdwPriority, out pitemid); + } + + int IVsProject.OpenItem(uint itemid, ref Guid rguidLogicalView, IntPtr punkDocDataExisting, out IVsWindowFrame ppWindowFrame) { + return _innerProject.OpenItem(itemid, rguidLogicalView, punkDocDataExisting, out ppWindowFrame); + } + + #endregion + + #region IVsFilterAddProjectItemDlg Members + + int IVsFilterAddProjectItemDlg.FilterListItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterListItemByTemplateFile(ref Guid rguidProjectItemTemplates, string pszTemplateFile, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterTreeItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterTreeItemByTemplateDir(ref Guid rguidProjectItemTemplates, string pszTemplateDir, out int pfFilter) { + // https://pytools.codeplex.com/workitem/1313 + // ASP.NET will filter some things out, including .css files, which we don't want it to do. + // So we shut that down by not forwarding this to any inner projects, which is fine, because + // Python projects don't implement this interface either. + pfFilter = 0; + return VSConstants.S_OK; + } + + #endregion + } +} diff --git a/Python/Product/Uap/Project/PythonUapProjectConfig.cs b/Python/Product/Uap/Project/PythonUapProjectConfig.cs new file mode 100644 index 0000000000..583c6ad352 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProjectConfig.cs @@ -0,0 +1,961 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Web; +using System.Windows.Forms; +using System.Xml.Linq; +using Microsoft.PythonTools.Debugger.DebugEngine; +using Microsoft.PythonTools.Debugger.Remote; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Project { + /// + /// Merges the PTVS IVsCfg object with the Venus IVsCfg implementation redirecting + /// things appropriately to either one. + /// + class PythonUapProjectConfig : + IVsCfg, + IVsProjectCfg, + IVsProjectCfg2, + IVsProjectFlavorCfg, + IVsDebuggableProjectCfg, + ISpecifyPropertyPages, + IVsSpecifyProjectDesignerPages, + IVsCfgBrowseObject, + IVsDeployableProjectCfg, + IVsProjectCfgDebugTargetSelection, + IVsQueryDebuggableProjectCfg, + IVsQueryDebuggableProjectCfg2, + IVsAppContainerProjectDeployCallback { + private readonly IVsCfg _pythonCfg; + private readonly IVsProjectFlavorCfg _uapCfg; + private readonly object syncObject = new object(); + private EventSinkCollection deployCallbackCollection = new EventSinkCollection(); + private IVsAppContainerProjectDeployOperation deployOp; + private IVsTask appContainerBootstrapperOperation; + private IVsOutputWindowPane outputWindow = null; + private IVsDebuggerDeployConnection connection = null; + private string deployPackageMoniker; + private string deployAppUserModelID; + + private const string RemoteTarget = "Remote Machine"; + + public PythonUapProjectConfig(IVsCfg pythonCfg, IVsProjectFlavorCfg uapConfig) { + _pythonCfg = pythonCfg; + _uapCfg = uapConfig; + } + + internal IVsHierarchy PythonConfig { + get { + IVsHierarchy proj = null; + + var browseObj = _pythonCfg as IVsCfgBrowseObject; + + if (browseObj != null) { + uint itemId = 0; + + browseObj.GetProjectItem(out proj, out itemId); + } + return proj; + } + } + + internal string LayoutDir { + get; private set; + } + + internal string DeployPackageMoniker { + get { return this.deployPackageMoniker; } + } + + internal string DeployAppUserModelID { + get { return this.deployAppUserModelID; } + } + + private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remoteMachine, string secret, string port, string sourceDir, string targetDir) { + bool attached = false; + bool stoppedDebugging = false; + const int attachRetryLimit = 10; + int attachRetryCount = 0; + + var qualifierString = string.Format( + "tcp://{0}@{1}:{2}?{3}={4}&{5}={6}&{7}={8}", + secret, + remoteMachine, + port, + AD7Engine.SourceDirectoryKey, + HttpUtility.UrlEncode(sourceDir), + AD7Engine.TargetDirectoryKey, + HttpUtility.UrlEncode(targetDir), + AD7Engine.TargetHostType, + HttpUtility.UrlEncode(AD7Engine.TargetUap)); + + var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); + var debugger = (EnvDTE90.Debugger3)dte.Debugger; + + var transport = default(EnvDTE80.Transport); + var transports = debugger.Transports; + + for (int i = 1; i <= transports.Count; ++i) { + var t = transports.Item(i); + Guid tid; + if (Guid.TryParse(t.ID, out tid) && tid == PythonRemoteDebugPortSupplier.PortSupplierGuid) { + transport = t; + } + } + + System.Diagnostics.Debug.Assert(transport != null, "Python remote debugging transport is missing."); + + if (transport == null) { + return; + } + + while (!attached && attachRetryCount++ < attachRetryLimit) { + try { + if (debugger.DebuggedProcesses == null || debugger.DebuggedProcesses.Count == 0) { + // We are no longer debugging, so just bail + stoppedDebugging = true; + break; + } else if (debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode) { + attachRetryCount--; + await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)); + continue; + } + + var processes = debugger.GetProcesses(transport, qualifierString); + + if (processes.Count > 0) { + foreach (EnvDTE.Process process in processes) { + process.Attach(); + attached = true; + System.Diagnostics.Debug.WriteLine("Successfully attached to a python remote process"); + break; + } + + if (attached) { + IVsDebugger vsDebugger = Package.GetGlobalService(typeof(SVsShellDebugger)) as IVsDebugger; + + if (vsDebugger != null) { + vsDebugger.UnadviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); + } + + break; + } + } + } catch (COMException comException) { + // In the case where the debug client does not setup the PTVSD server in time for the Attach to work, we will + // get this exception. We retry a few times to ensure client has time to start the Python debug server + System.Diagnostics.Debug.WriteLine("Failure during attach to remote Python process:\r\n{0}", comException); + } + + await System.Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(100)); + } + + if (!attached && !stoppedDebugging) { + MessageBox.Show("Could not attach to remote Python debug session.", null, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + #region IVsCfg Members + + public int get_DisplayName(out string pbstrDisplayName) { + int ret = _pythonCfg.get_DisplayName(out pbstrDisplayName); + + return ret; + } + + public int get_IsDebugOnly(out int pfIsDebugOnly) { + return _pythonCfg.get_IsDebugOnly(out pfIsDebugOnly); + } + + public int get_IsReleaseOnly(out int pfIsReleaseOnly) { + return _pythonCfg.get_IsReleaseOnly(out pfIsReleaseOnly); + } + + #endregion + + #region IVsProjectCfg Members + + public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.EnumOutputs(out ppIVsEnumOutputs); + } + ppIVsEnumOutputs = null; + return VSConstants.E_NOTIMPL; + } + + public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.OpenOutput(szOutputCanonicalName, out ppIVsOutput); + } + ppIVsOutput = null; + return VSConstants.E_NOTIMPL; + } + + public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProjectCfg) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_BuildableProjectCfg(out ppIVsBuildableProjectCfg); + } + ppIVsBuildableProjectCfg = null; + return VSConstants.E_NOTIMPL; + } + + public int get_CanonicalName(out string pbstrCanonicalName) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_CanonicalName(out pbstrCanonicalName); + } + pbstrCanonicalName = null; + return VSConstants.E_NOTIMPL; + } + + public int get_IsPackaged(out int pfIsPackaged) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_IsPackaged(out pfIsPackaged); + } + pfIsPackaged = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_IsSpecifyingOutputSupported(out pfIsSpecifyingOutputSupported); + } + pfIsSpecifyingOutputSupported = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_Platform(out Guid pguidPlatform) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_Platform(out pguidPlatform); + } + pguidPlatform = Guid.Empty; + return VSConstants.E_NOTIMPL; + } + + public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvider) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_ProjectCfgProvider(out ppIVsProjectCfgProvider); + } + ppIVsProjectCfgProvider = null; + return VSConstants.E_NOTIMPL; + } + + public int get_RootURL(out string pbstrRootURL) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_RootURL(out pbstrRootURL); + } + pbstrRootURL = null; + return VSConstants.E_NOTIMPL; + } + + public int get_TargetCodePage(out uint puiTargetCodePage) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_TargetCodePage(out puiTargetCodePage); + } + puiTargetCodePage = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_UpdateSequenceNumber(ULARGE_INTEGER[] puliUSN) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_UpdateSequenceNumber(puliUSN); + } + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectCfg2 Members + + public int OpenOutputGroup(string szCanonicalName, out IVsOutputGroup ppIVsOutputGroup) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.OpenOutputGroup(szCanonicalName, out ppIVsOutputGroup); + } + ppIVsOutputGroup = null; + return VSConstants.E_NOTIMPL; + } + + public int OutputsRequireAppRoot(out int pfRequiresAppRoot) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.OutputsRequireAppRoot(out pfRequiresAppRoot); + } + pfRequiresAppRoot = 1; + return VSConstants.E_NOTIMPL; + } + + public int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) { + if (iidCfg == typeof(IVsDebuggableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg)); + return VSConstants.S_OK; + } + + if (iidCfg == typeof(IVsDeployableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDeployableProjectCfg)); + return VSConstants.S_OK; + } + + if (iidCfg == typeof(IVsQueryDebuggableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsQueryDebuggableProjectCfg)); + return VSConstants.S_OK; + } + + var projCfg = _uapCfg as IVsProjectFlavorCfg; + if (projCfg != null) { + return projCfg.get_CfgType(ref iidCfg, out ppCfg); + } + ppCfg = IntPtr.Zero; + return VSConstants.E_NOTIMPL; + } + + public int get_IsPrivate(out int pfPrivate) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_IsPrivate(out pfPrivate); + } + pfPrivate = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_OutputGroups(uint celt, IVsOutputGroup[] rgpcfg, uint[] pcActual = null) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_OutputGroups(celt, rgpcfg, pcActual); + } + return VSConstants.E_NOTIMPL; + } + + public int get_VirtualRoot(out string pbstrVRoot) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_VirtualRoot(out pbstrVRoot); + } + pbstrVRoot = null; + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectFlavorCfg Members + + public int Close() { + IVsProjectFlavorCfg cfg = _uapCfg as IVsProjectFlavorCfg; + if (cfg != null) { + return cfg.Close(); + } + return VSConstants.S_OK; + } + + #endregion + + #region IVsDebuggableProjectCfg Members + + public int DebugLaunch(uint grfLaunch) { + if (PythonConfig.IsAppxPackageableProject()) { + VsDebugTargetInfo2[] targets; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) + return hr; + + VsDebugTargetInfo4[] appPackageDebugTarget = new VsDebugTargetInfo4[1]; + int targetLength = (int)Marshal.SizeOf(typeof(VsDebugTargetInfo4)); + + // Setup the app-specific parameters + appPackageDebugTarget[0].AppPackageLaunchInfo.AppUserModelID = DeployAppUserModelID; + appPackageDebugTarget[0].AppPackageLaunchInfo.PackageMoniker = DeployPackageMoniker; + appPackageDebugTarget[0].AppPackageLaunchInfo.AppPlatform = VsAppPackagePlatform.APPPLAT_WindowsAppx; + +#if DEV14_OR_LATER + // Check if this project contains a startup task and set launch flag appropriately + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)this.PythonConfig; + string canonicalName; + string containsStartupTaskValue = null; + bool containsStartupTask = false; + + get_CanonicalName(out canonicalName); + + bps.GetPropertyValue("ContainsStartupTask", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out containsStartupTaskValue); + + if (containsStartupTaskValue != null && bool.TryParse(containsStartupTaskValue, out containsStartupTask) && containsStartupTask) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS140.DBGLAUNCH_ContainsStartupTask; + } +#endif + + appPackageDebugTarget[0].dlo = (uint)_DEBUG_LAUNCH_OPERATION4.DLO_AppPackageDebug; + appPackageDebugTarget[0].LaunchFlags = grfLaunch; + appPackageDebugTarget[0].bstrRemoteMachine = targets[0].bstrRemoteMachine; + appPackageDebugTarget[0].bstrExe = targets[0].bstrExe; + appPackageDebugTarget[0].bstrArg = targets[0].bstrArg; + appPackageDebugTarget[0].bstrCurDir = targets[0].bstrCurDir; + appPackageDebugTarget[0].bstrEnv = targets[0].bstrEnv; + appPackageDebugTarget[0].dwProcessId = targets[0].dwProcessId; + appPackageDebugTarget[0].pStartupInfo = IntPtr.Zero; + appPackageDebugTarget[0].guidLaunchDebugEngine = targets[0].guidLaunchDebugEngine; + appPackageDebugTarget[0].dwDebugEngineCount = targets[0].dwDebugEngineCount; + appPackageDebugTarget[0].pDebugEngines = targets[0].pDebugEngines; + appPackageDebugTarget[0].guidPortSupplier = targets[0].guidPortSupplier; + + appPackageDebugTarget[0].bstrPortName = targets[0].bstrPortName; + appPackageDebugTarget[0].bstrOptions = targets[0].bstrOptions; + appPackageDebugTarget[0].fSendToOutputWindow = targets[0].fSendToOutputWindow; + appPackageDebugTarget[0].pUnknown = targets[0].pUnknown; + appPackageDebugTarget[0].guidProcessLanguage = targets[0].guidProcessLanguage; + appPackageDebugTarget[0].project = PythonConfig; + + // Pass the debug launch targets to the debugger + IVsDebugger4 debugger4 = (IVsDebugger4)Package.GetGlobalService(typeof(SVsShellDebugger)); + + VsDebugTargetProcessInfo[] results = new VsDebugTargetProcessInfo[1]; + + IVsDebugger debugger = (IVsDebugger)debugger4; + + // Launch task to monitor to attach to Python remote process + var sourceDir = System.IO.Path.GetFullPath(PythonConfig.GetProjectProperty("ProjectDir")).Trim('\\'); + var targetDir = System.IO.Path.GetFullPath(this.LayoutDir).Trim('\\'); + var debugPort = "5678"; + var debugId = Guid.NewGuid(); + var debugXml = new XDocument(new XElement("dbg", + new XElement("arg", @"visualstudio_py_remote_launcher.py"), + new XElement("arg", debugPort), + new XElement("arg", debugId), + new XElement("arg", "--redirect-output"))); + + Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteDebugXml = debugXml.ToString(); + Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteProcessFunction = () => { + return RemoteProcessAttachAsync( + appPackageDebugTarget[0].bstrRemoteMachine, + debugId.ToString(), + debugPort, + sourceDir, + targetDir); + }; + + int result = debugger.AdviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); + System.Diagnostics.Debug.Assert(result == VSConstants.S_OK, string.Format(System.Globalization.CultureInfo.CurrentCulture, "Failure {0}", result)); + + debugger4.LaunchDebugTargets4(1, appPackageDebugTarget, results); + + return VSConstants.S_OK; + } else { + IVsDebuggableProjectCfg cfg = _pythonCfg as IVsDebuggableProjectCfg; + if (cfg != null) { + return cfg.DebugLaunch(grfLaunch); + } + return VSConstants.E_NOTIMPL; + } + } + + public int QueryDebugLaunch(uint grfLaunch, out int pfCanLaunch) { + pfCanLaunch = this.PythonConfig.IsAppxPackageableProject() ? 1 : 0; + return VSConstants.S_OK; + } + + #endregion + + #region ISpecifyPropertyPages Members + + public void GetPages(CAUUID[] pPages) { + var cfg = _pythonCfg as ISpecifyPropertyPages; + if (cfg != null) { + cfg.GetPages(pPages); + } + } + + #endregion + + #region IVsSpecifyProjectDesignerPages Members + + public int GetProjectDesignerPages(CAUUID[] pPages) { + var cfg = _pythonCfg as IVsSpecifyProjectDesignerPages; + if (cfg != null) { + return cfg.GetProjectDesignerPages(pPages); + } + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsCfgBrowseObject Members + + public int GetCfg(out IVsCfg ppCfg) { + ppCfg = this; + return VSConstants.S_OK; + } + + public int GetProjectItem(out IVsHierarchy pHier, out uint pItemid) { + var cfg = _pythonCfg as IVsCfgBrowseObject; + if (cfg != null) { + return cfg.GetProjectItem(out pHier, out pItemid); + } + pHier = null; + pItemid = 0; + return VSConstants.E_NOTIMPL; + } + + #endregion + + + #region IVsDeployableProjectCfg Members + + public int AdviseDeployStatusCallback(IVsDeployStatusCallback pIVsDeployStatusCallback, out uint pdwCookie) { + if (pIVsDeployStatusCallback == null) { + pdwCookie = 0; + return VSConstants.E_UNEXPECTED; + } + + lock (syncObject) { + pdwCookie = deployCallbackCollection.Add(pIVsDeployStatusCallback); + } + + return VSConstants.S_OK; + } + + public int Commit(uint dwReserved) { + return VSConstants.S_OK; + } + + public int QueryStartDeploy(uint dwOptions, int[] pfSupported, int[] pfReady) { + if (pfSupported.Length > 0) { + // Only Appx package producing appcontainer projects should support deployment + pfSupported[0] = 1; + } + + if (pfReady.Length > 0) { + lock (syncObject) { + pfReady[0] = (deployOp == null && appContainerBootstrapperOperation == null) ? 1 : 0; + } + } + + return VSConstants.S_OK; + } + + public int QueryStatusDeploy(out int pfDeployDone) { + lock (syncObject) { + pfDeployDone = (deployOp == null && appContainerBootstrapperOperation == null) ? 1 : 0; + } + + return VSConstants.S_OK; + } + + public int Rollback(uint dwReserved) { + return VSConstants.S_OK; + } + + public int StartDeploy(IVsOutputWindowPane pIVsOutputWindowPane, uint dwOptions) { + int continueOn = 1; + + outputWindow = pIVsOutputWindowPane; + + if (connection != null) { + lock (syncObject) { + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + + // Loop through deploy status callbacks + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + if (ErrorHandler.Failed(callback.OnStartDeploy(ref continueOn))) { + continueOn = 0; + } + + if (continueOn == 0) { + outputWindow = null; + return VSConstants.E_ABORT; + } + } + + try { + VsDebugTargetInfo2[] targets; + uint deployFlags = (uint)(_AppContainerDeployOptions.ACDO_NetworkLoopbackEnable | _AppContainerDeployOptions.ACDO_SetNetworkLoopback | _AppContainerDeployOptions.ACDO_ForceCleanLayout); + string recipeFile = null; + string layoutDir = null; + var pythonProject = PythonConfig; + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)pythonProject; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) { + NotifyEndDeploy(0); + return hr; + } + + string canonicalName; + + get_CanonicalName(out canonicalName); + + bps.GetPropertyValue("AppxPackageRecipe", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out recipeFile); + bps.GetPropertyValue("LayoutDir", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out layoutDir); + + string projectUniqueName = null; + IVsSolution vsSolution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution; + if (vsSolution != null) { + hr = vsSolution.GetUniqueNameOfProject(pythonProject, out projectUniqueName); + } + + IVsAppContainerProjectDeploy deployHelper = (IVsAppContainerProjectDeploy)Package.GetGlobalService(typeof(SVsAppContainerProjectDeploy)); + if (String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + deployOp = deployHelper.StartDeployAsync(deployFlags, recipeFile, layoutDir, projectUniqueName, this); + } else { + IVsDebuggerDeploy deploy = (IVsDebuggerDeploy)Package.GetGlobalService(typeof(SVsShellDebugger)); + IVsDebuggerDeployConnection deployConnection; + + hr = deploy.ConnectToTargetComputer(targets[0].bstrRemoteMachine, VsDebugRemoteAuthenticationMode.VSAUTHMODE_None, out deployConnection); + if (ErrorHandler.Failed(hr)) { + NotifyEndDeploy(0); + return hr; + } + + connection = deployConnection; + + deployOp = deployHelper.StartRemoteDeployAsync(deployFlags, connection, recipeFile, projectUniqueName, this); + } + } catch (Exception) { + if (connection != null) { + lock (syncObject) { + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + connection = null; + + NotifyEndDeploy(0); + + // Rethrow exception + throw; + } + + return VSConstants.S_OK; + } + + public int StopDeploy(int fSync) { + IVsTask bootstrapOp = null; + IVsAppContainerProjectDeployOperation deployOp = null; + int result = VSConstants.S_OK; + + lock (syncObject) { + bootstrapOp = appContainerBootstrapperOperation; + deployOp = this.deployOp; + appContainerBootstrapperOperation = null; + this.deployOp = null; + } + + if (bootstrapOp != null) { + bootstrapOp.Cancel(); + if (fSync != 0) { + try { + bootstrapOp.Wait(); + } catch (Exception e) { + if (outputWindow != null) { + outputWindow.OutputString(e.ToString()); + } + result = VSConstants.E_FAIL; + } + } + } + + if (deployOp != null) { + deployOp.StopDeploy(fSync != 0); + } + + return result; + } + + int IVsDeployableProjectCfg.UnadviseDeployStatusCallback(uint dwCookie) { + lock (syncObject) { + deployCallbackCollection.RemoveAt(dwCookie); + } + + return VSConstants.S_OK; + } + + int IVsDeployableProjectCfg.WaitDeploy(uint dwMilliseconds, int fTickWhenMessageQNotEmpty) { + return VSConstants.S_OK; + } + + #endregion + + #region IVsAppContainerProjectDeployCallback Members + + void IVsAppContainerProjectDeployCallback.OnEndDeploy(bool successful, string deployedPackageMoniker, string deployedAppUserModelID) { + try { + if (successful) { + deployPackageMoniker = deployedPackageMoniker; + deployAppUserModelID = deployedAppUserModelID; + NotifyEndDeploy(1); + + var result = deployOp.GetDeployResult(); + + this.LayoutDir = result.LayoutFolder; + } else { + deployPackageMoniker = null; + deployAppUserModelID = null; + NotifyEndDeploy(0); + } + } finally { + lock (syncObject) { + deployOp = null; + + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + } + + void IVsAppContainerProjectDeployCallback.OutputMessage(string message) { + if (null != outputWindow) { + outputWindow.OutputString(message); + } + } + + #endregion + + #region IVsQueryDebuggableProjectCfg2 Members + void IVsQueryDebuggableProjectCfg2.QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTargetInfo4[] rgDebugTargetInfo, uint[] pcActual) { + if (cTargets <= 0) { + if (pcActual == null) { + Marshal.ThrowExceptionForHR(VSConstants.E_POINTER); + } + + pcActual[0] = 1; + return; + } + + if (pcActual != null) { + pcActual[0] = 0; + } + + VsDebugTargetInfo2[] targets; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) + Marshal.ThrowExceptionForHR(hr); + + int targetLength = (int)Marshal.SizeOf(typeof(VsDebugTargetInfo4)); + + rgDebugTargetInfo[0].AppPackageLaunchInfo.AppUserModelID = deployAppUserModelID; + rgDebugTargetInfo[0].AppPackageLaunchInfo.PackageMoniker = deployPackageMoniker; + + bool isSimulator = GetDebugFlag("UseSimulator", false); + + if (isSimulator && String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS6.DBGLAUNCH_StartInSimulator; + } + + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_DetachOnStop; + + bool containsStartupTask = GetDebugFlag("ContainsStartupTask", false); + +#if DEV14_OR_LATER + if (containsStartupTask) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS140.DBGLAUNCH_ContainsStartupTask; + } +#endif + + rgDebugTargetInfo[0].dlo = (uint)_DEBUG_LAUNCH_OPERATION4.DLO_AppPackageDebug; + rgDebugTargetInfo[0].LaunchFlags = grfLaunch; + rgDebugTargetInfo[0].bstrRemoteMachine = targets[0].bstrRemoteMachine; + rgDebugTargetInfo[0].bstrExe = targets[0].bstrExe; + rgDebugTargetInfo[0].bstrArg = targets[0].bstrArg; + rgDebugTargetInfo[0].bstrCurDir = targets[0].bstrCurDir; + rgDebugTargetInfo[0].bstrEnv = targets[0].bstrEnv; + rgDebugTargetInfo[0].dwProcessId = targets[0].dwProcessId; + rgDebugTargetInfo[0].pStartupInfo = IntPtr.Zero; + rgDebugTargetInfo[0].guidLaunchDebugEngine = targets[0].guidLaunchDebugEngine; + rgDebugTargetInfo[0].dwDebugEngineCount = targets[0].dwDebugEngineCount; + rgDebugTargetInfo[0].pDebugEngines = targets[0].pDebugEngines; + rgDebugTargetInfo[0].guidPortSupplier = targets[0].guidPortSupplier; + rgDebugTargetInfo[0].bstrPortName = targets[0].bstrPortName; + rgDebugTargetInfo[0].bstrOptions = targets[0].bstrOptions; + rgDebugTargetInfo[0].fSendToOutputWindow = targets[0].fSendToOutputWindow; + rgDebugTargetInfo[0].pUnknown = targets[0].pUnknown; + rgDebugTargetInfo[0].guidProcessLanguage = targets[0].guidProcessLanguage; + + if (pcActual != null) { + pcActual[0] = 1; + } + } + #endregion + + #region IVsProjectCfgDebugTargetSelection Members + void IVsProjectCfgDebugTargetSelection.GetCurrentDebugTarget(out Guid pguidDebugTargetType, out uint pDebugTargetTypeId, out string pbstrCurrentDebugTarget) { + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)PythonConfig; + + pguidDebugTargetType = VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet; + pDebugTargetTypeId = VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine; + pbstrCurrentDebugTarget = RemoteTarget; + } + + Array IVsProjectCfgDebugTargetSelection.GetDebugTargetListOfType(Guid guidDebugTargetType, uint debugTargetTypeId) { + string[] result = new string[1]; + if (guidDebugTargetType != VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet) { + return new string[0]; + } + + switch (debugTargetTypeId) { + case VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine: + result[0] = RemoteTarget; + break; + default: + return new string[0]; + } + + return result; + } + + bool IVsProjectCfgDebugTargetSelection.HasDebugTargets(IVsDebugTargetSelectionService pDebugTargetSelectionService, out Array pbstrSupportedTargetCommandIDs) { + pbstrSupportedTargetCommandIDs = new string[] { + String.Join(":", VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet, VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine) + }; + + return true; + } + + void IVsProjectCfgDebugTargetSelection.SetCurrentDebugTarget(Guid guidDebugTargetType, uint debugTargetTypeId, string bstrCurrentDebugTarget) { + if (guidDebugTargetType == VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet) { + } + } + + #endregion + + internal int QueryDebugTargets(out VsDebugTargetInfo2[] targets) { + IntPtr queryDebuggableProjectCfgPtr = IntPtr.Zero; + targets = null; + + Guid guid = typeof(IVsQueryDebuggableProjectCfg).GUID; + int hr = get_CfgType(ref guid, out queryDebuggableProjectCfgPtr); + if (ErrorHandler.Failed(hr)) + return hr; + + object queryDebuggableProjectCfgObject = Marshal.GetObjectForIUnknown(queryDebuggableProjectCfgPtr); + if (queryDebuggableProjectCfgObject == null) + return VSConstants.E_UNEXPECTED; + + IVsQueryDebuggableProjectCfg baseQueryDebugbableCfg = queryDebuggableProjectCfgObject as IVsQueryDebuggableProjectCfg; + if (baseQueryDebugbableCfg == null) + return VSConstants.E_UNEXPECTED; + + uint[] targetsCountOutput = new uint[1]; + hr = baseQueryDebugbableCfg.QueryDebugTargets(0, 0, null, targetsCountOutput); + if (ErrorHandler.Failed(hr)) + return hr; + uint numberOfDebugTargets = targetsCountOutput[0]; + + targets = new VsDebugTargetInfo2[numberOfDebugTargets]; + hr = baseQueryDebugbableCfg.QueryDebugTargets(0, numberOfDebugTargets, targets, null); + if (ErrorHandler.Failed(hr)) + return hr; + + if (string.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + MessageBox.Show( + "The project cannot be deployed or debugged because there is not a remote machine specified in Debug settings.", + "Python Tools for Visual Studio", MessageBoxButtons.OK, MessageBoxIcon.Error); + return VSConstants.E_ABORT; + } + + return hr; + } + + private bool GetDebugFlag(string name, bool defaultValue) { + string value; + string canonicalName; + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)PythonConfig; + + get_CanonicalName(out canonicalName); + + int hr = bps.GetPropertyValue(name, canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out value); + + if (Microsoft.VisualStudio.ErrorHandler.Failed(hr)) + return defaultValue; + + return value.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + private int Deploy() { + try { + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + callback.OnEndDeploy(1); + } + } finally { + lock (syncObject) { + appContainerBootstrapperOperation = null; + deployOp = null; + } + } + + this.outputWindow = null; + + return VSConstants.S_OK; + } + + private void NotifyEndDeploy(int success) { + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + callback.OnEndDeploy(success); + } + + outputWindow = null; + } + + int IVsQueryDebuggableProjectCfg.QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTargetInfo2[] rgDebugTargetInfo, uint[] pcActual) { + var project = PythonConfig; + + if (pcActual != null && pcActual.Length > 0) { + pcActual[0] = 1; + } + + if (rgDebugTargetInfo != null && rgDebugTargetInfo.Length > 0) { + IList debugEngineGuids = new Guid[] { VSConstants.DebugEnginesGuids.NativeOnly_guid }; + + rgDebugTargetInfo[0] = new VsDebugTargetInfo2(); + + rgDebugTargetInfo[0].bstrExe = project.GetProjectProperty("Name"); + rgDebugTargetInfo[0].bstrRemoteMachine = project.GetProjectProperty("RemoteDebugMachine"); + rgDebugTargetInfo[0].guidPortSupplier = VSConstants.DebugPortSupplierGuids.NoAuth_guid; + rgDebugTargetInfo[0].guidLaunchDebugEngine = debugEngineGuids[0]; + rgDebugTargetInfo[0].dwDebugEngineCount = (uint)debugEngineGuids.Count; + rgDebugTargetInfo[0].pDebugEngines = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * debugEngineGuids.Count); + + for (var i = 0; i < debugEngineGuids.Count; i++) { + Marshal.StructureToPtr(debugEngineGuids[i], + IntPtr.Add(rgDebugTargetInfo[0].pDebugEngines, i * Marshal.SizeOf(typeof(Guid))), + false); + } + } + + return VSConstants.S_OK; + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapProjectFactory.cs b/Python/Product/Uap/Project/PythonUapProjectFactory.cs new file mode 100644 index 0000000000..60a9243890 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProjectFactory.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Flavor; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid(GuidList.guidUapFactoryString)] + public class PythonUapProjectFactory : FlavoredProjectFactoryBase { + private PythonUapPackage _package; + + public PythonUapProjectFactory(PythonUapPackage package) { + _package = package; + } + + protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) { + return new PythonUapProject { Package = _package }; + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPage.cs b/Python/Product/Uap/Project/PythonUapPropertyPage.cs new file mode 100644 index 0000000000..85aed0126e --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPage.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.VisualStudioTools.Project; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid(GuidList.guidUapPropertyPageString)] + class PythonUapPropertyPage : CommonPropertyPage { + private readonly PythonUapPropertyPageControl _control; + + public const string RemoteMachineSetting = "RemoteDebugMachine"; + public const string UapUserSetting = "UserSettingsChanged"; + + public PythonUapPropertyPage() { + _control = new PythonUapPropertyPageControl(this); + } + + public override Control Control { + get { return _control; } + } + + public override void Apply() { + SetConfigUserProjectProperty(RemoteMachineSetting, _control.RemoteDebugMachine); + + // Workaround to reload user project file + SetProjectProperty(UapUserSetting, DateTime.UtcNow.ToString()); + SetProjectProperty(UapUserSetting, string.Empty); + + IsDirty = false; + } + + public override void LoadSettings() { + Loading = true; + try { + _control.RemoteDebugMachine = GetConfigUserProjectProperty(RemoteMachineSetting); + IsDirty = false; + } finally { + Loading = false; + } + } + + public override string Name { + get { return Resources.UapPropertyPageTitle; } + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs new file mode 100644 index 0000000000..d5bf870632 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs @@ -0,0 +1,147 @@ +namespace Microsoft.PythonTools.Uap.Project { + partial class PythonUapPropertyPageControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + this._remoteMachineLabel = new System.Windows.Forms.Label(); + this._remoteMachine = new System.Windows.Forms.TextBox(); + this._uapGroup = new System.Windows.Forms.GroupBox(); + this._toolTip = new System.Windows.Forms.ToolTip(this.components); + tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + tableLayoutPanel2.SuspendLayout(); + tableLayoutPanel1.SuspendLayout(); + this._uapGroup.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + tableLayoutPanel2.AutoSize = true; + tableLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + tableLayoutPanel2.ColumnCount = 2; + tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel2.Controls.Add(this._remoteMachineLabel, 0, 1); + tableLayoutPanel2.Controls.Add(this._remoteMachine, 1, 1); + tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel2.Location = new System.Drawing.Point(6, 21); + tableLayoutPanel2.Name = "tableLayoutPanel2"; + tableLayoutPanel2.RowCount = 2; + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.Size = new System.Drawing.Size(406, 26); + tableLayoutPanel2.TabIndex = 0; + // + // _remoteMachineLabel + // + this._remoteMachineLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._remoteMachineLabel.AutoSize = true; + this._remoteMachineLabel.Location = new System.Drawing.Point(6, 6); + this._remoteMachineLabel.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this._remoteMachineLabel.Name = "_remoteMachineLabel"; + this._remoteMachineLabel.Size = new System.Drawing.Size(91, 13); + this._remoteMachineLabel.TabIndex = 2; + this._remoteMachineLabel.Text = "&Remote Machine:"; + this._remoteMachineLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // _remoteMachine + // + this._remoteMachine.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._remoteMachine.Location = new System.Drawing.Point(109, 3); + this._remoteMachine.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); + this._remoteMachine.MinimumSize = new System.Drawing.Size(50, 4); + this._remoteMachine.Name = "_remoteMachine"; + this._remoteMachine.Size = new System.Drawing.Size(291, 20); + this._remoteMachine.TabIndex = 3; + this._remoteMachine.TextChanged += new System.EventHandler(this.Setting_TextChanged); + // + // tableLayoutPanel1 + // + tableLayoutPanel1.AutoSize = true; + tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + tableLayoutPanel1.ColumnCount = 1; + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel1.Controls.Add(this._uapGroup, 0, 0); + tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 2; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + tableLayoutPanel1.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel1.TabIndex = 0; + // + // _uapGroup + // + this._uapGroup.AutoSize = true; + this._uapGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._uapGroup.Controls.Add(tableLayoutPanel2); + this._uapGroup.Dock = System.Windows.Forms.DockStyle.Fill; + this._uapGroup.Location = new System.Drawing.Point(6, 8); + this._uapGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapGroup.Name = "_uapGroup"; + this._uapGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapGroup.Size = new System.Drawing.Size(418, 55); + this._uapGroup.TabIndex = 0; + this._uapGroup.TabStop = false; + this._uapGroup.Text = "Debug Settings"; + // + // PythonUapPropertyPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.Controls.Add(tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this.Name = "PythonUapPropertyPageControl"; + this.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel2.ResumeLayout(false); + tableLayoutPanel2.PerformLayout(); + tableLayoutPanel1.ResumeLayout(false); + tableLayoutPanel1.PerformLayout(); + this._uapGroup.ResumeLayout(false); + this._uapGroup.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox _uapGroup; + private System.Windows.Forms.ToolTip _toolTip; + private System.Windows.Forms.Label _remoteMachineLabel; + private System.Windows.Forms.TextBox _remoteMachine; + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs new file mode 100644 index 0000000000..1651140c25 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using Microsoft.PythonTools; +using Microsoft.PythonTools.Project; +using Microsoft.VisualStudioTools.Project; + +namespace Microsoft.PythonTools.Uap.Project { + public partial class PythonUapPropertyPageControl : UserControl { + private readonly PythonUapPropertyPage _properties; + + private PythonUapPropertyPageControl() { + InitializeComponent(); + + _toolTip.SetToolTip(_remoteMachine, Resources.UapRemoteMachineHelp); + _toolTip.SetToolTip(_remoteMachineLabel, Resources.UapRemoteMachineHelp); + } + + internal PythonUapPropertyPageControl(PythonUapPropertyPage properties) + : this() { + _properties = properties; + } + + public string RemoteDebugMachine { + get { return _remoteMachine.Text; } + set { _remoteMachine.Text = value; } + } + + private void Setting_TextChanged(object sender, EventArgs e) { + if (_properties != null) { + _properties.IsDirty = true; + } + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx new file mode 100644 index 0000000000..1f83e4cc41 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + 17, 17 + + \ No newline at end of file diff --git a/Python/Product/Uap/Properties/AssemblyInfo.cs b/Python/Product/Uap/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0e203b3dd7 --- /dev/null +++ b/Python/Product/Uap/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Python Tools for Visual Studio - UAP Integration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en-US")] + diff --git a/Python/Product/Uap/PythonUapPackage.cs b/Python/Product/Uap/PythonUapPackage.cs new file mode 100644 index 0000000000..50cf58e2a5 --- /dev/null +++ b/Python/Product/Uap/PythonUapPackage.cs @@ -0,0 +1,125 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using EnvDTE90a; +using Microsoft.PythonTools.Debugger.DebugEngine; +using Microsoft.PythonTools.Uap.Interpreter; +using Microsoft.PythonTools.Uap.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.PythonTools.Uap { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. + /// + // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is + // a package. + [PackageRegistration(UseManagedResourcesOnly = true)] + + // This attribute is needed to let the shell know that this package exposes some menus. + [Guid(GuidList.guidUapPkgString)] + + [ProvideObject(typeof(PythonUapPropertyPage))] + [ProvideObject(typeof(PythonUapProject))] + [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasAppContainerProject_string)] + [Description("Python Tools UAP Interpreter")] + [ProvideProjectFactory(typeof(PythonUapProjectFactory), null, null, null, null, ".\\NullPath", LanguageVsTemplate = PythonConstants.LanguageName)] + [ProvidePythonInterpreterFactoryProvider(PythonUapInterpreterFactory.InterpreterGuidString, typeof(PythonUapInterpreterFactoryProvider))] + public sealed class PythonUapPackage : Package { + internal static PythonUapPackage Instance; + + /// + /// Default constructor of the package. + /// Inside this method you can place any initialization code that does not require + /// any Visual Studio service because at this point the package object is created but + /// not sited yet inside Visual Studio environment. The place to do all the other + /// initialization is the Initialize method. + /// + public PythonUapPackage() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this)); + Instance = this; + } + + ///////////////////////////////////////////////////////////////////////////// + // Overridden Package Implementation + #region Package Members + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initialization code that rely on services provided by VisualStudio. + /// + protected override void Initialize() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); + base.Initialize(); + + RegisterProjectFactory(new PythonUapProjectFactory(this)); + } + + #endregion + + internal new object GetService(Type serviceType) { + return base.GetService(serviceType); + } + + public EnvDTE.DTE DTE { + get { + return (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE)); + } + } + + internal static PythonUapProject GetProject(IServiceProvider serviceProvider, string filename) { + IVsHierarchy hierarchy; + IVsRunningDocumentTable rdt = serviceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + uint itemid; + IntPtr docData = IntPtr.Zero; + uint cookie; + try { + int hr = rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, + filename, + out hierarchy, + out itemid, + out docData, + out cookie); + + if (ErrorHandler.Succeeded(hr)) { + rdt.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, cookie); + } + var res = hierarchy as PythonUapProject; + if (res != null) { + return res; + } + return null; + } finally { + if (docData != IntPtr.Zero) { + Marshal.Release(docData); + } + } + } + } +} diff --git a/Python/Product/Uap/Resources.Designer.cs b/Python/Product/Uap/Resources.Designer.cs new file mode 100644 index 0000000000..6dc644c56b --- /dev/null +++ b/Python/Product/Uap/Resources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.PythonTools.Uap { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PythonTools.Uap.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to UAP Project Settings. + /// + internal static string UapPropertyPageTitle { + get { + return ResourceManager.GetString("UapPropertyPageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remote machine for deployment. + /// + internal static string UapRemoteMachineHelp { + get { + return ResourceManager.GetString("UapRemoteMachineHelp", resourceCulture); + } + } + } +} diff --git a/Python/Product/Uap/Resources.resx b/Python/Product/Uap/Resources.resx new file mode 100644 index 0000000000..8ae2e557a5 --- /dev/null +++ b/Python/Product/Uap/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UAP Project Settings + + + Remote machine for deployment + + \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx b/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest b/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest new file mode 100644 index 0000000000..e8175443d1 --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest @@ -0,0 +1,67 @@ + + + + + + + + + + $projectname$ + $XmlEscapedPublisher$ + StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pyuapbackgroundservice.dll + + + + + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj new file mode 100644 index 0000000000..773e701a54 --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj @@ -0,0 +1,61 @@ + + + + $safeprojectname$ + Debug + x86 + x86 + 2.0 + 8.2 + Windows Store + true + 14.0 + $guid1$ + . + . + StartupTask.py + true + + $projectname$_TemporaryKey.pfx + $currentuiculturename$ + {c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e};{888888A0-9F3D-457C-B088-3A5042F75D52} + {86767848-40B4-4007-8BCC-A3835EDF0E69} + 3.5 + true + + + + true + false + bin\Debug\$(Platform)\ + + + true + false + bin\Release\$(Platform)\ + + + + + + + Designer + + + + + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + + + + 14.0 + 2.2 + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + $(MSBuildProgramFiles32)\Microsoft Visual Studio $(VisualStudioVersion)\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate new file mode 100644 index 0000000000..406eb323ac --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate @@ -0,0 +1,31 @@ + + + Background Application (IoT) + A project for creating an Python IoT Background Application + + Python + 20 + 2 + true + Microsoft.Ptvs.WinRT.UAP.IOT.BackgroundApplication + BackgroundApplication + true + Windows + 6.1.0 + + + + Package.appxmanifest + StartupTask.py + Application_TemporaryKey.pfx + + + + Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Microsoft.VisualStudio.WinRT.TemplateWizards.CreateProjectCertificate.Wizard + + + Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Microsoft.VisualStudio.WinRT.TemplateWizards.PlatformVersion.Wizard + + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py new file mode 100644 index 0000000000..df1dc6821e --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py @@ -0,0 +1 @@ +print('Hello World') diff --git a/Python/Product/Uap/Uap.csproj b/Python/Product/Uap/Uap.csproj new file mode 100644 index 0000000000..3baed11995 --- /dev/null +++ b/Python/Product/Uap/Uap.csproj @@ -0,0 +1,323 @@ + + + + + + 12.0 + + + + + 4.0 + + + + + 14.0 + + + + + 4.0 + + + + + + Debug + AnyCPU + 2.0 + {D28045D6-232C-4101-A5CB-AC4A68E92211} + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + Microsoft.PythonTools.Uap + Microsoft.PythonTools.Uap + true + SAK + SAK + SAK + SAK + 1762 + + + AnyCPU + + + + True + + + True + + + + + + + + + True + + + + + + + True + + + + + + + + + + + + + + + false + + + + True + + + + + + + + + + + + + + + + + + + + + + True + + + + $(VSSDKDir)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Debugger.Engine.dll + + + + + $(DevEnvDir)Microsoft.VisualStudio.Web.Html.dll + + + + + {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2} + 8 + 0 + 0 + primary + False + False + + + {26AD1324-4B7C-44BC-84F8-B86AED45729F} + 10 + 0 + 0 + primary + False + False + + + {1A31287A-4D7D-413E-8E32-3B374931BD89} + 8 + 0 + 0 + primary + False + False + + + {2CE2370E-D744-4936-A090-3FFFE667B0E1} + 9 + 0 + 0 + primary + False + False + + + {1CBA492E-7263-47BB-87FE-639000619B15} + 8 + 0 + 0 + primary + False + False + + + {00020430-0000-0000-C000-000000000046} + 2 + 0 + 0 + primary + False + False + + + + + Python + Windows IoT Core + + + Python + + + + + + + + + Project\SolutionListener.cs + + + Project\CommonUtils.cs + + + ExceptionExtensions.cs + + + + + + + + + + + + usercontrol + + + PythonUapPropertyPageControl.cs + + + + + True + True + Resources.resx + + + + Designer + + + Designer + + + + Designer + + + + + true + Microsoft.VSPackage + Designer + + + + + true + PreserveNewest + Designer + + + Designer + + + + Designer + + + + + PythonUapPropertyPageControl.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + PreserveNewest + true + . + + + PreserveNewest + true + . + + + + + {A85D479D-67A9-4BDB-904A-7D86DAF68A6F} + Analysis + + + {DECC7971-FA58-4DB0-9561-BFFADD393BBD} + Debugger + + + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + PythonTools + False + + + + + true + + + + $(IntermediateOutputPath)\RemoteDebugger.vsdconfig + + + + <_Arguments>@(VsdConfigXml, ' ') @(IntermediateAssembly->'"%(FullPath)"', ' ') "$(VsdConfigOutput)" + + + + + + + true + . + PreserveNewest + $([System.IO.Path]::GetFileName($(VsdConfigOutput))) + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/VSPackage.resx b/Python/Product/Uap/VSPackage.resx new file mode 100644 index 0000000000..25ba4c515b --- /dev/null +++ b/Python/Product/Uap/VSPackage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Python Tools for Visual Studio - UAP Integration + + + Provides templates and integration for the UAP framework. + + \ No newline at end of file diff --git a/Python/Product/Uap/source.extension.vsixmanifest b/Python/Product/Uap/source.extension.vsixmanifest new file mode 100644 index 0000000000..b42b5127d1 --- /dev/null +++ b/Python/Product/Uap/source.extension.vsixmanifest @@ -0,0 +1,47 @@ + + + + Python Tools for Visual Studio - UAP + Microsoft + 2.2 + Provides templates and integration for the UAP framework. + http://pytools.codeplex.com + http://pytools.codeplex.com + PythonProject.ico + PythonProjectBig.ico + 1033 + true + + + + Pro + Ultimate + IntegratedShell + WDExpress + VWDExpress + + + + + + + Visual Studio MPF + + + Python Tools for Visual Studio + http://pytools.codeplex.com/ + + + Python Tools for Visual Studio - Interactive + http://pytools.codeplex.com/ + + + + |%CurrentProject%;PkgdefProjectOutputGroup| + |%CurrentProject%| + ProjectTemplates + ItemTemplates + RemoteDebugger.vsdconfig + + diff --git a/Python/PythonTools.sln b/Python/PythonTools.sln index 38c920f204d15805df8a9849fb767b1d94ec817b..6203b53bbce35a1f1aeba196dd2d33db7ab4eeeb 100644 GIT binary patch literal 222278 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 aJTG&ii*%+TwvW@vR!EsfD`eIibN>gQ^A+C! diff --git a/Python/Setup/BuildRelease.ps1 b/Python/Setup/BuildRelease.ps1 index bfcb2ace14..2cc4b5b279 100644 --- a/Python/Setup/BuildRelease.ps1 +++ b/Python/Setup/BuildRelease.ps1 @@ -261,6 +261,7 @@ $managed_files = ( "Microsoft.PythonTools.WebRole.dll", "Microsoft.PythonTools.Django.dll", "Microsoft.PythonTools.VsLogger.dll", + "Microsoft.PythonTools.Uap.dll", "Microsoft.PythonTools.AzureSetup.exe", "Microsoft.IronPythonTools.Resolver.dll" ) diff --git a/Python/Setup/MergeModule.wxi b/Python/Setup/MergeModule.wxi index 412e54b73d..19a5beaa17 100644 --- a/Python/Setup/MergeModule.wxi +++ b/Python/Setup/MergeModule.wxi @@ -47,6 +47,9 @@ + + + diff --git a/Python/Setup/PythonTools/PythonToolsFiles.proj b/Python/Setup/PythonTools/PythonToolsFiles.proj index e8d3d6960e..4e08c420f3 100644 --- a/Python/Setup/PythonTools/PythonToolsFiles.proj +++ b/Python/Setup/PythonTools/PythonToolsFiles.proj @@ -44,6 +44,7 @@ !(bindpath.bin)\PyDebugAttachX86.dll; visualstudio_py_repl.py; visualstudio_ipython_repl.py; + visualstudio_py_remote_launcher.py; visualstudio_py_launcher.py; visualstudio_py_debugger.py; visualstudio_py_util.py" /> @@ -135,7 +136,11 @@ CompletionDB - + + + CompletionDB + + $(DefineConstants); @@ -85,6 +86,10 @@ DjangoMsm {16671BE6-DD23-41D9-841A-0B80D47A090D} + + UapMsm + {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} + VsLoggerMsm {639BFC80-B31B-4D3F-9DF3-A65913000B4B} diff --git a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs index eda5726c0c..e9c9a3ede2 100644 --- a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs +++ b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs @@ -197,6 +197,15 @@ + + + + + + + + + @@ -252,6 +261,12 @@ + + + + + + diff --git a/Python/Setup/Uap/Uap.wixproj b/Python/Setup/Uap/Uap.wixproj new file mode 100644 index 0000000000..5213c9c543 --- /dev/null +++ b/Python/Setup/Uap/Uap.wixproj @@ -0,0 +1,33 @@ + + + + + 3.5 + {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} + 2.0 + Uap + Module + false + $(DefineConstants);ProductSuffix=Uap + SAK + SAK + SAK + SAK + + + + + + + Microsoft.PythonTools.Uap + {D28045D6-232C-4101-A5CB-AC4A68E92211} + + + + + MergeModule.wxi + + + + + \ No newline at end of file diff --git a/Python/Setup/Uap/Uap.wxs b/Python/Setup/Uap/Uap.wxs new file mode 100644 index 0000000000..d65abdcad1 --- /dev/null +++ b/Python/Setup/Uap/Uap.wxs @@ -0,0 +1,20 @@ + + + + + + + + + NOT ALLUSERS = 1 + + + + + + + + + + + diff --git a/Python/Setup/Uap/UapFiles.proj b/Python/Setup/Uap/UapFiles.proj new file mode 100644 index 0000000000..4060e61055 --- /dev/null +++ b/Python/Setup/Uap/UapFiles.proj @@ -0,0 +1,42 @@ + + + + + UapFiles + + + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + UapProjectTemplates + + + + + + diff --git a/Python/Setup/setup.proj b/Python/Setup/setup.proj index edae64f88f..80bf0fc743 100644 --- a/Python/Setup/setup.proj +++ b/Python/Setup/setup.proj @@ -17,6 +17,7 @@ + diff --git a/Python/products.settings b/Python/products.settings index 4bfac28cc0..564ffddf21 100644 --- a/Python/products.settings +++ b/Python/products.settings @@ -1,5 +1,9 @@ + + true + false + false true From 7faeeb0409d1ceebb492b007c3b8e57547ff3e18 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Mon, 4 May 2015 13:52:01 -0700 Subject: [PATCH 03/30] Adding support for UAP based Python applications --- .../SharedProject/CommonConfigProvider.cs | 112 +- .../SharedProject/CommonProjectConfig.cs | 10 + .../SharedProject/CommonPropertyPage.cs | 165 ++- .../Product/SharedProject/ConfigProvider.cs | 11 +- Common/Product/SharedProject/ProjectConfig.cs | 35 +- .../SharedProject/ProjectFileConstants.cs | 3 + Common/Product/SharedProject/ProjectNode.cs | 31 +- Python/Product/Debugger/Debugger.csproj | 1 + .../DebugEngine/AD7BoundBreakpoint.cs | 11 +- .../Debugger/DebugEngine/AD7Engine.cs | 6 + .../DebugEngine/Remote/PythonRemoteProcess.cs | 21 +- .../Debugger/Debugger/PythonProcess.cs | 25 +- .../Debugger/Debugger/PythonStackFrame.cs | 2 +- .../Debugger/Transports/TcpTransport.cs | 2 +- Python/Product/PythonTools/PythonConstants.cs | 6 +- Python/Product/PythonTools/PythonTools.csproj | 4 + .../PythonTools/PythonTools/Extensions.cs | 24 +- .../Project/ImportWizard/ImportSettings.cs | 3 +- .../ImportWizard/ProjectCustomizations.cs | 24 + .../Project/PythonProjectConfig.cs | 23 + .../PythonTools/Project/PythonProjectNode.cs | 45 +- .../PythonTools/visualstudio_py_debugger.py | 142 +-- .../visualstudio_py_remote_launcher.py | 114 +++ .../Uap/Debugger/PythonRemoteDebugEvents.cs | 118 +++ .../RemoteDebugServerComponent.vsdconfigxml | 36 + Python/Product/Uap/Extensions.cs | 144 +++ Python/Product/Uap/Guids.cs | 26 + .../Uap/Interpreter/PythonUapInterpreter.cs | 237 +++++ .../PythonUapInterpreterFactory.cs | 40 + .../PythonUapInterpreterFactoryProvider.cs | 47 + .../Uap/Microsoft.PythonTools.Uap.targets | 66 ++ .../Product/Uap/Project/PythonUapProject.cs | 299 ++++++ .../Uap/Project/PythonUapProjectConfig.cs | 961 ++++++++++++++++++ .../Uap/Project/PythonUapProjectFactory.cs | 34 + .../Uap/Project/PythonUapPropertyPage.cs | 60 ++ .../PythonUapPropertyPageControl.Designer.cs | 147 +++ .../Project/PythonUapPropertyPageControl.cs | 49 + .../Project/PythonUapPropertyPageControl.resx | 129 +++ Python/Product/Uap/Properties/AssemblyInfo.cs | 30 + Python/Product/Uap/PythonUapPackage.cs | 125 +++ Python/Product/Uap/Resources.Designer.cs | 81 ++ Python/Product/Uap/Resources.resx | 126 +++ .../Application_TemporaryKey.pfx | 0 .../BackgroundService/Package.appxmanifest | 67 ++ .../PythonBackgroundService.pyproj | 61 ++ .../PythonBackgroundService.vstemplate | 31 + .../Projects/BackgroundService/StartupTask.py | 1 + Python/Product/Uap/Uap.csproj | 323 ++++++ Python/Product/Uap/VSPackage.resx | 126 +++ .../Product/Uap/source.extension.vsixmanifest | 47 + Python/PythonTools.sln | Bin 168906 -> 222278 bytes Python/Setup/BuildRelease.ps1 | 1 + Python/Setup/MergeModule.wxi | 3 + .../Setup/PythonTools/PythonToolsFiles.proj | 7 +- Python/Setup/PythonTools/_wingpio.idb | Bin 0 -> 4525 bytes Python/Setup/PythonTools/_wini2c.idb | Bin 0 -> 6744 bytes Python/Setup/PythonTools/_winspi.idb | Bin 0 -> 12954 bytes .../PythonToolsInstaller.wixproj | 5 + .../PythonToolsInstaller.wxs | 15 + Python/Setup/Uap/Uap.wixproj | 33 + Python/Setup/Uap/Uap.wxs | 20 + Python/Setup/Uap/UapFiles.proj | 42 + Python/Setup/setup.proj | 1 + Python/products.settings | 4 + 64 files changed, 4229 insertions(+), 133 deletions(-) create mode 100644 Python/Product/PythonTools/visualstudio_py_remote_launcher.py create mode 100644 Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs create mode 100644 Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml create mode 100644 Python/Product/Uap/Extensions.cs create mode 100644 Python/Product/Uap/Guids.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreter.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs create mode 100644 Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs create mode 100644 Python/Product/Uap/Microsoft.PythonTools.Uap.targets create mode 100644 Python/Product/Uap/Project/PythonUapProject.cs create mode 100644 Python/Product/Uap/Project/PythonUapProjectConfig.cs create mode 100644 Python/Product/Uap/Project/PythonUapProjectFactory.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPage.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.cs create mode 100644 Python/Product/Uap/Project/PythonUapPropertyPageControl.resx create mode 100644 Python/Product/Uap/Properties/AssemblyInfo.cs create mode 100644 Python/Product/Uap/PythonUapPackage.cs create mode 100644 Python/Product/Uap/Resources.Designer.cs create mode 100644 Python/Product/Uap/Resources.resx create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate create mode 100644 Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py create mode 100644 Python/Product/Uap/Uap.csproj create mode 100644 Python/Product/Uap/VSPackage.resx create mode 100644 Python/Product/Uap/source.extension.vsixmanifest create mode 100644 Python/Setup/PythonTools/_wingpio.idb create mode 100644 Python/Setup/PythonTools/_wini2c.idb create mode 100644 Python/Setup/PythonTools/_winspi.idb create mode 100644 Python/Setup/Uap/Uap.wixproj create mode 100644 Python/Setup/Uap/Uap.wxs create mode 100644 Python/Setup/Uap/UapFiles.proj diff --git a/Common/Product/SharedProject/CommonConfigProvider.cs b/Common/Product/SharedProject/CommonConfigProvider.cs index 60879b8b55..164a43fa82 100644 --- a/Common/Product/SharedProject/CommonConfigProvider.cs +++ b/Common/Product/SharedProject/CommonConfigProvider.cs @@ -13,46 +13,134 @@ * ***************************************************************************/ using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using System.Collections.Generic; namespace Microsoft.VisualStudioTools.Project { /// - /// Enables the Any CPU Platform form name for Dynamic Projects. + /// Enables the Any CPU, x86, x64, and ARM Platform form names for Dynamic Projects. /// Hooks language specific project config. + /// Projects that are platform aware should have a PlatformAware and AvailablePlatforms + /// property for configuration handling to function correctly. + /// PlatformAware value can be true or false. AvailablePlatforms is a comma separated string of supported platforms (e.g. "x86, X64") + /// If the PlatformAware property is ommited then this provider will only provide "Any CPU" platform. /// internal class CommonConfigProvider : ConfigProvider { private CommonProjectNode _project; + private bool _isPlatformAware; public CommonConfigProvider(CommonProjectNode project) : base(project) { + bool appxPackage, windowsAppContainer; _project = project; + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.PlatformAware), out _isPlatformAware); + + if (!_isPlatformAware) { + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AppxPackage), out appxPackage); + bool.TryParse(this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.WindowsAppContainer), out windowsAppContainer); + _isPlatformAware = appxPackage && windowsAppContainer; + } } #region overridden methods - protected override ProjectConfig CreateProjectConfiguration(string configName) { + protected override ProjectConfig CreateProjectConfiguration(string configName) { + if (_isPlatformAware) { + if (configName != null) { + var configParts = configName.Split('|'); + + if (configParts.Length == 2) { + var config = _project.MakeConfiguration(configName); + config.PlatformName = configParts[1]; + return config; + } + } + } + return _project.MakeConfiguration(configName); } public override int GetPlatformNames(uint celt, string[] names, uint[] actual) { - if (names != null) { - names[0] = "Any CPU"; + if (_isPlatformAware) { + var platforms = GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + else { + if (names != null) { + names[0] = AnyCPUPlatform; } - if (actual != null) { - actual[0] = 1; + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } } - return VSConstants.S_OK; + public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { + if (_isPlatformAware) { + var platforms = GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + else { + if (names != null) { + names[0] = AnyCPUPlatform; + } + + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } } - public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { - if (names != null) { - names[0] = "Any CPU"; + public override int GetCfgs(uint celt, IVsCfg[] a, uint[] actual, uint[] flags) { + if (_isPlatformAware) { + if (flags != null) { + flags[0] = 0; + } + + int i = 0; + string[] configList = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + string[] platformList = GetSupportedPlatformsFromProject(); + + if (a != null) { + foreach (string platformName in platformList) { + foreach (string configName in configList) { + a[i] = this.GetProjectConfiguration(configName + "|" + platformName); + i++; + if (i == celt) { + break; + } + } + if(i == celt) { + break; + } + } + } + else { + i = configList.Length * platformList.Length; + } + + if (actual != null) { + actual[0] = (uint)i; + } + + return VSConstants.S_OK; } - if (actual != null) { - actual[0] = 1; + return base.GetCfgs(celt, a, actual, flags); + } + + public override int GetCfgOfName(string name, string platName, out IVsCfg cfg) { + if (!string.IsNullOrEmpty(platName)) { + cfg = this.GetProjectConfiguration(name + "|" + platName); + + return VSConstants.S_OK; } + cfg = this.GetProjectConfiguration(name); return VSConstants.S_OK; } diff --git a/Common/Product/SharedProject/CommonProjectConfig.cs b/Common/Product/SharedProject/CommonProjectConfig.cs index 83b90daf55..7e2b28f645 100644 --- a/Common/Product/SharedProject/CommonProjectConfig.cs +++ b/Common/Product/SharedProject/CommonProjectConfig.cs @@ -28,6 +28,16 @@ public CommonProjectConfig(CommonProjectNode/*!*/ project, string configuration) _project = project; } + public virtual string Platform { + get { + return GetConfigurationProperty("Platform", false); + } + + set { + SetConfigurationProperty("Platform", value); + } + } + public override int DebugLaunch(uint flags) { IProjectLauncher starter = _project.GetLauncher(); diff --git a/Common/Product/SharedProject/CommonPropertyPage.cs b/Common/Product/SharedProject/CommonPropertyPage.cs index 06f53b33bc..168943a862 100644 --- a/Common/Product/SharedProject/CommonPropertyPage.cs +++ b/Common/Product/SharedProject/CommonPropertyPage.cs @@ -14,9 +14,13 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Drawing; +using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; +using Microsoft.Build.Construction; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; @@ -49,6 +53,10 @@ internal virtual CommonProjectNode Project { } } + internal virtual IEnumerable SelectedConfigs { + get; set; + } + protected void SetProjectProperty(string propertyName, string propertyValue) { // SetProjectProperty's implementation will check whether the value // has changed. @@ -59,6 +67,150 @@ protected string GetProjectProperty(string propertyName) { return Project.GetUnevaluatedProperty(propertyName); } + protected void SetUserProjectProperty(string propertyName, string propertyValue) { + // SetUserProjectProperty's implementation will check whether the value + // has changed. + Project.SetUserProjectProperty(propertyName, propertyValue); + } + + protected string GetUserProjectProperty(string propertyName) { + return Project.GetUserProjectProperty(propertyName); + } + + protected string GetConfigUserProjectProperty(string propertyName) { + if (SelectedConfigs == null) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + Project.CurrentConfig.GetPropertyValue("Configuration"), + Project.CurrentConfig.GetPropertyValue("Platform")); + + return GetUserPropertyUnderCondition(propertyName, condition); + } else { + StringCollection values = new StringCollection(); + + foreach (var config in SelectedConfigs) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + config.ConfigName, + config.Platform); + + values.Add(GetUserPropertyUnderCondition(propertyName, condition)); + } + + switch (values.Count) { + case 0: + return null; + case 1: + return values[0]; + default: + return ""; + } + } + } + + protected void SetConfigUserProjectProperty(string propertyName, string propertyValue) { + if (SelectedConfigs == null) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + Project.CurrentConfig.GetPropertyValue("Configuration"), + Project.CurrentConfig.GetPropertyValue("Platform")); + + SetUserPropertyUnderCondition(propertyName, propertyValue, condition); + } else { + foreach (var config in SelectedConfigs) { + string condition = string.Format(System.Globalization.CultureInfo.InvariantCulture, + ConfigProvider.configPlatformString, + config.ConfigName, + config.GetConfigurationProperty("Platform", false)); + + SetUserPropertyUnderCondition(propertyName, propertyValue, condition); + } + } + } + + private string GetUserPropertyUnderCondition(string propertyName, string condition) { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + + if (Project.UserBuildProject != null) { + if (conditionTrimmed.Length == 0) { + return Project.UserBuildProject.GetProperty(propertyName).UnevaluatedValue; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + ProjectPropertyGroupElement matchingGroup = null; + + foreach (ProjectPropertyGroupElement group in Project.UserBuildProject.Xml.PropertyGroups) { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) { + matchingGroup = group; + break; + } + } + + if (matchingGroup != null) { + foreach (ProjectPropertyElement property in matchingGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && (property.Condition == null || property.Condition.Length == 0)) { + return property.Value; + } + } + } + + } + + return null; + } + + /// + /// Emulates the behavior of SetProperty(name, value, condition) on the old MSBuild object model. + /// This finds a property group with the specified condition (or creates one if necessary) then sets the property in there. + /// + private void SetUserPropertyUnderCondition(string propertyName, string propertyValue, string condition) { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + const string userProjectCreateProperty = "UserProject"; + + if (Project.UserBuildProject == null) { + Project.SetUserProjectProperty(userProjectCreateProperty, null); + } + + if (conditionTrimmed.Length == 0) { + var userProp = Project.UserBuildProject.GetProperty(userProjectCreateProperty); + if (userProp != null) { + Project.UserBuildProject.RemoveProperty(userProp); + } + Project.UserBuildProject.SetProperty(propertyName, propertyValue); + return; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + ProjectPropertyGroupElement newGroup = null; + + foreach (ProjectPropertyGroupElement group in Project.UserBuildProject.Xml.PropertyGroups) { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) { + newGroup = group; + break; + } + } + + if (newGroup == null) { + newGroup = Project.UserBuildProject.Xml.AddPropertyGroup(); // Adds after last existing PG, else at start of project + newGroup.Condition = condition; + } + + foreach (ProjectPropertyElement property in newGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && (property.Condition == null || property.Condition.Length == 0)) { + property.Value = propertyValue; + return; + } + } + + newGroup.AddProperty(propertyName, propertyValue); + } + public bool Loading { get { return _loading; @@ -138,18 +290,19 @@ void IPropertyPage.SetObjects(uint count, object[] punk) { if (count > 0) { if (punk[0] is ProjectConfig) { - ArrayList configs = new ArrayList(); + if (_project == null) { + _project = (CommonProjectNode)((CommonProjectConfig)punk.First()).ProjectMgr; + } + + var configs = new List(); for (int i = 0; i < count; i++) { CommonProjectConfig config = (CommonProjectConfig)punk[i]; - if (_project == null) { - Project = (CommonProjectNode)config.ProjectMgr; - break; - } - configs.Add(config); } + + SelectedConfigs = configs; } else if (punk[0] is NodeProperties) { if (_project == null) { Project = (CommonProjectNode)(punk[0] as NodeProperties).HierarchyNode.ProjectMgr; diff --git a/Common/Product/SharedProject/ConfigProvider.cs b/Common/Product/SharedProject/ConfigProvider.cs index d6c028f245..1e1bdc0857 100644 --- a/Common/Product/SharedProject/ConfigProvider.cs +++ b/Common/Product/SharedProject/ConfigProvider.cs @@ -37,8 +37,11 @@ namespace Microsoft.VisualStudioTools.Project { [ComVisible(true)] internal abstract class ConfigProvider : IVsCfgProvider2 { internal const string configString = " '$(Configuration)' == '{0}' "; + internal const string configPlatformString = " '$(Configuration)|$(Platform)' == '{0}|{1}' "; internal const string AnyCPUPlatform = "Any CPU"; internal const string x86Platform = "x86"; + internal const string x64Platform = "x64"; + internal const string ARMPlatform = "ARM"; private ProjectNode project; private EventSinkCollection cfgEventSinks = new EventSinkCollection(); @@ -473,7 +476,7 @@ private string[] GetPlatformsFromProject() { string[] platforms = GetPropertiesConditionedOn(ProjectFileConstants.Platform); if (platforms == null || platforms.Length == 0) { - return new string[] { x86Platform, AnyCPUPlatform }; + return new string[] { x86Platform, AnyCPUPlatform, x64Platform, ARMPlatform }; } for (int i = 0; i < platforms.Length; i++) { @@ -487,7 +490,7 @@ private string[] GetPlatformsFromProject() { /// Return the supported platform names. /// /// An array of supported platform names. - private string[] GetSupportedPlatformsFromProject() { + internal string[] GetSupportedPlatformsFromProject() { string platforms = this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AvailablePlatforms); if (platforms == null) { @@ -523,7 +526,7 @@ private static string ConvertPlatformToVsProject(string oldPlatformName) { /// An array of available platform names /// A count of the actual number of platform names returned. /// The platforms array is never null. It is assured by the callers. - private static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) { + internal static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) { Utilities.ArgumentNotNull("platforms", platforms); if (names == null) { if (actual == null || actual.Length == 0) { @@ -564,7 +567,7 @@ private static int GetPlatforms(uint celt, string[] names, uint[] actual, string /// /// Get all the configurations in the project. /// - private string[] GetPropertiesConditionedOn(string constant) { + internal string[] GetPropertiesConditionedOn(string constant) { List configurations = null; this.project.BuildProject.ReevaluateIfNecessary(); this.project.BuildProject.ConditionedProperties.TryGetValue(constant, out configurations); diff --git a/Common/Product/SharedProject/ProjectConfig.cs b/Common/Product/SharedProject/ProjectConfig.cs index 187451f05d..6583008e4f 100644 --- a/Common/Product/SharedProject/ProjectConfig.cs +++ b/Common/Product/SharedProject/ProjectConfig.cs @@ -48,8 +48,19 @@ internal abstract class ProjectConfig : private IVsProjectFlavorCfg flavoredCfg; private List outputGroups; private BuildableProjectConfig buildableCfg; + private string platformName; #region properties + + public string PlatformName { + get { + return platformName; + } + set { + platformName = value; + } + } + internal ProjectNode ProjectMgr { get { return this.project; @@ -97,7 +108,20 @@ internal IList OutputGroups { #region ctors internal ProjectConfig(ProjectNode project, string configuration) { this.project = project; - this.configName = configuration; + if (configuration.Contains("|")) { // If configuration is in the form "|" + + string[] configStrArray = configuration.Split('|'); + if (2 == configStrArray.Length) { + this.configName = configStrArray[0]; + this.platformName = configStrArray[1]; + } + else { + throw new Exception(string.Format(CultureInfo.InvariantCulture, "Invalid configuration format: {0}", configuration)); + } + } + else { // If configuration is in the form "" + this.configName = configuration; + } var flavoredCfgProvider = ProjectMgr.GetOuterInterface(); Utilities.ArgumentNotNull("flavoredCfgProvider", flavoredCfgProvider); @@ -107,7 +131,7 @@ internal ProjectConfig(ProjectNode project, string configuration) { // if the flavored object support XML fragment, initialize it IPersistXMLFragment persistXML = flavoredCfg as IPersistXMLFragment; if (null != persistXML) { - this.project.LoadXmlFragment(persistXML, configName); + this.project.LoadXmlFragment(persistXML, configName, platformName); } } #endregion @@ -240,7 +264,12 @@ public virtual int GetProjectDesignerPages(CAUUID[] pages) { /// first part is the config name, 2nd part is the platform name /// public virtual int get_DisplayName(out string name) { - name = DisplayName; + if (!string.IsNullOrEmpty(PlatformName)) { + name = ConfigName + "|" + PlatformName; + + } else { + name = DisplayName; + } return VSConstants.S_OK; } diff --git a/Common/Product/SharedProject/ProjectFileConstants.cs b/Common/Product/SharedProject/ProjectFileConstants.cs index efd04b18e2..ff70be332b 100644 --- a/Common/Product/SharedProject/ProjectFileConstants.cs +++ b/Common/Product/SharedProject/ProjectFileConstants.cs @@ -89,6 +89,9 @@ public static class ProjectFileConstants { public const string FlavorProperties = "FlavorProperties"; public const string VisualStudio = "VisualStudio"; public const string User = "User"; + public const string PlatformAware = "PlatformAware"; + public const string AppxPackage = "AppxPackage"; + public const string WindowsAppContainer = "WindowsAppContainer"; } public static class ProjectFileAttributeValue { diff --git a/Common/Product/SharedProject/ProjectNode.cs b/Common/Product/SharedProject/ProjectNode.cs index 7c6cae1b8a..ebafc9d6ed 100644 --- a/Common/Product/SharedProject/ProjectNode.cs +++ b/Common/Product/SharedProject/ProjectNode.cs @@ -949,6 +949,12 @@ public override object GetProperty(int propId) { case __VSHPROPID.VSHPROPID_ExpandByDefault: return true; + case __VSHPROPID.VSHPROPID_DefaultEnableDeployProjectCfg: + return true; + + case __VSHPROPID.VSHPROPID_DefaultEnableBuildProjectCfg: + return true; + // Use the same icon as if the folder was closed case __VSHPROPID.VSHPROPID_OpenFolderIconIndex: return GetProperty((int)__VSHPROPID.VSHPROPID_IconIndex); @@ -2003,7 +2009,7 @@ protected virtual Guid[] GetConfigurationIndependentPropertyPages() { /// /// protected virtual Guid[] GetConfigurationDependentPropertyPages() { - return new Guid[0]; + return new Guid[] { Guid.Empty }; } /// @@ -2956,7 +2962,7 @@ protected internal virtual void ProcessDependentFileNodes(IList subitems protected internal virtual void LoadNonBuildInformation() { IPersistXMLFragment outerHierarchy = GetOuterInterface(); if (outerHierarchy != null) { - this.LoadXmlFragment(outerHierarchy, null); + this.LoadXmlFragment(outerHierarchy, null, null); } } @@ -3371,7 +3377,8 @@ protected void AddCATIDMapping(Type type, Guid catid) { /// /// Object that support being initialized with an XML fragment /// Name of the configuration being initialized, null if it is the project - protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName) { + /// Name of the platform being initialized, null is ok + protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName, string platformName) { Utilities.ArgumentNotNull("persistXmlFragment", persistXmlFragment); if (xmlFragments == null) { @@ -3395,15 +3402,20 @@ protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, if (child.Attributes.Count > 0) { string guid = String.Empty; string configuration = String.Empty; + string platform = String.Empty; if (child.Attributes[ProjectFileConstants.Guid] != null) guid = child.Attributes[ProjectFileConstants.Guid].Value; if (child.Attributes[ProjectFileConstants.Configuration] != null) configuration = child.Attributes[ProjectFileConstants.Configuration].Value; + if (child.Attributes[ProjectFileConstants.Platform] != null) + platform = child.Attributes[ProjectFileConstants.Platform].Value; if (String.Compare(child.Name, ProjectFileConstants.FlavorProperties, StringComparison.OrdinalIgnoreCase) == 0 && String.Compare(guid, flavorGuidString, StringComparison.OrdinalIgnoreCase) == 0 && ((String.IsNullOrEmpty(configName) && String.IsNullOrEmpty(configuration)) - || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0))) { + || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0)) + && ((String.IsNullOrEmpty(platformName) && String.IsNullOrEmpty(platform)) + || (String.Compare(platform, platformName, StringComparison.OrdinalIgnoreCase) == 0))) { // we found the matching fragment fragment = child.InnerXml; node = child; @@ -3463,7 +3475,7 @@ protected void PersistXMLFragments() { ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, out fragment, 1)); if (!String.IsNullOrEmpty(fragment)) { // Add the fragment to our XML - WrapXmlFragment(doc, root, flavor, null, fragment); + WrapXmlFragment(doc, root, flavor, null, null, fragment); } // While we don't yet support user files, our flavors might, so we will store that in the project file until then // TODO: Refactor this code when we support user files @@ -3471,7 +3483,7 @@ protected void PersistXMLFragments() { ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, out fragment, 1)); if (!String.IsNullOrEmpty(fragment)) { // Add the fragment to our XML - XmlElement node = WrapXmlFragment(doc, root, flavor, null, fragment); + XmlElement node = WrapXmlFragment(doc, root, flavor, null, null, fragment); node.Attributes.Append(doc.CreateAttribute(ProjectFileConstants.User)); } } @@ -3482,7 +3494,7 @@ protected void PersistXMLFragments() { string fragment; ErrorHandler.ThrowOnFailure(((ProjectConfig)config).GetXmlFragment(flavor, _PersistStorageType.PST_PROJECT_FILE, out fragment)); if (!String.IsNullOrEmpty(fragment)) { - WrapXmlFragment(doc, root, flavor, ((ProjectConfig)config).ConfigName, fragment); + WrapXmlFragment(doc, root, flavor, ((ProjectConfig)config).ConfigName, ((ProjectConfig)config).PlatformName, fragment); } } } @@ -5091,7 +5103,7 @@ internal void OnAfterProjectOpen() { this.projectOpened = true; } - private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string fragment) { + private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string platform, string fragment) { XmlElement node = document.CreateElement(ProjectFileConstants.FlavorProperties); XmlAttribute attribute = document.CreateAttribute(ProjectFileConstants.Guid); attribute.Value = flavor.ToString("B"); @@ -5100,6 +5112,9 @@ private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, attribute = document.CreateAttribute(ProjectFileConstants.Configuration); attribute.Value = configuration; node.Attributes.Append(attribute); + attribute = document.CreateAttribute(ProjectFileConstants.Platform); + attribute.Value = platform; + node.Attributes.Append(attribute); } node.InnerXml = fragment; root.AppendChild(node); diff --git a/Python/Product/Debugger/Debugger.csproj b/Python/Product/Debugger/Debugger.csproj index 78c71f2fd9..f80f6e6fde 100644 --- a/Python/Product/Debugger/Debugger.csproj +++ b/Python/Product/Debugger/Debugger.csproj @@ -82,6 +82,7 @@ global + diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs index 61332b3521..4a02742539 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs @@ -108,7 +108,16 @@ int IDebugBoundBreakpoint2.SetCondition(BP_CONDITION bpCondition) { } int IDebugBoundBreakpoint2.GetHitCount(out uint pdwHitCount) { - pdwHitCount = (uint)_breakpoint.GetHitCountAsync().GetAwaiter().GetResult(); + var remoteProcess = _engine.Process as Remote.PythonRemoteProcess; + if (remoteProcess != null && remoteProcess.TargetHostType == AD7Engine.TargetUap) { + // Target is UAP host and we will just assume breakpoint hit count is 1 from this + // remote debug type due to issues with communicating this command + pdwHitCount = 1; + } else { + var task = _breakpoint.GetHitCountAsync(); + pdwHitCount = (uint)task.GetAwaiter().GetResult(); + } + return VSConstants.S_OK; } diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs index 626412d044..73c39f03e6 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs @@ -89,6 +89,12 @@ public sealed class AD7Engine : IDebugEngine2, IDebugEngineLaunch2, IDebugProgra public const string DebugEngineId = "{EC1375B7-E2CE-43E8-BF75-DC638DE1F1F9}"; public const string DebugEngineName = "Python"; public static Guid DebugEngineGuid = new Guid(DebugEngineId); + public const string SourceDirectoryKey = "sd"; + public const string TargetDirectoryKey = "td"; + public const string TargetHostType = "host"; + + public const string TargetUap = "uap"; + /// /// Specifies the version of the language which is being debugged. One of /// V24, V25, V26, V27, V30, V31 or V32. diff --git a/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs b/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs index 8378c6473d..c1e24a034e 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/Remote/PythonRemoteProcess.cs @@ -13,13 +13,13 @@ * ***************************************************************************/ using System; -using System.Diagnostics; using System.IO; -using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Text; +using System.Web; using System.Windows.Forms; +using Microsoft.PythonTools.Debugger.DebugEngine; using Microsoft.PythonTools.Debugger.Transports; using Microsoft.PythonTools.Parsing; @@ -37,10 +37,27 @@ internal class PythonRemoteProcess : PythonProcess { private PythonRemoteProcess(int pid, Uri uri, PythonLanguageVersion langVer) : base(pid, langVer) { Uri = uri; + ParseQueryString(); } public Uri Uri { get; private set; } + internal string TargetHostType { get; private set; } + + internal void ParseQueryString() { + if (Uri != null && Uri.Query != null) { + var queryParts = HttpUtility.ParseQueryString(Uri.Query); + + var sourceDir = queryParts[AD7Engine.SourceDirectoryKey]; + var targetDir = queryParts[AD7Engine.TargetDirectoryKey]; + + TargetHostType = queryParts[AD7Engine.TargetHostType]; + + if (!string.IsNullOrWhiteSpace(sourceDir) && !string.IsNullOrWhiteSpace(targetDir)) + AddDirMapping(new string[] { sourceDir, targetDir }); + } + } + /// /// Connects to and performs the initial handshake with the remote debugging server, verifying protocol signature and version number, /// and returns the opened stream in a state ready to receive further ptvsd commands (e.g. attach). diff --git a/Python/Product/Debugger/Debugger/PythonProcess.cs b/Python/Product/Debugger/Debugger/PythonProcess.cs index ad7cb7d06c..c1d8bf93dc 100644 --- a/Python/Product/Debugger/Debugger/PythonProcess.cs +++ b/Python/Product/Debugger/Debugger/PythonProcess.cs @@ -62,6 +62,7 @@ class PythonProcess : IDisposable { protected PythonProcess(int pid, PythonLanguageVersion languageVersion) { _pid = pid; _langVersion = languageVersion; + _dirMapping = new List(); } private PythonProcess(int pid) { @@ -185,6 +186,12 @@ public void Dispose() { GC.SuppressFinalize(this); } + internal void AddDirMapping(string[] mapping) { + if (mapping != null) { + _dirMapping.Add(mapping); + } + } + protected virtual void Dispose(bool disposing) { DebugConnectionListener.UnregisterProcess(_processGuid); @@ -627,6 +634,8 @@ private void HandleDebuggerOutput(Stream stream) { long tid = stream.ReadInt64(); string output = stream.ReadString(); + System.Diagnostics.Debug.WriteLine(output); + PythonThread thread; if (_threads.TryGetValue(tid, out thread)) { var outputEvent = DebuggerOutput; @@ -1043,16 +1052,14 @@ internal string MapFile(string file, bool toDebuggee = true) { string mapTo = mappingInfo[toDebuggee ? 1 : 0]; if (file.StartsWith(mapFrom, StringComparison.OrdinalIgnoreCase)) { - if (file.StartsWith(mapFrom, StringComparison.OrdinalIgnoreCase)) { - int len = mapFrom.Length; - if (!mappingInfo[0].EndsWith("\\")) { - len++; - } - - string newFile = Path.Combine(mapTo, file.Substring(len)); - Debug.WriteLine(String.Format("Filename mapped from {0} to {1}", file, newFile)); - return newFile; + int len = mapFrom.Length; + if (!mappingInfo[0].EndsWith("\\")) { + len++; } + + string newFile = Path.Combine(mapTo, file.Substring(len)); + Debug.WriteLine("Filename mapped from {0} to {1}", file, newFile); + return newFile; } } } diff --git a/Python/Product/Debugger/Debugger/PythonStackFrame.cs b/Python/Product/Debugger/Debugger/PythonStackFrame.cs index ceefa0e3f0..b9a40259df 100644 --- a/Python/Product/Debugger/Debugger/PythonStackFrame.cs +++ b/Python/Product/Debugger/Debugger/PythonStackFrame.cs @@ -204,7 +204,7 @@ public bool SetLineNumber(int lineNo) { /// And with the current statement being pass, the qualified name is "D.e in c in A.b". /// public string GetQualifiedFunctionName() { - var ast = _thread.Process.GetAst(_filename); + var ast = _thread.Process.GetAst(FileName); if (ast == null) { return FunctionName; } diff --git a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs index c17f8f1a48..e1fba47486 100644 --- a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs +++ b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs @@ -21,7 +21,7 @@ internal class TcpTransport : IDebuggerTransport { public const ushort DefaultPort = 5678; public Exception Validate(Uri uri) { - if (uri.PathAndQuery != "/") { + if (uri.AbsolutePath != "/") { return new FormatException("tcp:// URI cannot contain a path"); } return null; diff --git a/Python/Product/PythonTools/PythonConstants.cs b/Python/Product/PythonTools/PythonConstants.cs index 8c03ce41cd..825508593e 100644 --- a/Python/Product/PythonTools/PythonConstants.cs +++ b/Python/Product/PythonTools/PythonConstants.cs @@ -40,9 +40,9 @@ public static class PythonConstants { internal const string WebProjectFactoryGuid = "1b580a1a-fdb3-4b32-83e1-6407eb2722e6"; internal const string EditorFactoryGuid = "888888c4-36f9-4453-90aa-29fa4d2e5706"; internal const string ProjectNodeGuid = "8888881a-afb8-42b1-8398-e60d69ee864d"; - internal const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; - internal const string DebugPropertyPageGuid = "9A46BC86-34CB-4597-83E5-498E3BDBA20A"; - internal const string PublishPropertyPageGuid = "63DF0877-CF53-4975-B200-2B11D669AB00"; + public const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; + public const string DebugPropertyPageGuid = "9A46BC86-34CB-4597-83E5-498E3BDBA20A"; + public const string PublishPropertyPageGuid = "63DF0877-CF53-4975-B200-2B11D669AB00"; internal const string WebPropertyPageGuid = "76EED3B5-14B1-413B-937A-F6F79AC1F8C8"; internal const string EditorFactoryPromptForEncodingGuid = "CA887E0B-55C6-4AE9-B5CF-A2EEFBA90A3E"; diff --git a/Python/Product/PythonTools/PythonTools.csproj b/Python/Product/PythonTools/PythonTools.csproj index f99d53b296..3e7ec34a19 100644 --- a/Python/Product/PythonTools/PythonTools.csproj +++ b/Python/Product/PythonTools/PythonTools.csproj @@ -1153,6 +1153,10 @@ true PreserveNewest + + true + PreserveNewest + diff --git a/Python/Product/PythonTools/PythonTools/Extensions.cs b/Python/Product/PythonTools/PythonTools/Extensions.cs index 2347460773..2a783b45b0 100644 --- a/Python/Product/PythonTools/PythonTools/Extensions.cs +++ b/Python/Product/PythonTools/PythonTools/Extensions.cs @@ -28,6 +28,7 @@ using Microsoft.PythonTools.Editor.Core; using Microsoft.PythonTools.Intellisense; using Microsoft.PythonTools.Interpreter; +using Microsoft.PythonTools.InterpreterList; using Microsoft.PythonTools.Parsing.Ast; using Microsoft.PythonTools.Project; using Microsoft.PythonTools.Repl; @@ -43,15 +44,30 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudioTools; +using Microsoft.VisualStudioTools.Project; + +// Disables obsolete warning for ISmartTag* interfaces - http://go.microsoft.com/fwlink/?LinkId=394601 +#pragma warning disable 618 namespace Microsoft.PythonTools { - using Microsoft.PythonTools.InterpreterList; - using Task = System.Threading.Tasks.Task; #if INTERACTIVE_WINDOW using IReplEvaluator = IInteractiveEngine; #endif public static class Extensions { + internal static bool IsAppxPackageableProject(this ProjectNode projectNode) { + var appxProp = projectNode.BuildProject.GetPropertyValue(ProjectFileConstants.AppxPackage); + var containerProp = projectNode.BuildProject.GetPropertyValue(ProjectFileConstants.WindowsAppContainer); + var appxFlag = false; + var containerFlag = false; + + if (bool.TryParse(appxProp, out appxFlag) && bool.TryParse(containerProp, out containerFlag)) { + return appxFlag && containerFlag; + } else { + return false; + } + } + public static StandardGlyphGroup ToGlyphGroup(this PythonMemberType objectType) { StandardGlyphGroup group; switch (objectType) { @@ -702,8 +718,8 @@ internal static T Peek(this List list) { return list[list.Count - 1]; } - internal static Task StartNew(this TaskScheduler scheduler, Action func) { - return Task.Factory.StartNew(func, default(CancellationToken), TaskCreationOptions.None, scheduler); + internal static System.Threading.Tasks.Task StartNew(this TaskScheduler scheduler, Action func) { + return System.Threading.Tasks.Task.Factory.StartNew(func, default(CancellationToken), TaskCreationOptions.None, scheduler); } internal static int GetStartIncludingIndentation(this Node self, PythonAst ast) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs index 3f7bb01d1f..0db9e064df 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs @@ -45,7 +45,8 @@ internal class ImportSettings : DependencyObject { BottleProjectCustomization.Instance, DjangoProjectCustomization.Instance, FlaskProjectCustomization.Instance, - GenericWebProjectCustomization.Instance + GenericWebProjectCustomization.Instance, + UapProjectCustomization.Instance }; public ImportSettings(IInterpreterOptionsService service) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs index b70302caa8..4d527a928a 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs @@ -193,4 +193,28 @@ Dictionary groups project.AddImport(@"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets"); } } + + class UapProjectCustomization : ProjectCustomization { + public static readonly ProjectCustomization Instance = new UapProjectCustomization(); + + private UapProjectCustomization() { } + + public override string DisplayName { + get { + return SR.GetString(SR.ImportWizardGenericWebProjectCustomization); + } + } + + public override void Process( + ProjectRootElement project, + Dictionary groups + ) { + ProjectPropertyGroupElement globals; + if (!groups.TryGetValue("Globals", out globals)) { + globals = project.AddPropertyGroup(); + } + + AddOrSetProperty(globals, "ProjectTypeGuids", "{c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e};{888888A0-9F3D-457C-B088-3A5042F75D52}"); + } + } } diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs index 4dcb084e1a..a39467aa4f 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectConfig.cs @@ -21,12 +21,35 @@ namespace Microsoft.PythonTools.Project { class PythonProjectConfig : CommonProjectConfig { private readonly PythonProjectNode _project; + private string _platform; public PythonProjectConfig(PythonProjectNode project, string configuration) : base(project, configuration) { _project = project; } + public override string Platform { + get { + return _platform; + } + set { + _platform = value; + } + } + + /// + /// The display name is a two part item + /// first part is the config name, 2nd part is the platform name + /// + public override int get_DisplayName(out string name) { + if (!string.IsNullOrEmpty(Platform)) { + name = ConfigName + "|" + Platform; + return VSConstants.S_OK; + } else { + return base.get_DisplayName(out name); + } + } + public override int DebugLaunch(uint flags) { if (_project.ShouldWarnOnLaunch) { var pyService = ProjectMgr.Site.GetPythonToolsService(); diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs index f1e1ca851c..737b03adb0 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs @@ -167,6 +167,19 @@ protected override Stream ProjectIconsImageStripStream { } } + protected internal override void SetCurrentConfiguration() { + base.SetCurrentConfiguration(); + + if (!IsProjectOpened) + return; + + if (this.IsAppxPackageableProject()) { + EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project; + + this.BuildProject.SetGlobalProperty(ProjectFileConstants.Platform, automationObject.ConfigurationManager.ActiveConfiguration.PlatformName); + } + } + internal int GetIconIndex(PythonProjectImageName name) { return ImageOffset + (int)name; } @@ -197,6 +210,9 @@ public override CommonFileNode CreateNonCodeFileNode(ProjectElement item) { return new PythonNonCodeFileNode(this, item); } + protected override ConfigProvider CreateConfigProvider() { + return new CommonConfigProvider(this); + } protected override ReferenceContainerNode CreateReferenceContainerNode() { return new PythonReferenceContainerNode(this); } @@ -307,14 +323,29 @@ public override int GenerateUniqueItemName(uint itemIdLoc, string ext, string su return base.GenerateUniqueItemName(itemIdLoc, ext, suggestedRoot, out itemName); } + public override MSBuildResult Build(string config, string target) { + if (this.IsAppxPackageableProject()) { + // Ensure that AnyCPU is not the default Platform if this is an AppX project + // Use x86 instead + var platform = this.BuildProject.GetPropertyValue(GlobalProperty.Platform.ToString()); + + if (platform == ProjectConfig.AnyCPU) { + this.BuildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), ConfigProvider.x86Platform); + } + } + return base.Build(config, target); + } + protected override void Reload() { - _searchPathContainer = new CommonSearchPathContainerNode(this); - this.AddChild(_searchPathContainer); - RefreshCurrentWorkingDirectory(); - RefreshSearchPaths(); - _interpretersContainer = new InterpretersContainerNode(this); - this.AddChild(_interpretersContainer); - RefreshInterpreters(alwaysCollapse: true); + if (!this.IsAppxPackageableProject()) { + _searchPathContainer = new CommonSearchPathContainerNode(this); + this.AddChild(_searchPathContainer); + RefreshCurrentWorkingDirectory(); + RefreshSearchPaths(); + _interpretersContainer = new InterpretersContainerNode(this); + this.AddChild(_interpretersContainer); + RefreshInterpreters(alwaysCollapse: true); + } OnProjectPropertyChanged += PythonProjectNode_OnProjectPropertyChanged; base.Reload(); diff --git a/Python/Product/PythonTools/visualstudio_py_debugger.py b/Python/Product/PythonTools/visualstudio_py_debugger.py index 5470c07cd9..485882081f 100644 --- a/Python/Product/PythonTools/visualstudio_py_debugger.py +++ b/Python/Product/PythonTools/visualstudio_py_debugger.py @@ -28,6 +28,7 @@ from os import path import ntpath import runpy +import datetime from codecs import BOM_UTF8 try: @@ -39,6 +40,7 @@ import visualstudio_py_util as _vspu except ImportError: import ptvsd.visualstudio_py_util as _vspu + to_bytes = _vspu.to_bytes exec_file = _vspu.exec_file exec_module = _vspu.exec_module @@ -207,7 +209,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, tb): send_lock.release() - + # start sending debug events again cur_thread = get_thread_from_id(thread.get_ident()) if cur_thread is not None: @@ -218,7 +220,7 @@ def __exit__(self, exc_type, exc_value, tb): detach_process() # swallow the exception, we're no longer debugging return True - + _SendLockCtx = _SendLockContextManager() SEND_BREAK_COMPLETE = False @@ -309,7 +311,7 @@ def is_repr_round_tripping(x): class StackOverflowException(Exception): pass else: StackOverflowException = RuntimeError - + ASBR = to_bytes('ASBR') SETL = to_bytes('SETL') THRF = to_bytes('THRF') @@ -349,7 +351,7 @@ def should_send_frame(frame): KNOWN_ZIPS = set() def is_file_in_zip(filename): - parent, name = path.split(filename) + parent, name = path.split(path.abspath(filename)) if parent in KNOWN_DIRECTORIES: return False elif parent in KNOWN_ZIPS: @@ -378,7 +380,7 @@ def lookup_local(frame, name): while bits and obj is not None and type(obj) is types.ModuleType: obj = getattr(obj, bits.pop(0), None) return obj - + if sys.version_info[0] >= 3: _EXCEPTIONS_MODULE = 'builtins' else: @@ -448,7 +450,7 @@ def should_break(self, thread, ex_type, ex_value, trace): break_type = BREAK_TYPE_NONE return break_type - + def is_handled(self, thread, ex_type, ex_value, trace): if trace is None: # get out if we didn't get a traceback @@ -459,9 +461,9 @@ def is_handled(self, thread, ex_type, ex_value, trace): # don't break if this is not the top of the traceback, # unless the previous frame was not debuggable return True - + cur_frame = trace.tb_frame - + while should_send_frame(cur_frame) and cur_frame.f_code is not None and cur_frame.f_code.co_filename is not None: filename = path.normcase(cur_frame.f_code.co_filename) if is_file_in_zip(filename): @@ -470,11 +472,11 @@ def is_handled(self, thread, ex_type, ex_value, trace): if not is_same_py_file(filename, __file__): handlers = self.handler_cache.get(filename) - + if handlers is None: # req handlers for this file from the debug engine self.handler_lock.acquire() - + with _SendLockCtx: write_bytes(conn, REQH) write_string(conn, filename) @@ -506,7 +508,7 @@ def is_handled(self, thread, ex_type, ex_value, trace): cur_frame = cur_frame.f_back return False - + def add_exception(self, name, mode=BREAK_MODE_UNHANDLED): if name.startswith(_EXCEPTIONS_MODULE + '.'): name = name[len(_EXCEPTIONS_MODULE) + 1:] @@ -535,7 +537,7 @@ def should_debug_code(code): filename = path.normcase(code.co_filename) if not DEBUG_STDLIB: for prefix in PREFIXES: - if filename.startswith(prefix): + if prefix != '' and filename.startswith(prefix): return False for dont_debug_file in DONT_DEBUG: @@ -558,7 +560,7 @@ def breakpoint_path_match(vs_path, local_path): local_path_norm = path.normcase(local_path) if local_path_to_vs_path.get(local_path_norm) == vs_path_norm: return True - + # Walk the local filesystem from local_path up, matching agains win_path component by component, # and stop when we no longer see an __init__.py. This should give a reasonably close approximation # of matching the package name. @@ -574,7 +576,7 @@ def breakpoint_path_match(vs_path, local_path): # needed to, and matched all names on our way, so this is a match. if not path.exists(path.join(local_path, '__init__.py')): break - + local_path_to_vs_path[local_path_norm] = vs_path_norm return True @@ -582,26 +584,26 @@ def update_all_thread_stacks(blocking_thread = None, check_is_blocked = True): THREADS_LOCK.acquire() all_threads = list(THREADS.values()) THREADS_LOCK.release() - + for cur_thread in all_threads: if cur_thread is blocking_thread: continue - + cur_thread._block_starting_lock.acquire() if not check_is_blocked or not cur_thread._is_blocked: # release the lock, we're going to run user code to evaluate the frames cur_thread._block_starting_lock.release() - + frames = cur_thread.get_frame_list() - + # re-acquire the lock and make sure we're still not blocked. If so send # the frame list. cur_thread._block_starting_lock.acquire() if not check_is_blocked or not cur_thread._is_blocked: cur_thread.send_frame_list(frames) - + cur_thread._block_starting_lock.release() - + DJANGO_BREAKPOINTS = {} class DjangoBreakpointInfo(object): @@ -609,13 +611,13 @@ def __init__(self, filename): self._line_locations = None self.filename = filename self.breakpoints = {} - + def add_breakpoint(self, lineno, brkpt_id): self.breakpoints[lineno] = brkpt_id def remove_breakpoint(self, lineno): del self.breakpoints[lineno] - + @property def line_locations(self): if self._line_locations is None: @@ -752,7 +754,7 @@ def new_f(old_f, args, kwargs): tsk.tempval = new_f stackless.tasklet.setup(tsk, f, args, kwargs) return tsk - + def settrace(tsk, tb): if hasattr(tsk.frame, "f_trace"): tsk.frame.f_trace = tb @@ -764,7 +766,7 @@ def settrace(tsk, tb): stackless.set_schedule_callback(self._legacy_stackless_schedule_cb) else: stackless.set_schedule_callback(self._stackless_schedule_cb) - + def _stackless_legacy_schedule_cb(self, old, new): self.stepping = STEPPING_NONE # for those tasklets that started before we started tracing @@ -843,7 +845,7 @@ def trace_func(self, frame, event, arg): except (StackOverflowException, KeyboardInterrupt): # stack overflow, disable tracing return self.trace_func - + def handle_call(self, frame, arg): self.push_frame(frame) @@ -851,7 +853,7 @@ def handle_call(self, frame, arg): source_obj = get_django_frame_source(frame) if source_obj is not None: origin, (start, end) = source_obj - + active_bps = DJANGO_BREAKPOINTS.get(origin.name.lower()) should_break = False if active_bps is not None: @@ -906,7 +908,7 @@ def handle_call(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'call', arg) return self.trace_func - + def should_block_on_frame(self, frame): if not should_debug_code(frame.f_code): return False @@ -942,7 +944,7 @@ def handle_line(self, frame, arg): if self.should_block_on_frame(frame): # don't step complete in our own debugger / non-user code step_complete = True elif stepping == STEPPING_LAUNCH_BREAK or stepping == STEPPING_ATTACH_BREAK: - # If launching rather than attaching, don't break into inital Python code needed to set things up + # If launching rather than attaching, don't break into initial Python code needed to set things up if stepping == STEPPING_LAUNCH_BREAK and (not MODULES or not self.should_block_on_frame(frame)): handle_breakpoints = False else: @@ -1027,7 +1029,7 @@ def handle_line(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'line', arg) return self.trace_func - + def handle_return(self, frame, arg): self.pop_frame() @@ -1064,7 +1066,7 @@ def handle_return(self, frame, arg): # restore previous frames trace function if there is one if self.trace_func_stack: self.prev_trace_func = self.trace_func_stack.pop() - + def handle_exception(self, frame, arg): if self.stepping == STEPPING_ATTACH_BREAK: self.block_maybe_attach() @@ -1082,15 +1084,15 @@ def handle_exception(self, frame, arg): self.prev_trace_func = old_trace_func(frame, 'exception', arg) return self.trace_func - + def handle_c_call(self, frame, arg): # break points? pass - + def handle_c_return(self, frame, arg): # step out of ? pass - + def handle_c_exception(self, frame, arg): pass @@ -1104,7 +1106,7 @@ def block_maybe_attach(self): will_block_now = False attach_sent_break = True attach_lock.release() - + probe_stack() stepping = self.stepping self.stepping = STEPPING_NONE @@ -1120,7 +1122,7 @@ def block_cond(): return report_process_loaded(self.id) update_all_thread_stacks(self) self.block(block_cond) - + def async_break(self): def async_break_send(): with _SendLockCtx: @@ -1146,10 +1148,10 @@ def block(self, block_lambda, keep_stopped_on_line = False): """blocks the current thread until the debugger resumes it""" assert not self._is_blocked #assert self.id == thread.get_ident(), 'wrong thread identity' + str(self.id) + ' ' + str(thread.get_ident()) # we should only ever block ourselves - + # send thread frames before we block self.enum_thread_frames_locally() - + if not keep_stopped_on_line: self.stopped_on_line = self.cur_frame.f_lineno @@ -1169,7 +1171,7 @@ def block(self, block_lambda, keep_stopped_on_line = False): self.unblock_work() self.unblock_work = None self._is_working = False - + self._block_starting_lock.acquire() assert self._is_blocked self._is_blocked = False @@ -1179,7 +1181,7 @@ def unblock(self): """unblocks the current thread allowing it to continue to run""" assert self._is_blocked assert self.id != thread.get_ident() # only someone else should unblock us - + self._block_lock.release() def schedule_work(self, work): @@ -1188,26 +1190,26 @@ def schedule_work(self, work): def run_on_thread(self, text, cur_frame, execution_id, frame_kind, repr_kind = PYTHON_EVALUATION_RESULT_REPR_KIND_NORMAL): self._block_starting_lock.acquire() - + if not self._is_blocked: report_execution_error('', execution_id) elif not self._is_working: self.schedule_work(lambda : self.run_locally(text, cur_frame, execution_id, frame_kind, repr_kind)) else: report_execution_error('', execution_id) - + self._block_starting_lock.release() def run_on_thread_no_report(self, text, cur_frame, frame_kind): self._block_starting_lock.acquire() - + if not self._is_blocked: pass elif not self._is_working: self.schedule_work(lambda : self.run_locally_no_report(text, cur_frame, frame_kind)) else: pass - + self._block_starting_lock.release() def enum_child_on_thread(self, text, cur_frame, execution_id, frame_kind): @@ -1331,7 +1333,7 @@ def enum_child_locally(self, expr, cur_frame, execution_id, frame_kind): break key_repr = safe_repr(key) - + # Some objects are enumerable but not indexable, or repr(key) is not a valid Python expression. For those, we # cannot use obj[key] to get the item by its key, and have to retrieve it by index from enumerate() instead. try: @@ -1362,7 +1364,7 @@ def enum_child_locally(self, expr, cur_frame, execution_id, frame_kind): def get_frame_list(self): frames = [] cur_frame = self.cur_frame - + while should_send_frame(cur_frame): # calculate the ending line number lineno = cur_frame.f_code.co_firstlineno @@ -1409,7 +1411,7 @@ def get_frame_list(self): f_globals = cur_frame.f_globals if f_globals: # ensure globals to work with (IPy may have None for cur_frame.f_globals for frames within stdlib) self.collect_variables(vars, f_globals, cur_frame.f_code.co_names, treated, skip_unknown = True) - + frame_info = None if source_obj is not None: @@ -1451,9 +1453,9 @@ def get_frame_list(self): ) frames.append(frame_info) - + cur_frame = cur_frame.f_back - + return frames def collect_variables(self, vars, objects, names, treated, skip_unknown = False): @@ -1481,7 +1483,7 @@ def send_frame_list(self, frames, thread_name = None): write_bytes(conn, THRF) write_int(conn, self.id) write_string(conn, thread_name) - + # send the frame count write_int(conn, len(frames)) for firstlineno, lineno, curlineno, name, filename, argcount, variables, frameKind, sourceFile, sourceLine in frames: @@ -1489,16 +1491,16 @@ def send_frame_list(self, frames, thread_name = None): write_int(conn, firstlineno) write_int(conn, lineno) write_int(conn, curlineno) - + write_string(conn, name) write_string(conn, filename) write_int(conn, argcount) - + write_int(conn, frameKind) if frameKind == FRAME_KIND_DJANGO: write_string(conn, sourceFile) write_int(conn, sourceLine) - + write_int(conn, len(variables)) for name, type_obj, safe_repr_obj, hex_repr_obj, type_name, obj_len in variables: write_string(conn, name) @@ -1604,7 +1606,7 @@ def loop(self): pass except: traceback.print_exc() - + def command_step_into(self): tid = read_int(self.conn) thread = get_thread_from_id(tid) @@ -1620,7 +1622,7 @@ def command_step_out(self): assert thread._is_blocked thread.stepping = STEPPING_OUT self.command_resume_all() - + def command_step_over(self): # set step over tid = read_int(self.conn) @@ -1660,7 +1662,7 @@ def command_set_breakpoint_condition(self): breakpoint_id = read_int(self.conn) kind = read_int(self.conn) condition = read_string(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) if bp is not None: bp.condition_kind = kind @@ -1679,7 +1681,7 @@ def command_set_breakpoint_pass_count(self): def command_set_breakpoint_hit_count(self): breakpoint_id = read_int(self.conn) count = read_int(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) if bp is not None: bp.hit_count = count @@ -1687,7 +1689,7 @@ def command_set_breakpoint_hit_count(self): def command_get_breakpoint_hit_count(self): req_id = read_int(self.conn) breakpoint_id = read_int(self.conn) - + bp = BreakpointInfo.find_by_id(breakpoint_id) count = 0 if bp is not None: @@ -1767,7 +1769,7 @@ def command_resume_all(self): if thread._is_blocked: thread.unblock() thread._block_starting_lock.release() - + def command_resume_thread(self): tid = read_int(self.conn) THREADS_LOCK.acquire() @@ -1880,11 +1882,11 @@ def command_enum_children(self): fid = read_int(self.conn) # frame id eid = read_int(self.conn) # execution id frame_kind = read_int(self.conn) # frame kind - + thread, cur_frame = self.get_thread_and_frame(tid, fid, frame_kind) if thread is not None and cur_frame is not None: thread.enum_child_on_thread(text, cur_frame, eid, frame_kind) - + def get_thread_and_frame(self, tid, fid, frame_kind): thread = get_thread_from_id(tid) cur_frame = None @@ -1913,7 +1915,7 @@ def command_detach(self): for callback in DETACH_CALLBACKS: callback() - + raise DebuggerExitException() def command_last_ack(self): @@ -1959,12 +1961,12 @@ def report_exception(frame, exc_info, tid, break_type): exc_name = get_exception_name(exc_type) exc_value = exc_info[1] tb_value = exc_info[2] - + if type(exc_value) is tuple: # exception object hasn't been created yet, create it now # so we can get the correct msg. exc_value = exc_type(*exc_value) - + excp_text = str(exc_value) with _SendLockCtx: @@ -2220,7 +2222,7 @@ def detach_threads(): THREADS_LOCK.acquire() THREADS.clear() THREADS_LOCK.release() - + BREAKPOINTS.clear() def new_thread(tid = None, set_break = False, frame = None): @@ -2282,11 +2284,11 @@ def __init__(self, old_out, is_stdout): def flush(self): if self.old_out: self.old_out.flush() - + def writelines(self, lines): for line in lines: self.write(line) - + @property def encoding(self): return 'utf8' @@ -2300,13 +2302,13 @@ def write(self, value): write_string(conn, value) if self.old_out: self.old_out.write(value) - + def isatty(self): return True def next(self): pass - + @property def name(self): if self.is_stdout: @@ -2366,8 +2368,8 @@ def print_exception(exc_type, exc_value, exc_tb): if tb: print('Traceback (most recent call last):') for out in traceback.format_list(tb): - sys.stdout.write(out) - + sys.stderr.write(out) + # print the exception for out in traceback.format_exception_only(exc_type, exc_value): sys.stdout.write(out) diff --git a/Python/Product/PythonTools/visualstudio_py_remote_launcher.py b/Python/Product/PythonTools/visualstudio_py_remote_launcher.py new file mode 100644 index 0000000000..1ded935b52 --- /dev/null +++ b/Python/Product/PythonTools/visualstudio_py_remote_launcher.py @@ -0,0 +1,114 @@ + # ############################################################################ + # + # Copyright (c) Microsoft Corporation. + # + # This source code is subject to terms and conditions of the Apache License, Version 2.0. A + # copy of the license can be found in the License.html file at the root of this distribution. If + # you cannot locate the Apache License, Version 2.0, please send an email to + # vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + # by the terms of the Apache License, Version 2.0. + # + # You must not remove this notice, or any other, from this software. + # + # ########################################################################### + +""" +Starts Debugging, expected to start with normal program +to start as first argument and directory to run from as +the second argument. +""" +import os +import sys +import ptvsd +import visualstudio_py_debugger as vspd + +def debug_remote( + file, + port_num, + debug_id, + wait_on_exception, + redirect_output, + wait_on_exit, + break_on_systemexit_zero, + debug_stdlib, + run_as +): + global BREAK_ON_SYSTEMEXIT_ZERO, DEBUG_STDLIB + BREAK_ON_SYSTEMEXIT_ZERO = break_on_systemexit_zero + DEBUG_STDLIB = debug_stdlib + + print('Remote launcher starting ptvsd attach wait with File: %s, Port: %d, Id: %s\n' % (file, port_num, debug_id)) + + ptvsd.enable_attach(debug_id, address = ('0.0.0.0', port_num), redirect_output = redirect_output) + ptvsd.wait_for_attach() + + # now execute main file + globals_obj = {'__name__': '__main__'} + if run_as == 'module': + vspd.exec_module(file, globals_obj) + elif run_as == 'code': + vspd.exec_code(file, '', globals_obj) + else: + vspd.exec_file(file, globals_obj) + +# arguments are port, debug id, normal arguments which should include a filename to execute + +# change to directory we expected to start from +port_num = int(sys.argv[1]) +debug_id = sys.argv[2] + +del sys.argv[0:3] + +wait_on_exception = False +redirect_output = False +wait_on_exit = False +break_on_systemexit_zero = False +debug_stdlib = False +run_as = 'script' + +for opt in [ + # Order is important for these options. + 'redirect_output', + 'wait_on_exception', + 'wait_on_exit', + 'break_on_systemexit_zero', + 'debug_stdlib' +]: + if sys.argv and sys.argv[0] == '--' + opt.replace('_', '-'): + globals()[opt] = True + del sys.argv[0] + +# set run_as mode appropriately +if sys.argv and sys.argv[0] == '-m': + run_as = 'module' + del sys.argv[0] + +if sys.argv and sys.argv[0] == '-c': + run_as = 'code' + del sys.argv[0] + +# preserve filename before we del sys +file = sys.argv[0] + +# fix sys.path to be the script file dir +sys.path[0] = '' + +# exclude ourselves from being debugged +vspd.DONT_DEBUG.append(os.path.normcase(__file__)) + +# remove all state we imported +del sys, os + +import sys + +debug_remote( + file, + port_num, + debug_id, + wait_on_exception, + redirect_output, + wait_on_exit, + break_on_systemexit_zero, + debug_stdlib, + run_as +) diff --git a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs new file mode 100644 index 0000000000..8c68c53ab9 --- /dev/null +++ b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs @@ -0,0 +1,118 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Xml.Linq; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger; +using Microsoft.VisualStudio.Debugger.ComponentInterfaces; +using Microsoft.VisualStudio.Debugger.Exceptions; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Debugger { + internal class PythonRemoteDebugEvents : IVsDebuggerEvents, IDebugEventCallback2, IDkmExceptionTriggerHitNotification { + private static readonly Lazy instance = new Lazy(); + + // This exception code is a Win32 exception that is expected to be thrown by the debug client to trigger this code flow + // i.e. whatever remote process is launching the debug server, should throw and catch this exception code before starting + // remote Python debug server to have automatic attach work + internal const uint RemoteDebugStartExceptionCode = 0xEDCBA987; + + public const string RemoteDebugExceptionId = "E7DD0845-FB1A-4A45-8192-44953C0ACC51"; + public static readonly Guid RemoteDebugExceptionGuid = new Guid(RemoteDebugExceptionId); + + public static PythonRemoteDebugEvents Instance { + get { return instance.Value; } + } + + public Func AttachRemoteProcessFunction { get; set; } + + public string AttachRemoteDebugXml { get; set; } + + public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { + try { + if (riidEvent == typeof(IDebugProgramCreateEvent2).GUID) { + Guid processId; + + // A program was created and attached + if (pProcess != null) { + if (VSConstants.S_OK == pProcess.GetProcessId(out processId)) { + DkmProcess dkmProcess = DkmProcess.FindProcess(processId); + + if (dkmProcess != null) { + var debugTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugStartExceptionCode); + + // Try to add exception trigger for when a remote debugger server is started for Python + dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, debugTrigger); + } + } + } + } + + return VSConstants.S_OK; + } finally { + if (pEngine != null && Marshal.IsComObject(pEngine)) { + Marshal.ReleaseComObject(pEngine); + } + + if (pProcess != null && Marshal.IsComObject(pProcess)) { + Marshal.ReleaseComObject(pProcess); + } + + if (pProgram != null && Marshal.IsComObject(pProgram)) { + Marshal.ReleaseComObject(pProgram); + } + + if (pThread != null && Marshal.IsComObject(pThread)) { + Marshal.ReleaseComObject(pThread); + } + + if (pEvent != null && Marshal.IsComObject(pEvent)) { + Marshal.ReleaseComObject(pEvent); + } + } + } + + void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTriggerHit hit, DkmEventDescriptorS eventDescriptor) { + var remoteProcessTask = default(System.Threading.Tasks.Task); + + using (var evt = new System.Threading.ManualResetEvent(false)) { + + ThreadHelper.Generic.Invoke(() => { + try { + var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; + + // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration + // of the debugger + const int exceptionParameterCount = 2; + + if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { + // If we have a port and debug id, we'll go ahead and tell the client we are present + if (Instance.AttachRemoteProcessFunction != null && Instance.AttachRemoteDebugXml != null) { + // Write back that debugger is present + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); + + // Write back the details of the debugger arguments + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Encoding.Unicode.GetBytes(Instance.AttachRemoteDebugXml)); + } + } + } finally { + evt.Set(); + } + }); + + evt.WaitOne(); + + eventDescriptor.Suppress(); + + // Start the task to attach to the remote Python debugger session + remoteProcessTask = System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); + } + } + + public int OnModeChange(DBGMODE dbgmodeNew) { + return VSConstants.S_OK; + } + } +} diff --git a/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml b/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml new file mode 100644 index 0000000000..1880da70bd --- /dev/null +++ b/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Python/Product/Uap/Extensions.cs b/Python/Product/Uap/Extensions.cs new file mode 100644 index 0000000000..576c1f360d --- /dev/null +++ b/Python/Product/Uap/Extensions.cs @@ -0,0 +1,144 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.PythonTools.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudioTools.Project; +using Microsoft.VisualStudioTools.Project.Automation; + +namespace Microsoft.PythonTools.Uap { + static class Extensions { + + internal static string GetProjectProperty(this IVsHierarchy projectNode, string name) { + return projectNode.GetProject().GetPythonProject().GetProperty(name); + } + + internal static bool IsAppxPackageableProject(this IVsHierarchy projectNode) { + var appxProp = projectNode.GetProjectProperty(ProjectFileConstants.AppxPackage); + var containerProp = projectNode.GetProjectProperty(ProjectFileConstants.WindowsAppContainer); + + return Convert.ToBoolean(appxProp) && Convert.ToBoolean(containerProp); + } + + internal static IPythonProject2 GetPythonProject(this EnvDTE.Project project) { + return project.GetCommonProject() as IPythonProject2; + } + + internal static EnvDTE.Project GetProject(this IVsHierarchy hierarchy) { + object project; + + ErrorHandler.ThrowOnFailure( + hierarchy.GetProperty( + VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ExtObject, + out project + ) + ); + + return (project as EnvDTE.Project); + } + + internal static object GetCommonProject(this EnvDTE.Project project) { + OAProject oaProj = project as OAProject; + if (oaProj != null) { + var common = oaProj.Project; + if (common != null) { + return common; + } + } + return null; + } + + internal static Guid GetItemType(this VSITEMSELECTION vsItemSelection) { + Guid typeGuid; + try { + ErrorHandler.ThrowOnFailure( + vsItemSelection.pHier.GetGuidProperty( + vsItemSelection.itemid, + (int)__VSHPROPID.VSHPROPID_TypeGuid, + out typeGuid + ) + ); + } catch (System.Runtime.InteropServices.COMException) { + return Guid.Empty; + } + return typeGuid; + } + + internal static bool IsFolder(this VSITEMSELECTION item) { + return item.GetItemType() == VSConstants.GUID_ItemType_PhysicalFolder || + item.itemid == VSConstants.VSITEMID_ROOT; + } + + internal static bool IsNonMemberItem(this VSITEMSELECTION item) { + object obj; + try { + ErrorHandler.ThrowOnFailure( + item.pHier.GetProperty( + item.itemid, + (int)__VSHPROPID.VSHPROPID_IsNonMemberItem, + out obj + ) + ); + } catch (System.Runtime.InteropServices.COMException) { + return false; + } + return (obj as bool?) ?? false; + } + + internal static string Name(this VSITEMSELECTION item) { + return item.pHier.GetItemName(item.itemid); + } + + internal static string GetItemName(this IVsHierarchy hier, uint itemid) { + object name; + ErrorHandler.ThrowOnFailure(hier.GetProperty(itemid, (int)__VSHPROPID.VSHPROPID_Name, out name)); + return (string)name; + } + + internal static VSITEMSELECTION GetParent(this VSITEMSELECTION vsItemSelection) { + object parent; + ErrorHandler.ThrowOnFailure( + vsItemSelection.pHier.GetProperty( + vsItemSelection.itemid, + (int)__VSHPROPID.VSHPROPID_Parent, + out parent + ) + ); + + var res = new VSITEMSELECTION(); + var i = parent as int?; + if (i.HasValue) { + res.itemid = (uint)i.GetValueOrDefault(); + } else { + var ip = parent as IntPtr?; + res.itemid = (uint)ip.GetValueOrDefault().ToInt32(); + } + + res.pHier = vsItemSelection.pHier; + return res; + } + + internal static VSITEMSELECTION GetParentFolder(this VSITEMSELECTION vsItemSelection) { + var parent = vsItemSelection.GetParent(); + while (!parent.IsFolder()) { + parent = parent.GetParent(); + } + return parent; + } + } +} diff --git a/Python/Product/Uap/Guids.cs b/Python/Product/Uap/Guids.cs new file mode 100644 index 0000000000..815e0e5dc1 --- /dev/null +++ b/Python/Product/Uap/Guids.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// Guids.cs +// MUST match guids.h +using System; + +namespace Microsoft.PythonTools.Uap { + static class GuidList { + public const string guidUapPkgString = "0a078d3c-15a9-47f5-8418-9ee5db43993d"; + public const string guidUapFactoryString = "c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e"; + public const string guidUapPropertyPageString = "700c8e09-f81c-4fb8-a386-508fb48c372d"; + public static readonly Guid guidOfficeSharePointCmdSet = new Guid("d26c976c-8ee8-4ec4-8746-f5f7702a17c5"); + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs new file mode 100644 index 0000000000..c275773806 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs @@ -0,0 +1,237 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.PythonTools.Analysis; +using Microsoft.PythonTools.Interpreter; +using Microsoft.VisualStudioTools; + +namespace Microsoft.PythonTools.Uap.Interpreter { + class PythonUapInterpreter : IPythonInterpreter, IPythonInterpreterWithProjectReferences2, IDisposable { + readonly Version _langVersion; + private PythonInterpreterFactoryWithDatabase _factory; + private PythonTypeDatabase _typeDb; + private HashSet _references; + + public PythonUapInterpreter(PythonInterpreterFactoryWithDatabase factory) { + _langVersion = factory.Configuration.Version; + _factory = factory; + _typeDb = _factory.GetCurrentDatabase(); + _factory.NewDatabaseAvailable += OnNewDatabaseAvailable; + } + + private async void OnNewDatabaseAvailable(object sender, EventArgs e) { + var factory = _factory; + if (factory == null) { + // We have been disposed already, so ignore this event + return; + } + + _typeDb = factory.GetCurrentDatabase(); + + if (_references != null) { + _typeDb = _typeDb.Clone(); + foreach (var reference in _references) { + string modName; + try { + modName = Path.GetFileNameWithoutExtension(reference.Name); + } catch (ArgumentException) { + continue; + } + + await Task.Yield(); + } + } + + var evt = ModuleNamesChanged; + if (evt != null) { + evt(this, EventArgs.Empty); + } + } + + #region IPythonInterpreter Members + + public IPythonType GetBuiltinType(BuiltinTypeId id) { + if (id == BuiltinTypeId.Unknown) { + return null; + } + + if (_typeDb == null) { + throw new KeyNotFoundException(string.Format("{0} ({1})", id, (int)id)); + } + + var name = GetBuiltinTypeName(id, _typeDb.LanguageVersion); + var res = _typeDb.BuiltinModule.GetAnyMember(name) as IPythonType; + if (res == null) { + throw new KeyNotFoundException(string.Format("{0} ({1})", id, (int)id)); + } + return res; + } + + + public IList GetModuleNames() { + if (_typeDb == null) { + return new string[0]; + } + return new List(_typeDb.GetModuleNames()); + } + + public IPythonModule ImportModule(string name) { + if (_typeDb == null) { + return null; + } + return _typeDb.GetModule(name); + } + + public IModuleContext CreateModuleContext() { + return null; + } + + public void Initialize(PythonAnalyzer state) { + } + + public event EventHandler ModuleNamesChanged; + + public Task AddReferenceAsync(ProjectReference reference, CancellationToken cancellationToken = default(CancellationToken)) { + if (reference == null) { + return MakeExceptionTask(new ArgumentNullException("reference")); + } + + if (_references == null) { + _references = new HashSet(); + // If we needed to set _references, then we also need to clone + // _typeDb to avoid adding modules to the shared database. + if (_typeDb != null) { + _typeDb = _typeDb.Clone(); + } + } + + switch (reference.Kind) { + case ProjectReferenceKind.ExtensionModule: + _references.Add(reference); + string filename; + try { + filename = Path.GetFileNameWithoutExtension(reference.Name); + } catch (Exception e) { + return MakeExceptionTask(e); + } + + if (_typeDb != null) { + return Task.Factory.StartNew(EmptyTask); + } + break; + } + + return Task.Factory.StartNew(EmptyTask); + } + + public void RemoveReference(ProjectReference reference) { + switch (reference.Kind) { + case ProjectReferenceKind.ExtensionModule: + if (_references != null && _references.Remove(reference) && _typeDb != null) { + RaiseModulesChanged(null); + } + break; + } + } + + public IEnumerable GetReferences() { + return _references != null ? _references : Enumerable.Empty(); + } + + private static Task MakeExceptionTask(Exception e) { + var res = new TaskCompletionSource(); + res.SetException(e); + return res.Task; + } + + private static void EmptyTask() { + } + + private void RaiseModulesChanged(Task task) { + if (task != null && task.Exception != null) { + throw task.Exception; + } + var modNamesChanged = ModuleNamesChanged; + if (modNamesChanged != null) { + modNamesChanged(this, EventArgs.Empty); + } + } + + public static string GetBuiltinTypeName(BuiltinTypeId id, Version languageVersion) { + string name; + switch (id) { + case BuiltinTypeId.Bool: name = "bool"; break; + case BuiltinTypeId.Complex: name = "complex"; break; + case BuiltinTypeId.Dict: name = "dict"; break; + case BuiltinTypeId.Float: name = "float"; break; + case BuiltinTypeId.Int: name = "int"; break; + case BuiltinTypeId.List: name = "list"; break; + case BuiltinTypeId.Long: name = languageVersion.Major == 3 ? "int" : "long"; break; + case BuiltinTypeId.Object: name = "object"; break; + case BuiltinTypeId.Set: name = "set"; break; + case BuiltinTypeId.Str: name = "str"; break; + case BuiltinTypeId.Unicode: name = languageVersion.Major == 3 ? "str" : "unicode"; break; + case BuiltinTypeId.Bytes: name = languageVersion.Major == 3 ? "bytes" : "str"; break; + case BuiltinTypeId.Tuple: name = "tuple"; break; + case BuiltinTypeId.Type: name = "type"; break; + + case BuiltinTypeId.BuiltinFunction: name = "builtin_function"; break; + case BuiltinTypeId.BuiltinMethodDescriptor: name = "builtin_method_descriptor"; break; + case BuiltinTypeId.DictKeys: name = "dict_keys"; break; + case BuiltinTypeId.DictValues: name = "dict_values"; break; + case BuiltinTypeId.DictItems: name = "dict_items"; break; + case BuiltinTypeId.Function: name = "function"; break; + case BuiltinTypeId.Generator: name = "generator"; break; + case BuiltinTypeId.NoneType: name = "NoneType"; break; + case BuiltinTypeId.Ellipsis: name = "ellipsis"; break; + case BuiltinTypeId.Module: name = "module_type"; break; + case BuiltinTypeId.ListIterator: name = "list_iterator"; break; + case BuiltinTypeId.TupleIterator: name = "tuple_iterator"; break; + case BuiltinTypeId.SetIterator: name = "set_iterator"; break; + case BuiltinTypeId.StrIterator: name = "str_iterator"; break; + case BuiltinTypeId.UnicodeIterator: name = languageVersion.Major == 3 ? "str_iterator" : "unicode_iterator"; break; + case BuiltinTypeId.BytesIterator: name = languageVersion.Major == 3 ? "bytes_iterator" : "str_iterator"; break; + case BuiltinTypeId.CallableIterator: name = "callable_iterator"; break; + + case BuiltinTypeId.Property: name = "property"; break; + case BuiltinTypeId.ClassMethod: name = "classmethod"; break; + case BuiltinTypeId.StaticMethod: name = "staticmethod"; break; + case BuiltinTypeId.FrozenSet: name = "frozenset"; break; + + case BuiltinTypeId.Unknown: + default: + return null; + } + return name; + } + #endregion + + + public void Dispose() { + _typeDb = null; + + var factory = _factory; + _factory = null; + if (factory != null) { + factory.NewDatabaseAvailable -= OnNewDatabaseAvailable; + } + } + } +} diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs new file mode 100644 index 0000000000..c29800ad48 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.PythonTools.Interpreter; + +namespace Microsoft.PythonTools.Uap.Interpreter { + class PythonUapInterpreterFactory : PythonInterpreterFactoryWithDatabase { + public const string InterpreterGuidString = "{86767848-40B4-4007-8BCC-A3835EDF0E69}"; + public static readonly Guid InterpreterGuid = new Guid(InterpreterGuidString); + + public PythonUapInterpreterFactory() + : base( + InterpreterGuid, + "Python UAP", + new InterpreterConfiguration(new Version(3, 5)), + true) { + } + + public override string Description { + get { + return base.Description; + } + } + public override IPythonInterpreter MakeInterpreter(PythonInterpreterFactoryWithDatabase factory) { + return new PythonUapInterpreter(factory); + } + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs new file mode 100644 index 0000000000..aca47017f9 --- /dev/null +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.PythonTools.Interpreter; + +namespace Microsoft.PythonTools.Uap.Interpreter { + [Export(typeof(IPythonInterpreterFactoryProvider))] + class PythonUapInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { + private HashSet _factories = null; + + public event EventHandler InterpreterFactoriesChanged; + + public PythonUapInterpreterFactoryProvider() { + } + + private void DiscoverFactories() { + if (_factories == null) { + _factories = new HashSet(); + + _factories.Add(new PythonUapInterpreterFactory()); + + if (InterpreterFactoriesChanged != null) { + InterpreterFactoriesChanged(this, new EventArgs()); + } + } + } + + public IEnumerable GetInterpreterFactories() { + DiscoverFactories(); + return _factories; + } + } +} \ No newline at end of file diff --git a/Python/Product/Uap/Microsoft.PythonTools.Uap.targets b/Python/Product/Uap/Microsoft.PythonTools.Uap.targets new file mode 100644 index 0000000000..5480c6c45d --- /dev/null +++ b/Python/Product/Uap/Microsoft.PythonTools.Uap.targets @@ -0,0 +1,66 @@ + + + + + + ARM,x86,x64 + $(MSBuildProjectDirectory) + $([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(ProjectHome))))) + $(QualifiedProjectHome)\ + + + + + BuiltProjectOutputGroupFast; + $(BuiltProjectOutputGroupDependsOn) + + + + + + + <_BuiltProjectOutputGroupFastOutput Remove="@(_BuiltProjectOutputGroupFastOutput)" /> + + + + + + + + + + + + + + + + + + + + + + <_SourceFilesProjectOutputGroupOutput Remove="@(_SourceFilesProjectOutputGroupOutput)" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/Project/PythonUapProject.cs b/Python/Product/Uap/Project/PythonUapProject.cs new file mode 100644 index 0000000000..b09136470f --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProject.cs @@ -0,0 +1,299 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Flavor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudioTools; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid("27BB1268-135A-4409-914F-7AA64AD8195D")] + partial class PythonUapProject : + FlavoredProjectBase, + IOleCommandTarget, + IVsProjectFlavorCfgProvider, + IVsProject, + IVsFilterAddProjectItemDlg { + private PythonUapPackage _package; + internal IVsProject _innerProject; + internal IVsProject3 _innerProject3; + private IVsProjectFlavorCfgProvider _innerVsProjectFlavorCfgProvider; + private static Guid PythonProjectGuid = new Guid(PythonConstants.ProjectFactoryGuid); + private IOleCommandTarget _menuService; + + public PythonUapProject() { + } + + internal PythonUapPackage Package { + get { return _package; } + set { + Debug.Assert(_package == null); + if (_package != null) { + throw new InvalidOperationException("PythonUapProject.Package must only be set once"); + } + _package = value; + } + } + + #region IVsAggregatableProject + + /// + /// Do the initialization here (such as loading flavor specific + /// information from the project) + /// + protected override void InitializeForOuter(string fileName, string location, string name, uint flags, ref Guid guidProject, out bool cancel) { + base.InitializeForOuter(fileName, location, name, flags, ref guidProject, out cancel); + } + + #endregion + + protected override int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds, VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText) { + if (pguidCmdGroup == GuidList.guidOfficeSharePointCmdSet) { + for (int i = 0; i < prgCmds.Length; i++) { + // Report it as supported so that it's not routed any + // further, but disable it and make it invisible. + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_INVISIBLE); + } + return VSConstants.S_OK; + } + + return base.QueryStatusCommand(itemid, ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + protected override void SetInnerProject(IntPtr innerIUnknown) { + var inner = Marshal.GetObjectForIUnknown(innerIUnknown); + + // The reason why we keep a reference to those is that doing a QI after being + // aggregated would do the AddRef on the outer object. + _innerVsProjectFlavorCfgProvider = inner as IVsProjectFlavorCfgProvider; + _innerProject = inner as IVsProject; + _innerProject3 = inner as IVsProject3; + _innerVsHierarchy = inner as IVsHierarchy; + + // Ensure we have a service provider as this is required for menu items to work + if (this.serviceProvider == null) { + this.serviceProvider = (IServiceProvider)Package; + } + + // Now let the base implementation set the inner object + base.SetInnerProject(innerIUnknown); + + // Get access to the menu service used by FlavoredProjectBase. We + // need to forward IOleCommandTarget functions to this object, since + // we override the FlavoredProjectBase implementation with no way to + // call it directory. + // (This must run after we called base.SetInnerProject) + _menuService = (IOleCommandTarget)((IServiceProvider)this).GetService(typeof(IMenuCommandService)); + if (_menuService == null) { + throw new InvalidOperationException("Cannot initialize Uap project"); + } + } + + protected override int GetProperty(uint itemId, int propId, out object property) { + switch ((__VSHPROPID2)propId) { + case __VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList: + { + var res = base.GetProperty(itemId, propId, out property); + if (ErrorHandler.Succeeded(res)) { + var guids = GetGuidsFromList(property as string); + guids.RemoveAll(g => CfgSpecificPropertyPagesToRemove.Contains(g)); + guids.AddRange(CfgSpecificPropertyPagesToAdd); + property = MakeListFromGuids(guids); + } + return res; + } + case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList: + { + var res = base.GetProperty(itemId, propId, out property); + if (ErrorHandler.Succeeded(res)) { + var guids = GetGuidsFromList(property as string); + guids.RemoveAll(g => PropertyPagesToRemove.Contains(g)); + guids.AddRange(PropertyPagesToAdd); + property = MakeListFromGuids(guids); + } + return res; + } + } + + switch ((__VSHPROPID6)propId) { + case __VSHPROPID6.VSHPROPID_Subcaption: + { + var bps = this._innerProject as IVsBuildPropertyStorage; + string descriptor = null; + + if (bps != null) { + var res = bps.GetPropertyValue("TargetOsAndVersion", null, (uint)_PersistStorageType.PST_PROJECT_FILE, out descriptor); + property = descriptor; + return res; + } + break; + } + } + + return base.GetProperty(itemId, propId, out property); + } + + private static Guid[] PropertyPagesToAdd = new Guid[0]; + + private static Guid[] CfgSpecificPropertyPagesToAdd = new Guid[] { + new Guid(GuidList.guidUapPropertyPageString) + }; + + private static HashSet PropertyPagesToRemove = new HashSet { + new Guid("{8C0201FE-8ECA-403C-92A3-1BC55F031979}"), // typeof(DeployPropertyPageComClass) + new Guid("{ED3B544C-26D8-4348-877B-A1F7BD505ED9}"), // typeof(DatabaseDeployPropertyPageComClass) + new Guid("{909D16B3-C8E8-43D1-A2B8-26EA0D4B6B57}"), // Microsoft.VisualStudio.Web.Application.WebPropertyPage + new Guid("{379354F2-BBB3-4BA9-AA71-FBE7B0E5EA94}"), // Microsoft.VisualStudio.Web.Application.SilverlightLinksPage + new Guid("{A553AD0B-2F9E-4BCE-95B3-9A1F7074BC27}"), // Package/Publish Web + new Guid("{9AB2347D-948D-4CD2-8DBE-F15F0EF78ED3}"), // Package/Publish SQL + new Guid(PythonConstants.DebugPropertyPageGuid), + new Guid(PythonConstants.GeneralPropertyPageGuid), + new Guid(PythonConstants.PublishPropertyPageGuid) + }; + + internal static HashSet CfgSpecificPropertyPagesToRemove = new HashSet(new Guid[] { Guid.Empty }); + + private static List GetGuidsFromList(string guidList) { + if (string.IsNullOrEmpty(guidList)) { + return new List(); + } + + Guid value; + return guidList.Split(';') + .Select(str => Guid.TryParse(str, out value) ? (Guid?)value : null) + .Where(g => g.HasValue) + .Select(g => g.Value) + .ToList(); + } + + private static string MakeListFromGuids(IEnumerable guidList) { + return string.Join(";", guidList.Select(g => g.ToString("B"))); + } + + internal string RemovePropertyPagesFromList(string propertyPagesList, string[] pagesToRemove) { + if (pagesToRemove == null || !pagesToRemove.Any()) { + return propertyPagesList; + } + + var guidsToRemove = new HashSet( + pagesToRemove.Select(str => { Guid guid; return Guid.TryParse(str, out guid) ? guid : Guid.Empty; }) + ); + guidsToRemove.Add(Guid.Empty); + + return string.Join( + ";", + propertyPagesList.Split(';') + .Where(str => !string.IsNullOrEmpty(str)) + .Select(str => { Guid guid; return Guid.TryParse(str, out guid) ? guid : Guid.Empty; }) + .Except(guidsToRemove) + .Select(guid => guid.ToString("B")) + ); + } + + int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + return _menuService.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + return _menuService.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #region IVsProjectFlavorCfgProvider Members + + public int CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg) { + // We're flavored with a Windows Store Application project and our normal + // project... But we don't want the web application project to + // influence our config as that alters our debug launch story. We + // control that w/ the web project which is actually just letting + // the base Python project handle it. So we keep the base Python + // project config here. + IVsProjectFlavorCfg uapCfg; + ErrorHandler.ThrowOnFailure( + _innerVsProjectFlavorCfgProvider.CreateProjectFlavorCfg( + pBaseProjectCfg, + out uapCfg + ) + ); + ppFlavorCfg = new PythonUapProjectConfig(pBaseProjectCfg, uapCfg); + return VSConstants.S_OK; + } + + #endregion + + #region IVsProject Members + + int IVsProject.AddItem(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, VSADDRESULT[] pResult) { + return _innerProject.AddItem(itemidLoc, dwAddItemOperation, pszItemName, cFilesToOpen, rgpszFilesToOpen, hwndDlgOwner, pResult); + } + + int IVsProject.GenerateUniqueItemName(uint itemidLoc, string pszExt, string pszSuggestedRoot, out string pbstrItemName) { + return _innerProject.GenerateUniqueItemName(itemidLoc, pszExt, pszSuggestedRoot, out pbstrItemName); + } + + int IVsProject.GetItemContext(uint itemid, out VisualStudio.OLE.Interop.IServiceProvider ppSP) { + return _innerProject.GetItemContext(itemid, out ppSP); + } + + int IVsProject.GetMkDocument(uint itemid, out string pbstrMkDocument) { + return _innerProject.GetMkDocument(itemid, out pbstrMkDocument); + } + + int IVsProject.IsDocumentInProject(string pszMkDocument, out int pfFound, VSDOCUMENTPRIORITY[] pdwPriority, out uint pitemid) { + return _innerProject.IsDocumentInProject(pszMkDocument, out pfFound, pdwPriority, out pitemid); + } + + int IVsProject.OpenItem(uint itemid, ref Guid rguidLogicalView, IntPtr punkDocDataExisting, out IVsWindowFrame ppWindowFrame) { + return _innerProject.OpenItem(itemid, rguidLogicalView, punkDocDataExisting, out ppWindowFrame); + } + + #endregion + + #region IVsFilterAddProjectItemDlg Members + + int IVsFilterAddProjectItemDlg.FilterListItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterListItemByTemplateFile(ref Guid rguidProjectItemTemplates, string pszTemplateFile, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterTreeItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) { + pfFilter = 0; + return VSConstants.S_OK; + } + + int IVsFilterAddProjectItemDlg.FilterTreeItemByTemplateDir(ref Guid rguidProjectItemTemplates, string pszTemplateDir, out int pfFilter) { + // https://pytools.codeplex.com/workitem/1313 + // ASP.NET will filter some things out, including .css files, which we don't want it to do. + // So we shut that down by not forwarding this to any inner projects, which is fine, because + // Python projects don't implement this interface either. + pfFilter = 0; + return VSConstants.S_OK; + } + + #endregion + } +} diff --git a/Python/Product/Uap/Project/PythonUapProjectConfig.cs b/Python/Product/Uap/Project/PythonUapProjectConfig.cs new file mode 100644 index 0000000000..583c6ad352 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProjectConfig.cs @@ -0,0 +1,961 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Web; +using System.Windows.Forms; +using System.Xml.Linq; +using Microsoft.PythonTools.Debugger.DebugEngine; +using Microsoft.PythonTools.Debugger.Remote; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Project { + /// + /// Merges the PTVS IVsCfg object with the Venus IVsCfg implementation redirecting + /// things appropriately to either one. + /// + class PythonUapProjectConfig : + IVsCfg, + IVsProjectCfg, + IVsProjectCfg2, + IVsProjectFlavorCfg, + IVsDebuggableProjectCfg, + ISpecifyPropertyPages, + IVsSpecifyProjectDesignerPages, + IVsCfgBrowseObject, + IVsDeployableProjectCfg, + IVsProjectCfgDebugTargetSelection, + IVsQueryDebuggableProjectCfg, + IVsQueryDebuggableProjectCfg2, + IVsAppContainerProjectDeployCallback { + private readonly IVsCfg _pythonCfg; + private readonly IVsProjectFlavorCfg _uapCfg; + private readonly object syncObject = new object(); + private EventSinkCollection deployCallbackCollection = new EventSinkCollection(); + private IVsAppContainerProjectDeployOperation deployOp; + private IVsTask appContainerBootstrapperOperation; + private IVsOutputWindowPane outputWindow = null; + private IVsDebuggerDeployConnection connection = null; + private string deployPackageMoniker; + private string deployAppUserModelID; + + private const string RemoteTarget = "Remote Machine"; + + public PythonUapProjectConfig(IVsCfg pythonCfg, IVsProjectFlavorCfg uapConfig) { + _pythonCfg = pythonCfg; + _uapCfg = uapConfig; + } + + internal IVsHierarchy PythonConfig { + get { + IVsHierarchy proj = null; + + var browseObj = _pythonCfg as IVsCfgBrowseObject; + + if (browseObj != null) { + uint itemId = 0; + + browseObj.GetProjectItem(out proj, out itemId); + } + return proj; + } + } + + internal string LayoutDir { + get; private set; + } + + internal string DeployPackageMoniker { + get { return this.deployPackageMoniker; } + } + + internal string DeployAppUserModelID { + get { return this.deployAppUserModelID; } + } + + private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remoteMachine, string secret, string port, string sourceDir, string targetDir) { + bool attached = false; + bool stoppedDebugging = false; + const int attachRetryLimit = 10; + int attachRetryCount = 0; + + var qualifierString = string.Format( + "tcp://{0}@{1}:{2}?{3}={4}&{5}={6}&{7}={8}", + secret, + remoteMachine, + port, + AD7Engine.SourceDirectoryKey, + HttpUtility.UrlEncode(sourceDir), + AD7Engine.TargetDirectoryKey, + HttpUtility.UrlEncode(targetDir), + AD7Engine.TargetHostType, + HttpUtility.UrlEncode(AD7Engine.TargetUap)); + + var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); + var debugger = (EnvDTE90.Debugger3)dte.Debugger; + + var transport = default(EnvDTE80.Transport); + var transports = debugger.Transports; + + for (int i = 1; i <= transports.Count; ++i) { + var t = transports.Item(i); + Guid tid; + if (Guid.TryParse(t.ID, out tid) && tid == PythonRemoteDebugPortSupplier.PortSupplierGuid) { + transport = t; + } + } + + System.Diagnostics.Debug.Assert(transport != null, "Python remote debugging transport is missing."); + + if (transport == null) { + return; + } + + while (!attached && attachRetryCount++ < attachRetryLimit) { + try { + if (debugger.DebuggedProcesses == null || debugger.DebuggedProcesses.Count == 0) { + // We are no longer debugging, so just bail + stoppedDebugging = true; + break; + } else if (debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode) { + attachRetryCount--; + await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(1)); + continue; + } + + var processes = debugger.GetProcesses(transport, qualifierString); + + if (processes.Count > 0) { + foreach (EnvDTE.Process process in processes) { + process.Attach(); + attached = true; + System.Diagnostics.Debug.WriteLine("Successfully attached to a python remote process"); + break; + } + + if (attached) { + IVsDebugger vsDebugger = Package.GetGlobalService(typeof(SVsShellDebugger)) as IVsDebugger; + + if (vsDebugger != null) { + vsDebugger.UnadviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); + } + + break; + } + } + } catch (COMException comException) { + // In the case where the debug client does not setup the PTVSD server in time for the Attach to work, we will + // get this exception. We retry a few times to ensure client has time to start the Python debug server + System.Diagnostics.Debug.WriteLine("Failure during attach to remote Python process:\r\n{0}", comException); + } + + await System.Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(100)); + } + + if (!attached && !stoppedDebugging) { + MessageBox.Show("Could not attach to remote Python debug session.", null, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + #region IVsCfg Members + + public int get_DisplayName(out string pbstrDisplayName) { + int ret = _pythonCfg.get_DisplayName(out pbstrDisplayName); + + return ret; + } + + public int get_IsDebugOnly(out int pfIsDebugOnly) { + return _pythonCfg.get_IsDebugOnly(out pfIsDebugOnly); + } + + public int get_IsReleaseOnly(out int pfIsReleaseOnly) { + return _pythonCfg.get_IsReleaseOnly(out pfIsReleaseOnly); + } + + #endregion + + #region IVsProjectCfg Members + + public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.EnumOutputs(out ppIVsEnumOutputs); + } + ppIVsEnumOutputs = null; + return VSConstants.E_NOTIMPL; + } + + public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.OpenOutput(szOutputCanonicalName, out ppIVsOutput); + } + ppIVsOutput = null; + return VSConstants.E_NOTIMPL; + } + + public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProjectCfg) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_BuildableProjectCfg(out ppIVsBuildableProjectCfg); + } + ppIVsBuildableProjectCfg = null; + return VSConstants.E_NOTIMPL; + } + + public int get_CanonicalName(out string pbstrCanonicalName) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_CanonicalName(out pbstrCanonicalName); + } + pbstrCanonicalName = null; + return VSConstants.E_NOTIMPL; + } + + public int get_IsPackaged(out int pfIsPackaged) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_IsPackaged(out pfIsPackaged); + } + pfIsPackaged = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_IsSpecifyingOutputSupported(out pfIsSpecifyingOutputSupported); + } + pfIsSpecifyingOutputSupported = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_Platform(out Guid pguidPlatform) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_Platform(out pguidPlatform); + } + pguidPlatform = Guid.Empty; + return VSConstants.E_NOTIMPL; + } + + public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvider) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_ProjectCfgProvider(out ppIVsProjectCfgProvider); + } + ppIVsProjectCfgProvider = null; + return VSConstants.E_NOTIMPL; + } + + public int get_RootURL(out string pbstrRootURL) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_RootURL(out pbstrRootURL); + } + pbstrRootURL = null; + return VSConstants.E_NOTIMPL; + } + + public int get_TargetCodePage(out uint puiTargetCodePage) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_TargetCodePage(out puiTargetCodePage); + } + puiTargetCodePage = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_UpdateSequenceNumber(ULARGE_INTEGER[] puliUSN) { + IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + if (projCfg != null) { + return projCfg.get_UpdateSequenceNumber(puliUSN); + } + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectCfg2 Members + + public int OpenOutputGroup(string szCanonicalName, out IVsOutputGroup ppIVsOutputGroup) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.OpenOutputGroup(szCanonicalName, out ppIVsOutputGroup); + } + ppIVsOutputGroup = null; + return VSConstants.E_NOTIMPL; + } + + public int OutputsRequireAppRoot(out int pfRequiresAppRoot) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.OutputsRequireAppRoot(out pfRequiresAppRoot); + } + pfRequiresAppRoot = 1; + return VSConstants.E_NOTIMPL; + } + + public int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) { + if (iidCfg == typeof(IVsDebuggableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg)); + return VSConstants.S_OK; + } + + if (iidCfg == typeof(IVsDeployableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDeployableProjectCfg)); + return VSConstants.S_OK; + } + + if (iidCfg == typeof(IVsQueryDebuggableProjectCfg).GUID) { + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsQueryDebuggableProjectCfg)); + return VSConstants.S_OK; + } + + var projCfg = _uapCfg as IVsProjectFlavorCfg; + if (projCfg != null) { + return projCfg.get_CfgType(ref iidCfg, out ppCfg); + } + ppCfg = IntPtr.Zero; + return VSConstants.E_NOTIMPL; + } + + public int get_IsPrivate(out int pfPrivate) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_IsPrivate(out pfPrivate); + } + pfPrivate = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_OutputGroups(uint celt, IVsOutputGroup[] rgpcfg, uint[] pcActual = null) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_OutputGroups(celt, rgpcfg, pcActual); + } + return VSConstants.E_NOTIMPL; + } + + public int get_VirtualRoot(out string pbstrVRoot) { + IVsProjectCfg2 projCfg = _pythonCfg as IVsProjectCfg2; + if (projCfg != null) { + return projCfg.get_VirtualRoot(out pbstrVRoot); + } + pbstrVRoot = null; + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectFlavorCfg Members + + public int Close() { + IVsProjectFlavorCfg cfg = _uapCfg as IVsProjectFlavorCfg; + if (cfg != null) { + return cfg.Close(); + } + return VSConstants.S_OK; + } + + #endregion + + #region IVsDebuggableProjectCfg Members + + public int DebugLaunch(uint grfLaunch) { + if (PythonConfig.IsAppxPackageableProject()) { + VsDebugTargetInfo2[] targets; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) + return hr; + + VsDebugTargetInfo4[] appPackageDebugTarget = new VsDebugTargetInfo4[1]; + int targetLength = (int)Marshal.SizeOf(typeof(VsDebugTargetInfo4)); + + // Setup the app-specific parameters + appPackageDebugTarget[0].AppPackageLaunchInfo.AppUserModelID = DeployAppUserModelID; + appPackageDebugTarget[0].AppPackageLaunchInfo.PackageMoniker = DeployPackageMoniker; + appPackageDebugTarget[0].AppPackageLaunchInfo.AppPlatform = VsAppPackagePlatform.APPPLAT_WindowsAppx; + +#if DEV14_OR_LATER + // Check if this project contains a startup task and set launch flag appropriately + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)this.PythonConfig; + string canonicalName; + string containsStartupTaskValue = null; + bool containsStartupTask = false; + + get_CanonicalName(out canonicalName); + + bps.GetPropertyValue("ContainsStartupTask", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out containsStartupTaskValue); + + if (containsStartupTaskValue != null && bool.TryParse(containsStartupTaskValue, out containsStartupTask) && containsStartupTask) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS140.DBGLAUNCH_ContainsStartupTask; + } +#endif + + appPackageDebugTarget[0].dlo = (uint)_DEBUG_LAUNCH_OPERATION4.DLO_AppPackageDebug; + appPackageDebugTarget[0].LaunchFlags = grfLaunch; + appPackageDebugTarget[0].bstrRemoteMachine = targets[0].bstrRemoteMachine; + appPackageDebugTarget[0].bstrExe = targets[0].bstrExe; + appPackageDebugTarget[0].bstrArg = targets[0].bstrArg; + appPackageDebugTarget[0].bstrCurDir = targets[0].bstrCurDir; + appPackageDebugTarget[0].bstrEnv = targets[0].bstrEnv; + appPackageDebugTarget[0].dwProcessId = targets[0].dwProcessId; + appPackageDebugTarget[0].pStartupInfo = IntPtr.Zero; + appPackageDebugTarget[0].guidLaunchDebugEngine = targets[0].guidLaunchDebugEngine; + appPackageDebugTarget[0].dwDebugEngineCount = targets[0].dwDebugEngineCount; + appPackageDebugTarget[0].pDebugEngines = targets[0].pDebugEngines; + appPackageDebugTarget[0].guidPortSupplier = targets[0].guidPortSupplier; + + appPackageDebugTarget[0].bstrPortName = targets[0].bstrPortName; + appPackageDebugTarget[0].bstrOptions = targets[0].bstrOptions; + appPackageDebugTarget[0].fSendToOutputWindow = targets[0].fSendToOutputWindow; + appPackageDebugTarget[0].pUnknown = targets[0].pUnknown; + appPackageDebugTarget[0].guidProcessLanguage = targets[0].guidProcessLanguage; + appPackageDebugTarget[0].project = PythonConfig; + + // Pass the debug launch targets to the debugger + IVsDebugger4 debugger4 = (IVsDebugger4)Package.GetGlobalService(typeof(SVsShellDebugger)); + + VsDebugTargetProcessInfo[] results = new VsDebugTargetProcessInfo[1]; + + IVsDebugger debugger = (IVsDebugger)debugger4; + + // Launch task to monitor to attach to Python remote process + var sourceDir = System.IO.Path.GetFullPath(PythonConfig.GetProjectProperty("ProjectDir")).Trim('\\'); + var targetDir = System.IO.Path.GetFullPath(this.LayoutDir).Trim('\\'); + var debugPort = "5678"; + var debugId = Guid.NewGuid(); + var debugXml = new XDocument(new XElement("dbg", + new XElement("arg", @"visualstudio_py_remote_launcher.py"), + new XElement("arg", debugPort), + new XElement("arg", debugId), + new XElement("arg", "--redirect-output"))); + + Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteDebugXml = debugXml.ToString(); + Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteProcessFunction = () => { + return RemoteProcessAttachAsync( + appPackageDebugTarget[0].bstrRemoteMachine, + debugId.ToString(), + debugPort, + sourceDir, + targetDir); + }; + + int result = debugger.AdviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); + System.Diagnostics.Debug.Assert(result == VSConstants.S_OK, string.Format(System.Globalization.CultureInfo.CurrentCulture, "Failure {0}", result)); + + debugger4.LaunchDebugTargets4(1, appPackageDebugTarget, results); + + return VSConstants.S_OK; + } else { + IVsDebuggableProjectCfg cfg = _pythonCfg as IVsDebuggableProjectCfg; + if (cfg != null) { + return cfg.DebugLaunch(grfLaunch); + } + return VSConstants.E_NOTIMPL; + } + } + + public int QueryDebugLaunch(uint grfLaunch, out int pfCanLaunch) { + pfCanLaunch = this.PythonConfig.IsAppxPackageableProject() ? 1 : 0; + return VSConstants.S_OK; + } + + #endregion + + #region ISpecifyPropertyPages Members + + public void GetPages(CAUUID[] pPages) { + var cfg = _pythonCfg as ISpecifyPropertyPages; + if (cfg != null) { + cfg.GetPages(pPages); + } + } + + #endregion + + #region IVsSpecifyProjectDesignerPages Members + + public int GetProjectDesignerPages(CAUUID[] pPages) { + var cfg = _pythonCfg as IVsSpecifyProjectDesignerPages; + if (cfg != null) { + return cfg.GetProjectDesignerPages(pPages); + } + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsCfgBrowseObject Members + + public int GetCfg(out IVsCfg ppCfg) { + ppCfg = this; + return VSConstants.S_OK; + } + + public int GetProjectItem(out IVsHierarchy pHier, out uint pItemid) { + var cfg = _pythonCfg as IVsCfgBrowseObject; + if (cfg != null) { + return cfg.GetProjectItem(out pHier, out pItemid); + } + pHier = null; + pItemid = 0; + return VSConstants.E_NOTIMPL; + } + + #endregion + + + #region IVsDeployableProjectCfg Members + + public int AdviseDeployStatusCallback(IVsDeployStatusCallback pIVsDeployStatusCallback, out uint pdwCookie) { + if (pIVsDeployStatusCallback == null) { + pdwCookie = 0; + return VSConstants.E_UNEXPECTED; + } + + lock (syncObject) { + pdwCookie = deployCallbackCollection.Add(pIVsDeployStatusCallback); + } + + return VSConstants.S_OK; + } + + public int Commit(uint dwReserved) { + return VSConstants.S_OK; + } + + public int QueryStartDeploy(uint dwOptions, int[] pfSupported, int[] pfReady) { + if (pfSupported.Length > 0) { + // Only Appx package producing appcontainer projects should support deployment + pfSupported[0] = 1; + } + + if (pfReady.Length > 0) { + lock (syncObject) { + pfReady[0] = (deployOp == null && appContainerBootstrapperOperation == null) ? 1 : 0; + } + } + + return VSConstants.S_OK; + } + + public int QueryStatusDeploy(out int pfDeployDone) { + lock (syncObject) { + pfDeployDone = (deployOp == null && appContainerBootstrapperOperation == null) ? 1 : 0; + } + + return VSConstants.S_OK; + } + + public int Rollback(uint dwReserved) { + return VSConstants.S_OK; + } + + public int StartDeploy(IVsOutputWindowPane pIVsOutputWindowPane, uint dwOptions) { + int continueOn = 1; + + outputWindow = pIVsOutputWindowPane; + + if (connection != null) { + lock (syncObject) { + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + + // Loop through deploy status callbacks + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + if (ErrorHandler.Failed(callback.OnStartDeploy(ref continueOn))) { + continueOn = 0; + } + + if (continueOn == 0) { + outputWindow = null; + return VSConstants.E_ABORT; + } + } + + try { + VsDebugTargetInfo2[] targets; + uint deployFlags = (uint)(_AppContainerDeployOptions.ACDO_NetworkLoopbackEnable | _AppContainerDeployOptions.ACDO_SetNetworkLoopback | _AppContainerDeployOptions.ACDO_ForceCleanLayout); + string recipeFile = null; + string layoutDir = null; + var pythonProject = PythonConfig; + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)pythonProject; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) { + NotifyEndDeploy(0); + return hr; + } + + string canonicalName; + + get_CanonicalName(out canonicalName); + + bps.GetPropertyValue("AppxPackageRecipe", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out recipeFile); + bps.GetPropertyValue("LayoutDir", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out layoutDir); + + string projectUniqueName = null; + IVsSolution vsSolution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution; + if (vsSolution != null) { + hr = vsSolution.GetUniqueNameOfProject(pythonProject, out projectUniqueName); + } + + IVsAppContainerProjectDeploy deployHelper = (IVsAppContainerProjectDeploy)Package.GetGlobalService(typeof(SVsAppContainerProjectDeploy)); + if (String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + deployOp = deployHelper.StartDeployAsync(deployFlags, recipeFile, layoutDir, projectUniqueName, this); + } else { + IVsDebuggerDeploy deploy = (IVsDebuggerDeploy)Package.GetGlobalService(typeof(SVsShellDebugger)); + IVsDebuggerDeployConnection deployConnection; + + hr = deploy.ConnectToTargetComputer(targets[0].bstrRemoteMachine, VsDebugRemoteAuthenticationMode.VSAUTHMODE_None, out deployConnection); + if (ErrorHandler.Failed(hr)) { + NotifyEndDeploy(0); + return hr; + } + + connection = deployConnection; + + deployOp = deployHelper.StartRemoteDeployAsync(deployFlags, connection, recipeFile, projectUniqueName, this); + } + } catch (Exception) { + if (connection != null) { + lock (syncObject) { + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + connection = null; + + NotifyEndDeploy(0); + + // Rethrow exception + throw; + } + + return VSConstants.S_OK; + } + + public int StopDeploy(int fSync) { + IVsTask bootstrapOp = null; + IVsAppContainerProjectDeployOperation deployOp = null; + int result = VSConstants.S_OK; + + lock (syncObject) { + bootstrapOp = appContainerBootstrapperOperation; + deployOp = this.deployOp; + appContainerBootstrapperOperation = null; + this.deployOp = null; + } + + if (bootstrapOp != null) { + bootstrapOp.Cancel(); + if (fSync != 0) { + try { + bootstrapOp.Wait(); + } catch (Exception e) { + if (outputWindow != null) { + outputWindow.OutputString(e.ToString()); + } + result = VSConstants.E_FAIL; + } + } + } + + if (deployOp != null) { + deployOp.StopDeploy(fSync != 0); + } + + return result; + } + + int IVsDeployableProjectCfg.UnadviseDeployStatusCallback(uint dwCookie) { + lock (syncObject) { + deployCallbackCollection.RemoveAt(dwCookie); + } + + return VSConstants.S_OK; + } + + int IVsDeployableProjectCfg.WaitDeploy(uint dwMilliseconds, int fTickWhenMessageQNotEmpty) { + return VSConstants.S_OK; + } + + #endregion + + #region IVsAppContainerProjectDeployCallback Members + + void IVsAppContainerProjectDeployCallback.OnEndDeploy(bool successful, string deployedPackageMoniker, string deployedAppUserModelID) { + try { + if (successful) { + deployPackageMoniker = deployedPackageMoniker; + deployAppUserModelID = deployedAppUserModelID; + NotifyEndDeploy(1); + + var result = deployOp.GetDeployResult(); + + this.LayoutDir = result.LayoutFolder; + } else { + deployPackageMoniker = null; + deployAppUserModelID = null; + NotifyEndDeploy(0); + } + } finally { + lock (syncObject) { + deployOp = null; + + if (connection != null) { + connection.Dispose(); + connection = null; + } + } + } + } + + void IVsAppContainerProjectDeployCallback.OutputMessage(string message) { + if (null != outputWindow) { + outputWindow.OutputString(message); + } + } + + #endregion + + #region IVsQueryDebuggableProjectCfg2 Members + void IVsQueryDebuggableProjectCfg2.QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTargetInfo4[] rgDebugTargetInfo, uint[] pcActual) { + if (cTargets <= 0) { + if (pcActual == null) { + Marshal.ThrowExceptionForHR(VSConstants.E_POINTER); + } + + pcActual[0] = 1; + return; + } + + if (pcActual != null) { + pcActual[0] = 0; + } + + VsDebugTargetInfo2[] targets; + int hr = QueryDebugTargets(out targets); + if (ErrorHandler.Failed(hr)) + Marshal.ThrowExceptionForHR(hr); + + int targetLength = (int)Marshal.SizeOf(typeof(VsDebugTargetInfo4)); + + rgDebugTargetInfo[0].AppPackageLaunchInfo.AppUserModelID = deployAppUserModelID; + rgDebugTargetInfo[0].AppPackageLaunchInfo.PackageMoniker = deployPackageMoniker; + + bool isSimulator = GetDebugFlag("UseSimulator", false); + + if (isSimulator && String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS6.DBGLAUNCH_StartInSimulator; + } + + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_DetachOnStop; + + bool containsStartupTask = GetDebugFlag("ContainsStartupTask", false); + +#if DEV14_OR_LATER + if (containsStartupTask) { + grfLaunch |= (uint)__VSDBGLAUNCHFLAGS140.DBGLAUNCH_ContainsStartupTask; + } +#endif + + rgDebugTargetInfo[0].dlo = (uint)_DEBUG_LAUNCH_OPERATION4.DLO_AppPackageDebug; + rgDebugTargetInfo[0].LaunchFlags = grfLaunch; + rgDebugTargetInfo[0].bstrRemoteMachine = targets[0].bstrRemoteMachine; + rgDebugTargetInfo[0].bstrExe = targets[0].bstrExe; + rgDebugTargetInfo[0].bstrArg = targets[0].bstrArg; + rgDebugTargetInfo[0].bstrCurDir = targets[0].bstrCurDir; + rgDebugTargetInfo[0].bstrEnv = targets[0].bstrEnv; + rgDebugTargetInfo[0].dwProcessId = targets[0].dwProcessId; + rgDebugTargetInfo[0].pStartupInfo = IntPtr.Zero; + rgDebugTargetInfo[0].guidLaunchDebugEngine = targets[0].guidLaunchDebugEngine; + rgDebugTargetInfo[0].dwDebugEngineCount = targets[0].dwDebugEngineCount; + rgDebugTargetInfo[0].pDebugEngines = targets[0].pDebugEngines; + rgDebugTargetInfo[0].guidPortSupplier = targets[0].guidPortSupplier; + rgDebugTargetInfo[0].bstrPortName = targets[0].bstrPortName; + rgDebugTargetInfo[0].bstrOptions = targets[0].bstrOptions; + rgDebugTargetInfo[0].fSendToOutputWindow = targets[0].fSendToOutputWindow; + rgDebugTargetInfo[0].pUnknown = targets[0].pUnknown; + rgDebugTargetInfo[0].guidProcessLanguage = targets[0].guidProcessLanguage; + + if (pcActual != null) { + pcActual[0] = 1; + } + } + #endregion + + #region IVsProjectCfgDebugTargetSelection Members + void IVsProjectCfgDebugTargetSelection.GetCurrentDebugTarget(out Guid pguidDebugTargetType, out uint pDebugTargetTypeId, out string pbstrCurrentDebugTarget) { + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)PythonConfig; + + pguidDebugTargetType = VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet; + pDebugTargetTypeId = VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine; + pbstrCurrentDebugTarget = RemoteTarget; + } + + Array IVsProjectCfgDebugTargetSelection.GetDebugTargetListOfType(Guid guidDebugTargetType, uint debugTargetTypeId) { + string[] result = new string[1]; + if (guidDebugTargetType != VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet) { + return new string[0]; + } + + switch (debugTargetTypeId) { + case VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine: + result[0] = RemoteTarget; + break; + default: + return new string[0]; + } + + return result; + } + + bool IVsProjectCfgDebugTargetSelection.HasDebugTargets(IVsDebugTargetSelectionService pDebugTargetSelectionService, out Array pbstrSupportedTargetCommandIDs) { + pbstrSupportedTargetCommandIDs = new string[] { + String.Join(":", VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet, VSConstants.AppPackageDebugTargets.cmdidAppPackage_RemoteMachine) + }; + + return true; + } + + void IVsProjectCfgDebugTargetSelection.SetCurrentDebugTarget(Guid guidDebugTargetType, uint debugTargetTypeId, string bstrCurrentDebugTarget) { + if (guidDebugTargetType == VSConstants.AppPackageDebugTargets.guidAppPackageDebugTargetCmdSet) { + } + } + + #endregion + + internal int QueryDebugTargets(out VsDebugTargetInfo2[] targets) { + IntPtr queryDebuggableProjectCfgPtr = IntPtr.Zero; + targets = null; + + Guid guid = typeof(IVsQueryDebuggableProjectCfg).GUID; + int hr = get_CfgType(ref guid, out queryDebuggableProjectCfgPtr); + if (ErrorHandler.Failed(hr)) + return hr; + + object queryDebuggableProjectCfgObject = Marshal.GetObjectForIUnknown(queryDebuggableProjectCfgPtr); + if (queryDebuggableProjectCfgObject == null) + return VSConstants.E_UNEXPECTED; + + IVsQueryDebuggableProjectCfg baseQueryDebugbableCfg = queryDebuggableProjectCfgObject as IVsQueryDebuggableProjectCfg; + if (baseQueryDebugbableCfg == null) + return VSConstants.E_UNEXPECTED; + + uint[] targetsCountOutput = new uint[1]; + hr = baseQueryDebugbableCfg.QueryDebugTargets(0, 0, null, targetsCountOutput); + if (ErrorHandler.Failed(hr)) + return hr; + uint numberOfDebugTargets = targetsCountOutput[0]; + + targets = new VsDebugTargetInfo2[numberOfDebugTargets]; + hr = baseQueryDebugbableCfg.QueryDebugTargets(0, numberOfDebugTargets, targets, null); + if (ErrorHandler.Failed(hr)) + return hr; + + if (string.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + MessageBox.Show( + "The project cannot be deployed or debugged because there is not a remote machine specified in Debug settings.", + "Python Tools for Visual Studio", MessageBoxButtons.OK, MessageBoxIcon.Error); + return VSConstants.E_ABORT; + } + + return hr; + } + + private bool GetDebugFlag(string name, bool defaultValue) { + string value; + string canonicalName; + IVsBuildPropertyStorage bps = (IVsBuildPropertyStorage)PythonConfig; + + get_CanonicalName(out canonicalName); + + int hr = bps.GetPropertyValue(name, canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out value); + + if (Microsoft.VisualStudio.ErrorHandler.Failed(hr)) + return defaultValue; + + return value.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + private int Deploy() { + try { + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + callback.OnEndDeploy(1); + } + } finally { + lock (syncObject) { + appContainerBootstrapperOperation = null; + deployOp = null; + } + } + + this.outputWindow = null; + + return VSConstants.S_OK; + } + + private void NotifyEndDeploy(int success) { + foreach (IVsDeployStatusCallback callback in deployCallbackCollection) { + callback.OnEndDeploy(success); + } + + outputWindow = null; + } + + int IVsQueryDebuggableProjectCfg.QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTargetInfo2[] rgDebugTargetInfo, uint[] pcActual) { + var project = PythonConfig; + + if (pcActual != null && pcActual.Length > 0) { + pcActual[0] = 1; + } + + if (rgDebugTargetInfo != null && rgDebugTargetInfo.Length > 0) { + IList debugEngineGuids = new Guid[] { VSConstants.DebugEnginesGuids.NativeOnly_guid }; + + rgDebugTargetInfo[0] = new VsDebugTargetInfo2(); + + rgDebugTargetInfo[0].bstrExe = project.GetProjectProperty("Name"); + rgDebugTargetInfo[0].bstrRemoteMachine = project.GetProjectProperty("RemoteDebugMachine"); + rgDebugTargetInfo[0].guidPortSupplier = VSConstants.DebugPortSupplierGuids.NoAuth_guid; + rgDebugTargetInfo[0].guidLaunchDebugEngine = debugEngineGuids[0]; + rgDebugTargetInfo[0].dwDebugEngineCount = (uint)debugEngineGuids.Count; + rgDebugTargetInfo[0].pDebugEngines = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * debugEngineGuids.Count); + + for (var i = 0; i < debugEngineGuids.Count; i++) { + Marshal.StructureToPtr(debugEngineGuids[i], + IntPtr.Add(rgDebugTargetInfo[0].pDebugEngines, i * Marshal.SizeOf(typeof(Guid))), + false); + } + } + + return VSConstants.S_OK; + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapProjectFactory.cs b/Python/Product/Uap/Project/PythonUapProjectFactory.cs new file mode 100644 index 0000000000..60a9243890 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapProjectFactory.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Flavor; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid(GuidList.guidUapFactoryString)] + public class PythonUapProjectFactory : FlavoredProjectFactoryBase { + private PythonUapPackage _package; + + public PythonUapProjectFactory(PythonUapPackage package) { + _package = package; + } + + protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) { + return new PythonUapProject { Package = _package }; + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPage.cs b/Python/Product/Uap/Project/PythonUapPropertyPage.cs new file mode 100644 index 0000000000..85aed0126e --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPage.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.VisualStudioTools.Project; + +namespace Microsoft.PythonTools.Uap.Project { + [Guid(GuidList.guidUapPropertyPageString)] + class PythonUapPropertyPage : CommonPropertyPage { + private readonly PythonUapPropertyPageControl _control; + + public const string RemoteMachineSetting = "RemoteDebugMachine"; + public const string UapUserSetting = "UserSettingsChanged"; + + public PythonUapPropertyPage() { + _control = new PythonUapPropertyPageControl(this); + } + + public override Control Control { + get { return _control; } + } + + public override void Apply() { + SetConfigUserProjectProperty(RemoteMachineSetting, _control.RemoteDebugMachine); + + // Workaround to reload user project file + SetProjectProperty(UapUserSetting, DateTime.UtcNow.ToString()); + SetProjectProperty(UapUserSetting, string.Empty); + + IsDirty = false; + } + + public override void LoadSettings() { + Loading = true; + try { + _control.RemoteDebugMachine = GetConfigUserProjectProperty(RemoteMachineSetting); + IsDirty = false; + } finally { + Loading = false; + } + } + + public override string Name { + get { return Resources.UapPropertyPageTitle; } + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs new file mode 100644 index 0000000000..d5bf870632 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs @@ -0,0 +1,147 @@ +namespace Microsoft.PythonTools.Uap.Project { + partial class PythonUapPropertyPageControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + this._remoteMachineLabel = new System.Windows.Forms.Label(); + this._remoteMachine = new System.Windows.Forms.TextBox(); + this._uapGroup = new System.Windows.Forms.GroupBox(); + this._toolTip = new System.Windows.Forms.ToolTip(this.components); + tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + tableLayoutPanel2.SuspendLayout(); + tableLayoutPanel1.SuspendLayout(); + this._uapGroup.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel2 + // + tableLayoutPanel2.AutoSize = true; + tableLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + tableLayoutPanel2.ColumnCount = 2; + tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel2.Controls.Add(this._remoteMachineLabel, 0, 1); + tableLayoutPanel2.Controls.Add(this._remoteMachine, 1, 1); + tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel2.Location = new System.Drawing.Point(6, 21); + tableLayoutPanel2.Name = "tableLayoutPanel2"; + tableLayoutPanel2.RowCount = 2; + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + tableLayoutPanel2.Size = new System.Drawing.Size(406, 26); + tableLayoutPanel2.TabIndex = 0; + // + // _remoteMachineLabel + // + this._remoteMachineLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._remoteMachineLabel.AutoSize = true; + this._remoteMachineLabel.Location = new System.Drawing.Point(6, 6); + this._remoteMachineLabel.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this._remoteMachineLabel.Name = "_remoteMachineLabel"; + this._remoteMachineLabel.Size = new System.Drawing.Size(91, 13); + this._remoteMachineLabel.TabIndex = 2; + this._remoteMachineLabel.Text = "&Remote Machine:"; + this._remoteMachineLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // _remoteMachine + // + this._remoteMachine.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._remoteMachine.Location = new System.Drawing.Point(109, 3); + this._remoteMachine.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); + this._remoteMachine.MinimumSize = new System.Drawing.Size(50, 4); + this._remoteMachine.Name = "_remoteMachine"; + this._remoteMachine.Size = new System.Drawing.Size(291, 20); + this._remoteMachine.TabIndex = 3; + this._remoteMachine.TextChanged += new System.EventHandler(this.Setting_TextChanged); + // + // tableLayoutPanel1 + // + tableLayoutPanel1.AutoSize = true; + tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + tableLayoutPanel1.ColumnCount = 1; + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel1.Controls.Add(this._uapGroup, 0, 0); + tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 2; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + tableLayoutPanel1.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel1.TabIndex = 0; + // + // _uapGroup + // + this._uapGroup.AutoSize = true; + this._uapGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._uapGroup.Controls.Add(tableLayoutPanel2); + this._uapGroup.Dock = System.Windows.Forms.DockStyle.Fill; + this._uapGroup.Location = new System.Drawing.Point(6, 8); + this._uapGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapGroup.Name = "_uapGroup"; + this._uapGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapGroup.Size = new System.Drawing.Size(418, 55); + this._uapGroup.TabIndex = 0; + this._uapGroup.TabStop = false; + this._uapGroup.Text = "Debug Settings"; + // + // PythonUapPropertyPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.Controls.Add(tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this.Name = "PythonUapPropertyPageControl"; + this.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel2.ResumeLayout(false); + tableLayoutPanel2.PerformLayout(); + tableLayoutPanel1.ResumeLayout(false); + tableLayoutPanel1.PerformLayout(); + this._uapGroup.ResumeLayout(false); + this._uapGroup.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox _uapGroup; + private System.Windows.Forms.ToolTip _toolTip; + private System.Windows.Forms.Label _remoteMachineLabel; + private System.Windows.Forms.TextBox _remoteMachine; + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs new file mode 100644 index 0000000000..1651140c25 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using Microsoft.PythonTools; +using Microsoft.PythonTools.Project; +using Microsoft.VisualStudioTools.Project; + +namespace Microsoft.PythonTools.Uap.Project { + public partial class PythonUapPropertyPageControl : UserControl { + private readonly PythonUapPropertyPage _properties; + + private PythonUapPropertyPageControl() { + InitializeComponent(); + + _toolTip.SetToolTip(_remoteMachine, Resources.UapRemoteMachineHelp); + _toolTip.SetToolTip(_remoteMachineLabel, Resources.UapRemoteMachineHelp); + } + + internal PythonUapPropertyPageControl(PythonUapPropertyPage properties) + : this() { + _properties = properties; + } + + public string RemoteDebugMachine { + get { return _remoteMachine.Text; } + set { _remoteMachine.Text = value; } + } + + private void Setting_TextChanged(object sender, EventArgs e) { + if (_properties != null) { + _properties.IsDirty = true; + } + } + } +} diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx new file mode 100644 index 0000000000..1f83e4cc41 --- /dev/null +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + 17, 17 + + \ No newline at end of file diff --git a/Python/Product/Uap/Properties/AssemblyInfo.cs b/Python/Product/Uap/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0e203b3dd7 --- /dev/null +++ b/Python/Product/Uap/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Python Tools for Visual Studio - UAP Integration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en-US")] + diff --git a/Python/Product/Uap/PythonUapPackage.cs b/Python/Product/Uap/PythonUapPackage.cs new file mode 100644 index 0000000000..50cf58e2a5 --- /dev/null +++ b/Python/Product/Uap/PythonUapPackage.cs @@ -0,0 +1,125 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using EnvDTE90a; +using Microsoft.PythonTools.Debugger.DebugEngine; +using Microsoft.PythonTools.Uap.Interpreter; +using Microsoft.PythonTools.Uap.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.PythonTools.Uap { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. + /// + // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is + // a package. + [PackageRegistration(UseManagedResourcesOnly = true)] + + // This attribute is needed to let the shell know that this package exposes some menus. + [Guid(GuidList.guidUapPkgString)] + + [ProvideObject(typeof(PythonUapPropertyPage))] + [ProvideObject(typeof(PythonUapProject))] + [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasAppContainerProject_string)] + [Description("Python Tools UAP Interpreter")] + [ProvideProjectFactory(typeof(PythonUapProjectFactory), null, null, null, null, ".\\NullPath", LanguageVsTemplate = PythonConstants.LanguageName)] + [ProvidePythonInterpreterFactoryProvider(PythonUapInterpreterFactory.InterpreterGuidString, typeof(PythonUapInterpreterFactoryProvider))] + public sealed class PythonUapPackage : Package { + internal static PythonUapPackage Instance; + + /// + /// Default constructor of the package. + /// Inside this method you can place any initialization code that does not require + /// any Visual Studio service because at this point the package object is created but + /// not sited yet inside Visual Studio environment. The place to do all the other + /// initialization is the Initialize method. + /// + public PythonUapPackage() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this)); + Instance = this; + } + + ///////////////////////////////////////////////////////////////////////////// + // Overridden Package Implementation + #region Package Members + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initialization code that rely on services provided by VisualStudio. + /// + protected override void Initialize() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); + base.Initialize(); + + RegisterProjectFactory(new PythonUapProjectFactory(this)); + } + + #endregion + + internal new object GetService(Type serviceType) { + return base.GetService(serviceType); + } + + public EnvDTE.DTE DTE { + get { + return (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE)); + } + } + + internal static PythonUapProject GetProject(IServiceProvider serviceProvider, string filename) { + IVsHierarchy hierarchy; + IVsRunningDocumentTable rdt = serviceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + uint itemid; + IntPtr docData = IntPtr.Zero; + uint cookie; + try { + int hr = rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, + filename, + out hierarchy, + out itemid, + out docData, + out cookie); + + if (ErrorHandler.Succeeded(hr)) { + rdt.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, cookie); + } + var res = hierarchy as PythonUapProject; + if (res != null) { + return res; + } + return null; + } finally { + if (docData != IntPtr.Zero) { + Marshal.Release(docData); + } + } + } + } +} diff --git a/Python/Product/Uap/Resources.Designer.cs b/Python/Product/Uap/Resources.Designer.cs new file mode 100644 index 0000000000..6dc644c56b --- /dev/null +++ b/Python/Product/Uap/Resources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.PythonTools.Uap { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PythonTools.Uap.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to UAP Project Settings. + /// + internal static string UapPropertyPageTitle { + get { + return ResourceManager.GetString("UapPropertyPageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remote machine for deployment. + /// + internal static string UapRemoteMachineHelp { + get { + return ResourceManager.GetString("UapRemoteMachineHelp", resourceCulture); + } + } + } +} diff --git a/Python/Product/Uap/Resources.resx b/Python/Product/Uap/Resources.resx new file mode 100644 index 0000000000..8ae2e557a5 --- /dev/null +++ b/Python/Product/Uap/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UAP Project Settings + + + Remote machine for deployment + + \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx b/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest b/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest new file mode 100644 index 0000000000..e8175443d1 --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest @@ -0,0 +1,67 @@ + + + + + + + + + + $projectname$ + $XmlEscapedPublisher$ + StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pyuapbackgroundservice.dll + + + + + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj new file mode 100644 index 0000000000..773e701a54 --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj @@ -0,0 +1,61 @@ + + + + $safeprojectname$ + Debug + x86 + x86 + 2.0 + 8.2 + Windows Store + true + 14.0 + $guid1$ + . + . + StartupTask.py + true + + $projectname$_TemporaryKey.pfx + $currentuiculturename$ + {c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e};{888888A0-9F3D-457C-B088-3A5042F75D52} + {86767848-40B4-4007-8BCC-A3835EDF0E69} + 3.5 + true + + + + true + false + bin\Debug\$(Platform)\ + + + true + false + bin\Release\$(Platform)\ + + + + + + + Designer + + + + + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + + + + 14.0 + 2.2 + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + $(MSBuildProgramFiles32)\Microsoft Visual Studio $(VisualStudioVersion)\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate new file mode 100644 index 0000000000..406eb323ac --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate @@ -0,0 +1,31 @@ + + + Background Application (IoT) + A project for creating an Python IoT Background Application + + Python + 20 + 2 + true + Microsoft.Ptvs.WinRT.UAP.IOT.BackgroundApplication + BackgroundApplication + true + Windows + 6.1.0 + + + + Package.appxmanifest + StartupTask.py + Application_TemporaryKey.pfx + + + + Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Microsoft.VisualStudio.WinRT.TemplateWizards.CreateProjectCertificate.Wizard + + + Microsoft.VisualStudio.WinRT.TemplateWizards, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Microsoft.VisualStudio.WinRT.TemplateWizards.PlatformVersion.Wizard + + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py new file mode 100644 index 0000000000..df1dc6821e --- /dev/null +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py @@ -0,0 +1 @@ +print('Hello World') diff --git a/Python/Product/Uap/Uap.csproj b/Python/Product/Uap/Uap.csproj new file mode 100644 index 0000000000..3baed11995 --- /dev/null +++ b/Python/Product/Uap/Uap.csproj @@ -0,0 +1,323 @@ + + + + + + 12.0 + + + + + 4.0 + + + + + 14.0 + + + + + 4.0 + + + + + + Debug + AnyCPU + 2.0 + {D28045D6-232C-4101-A5CB-AC4A68E92211} + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + Microsoft.PythonTools.Uap + Microsoft.PythonTools.Uap + true + SAK + SAK + SAK + SAK + 1762 + + + AnyCPU + + + + True + + + True + + + + + + + + + True + + + + + + + True + + + + + + + + + + + + + + + false + + + + True + + + + + + + + + + + + + + + + + + + + + + True + + + + $(VSSDKDir)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Debugger.Engine.dll + + + + + $(DevEnvDir)Microsoft.VisualStudio.Web.Html.dll + + + + + {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2} + 8 + 0 + 0 + primary + False + False + + + {26AD1324-4B7C-44BC-84F8-B86AED45729F} + 10 + 0 + 0 + primary + False + False + + + {1A31287A-4D7D-413E-8E32-3B374931BD89} + 8 + 0 + 0 + primary + False + False + + + {2CE2370E-D744-4936-A090-3FFFE667B0E1} + 9 + 0 + 0 + primary + False + False + + + {1CBA492E-7263-47BB-87FE-639000619B15} + 8 + 0 + 0 + primary + False + False + + + {00020430-0000-0000-C000-000000000046} + 2 + 0 + 0 + primary + False + False + + + + + Python + Windows IoT Core + + + Python + + + + + + + + + Project\SolutionListener.cs + + + Project\CommonUtils.cs + + + ExceptionExtensions.cs + + + + + + + + + + + + usercontrol + + + PythonUapPropertyPageControl.cs + + + + + True + True + Resources.resx + + + + Designer + + + Designer + + + + Designer + + + + + true + Microsoft.VSPackage + Designer + + + + + true + PreserveNewest + Designer + + + Designer + + + + Designer + + + + + PythonUapPropertyPageControl.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + PreserveNewest + true + . + + + PreserveNewest + true + . + + + + + {A85D479D-67A9-4BDB-904A-7D86DAF68A6F} + Analysis + + + {DECC7971-FA58-4DB0-9561-BFFADD393BBD} + Debugger + + + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + PythonTools + False + + + + + true + + + + $(IntermediateOutputPath)\RemoteDebugger.vsdconfig + + + + <_Arguments>@(VsdConfigXml, ' ') @(IntermediateAssembly->'"%(FullPath)"', ' ') "$(VsdConfigOutput)" + + + + + + + true + . + PreserveNewest + $([System.IO.Path]::GetFileName($(VsdConfigOutput))) + + + + + + \ No newline at end of file diff --git a/Python/Product/Uap/VSPackage.resx b/Python/Product/Uap/VSPackage.resx new file mode 100644 index 0000000000..25ba4c515b --- /dev/null +++ b/Python/Product/Uap/VSPackage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Python Tools for Visual Studio - UAP Integration + + + Provides templates and integration for the UAP framework. + + \ No newline at end of file diff --git a/Python/Product/Uap/source.extension.vsixmanifest b/Python/Product/Uap/source.extension.vsixmanifest new file mode 100644 index 0000000000..b42b5127d1 --- /dev/null +++ b/Python/Product/Uap/source.extension.vsixmanifest @@ -0,0 +1,47 @@ + + + + Python Tools for Visual Studio - UAP + Microsoft + 2.2 + Provides templates and integration for the UAP framework. + http://pytools.codeplex.com + http://pytools.codeplex.com + PythonProject.ico + PythonProjectBig.ico + 1033 + true + + + + Pro + Ultimate + IntegratedShell + WDExpress + VWDExpress + + + + + + + Visual Studio MPF + + + Python Tools for Visual Studio + http://pytools.codeplex.com/ + + + Python Tools for Visual Studio - Interactive + http://pytools.codeplex.com/ + + + + |%CurrentProject%;PkgdefProjectOutputGroup| + |%CurrentProject%| + ProjectTemplates + ItemTemplates + RemoteDebugger.vsdconfig + + diff --git a/Python/PythonTools.sln b/Python/PythonTools.sln index 38c920f204d15805df8a9849fb767b1d94ec817b..6203b53bbce35a1f1aeba196dd2d33db7ab4eeeb 100644 GIT binary patch literal 222278 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 aJTG&ii*%+TwvW@vR!EsfD`eIibN>gQ^A+C! diff --git a/Python/Setup/BuildRelease.ps1 b/Python/Setup/BuildRelease.ps1 index bfcb2ace14..2cc4b5b279 100644 --- a/Python/Setup/BuildRelease.ps1 +++ b/Python/Setup/BuildRelease.ps1 @@ -261,6 +261,7 @@ $managed_files = ( "Microsoft.PythonTools.WebRole.dll", "Microsoft.PythonTools.Django.dll", "Microsoft.PythonTools.VsLogger.dll", + "Microsoft.PythonTools.Uap.dll", "Microsoft.PythonTools.AzureSetup.exe", "Microsoft.IronPythonTools.Resolver.dll" ) diff --git a/Python/Setup/MergeModule.wxi b/Python/Setup/MergeModule.wxi index 412e54b73d..19a5beaa17 100644 --- a/Python/Setup/MergeModule.wxi +++ b/Python/Setup/MergeModule.wxi @@ -47,6 +47,9 @@ + + + diff --git a/Python/Setup/PythonTools/PythonToolsFiles.proj b/Python/Setup/PythonTools/PythonToolsFiles.proj index e8d3d6960e..4e08c420f3 100644 --- a/Python/Setup/PythonTools/PythonToolsFiles.proj +++ b/Python/Setup/PythonTools/PythonToolsFiles.proj @@ -44,6 +44,7 @@ !(bindpath.bin)\PyDebugAttachX86.dll; visualstudio_py_repl.py; visualstudio_ipython_repl.py; + visualstudio_py_remote_launcher.py; visualstudio_py_launcher.py; visualstudio_py_debugger.py; visualstudio_py_util.py" /> @@ -135,7 +136,11 @@ CompletionDB - + + + CompletionDB + + $(DefineConstants); @@ -85,6 +86,10 @@ DjangoMsm {16671BE6-DD23-41D9-841A-0B80D47A090D} + + UapMsm + {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} + VsLoggerMsm {639BFC80-B31B-4D3F-9DF3-A65913000B4B} diff --git a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs index eda5726c0c..e9c9a3ede2 100644 --- a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs +++ b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs @@ -197,6 +197,15 @@ + + + + + + + + + @@ -252,6 +261,12 @@ + + + + + + diff --git a/Python/Setup/Uap/Uap.wixproj b/Python/Setup/Uap/Uap.wixproj new file mode 100644 index 0000000000..5213c9c543 --- /dev/null +++ b/Python/Setup/Uap/Uap.wixproj @@ -0,0 +1,33 @@ + + + + + 3.5 + {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} + 2.0 + Uap + Module + false + $(DefineConstants);ProductSuffix=Uap + SAK + SAK + SAK + SAK + + + + + + + Microsoft.PythonTools.Uap + {D28045D6-232C-4101-A5CB-AC4A68E92211} + + + + + MergeModule.wxi + + + + + \ No newline at end of file diff --git a/Python/Setup/Uap/Uap.wxs b/Python/Setup/Uap/Uap.wxs new file mode 100644 index 0000000000..d65abdcad1 --- /dev/null +++ b/Python/Setup/Uap/Uap.wxs @@ -0,0 +1,20 @@ + + + + + + + + + NOT ALLUSERS = 1 + + + + + + + + + + + diff --git a/Python/Setup/Uap/UapFiles.proj b/Python/Setup/Uap/UapFiles.proj new file mode 100644 index 0000000000..4060e61055 --- /dev/null +++ b/Python/Setup/Uap/UapFiles.proj @@ -0,0 +1,42 @@ + + + + + UapFiles + + + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + UapProjectTemplates + + + + + + diff --git a/Python/Setup/setup.proj b/Python/Setup/setup.proj index edae64f88f..80bf0fc743 100644 --- a/Python/Setup/setup.proj +++ b/Python/Setup/setup.proj @@ -17,6 +17,7 @@ + diff --git a/Python/products.settings b/Python/products.settings index 4bfac28cc0..564ffddf21 100644 --- a/Python/products.settings +++ b/Python/products.settings @@ -1,5 +1,9 @@ + + true + false + false true From 5cf95b7259d857fb260cb2c85ae7e7c70880bb3c Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Tue, 5 May 2015 15:12:53 -0700 Subject: [PATCH 04/30] Pull-request commit fixes for UAP projects --- .../Analyzer/CompletionDB}/_wingpio.idb | Bin .../Analyzer/CompletionDB}/_wini2c.idb | Bin .../Analyzer/CompletionDB}/_winspi.idb | Bin .../Debugger/Debugger/PythonProcess.cs | 2 - .../ImportWizard/ProjectCustomizations.cs | 2 +- .../PythonTools/Project/ProjectResources.cs | 1 + .../PythonTools/Project/PythonProjectNode.cs | 2 +- Python/Product/PythonTools/Resources.resx | 3 + .../Uap/Debugger/PythonRemoteDebugEvents.cs | 59 ++++++++++-------- .../Uap/Interpreter/PythonUapInterpreter.cs | 18 +----- .../PythonUapInterpreterFactory.cs | 4 +- .../PythonUapInterpreterFactoryProvider.cs | 17 ++++- .../Uap/Project/PythonUapProjectConfig.cs | 9 ++- .../Projects/BackgroundService/StartupTask.py | 2 +- .../Setup/PythonTools/PythonToolsFiles.proj | 4 -- 15 files changed, 62 insertions(+), 61 deletions(-) rename Python/{Setup/PythonTools => Product/Analyzer/CompletionDB}/_wingpio.idb (100%) rename Python/{Setup/PythonTools => Product/Analyzer/CompletionDB}/_wini2c.idb (100%) rename Python/{Setup/PythonTools => Product/Analyzer/CompletionDB}/_winspi.idb (100%) diff --git a/Python/Setup/PythonTools/_wingpio.idb b/Python/Product/Analyzer/CompletionDB/_wingpio.idb similarity index 100% rename from Python/Setup/PythonTools/_wingpio.idb rename to Python/Product/Analyzer/CompletionDB/_wingpio.idb diff --git a/Python/Setup/PythonTools/_wini2c.idb b/Python/Product/Analyzer/CompletionDB/_wini2c.idb similarity index 100% rename from Python/Setup/PythonTools/_wini2c.idb rename to Python/Product/Analyzer/CompletionDB/_wini2c.idb diff --git a/Python/Setup/PythonTools/_winspi.idb b/Python/Product/Analyzer/CompletionDB/_winspi.idb similarity index 100% rename from Python/Setup/PythonTools/_winspi.idb rename to Python/Product/Analyzer/CompletionDB/_winspi.idb diff --git a/Python/Product/Debugger/Debugger/PythonProcess.cs b/Python/Product/Debugger/Debugger/PythonProcess.cs index c1d8bf93dc..4393036245 100644 --- a/Python/Product/Debugger/Debugger/PythonProcess.cs +++ b/Python/Product/Debugger/Debugger/PythonProcess.cs @@ -634,8 +634,6 @@ private void HandleDebuggerOutput(Stream stream) { long tid = stream.ReadInt64(); string output = stream.ReadString(); - System.Diagnostics.Debug.WriteLine(output); - PythonThread thread; if (_threads.TryGetValue(tid, out thread)) { var outputEvent = DebuggerOutput; diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs index 4d527a928a..cbc9682453 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs @@ -201,7 +201,7 @@ private UapProjectCustomization() { } public override string DisplayName { get { - return SR.GetString(SR.ImportWizardGenericWebProjectCustomization); + return SR.GetString(SR.ImportWizardUapProjectCustomization); } } diff --git a/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs b/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs index de4858f647..0e4998a4fa 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs @@ -105,6 +105,7 @@ internal class SR : CommonSR { public const string ImportWizardDjangoProjectCustomization = "ImportWizardDjangoProjectCustomization"; public const string ImportWizardFlaskProjectCustomization = "ImportWizardFlaskProjectCustomization"; public const string ImportWizardGenericWebProjectCustomization = "ImportWizardGenericWebProjectCustomization"; + public const string ImportWizardUapProjectCustomization = "ImportWizardUapProjectCustomization"; public const string ReplInitializationMessage = "ReplInitializationMessage"; public const string ReplEvaluatorInterpreterNotFound = "ReplEvaluatorInterpreterNotFound"; diff --git a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs index 737b03adb0..6619f2b3d3 100644 --- a/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs +++ b/Python/Product/PythonTools/PythonTools/Project/PythonProjectNode.cs @@ -174,7 +174,7 @@ protected internal override void SetCurrentConfiguration() { return; if (this.IsAppxPackageableProject()) { - EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project; + EnvDTE.Project automationObject = (EnvDTE.Project)GetAutomationObject(); this.BuildProject.SetGlobalProperty(ProjectFileConstants.Platform, automationObject.ConfigurationManager.ActiveConfiguration.PlatformName); } diff --git a/Python/Product/PythonTools/Resources.resx b/Python/Product/PythonTools/Resources.resx index c043cbe45b..43a6911b83 100644 --- a/Python/Product/PythonTools/Resources.resx +++ b/Python/Product/PythonTools/Resources.resx @@ -573,4 +573,7 @@ Packages that cannot be installed using pip may prevent all listed packages from Run the command without installing the packages + + Python UAP Project + \ No newline at end of file diff --git a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs index 8c68c53ab9..eb5ea14b6a 100644 --- a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs +++ b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs @@ -1,4 +1,18 @@ -using System; +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; using System.Runtime.InteropServices; using System.Text; using System.Xml.Linq; @@ -77,38 +91,29 @@ public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTriggerHit hit, DkmEventDescriptorS eventDescriptor) { var remoteProcessTask = default(System.Threading.Tasks.Task); - using (var evt = new System.Threading.ManualResetEvent(false)) { + ThreadHelper.Generic.Invoke(() => { + var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; - ThreadHelper.Generic.Invoke(() => { - try { - var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; + // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration + // of the debugger + const int exceptionParameterCount = 2; - // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration - // of the debugger - const int exceptionParameterCount = 2; + if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { + // If we have a port and debug id, we'll go ahead and tell the client we are present + if (Instance.AttachRemoteProcessFunction != null && Instance.AttachRemoteDebugXml != null) { + // Write back that debugger is present + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); - if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { - // If we have a port and debug id, we'll go ahead and tell the client we are present - if (Instance.AttachRemoteProcessFunction != null && Instance.AttachRemoteDebugXml != null) { - // Write back that debugger is present - hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); - - // Write back the details of the debugger arguments - hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Encoding.Unicode.GetBytes(Instance.AttachRemoteDebugXml)); - } - } - } finally { - evt.Set(); + // Write back the details of the debugger arguments + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Encoding.Unicode.GetBytes(Instance.AttachRemoteDebugXml)); } - }); - - evt.WaitOne(); + } + }); - eventDescriptor.Suppress(); + eventDescriptor.Suppress(); - // Start the task to attach to the remote Python debugger session - remoteProcessTask = System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); - } + // Start the task to attach to the remote Python debugger session + remoteProcessTask = System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); } public int OnModeChange(DBGMODE dbgmodeNew) { diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs index c275773806..a144f552e6 100644 --- a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs @@ -36,29 +36,13 @@ public PythonUapInterpreter(PythonInterpreterFactoryWithDatabase factory) { _factory.NewDatabaseAvailable += OnNewDatabaseAvailable; } - private async void OnNewDatabaseAvailable(object sender, EventArgs e) { + private void OnNewDatabaseAvailable(object sender, EventArgs e) { var factory = _factory; if (factory == null) { // We have been disposed already, so ignore this event return; } - _typeDb = factory.GetCurrentDatabase(); - - if (_references != null) { - _typeDb = _typeDb.Clone(); - foreach (var reference in _references) { - string modName; - try { - modName = Path.GetFileNameWithoutExtension(reference.Name); - } catch (ArgumentException) { - continue; - } - - await Task.Yield(); - } - } - var evt = ModuleNamesChanged; if (evt != null) { evt(this, EventArgs.Empty); diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs index c29800ad48..1c8310e2df 100644 --- a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs @@ -20,11 +20,11 @@ class PythonUapInterpreterFactory : PythonInterpreterFactoryWithDatabase { public const string InterpreterGuidString = "{86767848-40B4-4007-8BCC-A3835EDF0E69}"; public static readonly Guid InterpreterGuid = new Guid(InterpreterGuidString); - public PythonUapInterpreterFactory() + public PythonUapInterpreterFactory(InterpreterConfiguration configuration) : base( InterpreterGuid, "Python UAP", - new InterpreterConfiguration(new Version(3, 5)), + configuration, true) { } diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs index aca47017f9..56dffea526 100644 --- a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs +++ b/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs @@ -22,19 +22,30 @@ namespace Microsoft.PythonTools.Uap.Interpreter { class PythonUapInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { private HashSet _factories = null; + public const string DefaultVersion = "3.5"; + public event EventHandler InterpreterFactoriesChanged; public PythonUapInterpreterFactoryProvider() { + PythonVersion = DefaultVersion; + } + + public string PythonVersion { + get; set; } private void DiscoverFactories() { if (_factories == null) { + var defaultConfig = new InterpreterConfiguration(new Version(PythonVersion)); + _factories = new HashSet(); - _factories.Add(new PythonUapInterpreterFactory()); + _factories.Add(new PythonUapInterpreterFactory(defaultConfig)); + + var factoriesChanged = InterpreterFactoriesChanged; - if (InterpreterFactoriesChanged != null) { - InterpreterFactoriesChanged(this, new EventArgs()); + if (factoriesChanged != null) { + factoriesChanged(this, new EventArgs()); } } } diff --git a/Python/Product/Uap/Project/PythonUapProjectConfig.cs b/Python/Product/Uap/Project/PythonUapProjectConfig.cs index 583c6ad352..e5fca49fda 100644 --- a/Python/Product/Uap/Project/PythonUapProjectConfig.cs +++ b/Python/Product/Uap/Project/PythonUapProjectConfig.cs @@ -461,11 +461,14 @@ public int DebugLaunch(uint grfLaunch) { }; int result = debugger.AdviseDebugEventCallback(Debugger.PythonRemoteDebugEvents.Instance); - System.Diagnostics.Debug.Assert(result == VSConstants.S_OK, string.Format(System.Globalization.CultureInfo.CurrentCulture, "Failure {0}", result)); - debugger4.LaunchDebugTargets4(1, appPackageDebugTarget, results); + if (result == VSConstants.S_OK) { + debugger4.LaunchDebugTargets4(1, appPackageDebugTarget, results); + } else { + System.Diagnostics.Debug.Fail(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Failure {0}", result)); + } - return VSConstants.S_OK; + return result; } else { IVsDebuggableProjectCfg cfg = _pythonCfg as IVsDebuggableProjectCfg; if (cfg != null) { diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py index df1dc6821e..fd54070d3d 100644 --- a/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py +++ b/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py @@ -1 +1 @@ -print('Hello World') +# Add your code here to run in your startup task \ No newline at end of file diff --git a/Python/Setup/PythonTools/PythonToolsFiles.proj b/Python/Setup/PythonTools/PythonToolsFiles.proj index 4e08c420f3..fa819f1fe9 100644 --- a/Python/Setup/PythonTools/PythonToolsFiles.proj +++ b/Python/Setup/PythonTools/PythonToolsFiles.proj @@ -137,10 +137,6 @@ CompletionDB - - CompletionDB - - /// The display name is a two part item /// first part is the config name, 2nd part is the platform name /// public override int get_DisplayName(out string name) { - if (!string.IsNullOrEmpty(Platform)) { - name = ConfigName + "|" + Platform; + if (!string.IsNullOrEmpty(PlatformName)) { + name = ConfigName + "|" + PlatformName; return VSConstants.S_OK; } else { return base.get_DisplayName(out name); diff --git a/Python/Product/Uap/Project/PythonUapProjectConfig.cs b/Python/Product/Uap/Project/PythonUapProjectConfig.cs index e5fca49fda..842f339f67 100644 --- a/Python/Product/Uap/Project/PythonUapProjectConfig.cs +++ b/Python/Product/Uap/Project/PythonUapProjectConfig.cs @@ -14,6 +14,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; using System.Runtime.InteropServices; using System.Web; using System.Windows.Forms; @@ -56,7 +59,8 @@ class PythonUapProjectConfig : private string deployPackageMoniker; private string deployAppUserModelID; - private const string RemoteTarget = "Remote Machine"; + private const string RemoteTarget = "Remote Device"; + private const string DefaultRemoteDebugPort = "5678"; public PythonUapProjectConfig(IVsCfg pythonCfg, IVsProjectFlavorCfg uapConfig) { _pythonCfg = pythonCfg; @@ -440,9 +444,9 @@ public int DebugLaunch(uint grfLaunch) { IVsDebugger debugger = (IVsDebugger)debugger4; // Launch task to monitor to attach to Python remote process - var sourceDir = System.IO.Path.GetFullPath(PythonConfig.GetProjectProperty("ProjectDir")).Trim('\\'); - var targetDir = System.IO.Path.GetFullPath(this.LayoutDir).Trim('\\'); - var debugPort = "5678"; + var sourceDir = Path.GetFullPath(PythonConfig.GetProjectProperty("ProjectDir")).Trim('\\'); + var targetDir = Path.GetFullPath(this.LayoutDir).Trim('\\'); + var debugPort = PythonConfig.GetProjectProperty("RemoteDebugPort") ?? DefaultRemoteDebugPort; var debugId = Guid.NewGuid(); var debugXml = new XDocument(new XElement("dbg", new XElement("arg", @"visualstudio_py_remote_launcher.py"), @@ -465,7 +469,7 @@ public int DebugLaunch(uint grfLaunch) { if (result == VSConstants.S_OK) { debugger4.LaunchDebugTargets4(1, appPackageDebugTarget, results); } else { - System.Diagnostics.Debug.Fail(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Failure {0}", result)); + Debug.Fail(string.Format(CultureInfo.CurrentCulture, "Failure {0}", result)); } return result; diff --git a/Python/Product/Uap/Project/PythonUapPropertyPage.cs b/Python/Product/Uap/Project/PythonUapPropertyPage.cs index 85aed0126e..857ce9be6d 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPage.cs +++ b/Python/Product/Uap/Project/PythonUapPropertyPage.cs @@ -22,8 +22,10 @@ namespace Microsoft.PythonTools.Uap.Project { class PythonUapPropertyPage : CommonPropertyPage { private readonly PythonUapPropertyPageControl _control; - public const string RemoteMachineSetting = "RemoteDebugMachine"; + public const string RemoteDeviceSetting = "RemoteDebugMachine"; + public const string RemotePortSetting = "RemoteDebugPort"; public const string UapUserSetting = "UserSettingsChanged"; + public const decimal DefaultPort = 5678; public PythonUapPropertyPage() { _control = new PythonUapPropertyPageControl(this); @@ -34,7 +36,8 @@ public override Control Control { } public override void Apply() { - SetConfigUserProjectProperty(RemoteMachineSetting, _control.RemoteDebugMachine); + SetConfigUserProjectProperty(RemoteDeviceSetting, _control.RemoteDevice); + SetConfigUserProjectProperty(RemotePortSetting, _control.RemotePort.ToString()); // Workaround to reload user project file SetProjectProperty(UapUserSetting, DateTime.UtcNow.ToString()); @@ -46,7 +49,17 @@ public override void Apply() { public override void LoadSettings() { Loading = true; try { - _control.RemoteDebugMachine = GetConfigUserProjectProperty(RemoteMachineSetting); + var portSetting = GetConfigUserProjectProperty(RemotePortSetting); + decimal portValue = DefaultPort; + + _control.RemoteDevice = GetConfigUserProjectProperty(RemoteDeviceSetting); + + if (!decimal.TryParse(portSetting, out portValue)) { + portValue = DefaultPort; + } + + _control.RemotePort = portValue; + IsDirty = false; } finally { Loading = false; diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs index d5bf870632..31016cb422 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs @@ -24,65 +24,98 @@ protected override void Dispose(bool disposing) { /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + System.Windows.Forms.TableLayoutPanel _uapRemoteSettingsPanel; System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - this._remoteMachineLabel = new System.Windows.Forms.Label(); - this._remoteMachine = new System.Windows.Forms.TextBox(); - this._uapGroup = new System.Windows.Forms.GroupBox(); + this._remoteDeviceLabel = new System.Windows.Forms.Label(); + this._remoteDevice = new System.Windows.Forms.TextBox(); + this._remotePortLabel = new System.Windows.Forms.Label(); + this._remotePort = new System.Windows.Forms.NumericUpDown(); + this._uapDebugGroup = new System.Windows.Forms.GroupBox(); this._toolTip = new System.Windows.Forms.ToolTip(this.components); - tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + _uapRemoteSettingsPanel = new System.Windows.Forms.TableLayoutPanel(); tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - tableLayoutPanel2.SuspendLayout(); + _uapRemoteSettingsPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this._remotePort)).BeginInit(); tableLayoutPanel1.SuspendLayout(); - this._uapGroup.SuspendLayout(); + this._uapDebugGroup.SuspendLayout(); this.SuspendLayout(); // - // tableLayoutPanel2 - // - tableLayoutPanel2.AutoSize = true; - tableLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - tableLayoutPanel2.ColumnCount = 2; - tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - tableLayoutPanel2.Controls.Add(this._remoteMachineLabel, 0, 1); - tableLayoutPanel2.Controls.Add(this._remoteMachine, 1, 1); - tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; - tableLayoutPanel2.Location = new System.Drawing.Point(6, 21); - tableLayoutPanel2.Name = "tableLayoutPanel2"; - tableLayoutPanel2.RowCount = 2; - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - tableLayoutPanel2.Size = new System.Drawing.Size(406, 26); - tableLayoutPanel2.TabIndex = 0; - // - // _remoteMachineLabel - // - this._remoteMachineLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; - this._remoteMachineLabel.AutoSize = true; - this._remoteMachineLabel.Location = new System.Drawing.Point(6, 6); - this._remoteMachineLabel.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); - this._remoteMachineLabel.Name = "_remoteMachineLabel"; - this._remoteMachineLabel.Size = new System.Drawing.Size(91, 13); - this._remoteMachineLabel.TabIndex = 2; - this._remoteMachineLabel.Text = "&Remote Machine:"; - this._remoteMachineLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // - // _remoteMachine - // - this._remoteMachine.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this._remoteMachine.Location = new System.Drawing.Point(109, 3); - this._remoteMachine.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); - this._remoteMachine.MinimumSize = new System.Drawing.Size(50, 4); - this._remoteMachine.Name = "_remoteMachine"; - this._remoteMachine.Size = new System.Drawing.Size(291, 20); - this._remoteMachine.TabIndex = 3; - this._remoteMachine.TextChanged += new System.EventHandler(this.Setting_TextChanged); + // _uapRemoteSettingsPanel + // + _uapRemoteSettingsPanel.AutoSize = true; + _uapRemoteSettingsPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + _uapRemoteSettingsPanel.ColumnCount = 2; + _uapRemoteSettingsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + _uapRemoteSettingsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + _uapRemoteSettingsPanel.Controls.Add(this._remoteDeviceLabel, 0, 0); + _uapRemoteSettingsPanel.Controls.Add(this._remoteDevice, 1, 0); + _uapRemoteSettingsPanel.Controls.Add(this._remotePortLabel, 0, 1); + _uapRemoteSettingsPanel.Controls.Add(this._remotePort, 1, 1); + _uapRemoteSettingsPanel.Dock = System.Windows.Forms.DockStyle.Fill; + _uapRemoteSettingsPanel.Location = new System.Drawing.Point(6, 21); + _uapRemoteSettingsPanel.Name = "_uapRemoteSettingsPanel"; + _uapRemoteSettingsPanel.RowCount = 2; + _uapRemoteSettingsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + _uapRemoteSettingsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + _uapRemoteSettingsPanel.Size = new System.Drawing.Size(399, 52); + _uapRemoteSettingsPanel.TabIndex = 0; + // + // _remoteDeviceLabel + // + this._remoteDeviceLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._remoteDeviceLabel.AutoSize = true; + this._remoteDeviceLabel.Location = new System.Drawing.Point(6, 6); + this._remoteDeviceLabel.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this._remoteDeviceLabel.Name = "_remoteDeviceLabel"; + this._remoteDeviceLabel.Size = new System.Drawing.Size(84, 13); + this._remoteDeviceLabel.TabIndex = 2; + this._remoteDeviceLabel.Text = "Remote &Device:"; + this._remoteDeviceLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // _remoteDevice + // + this._remoteDevice.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this._remoteDevice.Location = new System.Drawing.Point(102, 3); + this._remoteDevice.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); + this._remoteDevice.MinimumSize = new System.Drawing.Size(50, 4); + this._remoteDevice.Name = "_remoteDevice"; + this._remoteDevice.Size = new System.Drawing.Size(291, 20); + this._remoteDevice.TabIndex = 4; + // + // _remotePortLabel + // + this._remotePortLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._remotePortLabel.AutoSize = true; + this._remotePortLabel.Location = new System.Drawing.Point(6, 32); + this._remotePortLabel.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0); + this._remotePortLabel.Name = "_remotePortLabel"; + this._remotePortLabel.Size = new System.Drawing.Size(69, 13); + this._remotePortLabel.TabIndex = 5; + this._remotePortLabel.Text = "Remote &Port:"; + this._remotePortLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // _remotePort + // + this._remotePort.Anchor = System.Windows.Forms.AnchorStyles.Left; + this._remotePort.Location = new System.Drawing.Point(102, 29); + this._remotePort.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); + this._remotePort.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this._remotePort.MinimumSize = new System.Drawing.Size(50, 0); + this._remotePort.Name = "_remotePort"; + this._remotePort.Size = new System.Drawing.Size(61, 20); + this._remotePort.TabIndex = 3; + this._remotePort.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this._remotePort.Value = new decimal(new int[] { + 5678, + 0, + 0, + 0}); + this._remotePort.TextChanged += new System.EventHandler(this.Setting_TextChanged); + this._remoteDevice.TextChanged += new System.EventHandler(this.Setting_TextChanged); // // tableLayoutPanel1 // @@ -90,7 +123,7 @@ private void InitializeComponent() { tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; tableLayoutPanel1.ColumnCount = 1; tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - tableLayoutPanel1.Controls.Add(this._uapGroup, 0, 0); + tableLayoutPanel1.Controls.Add(this._uapDebugGroup, 0, 0); tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); @@ -98,23 +131,23 @@ private void InitializeComponent() { tableLayoutPanel1.RowCount = 2; tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - tableLayoutPanel1.Size = new System.Drawing.Size(430, 91); + tableLayoutPanel1.Size = new System.Drawing.Size(423, 117); tableLayoutPanel1.TabIndex = 0; // - // _uapGroup - // - this._uapGroup.AutoSize = true; - this._uapGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this._uapGroup.Controls.Add(tableLayoutPanel2); - this._uapGroup.Dock = System.Windows.Forms.DockStyle.Fill; - this._uapGroup.Location = new System.Drawing.Point(6, 8); - this._uapGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); - this._uapGroup.Name = "_uapGroup"; - this._uapGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); - this._uapGroup.Size = new System.Drawing.Size(418, 55); - this._uapGroup.TabIndex = 0; - this._uapGroup.TabStop = false; - this._uapGroup.Text = "Debug Settings"; + // _uapDebugGroup + // + this._uapDebugGroup.AutoSize = true; + this._uapDebugGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._uapDebugGroup.Controls.Add(_uapRemoteSettingsPanel); + this._uapDebugGroup.Dock = System.Windows.Forms.DockStyle.Fill; + this._uapDebugGroup.Location = new System.Drawing.Point(6, 8); + this._uapDebugGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapDebugGroup.Name = "_uapDebugGroup"; + this._uapDebugGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uapDebugGroup.Size = new System.Drawing.Size(411, 81); + this._uapDebugGroup.TabIndex = 0; + this._uapDebugGroup.TabStop = false; + this._uapDebugGroup.Text = "Debug Settings"; // // PythonUapPropertyPageControl // @@ -125,13 +158,14 @@ private void InitializeComponent() { this.Controls.Add(tableLayoutPanel1); this.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.Name = "PythonUapPropertyPageControl"; - this.Size = new System.Drawing.Size(430, 91); - tableLayoutPanel2.ResumeLayout(false); - tableLayoutPanel2.PerformLayout(); + this.Size = new System.Drawing.Size(423, 117); + _uapRemoteSettingsPanel.ResumeLayout(false); + _uapRemoteSettingsPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this._remotePort)).EndInit(); tableLayoutPanel1.ResumeLayout(false); tableLayoutPanel1.PerformLayout(); - this._uapGroup.ResumeLayout(false); - this._uapGroup.PerformLayout(); + this._uapDebugGroup.ResumeLayout(false); + this._uapDebugGroup.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -139,9 +173,11 @@ private void InitializeComponent() { #endregion - private System.Windows.Forms.GroupBox _uapGroup; + private System.Windows.Forms.GroupBox _uapDebugGroup; private System.Windows.Forms.ToolTip _toolTip; - private System.Windows.Forms.Label _remoteMachineLabel; - private System.Windows.Forms.TextBox _remoteMachine; + private System.Windows.Forms.Label _remoteDeviceLabel; + private System.Windows.Forms.NumericUpDown _remotePort; + private System.Windows.Forms.Label _remotePortLabel; + private System.Windows.Forms.TextBox _remoteDevice; } } diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs index 1651140c25..a053bf3d1a 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs @@ -13,11 +13,7 @@ * ***************************************************************************/ using System; -using System.Collections.Generic; using System.Windows.Forms; -using Microsoft.PythonTools; -using Microsoft.PythonTools.Project; -using Microsoft.VisualStudioTools.Project; namespace Microsoft.PythonTools.Uap.Project { public partial class PythonUapPropertyPageControl : UserControl { @@ -26,8 +22,10 @@ public partial class PythonUapPropertyPageControl : UserControl { private PythonUapPropertyPageControl() { InitializeComponent(); - _toolTip.SetToolTip(_remoteMachine, Resources.UapRemoteMachineHelp); - _toolTip.SetToolTip(_remoteMachineLabel, Resources.UapRemoteMachineHelp); + _toolTip.SetToolTip(_remoteDevice, Resources.UapRemoteDeviceHelp); + _toolTip.SetToolTip(_remoteDeviceLabel, Resources.UapRemoteDeviceHelp); + _toolTip.SetToolTip(_remotePort, Resources.UapRemotePortHelp); + _toolTip.SetToolTip(_remotePortLabel, Resources.UapRemotePortHelp); } internal PythonUapPropertyPageControl(PythonUapPropertyPage properties) @@ -35,9 +33,14 @@ internal PythonUapPropertyPageControl(PythonUapPropertyPage properties) _properties = properties; } - public string RemoteDebugMachine { - get { return _remoteMachine.Text; } - set { _remoteMachine.Text = value; } + public string RemoteDevice { + get { return _remoteDevice.Text; } + set { _remoteDevice.Text = value; } + } + + public decimal RemotePort { + get { return _remotePort.Value; } + set { _remotePort.Value = value; } } private void Setting_TextChanged(object sender, EventArgs e) { diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx index 1f83e4cc41..0b5f6c5ca7 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx +++ b/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx @@ -117,7 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + False diff --git a/Python/Product/Uap/Resources.Designer.cs b/Python/Product/Uap/Resources.Designer.cs index 6dc644c56b..5d3d3e81c3 100644 --- a/Python/Product/Uap/Resources.Designer.cs +++ b/Python/Product/Uap/Resources.Designer.cs @@ -70,11 +70,20 @@ internal static string UapPropertyPageTitle { } /// - /// Looks up a localized string similar to Remote machine for deployment. + /// Looks up a localized string similar to Remote device name or IP address. /// - internal static string UapRemoteMachineHelp { + internal static string UapRemoteDeviceHelp { get { - return ResourceManager.GetString("UapRemoteMachineHelp", resourceCulture); + return ResourceManager.GetString("UapRemoteDeviceHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remote debug port to use for Python debugger. + /// + internal static string UapRemotePortHelp { + get { + return ResourceManager.GetString("UapRemotePortHelp", resourceCulture); } } } diff --git a/Python/Product/Uap/Resources.resx b/Python/Product/Uap/Resources.resx index 8ae2e557a5..53fa505ec8 100644 --- a/Python/Product/Uap/Resources.resx +++ b/Python/Product/Uap/Resources.resx @@ -120,7 +120,10 @@ UAP Project Settings - - Remote machine for deployment + + Remote device name or IP address + + + Remote debug port to use for Python debugger \ No newline at end of file From 99baf62232e5420f092b38e3834a636aecaf291f Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Wed, 6 May 2015 19:41:24 -0700 Subject: [PATCH 06/30] Removing ReleaseComObject calls and allowing COM objects to be released in finalizer instead --- .../Uap/Debugger/PythonRemoteDebugEvents.cs | 48 +++++-------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs index eb5ea14b6a..3441e27e77 100644 --- a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs +++ b/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs @@ -13,9 +13,7 @@ * ***************************************************************************/ using System; -using System.Runtime.InteropServices; using System.Text; -using System.Xml.Linq; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; @@ -45,47 +43,25 @@ public static PythonRemoteDebugEvents Instance { public string AttachRemoteDebugXml { get; set; } public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { - try { - if (riidEvent == typeof(IDebugProgramCreateEvent2).GUID) { - Guid processId; + if (riidEvent == typeof(IDebugProgramCreateEvent2).GUID) { + Guid processId; - // A program was created and attached - if (pProcess != null) { - if (VSConstants.S_OK == pProcess.GetProcessId(out processId)) { - DkmProcess dkmProcess = DkmProcess.FindProcess(processId); + // A program was created and attached + if (pProcess != null) { + if (VSConstants.S_OK == pProcess.GetProcessId(out processId)) { + DkmProcess dkmProcess = DkmProcess.FindProcess(processId); - if (dkmProcess != null) { - var debugTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugStartExceptionCode); + if (dkmProcess != null) { + var debugTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugStartExceptionCode); - // Try to add exception trigger for when a remote debugger server is started for Python - dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, debugTrigger); - } + // Try to add exception trigger for when a remote debugger server is started for Python + dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, debugTrigger); } } } - - return VSConstants.S_OK; - } finally { - if (pEngine != null && Marshal.IsComObject(pEngine)) { - Marshal.ReleaseComObject(pEngine); - } - - if (pProcess != null && Marshal.IsComObject(pProcess)) { - Marshal.ReleaseComObject(pProcess); - } - - if (pProgram != null && Marshal.IsComObject(pProgram)) { - Marshal.ReleaseComObject(pProgram); - } - - if (pThread != null && Marshal.IsComObject(pThread)) { - Marshal.ReleaseComObject(pThread); - } - - if (pEvent != null && Marshal.IsComObject(pEvent)) { - Marshal.ReleaseComObject(pEvent); - } } + + return VSConstants.S_OK; } void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTriggerHit hit, DkmEventDescriptorS eventDescriptor) { From ab5baf6f6fb7f7e4d5697eed9071d2710b527821 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Wed, 6 May 2015 21:29:13 -0700 Subject: [PATCH 07/30] Renaming UAP (Universal App Platform) to UWP (Universal Windows Platform) --- .../DebugEngine/AD7BoundBreakpoint.cs | 4 +- .../Debugger/DebugEngine/AD7Engine.cs | 2 +- .../Project/ImportWizard/ImportSettings.cs | 2 +- .../ImportWizard/ProjectCustomizations.cs | 8 +- .../PythonTools/Project/ProjectResources.cs | 2 +- Python/Product/PythonTools/Resources.resx | 4 +- .../Debugger/PythonRemoteDebugEvents.cs | 2 +- .../RemoteDebugServerComponent.vsdconfigxml | 4 +- Python/Product/{Uap => Uwp}/Extensions.cs | 2 +- Python/Product/{Uap => Uwp}/Guids.cs | 8 +- .../Interpreter/PythonUwpInterpreter.cs} | 6 +- .../PythonUwpInterpreterFactory.cs} | 10 +- .../PythonUwpInterpreterFactoryProvider.cs} | 8 +- .../Microsoft.PythonTools.Uwp.targets} | 0 .../Project/PythonUwpProject.cs} | 22 ++-- .../Project/PythonUwpProjectConfig.cs} | 38 +++---- .../Project/PythonUwpProjectFactory.cs} | 12 +-- .../Project/PythonUwpPropertyPage.cs} | 20 ++-- .../PythonUwpPropertyPageControl.Designer.cs} | 94 +++++++++--------- .../Project/PythonUwpPropertyPageControl.cs} | 18 ++-- .../PythonUwpPropertyPageControl.resx} | 2 +- .../{Uap => Uwp}/Properties/AssemblyInfo.cs | 2 +- .../PythonUwpPackage.cs} | 30 +++--- .../{Uap => Uwp}/Resources.Designer.cs | 18 ++-- Python/Product/{Uap => Uwp}/Resources.resx | 8 +- .../Application_TemporaryKey.pfx | 0 .../BackgroundService/Package.appxmanifest | 16 +-- .../PythonBackgroundService.pyproj | 8 +- .../PythonBackgroundService.vstemplate | 2 +- .../Projects/BackgroundService/StartupTask.py | 0 .../{Uap/Uap.csproj => Uwp/Uwp.csproj} | 32 +++--- Python/Product/{Uap => Uwp}/VSPackage.resx | 4 +- .../source.extension.vsixmanifest | 4 +- Python/PythonTools.sln | Bin 222278 -> 222278 bytes Python/Setup/BuildRelease.ps1 | 2 +- Python/Setup/MergeModule.wxi | 2 +- .../PythonToolsInstaller.wixproj | 6 +- .../PythonToolsInstaller.wxs | 10 +- .../{Uap/Uap.wixproj => Uwp/Uwp.wixproj} | 12 +-- Python/Setup/{Uap/Uap.wxs => Uwp/Uwp.wxs} | 4 +- .../{Uap/UapFiles.proj => Uwp/UwpFiles.proj} | 12 +-- Python/Setup/setup.proj | 2 +- Python/products.settings | 6 +- 43 files changed, 224 insertions(+), 224 deletions(-) rename Python/Product/{Uap => Uwp}/Debugger/PythonRemoteDebugEvents.cs (99%) rename Python/Product/{Uap => Uwp}/Debugger/RemoteDebugServerComponent.vsdconfigxml (91%) rename Python/Product/{Uap => Uwp}/Extensions.cs (99%) rename Python/Product/{Uap => Uwp}/Guids.cs (81%) rename Python/Product/{Uap/Interpreter/PythonUapInterpreter.cs => Uwp/Interpreter/PythonUwpInterpreter.cs} (98%) rename Python/Product/{Uap/Interpreter/PythonUapInterpreterFactory.cs => Uwp/Interpreter/PythonUwpInterpreterFactory.cs} (83%) rename Python/Product/{Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs => Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs} (88%) rename Python/Product/{Uap/Microsoft.PythonTools.Uap.targets => Uwp/Microsoft.PythonTools.Uwp.targets} (100%) rename Python/Product/{Uap/Project/PythonUapProject.cs => Uwp/Project/PythonUwpProject.cs} (96%) rename Python/Product/{Uap/Project/PythonUapProjectConfig.cs => Uwp/Project/PythonUwpProjectConfig.cs} (97%) rename Python/Product/{Uap/Project/PythonUapProjectFactory.cs => Uwp/Project/PythonUwpProjectFactory.cs} (76%) rename Python/Product/{Uap/Project/PythonUapPropertyPage.cs => Uwp/Project/PythonUwpPropertyPage.cs} (79%) rename Python/Product/{Uap/Project/PythonUapPropertyPageControl.Designer.cs => Uwp/Project/PythonUwpPropertyPageControl.Designer.cs} (73%) rename Python/Product/{Uap/Project/PythonUapPropertyPageControl.cs => Uwp/Project/PythonUwpPropertyPageControl.cs} (72%) rename Python/Product/{Uap/Project/PythonUapPropertyPageControl.resx => Uwp/Project/PythonUwpPropertyPageControl.resx} (98%) rename Python/Product/{Uap => Uwp}/Properties/AssemblyInfo.cs (95%) rename Python/Product/{Uap/PythonUapPackage.cs => Uwp/PythonUwpPackage.cs} (85%) rename Python/Product/{Uap => Uwp}/Resources.Designer.cs (86%) rename Python/Product/{Uap => Uwp}/Resources.resx (96%) rename Python/Product/{Uap => Uwp}/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx (100%) rename Python/Product/{Uap => Uwp}/Templates/Projects/BackgroundService/Package.appxmanifest (83%) rename Python/Product/{Uap => Uwp}/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj (91%) rename Python/Product/{Uap => Uwp}/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate (96%) rename Python/Product/{Uap => Uwp}/Templates/Projects/BackgroundService/StartupTask.py (100%) rename Python/Product/{Uap/Uap.csproj => Uwp/Uwp.csproj} (94%) rename Python/Product/{Uap => Uwp}/VSPackage.resx (97%) rename Python/Product/{Uap => Uwp}/source.extension.vsixmanifest (95%) rename Python/Setup/{Uap/Uap.wixproj => Uwp/Uwp.wixproj} (76%) rename Python/Setup/{Uap/Uap.wxs => Uwp/Uwp.wxs} (90%) rename Python/Setup/{Uap/UapFiles.proj => Uwp/UwpFiles.proj} (71%) diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs index 4a02742539..216f9dda61 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7BoundBreakpoint.cs @@ -109,8 +109,8 @@ int IDebugBoundBreakpoint2.SetCondition(BP_CONDITION bpCondition) { int IDebugBoundBreakpoint2.GetHitCount(out uint pdwHitCount) { var remoteProcess = _engine.Process as Remote.PythonRemoteProcess; - if (remoteProcess != null && remoteProcess.TargetHostType == AD7Engine.TargetUap) { - // Target is UAP host and we will just assume breakpoint hit count is 1 from this + if (remoteProcess != null && remoteProcess.TargetHostType == AD7Engine.TargetUwp) { + // Target is UWP host and we will just assume breakpoint hit count is 1 from this // remote debug type due to issues with communicating this command pdwHitCount = 1; } else { diff --git a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs index 73c39f03e6..b86f0b7e79 100644 --- a/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs +++ b/Python/Product/Debugger/Debugger/DebugEngine/AD7Engine.cs @@ -93,7 +93,7 @@ public sealed class AD7Engine : IDebugEngine2, IDebugEngineLaunch2, IDebugProgra public const string TargetDirectoryKey = "td"; public const string TargetHostType = "host"; - public const string TargetUap = "uap"; + public const string TargetUwp = "uwp"; /// /// Specifies the version of the language which is being debugged. One of diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs index 0db9e064df..8aa1f7a04c 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ImportSettings.cs @@ -46,7 +46,7 @@ internal class ImportSettings : DependencyObject { DjangoProjectCustomization.Instance, FlaskProjectCustomization.Instance, GenericWebProjectCustomization.Instance, - UapProjectCustomization.Instance + UwpProjectCustomization.Instance }; public ImportSettings(IInterpreterOptionsService service) { diff --git a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs index cbc9682453..4ee25f6202 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ImportWizard/ProjectCustomizations.cs @@ -194,14 +194,14 @@ Dictionary groups } } - class UapProjectCustomization : ProjectCustomization { - public static readonly ProjectCustomization Instance = new UapProjectCustomization(); + class UwpProjectCustomization : ProjectCustomization { + public static readonly ProjectCustomization Instance = new UwpProjectCustomization(); - private UapProjectCustomization() { } + private UwpProjectCustomization() { } public override string DisplayName { get { - return SR.GetString(SR.ImportWizardUapProjectCustomization); + return SR.GetString(SR.ImportWizardUwpProjectCustomization); } } diff --git a/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs b/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs index 0e4998a4fa..06994d5d4d 100644 --- a/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs +++ b/Python/Product/PythonTools/PythonTools/Project/ProjectResources.cs @@ -105,7 +105,7 @@ internal class SR : CommonSR { public const string ImportWizardDjangoProjectCustomization = "ImportWizardDjangoProjectCustomization"; public const string ImportWizardFlaskProjectCustomization = "ImportWizardFlaskProjectCustomization"; public const string ImportWizardGenericWebProjectCustomization = "ImportWizardGenericWebProjectCustomization"; - public const string ImportWizardUapProjectCustomization = "ImportWizardUapProjectCustomization"; + public const string ImportWizardUwpProjectCustomization = "ImportWizardUwpProjectCustomization"; public const string ReplInitializationMessage = "ReplInitializationMessage"; public const string ReplEvaluatorInterpreterNotFound = "ReplEvaluatorInterpreterNotFound"; diff --git a/Python/Product/PythonTools/Resources.resx b/Python/Product/PythonTools/Resources.resx index 43a6911b83..9612689718 100644 --- a/Python/Product/PythonTools/Resources.resx +++ b/Python/Product/PythonTools/Resources.resx @@ -573,7 +573,7 @@ Packages that cannot be installed using pip may prevent all listed packages from Run the command without installing the packages - - Python UAP Project + + Python UWP Project \ No newline at end of file diff --git a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs similarity index 99% rename from Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs rename to Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs index 3441e27e77..d629ee5b3d 100644 --- a/Python/Product/Uap/Debugger/PythonRemoteDebugEvents.cs +++ b/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs @@ -22,7 +22,7 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.PythonTools.Uap.Debugger { +namespace Microsoft.PythonTools.Uwp.Debugger { internal class PythonRemoteDebugEvents : IVsDebuggerEvents, IDebugEventCallback2, IDkmExceptionTriggerHitNotification { private static readonly Lazy instance = new Lazy(); diff --git a/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml b/Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml similarity index 91% rename from Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml rename to Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml index 1880da70bd..ba112d0d49 100644 --- a/Python/Product/Uap/Debugger/RemoteDebugServerComponent.vsdconfigxml +++ b/Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml @@ -19,9 +19,9 @@ + AssemblyName="Microsoft.PythonTools.Uwp"> - + diff --git a/Python/Product/Uap/Extensions.cs b/Python/Product/Uwp/Extensions.cs similarity index 99% rename from Python/Product/Uap/Extensions.cs rename to Python/Product/Uwp/Extensions.cs index 576c1f360d..58d6fbd7ac 100644 --- a/Python/Product/Uap/Extensions.cs +++ b/Python/Product/Uwp/Extensions.cs @@ -20,7 +20,7 @@ using Microsoft.VisualStudioTools.Project; using Microsoft.VisualStudioTools.Project.Automation; -namespace Microsoft.PythonTools.Uap { +namespace Microsoft.PythonTools.Uwp { static class Extensions { internal static string GetProjectProperty(this IVsHierarchy projectNode, string name) { diff --git a/Python/Product/Uap/Guids.cs b/Python/Product/Uwp/Guids.cs similarity index 81% rename from Python/Product/Uap/Guids.cs rename to Python/Product/Uwp/Guids.cs index 815e0e5dc1..abd2d60a8e 100644 --- a/Python/Product/Uap/Guids.cs +++ b/Python/Product/Uwp/Guids.cs @@ -16,11 +16,11 @@ // MUST match guids.h using System; -namespace Microsoft.PythonTools.Uap { +namespace Microsoft.PythonTools.Uwp { static class GuidList { - public const string guidUapPkgString = "0a078d3c-15a9-47f5-8418-9ee5db43993d"; - public const string guidUapFactoryString = "c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e"; - public const string guidUapPropertyPageString = "700c8e09-f81c-4fb8-a386-508fb48c372d"; + public const string guidUwpPkgString = "0a078d3c-15a9-47f5-8418-9ee5db43993d"; + public const string guidUwpFactoryString = "c85cbf2e-4147-4e9d-87e0-9a2fbf407f6e"; + public const string guidUwpPropertyPageString = "700c8e09-f81c-4fb8-a386-508fb48c372d"; public static readonly Guid guidOfficeSharePointCmdSet = new Guid("d26c976c-8ee8-4ec4-8746-f5f7702a17c5"); } } \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreter.cs similarity index 98% rename from Python/Product/Uap/Interpreter/PythonUapInterpreter.cs rename to Python/Product/Uwp/Interpreter/PythonUwpInterpreter.cs index a144f552e6..ef978679d7 100644 --- a/Python/Product/Uap/Interpreter/PythonUapInterpreter.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreter.cs @@ -22,14 +22,14 @@ using Microsoft.PythonTools.Interpreter; using Microsoft.VisualStudioTools; -namespace Microsoft.PythonTools.Uap.Interpreter { - class PythonUapInterpreter : IPythonInterpreter, IPythonInterpreterWithProjectReferences2, IDisposable { +namespace Microsoft.PythonTools.Uwp.Interpreter { + class PythonUwpInterpreter : IPythonInterpreter, IPythonInterpreterWithProjectReferences2, IDisposable { readonly Version _langVersion; private PythonInterpreterFactoryWithDatabase _factory; private PythonTypeDatabase _typeDb; private HashSet _references; - public PythonUapInterpreter(PythonInterpreterFactoryWithDatabase factory) { + public PythonUwpInterpreter(PythonInterpreterFactoryWithDatabase factory) { _langVersion = factory.Configuration.Version; _factory = factory; _typeDb = _factory.GetCurrentDatabase(); diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs similarity index 83% rename from Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs rename to Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs index 1c8310e2df..c18e25f307 100644 --- a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactory.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs @@ -15,15 +15,15 @@ using System; using Microsoft.PythonTools.Interpreter; -namespace Microsoft.PythonTools.Uap.Interpreter { - class PythonUapInterpreterFactory : PythonInterpreterFactoryWithDatabase { +namespace Microsoft.PythonTools.Uwp.Interpreter { + class PythonUwpInterpreterFactory : PythonInterpreterFactoryWithDatabase { public const string InterpreterGuidString = "{86767848-40B4-4007-8BCC-A3835EDF0E69}"; public static readonly Guid InterpreterGuid = new Guid(InterpreterGuidString); - public PythonUapInterpreterFactory(InterpreterConfiguration configuration) + public PythonUwpInterpreterFactory(InterpreterConfiguration configuration) : base( InterpreterGuid, - "Python UAP", + "Python UWP", configuration, true) { } @@ -34,7 +34,7 @@ public override string Description { } } public override IPythonInterpreter MakeInterpreter(PythonInterpreterFactoryWithDatabase factory) { - return new PythonUapInterpreter(factory); + return new PythonUwpInterpreter(factory); } } } \ No newline at end of file diff --git a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs similarity index 88% rename from Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs rename to Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs index 56dffea526..0217028235 100644 --- a/Python/Product/Uap/Interpreter/PythonUapInterpreterFactoryProvider.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs @@ -17,16 +17,16 @@ using System.ComponentModel.Composition; using Microsoft.PythonTools.Interpreter; -namespace Microsoft.PythonTools.Uap.Interpreter { +namespace Microsoft.PythonTools.Uwp.Interpreter { [Export(typeof(IPythonInterpreterFactoryProvider))] - class PythonUapInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { + class PythonUwpInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { private HashSet _factories = null; public const string DefaultVersion = "3.5"; public event EventHandler InterpreterFactoriesChanged; - public PythonUapInterpreterFactoryProvider() { + public PythonUwpInterpreterFactoryProvider() { PythonVersion = DefaultVersion; } @@ -40,7 +40,7 @@ private void DiscoverFactories() { _factories = new HashSet(); - _factories.Add(new PythonUapInterpreterFactory(defaultConfig)); + _factories.Add(new PythonUwpInterpreterFactory(defaultConfig)); var factoriesChanged = InterpreterFactoriesChanged; diff --git a/Python/Product/Uap/Microsoft.PythonTools.Uap.targets b/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets similarity index 100% rename from Python/Product/Uap/Microsoft.PythonTools.Uap.targets rename to Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets diff --git a/Python/Product/Uap/Project/PythonUapProject.cs b/Python/Product/Uwp/Project/PythonUwpProject.cs similarity index 96% rename from Python/Product/Uap/Project/PythonUapProject.cs rename to Python/Product/Uwp/Project/PythonUwpProject.cs index b09136470f..0f7304d981 100644 --- a/Python/Product/Uap/Project/PythonUapProject.cs +++ b/Python/Product/Uwp/Project/PythonUwpProject.cs @@ -26,30 +26,30 @@ using Microsoft.VisualStudioTools; using IServiceProvider = System.IServiceProvider; -namespace Microsoft.PythonTools.Uap.Project { +namespace Microsoft.PythonTools.Uwp.Project { [Guid("27BB1268-135A-4409-914F-7AA64AD8195D")] - partial class PythonUapProject : + partial class PythonUwpProject : FlavoredProjectBase, IOleCommandTarget, IVsProjectFlavorCfgProvider, IVsProject, IVsFilterAddProjectItemDlg { - private PythonUapPackage _package; + private PythonUwpPackage _package; internal IVsProject _innerProject; internal IVsProject3 _innerProject3; private IVsProjectFlavorCfgProvider _innerVsProjectFlavorCfgProvider; private static Guid PythonProjectGuid = new Guid(PythonConstants.ProjectFactoryGuid); private IOleCommandTarget _menuService; - public PythonUapProject() { + public PythonUwpProject() { } - internal PythonUapPackage Package { + internal PythonUwpPackage Package { get { return _package; } set { Debug.Assert(_package == null); if (_package != null) { - throw new InvalidOperationException("PythonUapProject.Package must only be set once"); + throw new InvalidOperationException("PythonUwpProject.Package must only be set once"); } _package = value; } @@ -105,7 +105,7 @@ protected override void SetInnerProject(IntPtr innerIUnknown) { // (This must run after we called base.SetInnerProject) _menuService = (IOleCommandTarget)((IServiceProvider)this).GetService(typeof(IMenuCommandService)); if (_menuService == null) { - throw new InvalidOperationException("Cannot initialize Uap project"); + throw new InvalidOperationException("Cannot initialize Uwp project"); } } @@ -156,7 +156,7 @@ protected override int GetProperty(uint itemId, int propId, out object property) private static Guid[] PropertyPagesToAdd = new Guid[0]; private static Guid[] CfgSpecificPropertyPagesToAdd = new Guid[] { - new Guid(GuidList.guidUapPropertyPageString) + new Guid(GuidList.guidUwpPropertyPageString) }; private static HashSet PropertyPagesToRemove = new HashSet { @@ -227,14 +227,14 @@ public int CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCf // control that w/ the web project which is actually just letting // the base Python project handle it. So we keep the base Python // project config here. - IVsProjectFlavorCfg uapCfg; + IVsProjectFlavorCfg uwpCfg; ErrorHandler.ThrowOnFailure( _innerVsProjectFlavorCfgProvider.CreateProjectFlavorCfg( pBaseProjectCfg, - out uapCfg + out uwpCfg ) ); - ppFlavorCfg = new PythonUapProjectConfig(pBaseProjectCfg, uapCfg); + ppFlavorCfg = new PythonUwpProjectConfig(pBaseProjectCfg, uwpCfg); return VSConstants.S_OK; } diff --git a/Python/Product/Uap/Project/PythonUapProjectConfig.cs b/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs similarity index 97% rename from Python/Product/Uap/Project/PythonUapProjectConfig.cs rename to Python/Product/Uwp/Project/PythonUwpProjectConfig.cs index 842f339f67..c6da2e7f88 100644 --- a/Python/Product/Uap/Project/PythonUapProjectConfig.cs +++ b/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs @@ -29,12 +29,12 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.PythonTools.Uap.Project { +namespace Microsoft.PythonTools.Uwp.Project { /// /// Merges the PTVS IVsCfg object with the Venus IVsCfg implementation redirecting /// things appropriately to either one. /// - class PythonUapProjectConfig : + class PythonUwpProjectConfig : IVsCfg, IVsProjectCfg, IVsProjectCfg2, @@ -49,7 +49,7 @@ class PythonUapProjectConfig : IVsQueryDebuggableProjectCfg2, IVsAppContainerProjectDeployCallback { private readonly IVsCfg _pythonCfg; - private readonly IVsProjectFlavorCfg _uapCfg; + private readonly IVsProjectFlavorCfg _uwpCfg; private readonly object syncObject = new object(); private EventSinkCollection deployCallbackCollection = new EventSinkCollection(); private IVsAppContainerProjectDeployOperation deployOp; @@ -62,9 +62,9 @@ class PythonUapProjectConfig : private const string RemoteTarget = "Remote Device"; private const string DefaultRemoteDebugPort = "5678"; - public PythonUapProjectConfig(IVsCfg pythonCfg, IVsProjectFlavorCfg uapConfig) { + public PythonUwpProjectConfig(IVsCfg pythonCfg, IVsProjectFlavorCfg uwpConfig) { _pythonCfg = pythonCfg; - _uapCfg = uapConfig; + _uwpCfg = uwpConfig; } internal IVsHierarchy PythonConfig { @@ -110,7 +110,7 @@ private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remote AD7Engine.TargetDirectoryKey, HttpUtility.UrlEncode(targetDir), AD7Engine.TargetHostType, - HttpUtility.UrlEncode(AD7Engine.TargetUap)); + HttpUtility.UrlEncode(AD7Engine.TargetUwp)); var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); var debugger = (EnvDTE90.Debugger3)dte.Debugger; @@ -199,7 +199,7 @@ public int get_IsReleaseOnly(out int pfIsReleaseOnly) { #region IVsProjectCfg Members public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.EnumOutputs(out ppIVsEnumOutputs); } @@ -208,7 +208,7 @@ public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { } public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.OpenOutput(szOutputCanonicalName, out ppIVsOutput); } @@ -217,7 +217,7 @@ public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { } public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProjectCfg) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_BuildableProjectCfg(out ppIVsBuildableProjectCfg); } @@ -226,7 +226,7 @@ public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProj } public int get_CanonicalName(out string pbstrCanonicalName) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_CanonicalName(out pbstrCanonicalName); } @@ -235,7 +235,7 @@ public int get_CanonicalName(out string pbstrCanonicalName) { } public int get_IsPackaged(out int pfIsPackaged) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_IsPackaged(out pfIsPackaged); } @@ -244,7 +244,7 @@ public int get_IsPackaged(out int pfIsPackaged) { } public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_IsSpecifyingOutputSupported(out pfIsSpecifyingOutputSupported); } @@ -253,7 +253,7 @@ public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported } public int get_Platform(out Guid pguidPlatform) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_Platform(out pguidPlatform); } @@ -262,7 +262,7 @@ public int get_Platform(out Guid pguidPlatform) { } public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvider) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_ProjectCfgProvider(out ppIVsProjectCfgProvider); } @@ -271,7 +271,7 @@ public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvi } public int get_RootURL(out string pbstrRootURL) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_RootURL(out pbstrRootURL); } @@ -280,7 +280,7 @@ public int get_RootURL(out string pbstrRootURL) { } public int get_TargetCodePage(out uint puiTargetCodePage) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_TargetCodePage(out puiTargetCodePage); } @@ -289,7 +289,7 @@ public int get_TargetCodePage(out uint puiTargetCodePage) { } public int get_UpdateSequenceNumber(ULARGE_INTEGER[] puliUSN) { - IVsProjectCfg projCfg = _uapCfg as IVsProjectCfg; + IVsProjectCfg projCfg = _uwpCfg as IVsProjectCfg; if (projCfg != null) { return projCfg.get_UpdateSequenceNumber(puliUSN); } @@ -334,7 +334,7 @@ public int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) { return VSConstants.S_OK; } - var projCfg = _uapCfg as IVsProjectFlavorCfg; + var projCfg = _uwpCfg as IVsProjectFlavorCfg; if (projCfg != null) { return projCfg.get_CfgType(ref iidCfg, out ppCfg); } @@ -373,7 +373,7 @@ public int get_VirtualRoot(out string pbstrVRoot) { #region IVsProjectFlavorCfg Members public int Close() { - IVsProjectFlavorCfg cfg = _uapCfg as IVsProjectFlavorCfg; + IVsProjectFlavorCfg cfg = _uwpCfg as IVsProjectFlavorCfg; if (cfg != null) { return cfg.Close(); } diff --git a/Python/Product/Uap/Project/PythonUapProjectFactory.cs b/Python/Product/Uwp/Project/PythonUwpProjectFactory.cs similarity index 76% rename from Python/Product/Uap/Project/PythonUapProjectFactory.cs rename to Python/Product/Uwp/Project/PythonUwpProjectFactory.cs index 60a9243890..07a648eec1 100644 --- a/Python/Product/Uap/Project/PythonUapProjectFactory.cs +++ b/Python/Product/Uwp/Project/PythonUwpProjectFactory.cs @@ -18,17 +18,17 @@ using Microsoft.VisualStudio.Shell.Flavor; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.PythonTools.Uap.Project { - [Guid(GuidList.guidUapFactoryString)] - public class PythonUapProjectFactory : FlavoredProjectFactoryBase { - private PythonUapPackage _package; +namespace Microsoft.PythonTools.Uwp.Project { + [Guid(GuidList.guidUwpFactoryString)] + public class PythonUwpProjectFactory : FlavoredProjectFactoryBase { + private PythonUwpPackage _package; - public PythonUapProjectFactory(PythonUapPackage package) { + public PythonUwpProjectFactory(PythonUwpPackage package) { _package = package; } protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) { - return new PythonUapProject { Package = _package }; + return new PythonUwpProject { Package = _package }; } } } diff --git a/Python/Product/Uap/Project/PythonUapPropertyPage.cs b/Python/Product/Uwp/Project/PythonUwpPropertyPage.cs similarity index 79% rename from Python/Product/Uap/Project/PythonUapPropertyPage.cs rename to Python/Product/Uwp/Project/PythonUwpPropertyPage.cs index 857ce9be6d..6e24d0e9c3 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPage.cs +++ b/Python/Product/Uwp/Project/PythonUwpPropertyPage.cs @@ -17,18 +17,18 @@ using System.Windows.Forms; using Microsoft.VisualStudioTools.Project; -namespace Microsoft.PythonTools.Uap.Project { - [Guid(GuidList.guidUapPropertyPageString)] - class PythonUapPropertyPage : CommonPropertyPage { - private readonly PythonUapPropertyPageControl _control; +namespace Microsoft.PythonTools.Uwp.Project { + [Guid(GuidList.guidUwpPropertyPageString)] + class PythonUwpPropertyPage : CommonPropertyPage { + private readonly PythonUwpPropertyPageControl _control; public const string RemoteDeviceSetting = "RemoteDebugMachine"; public const string RemotePortSetting = "RemoteDebugPort"; - public const string UapUserSetting = "UserSettingsChanged"; + public const string UwpUserSetting = "UserSettingsChanged"; public const decimal DefaultPort = 5678; - public PythonUapPropertyPage() { - _control = new PythonUapPropertyPageControl(this); + public PythonUwpPropertyPage() { + _control = new PythonUwpPropertyPageControl(this); } public override Control Control { @@ -40,8 +40,8 @@ public override void Apply() { SetConfigUserProjectProperty(RemotePortSetting, _control.RemotePort.ToString()); // Workaround to reload user project file - SetProjectProperty(UapUserSetting, DateTime.UtcNow.ToString()); - SetProjectProperty(UapUserSetting, string.Empty); + SetProjectProperty(UwpUserSetting, DateTime.UtcNow.ToString()); + SetProjectProperty(UwpUserSetting, string.Empty); IsDirty = false; } @@ -67,7 +67,7 @@ public override void LoadSettings() { } public override string Name { - get { return Resources.UapPropertyPageTitle; } + get { return Resources.UwpPropertyPageTitle; } } } } diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs b/Python/Product/Uwp/Project/PythonUwpPropertyPageControl.Designer.cs similarity index 73% rename from Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs rename to Python/Product/Uwp/Project/PythonUwpPropertyPageControl.Designer.cs index 31016cb422..8f2ebaa43b 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPageControl.Designer.cs +++ b/Python/Product/Uwp/Project/PythonUwpPropertyPageControl.Designer.cs @@ -1,5 +1,5 @@ -namespace Microsoft.PythonTools.Uap.Project { - partial class PythonUapPropertyPageControl { +namespace Microsoft.PythonTools.Uwp.Project { + partial class PythonUwpPropertyPageControl { /// /// Required designer variable. /// @@ -24,41 +24,41 @@ protected override void Dispose(bool disposing) { /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.TableLayoutPanel _uapRemoteSettingsPanel; + System.Windows.Forms.TableLayoutPanel _uwpRemoteSettingsPanel; System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; this._remoteDeviceLabel = new System.Windows.Forms.Label(); this._remoteDevice = new System.Windows.Forms.TextBox(); this._remotePortLabel = new System.Windows.Forms.Label(); this._remotePort = new System.Windows.Forms.NumericUpDown(); - this._uapDebugGroup = new System.Windows.Forms.GroupBox(); + this._uwpDebugGroup = new System.Windows.Forms.GroupBox(); this._toolTip = new System.Windows.Forms.ToolTip(this.components); - _uapRemoteSettingsPanel = new System.Windows.Forms.TableLayoutPanel(); + _uwpRemoteSettingsPanel = new System.Windows.Forms.TableLayoutPanel(); tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - _uapRemoteSettingsPanel.SuspendLayout(); + _uwpRemoteSettingsPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this._remotePort)).BeginInit(); tableLayoutPanel1.SuspendLayout(); - this._uapDebugGroup.SuspendLayout(); + this._uwpDebugGroup.SuspendLayout(); this.SuspendLayout(); // - // _uapRemoteSettingsPanel - // - _uapRemoteSettingsPanel.AutoSize = true; - _uapRemoteSettingsPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - _uapRemoteSettingsPanel.ColumnCount = 2; - _uapRemoteSettingsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - _uapRemoteSettingsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - _uapRemoteSettingsPanel.Controls.Add(this._remoteDeviceLabel, 0, 0); - _uapRemoteSettingsPanel.Controls.Add(this._remoteDevice, 1, 0); - _uapRemoteSettingsPanel.Controls.Add(this._remotePortLabel, 0, 1); - _uapRemoteSettingsPanel.Controls.Add(this._remotePort, 1, 1); - _uapRemoteSettingsPanel.Dock = System.Windows.Forms.DockStyle.Fill; - _uapRemoteSettingsPanel.Location = new System.Drawing.Point(6, 21); - _uapRemoteSettingsPanel.Name = "_uapRemoteSettingsPanel"; - _uapRemoteSettingsPanel.RowCount = 2; - _uapRemoteSettingsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - _uapRemoteSettingsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - _uapRemoteSettingsPanel.Size = new System.Drawing.Size(399, 52); - _uapRemoteSettingsPanel.TabIndex = 0; + // _uwpRemoteSettingsPanel + // + _uwpRemoteSettingsPanel.AutoSize = true; + _uwpRemoteSettingsPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + _uwpRemoteSettingsPanel.ColumnCount = 2; + _uwpRemoteSettingsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + _uwpRemoteSettingsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + _uwpRemoteSettingsPanel.Controls.Add(this._remoteDeviceLabel, 0, 0); + _uwpRemoteSettingsPanel.Controls.Add(this._remoteDevice, 1, 0); + _uwpRemoteSettingsPanel.Controls.Add(this._remotePortLabel, 0, 1); + _uwpRemoteSettingsPanel.Controls.Add(this._remotePort, 1, 1); + _uwpRemoteSettingsPanel.Dock = System.Windows.Forms.DockStyle.Fill; + _uwpRemoteSettingsPanel.Location = new System.Drawing.Point(6, 21); + _uwpRemoteSettingsPanel.Name = "_uwpRemoteSettingsPanel"; + _uwpRemoteSettingsPanel.RowCount = 2; + _uwpRemoteSettingsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + _uwpRemoteSettingsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + _uwpRemoteSettingsPanel.Size = new System.Drawing.Size(399, 52); + _uwpRemoteSettingsPanel.TabIndex = 0; // // _remoteDeviceLabel // @@ -123,7 +123,7 @@ private void InitializeComponent() { tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; tableLayoutPanel1.ColumnCount = 1; tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - tableLayoutPanel1.Controls.Add(this._uapDebugGroup, 0, 0); + tableLayoutPanel1.Controls.Add(this._uwpDebugGroup, 0, 0); tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); @@ -134,22 +134,22 @@ private void InitializeComponent() { tableLayoutPanel1.Size = new System.Drawing.Size(423, 117); tableLayoutPanel1.TabIndex = 0; // - // _uapDebugGroup + // _uwpDebugGroup // - this._uapDebugGroup.AutoSize = true; - this._uapDebugGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this._uapDebugGroup.Controls.Add(_uapRemoteSettingsPanel); - this._uapDebugGroup.Dock = System.Windows.Forms.DockStyle.Fill; - this._uapDebugGroup.Location = new System.Drawing.Point(6, 8); - this._uapDebugGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); - this._uapDebugGroup.Name = "_uapDebugGroup"; - this._uapDebugGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); - this._uapDebugGroup.Size = new System.Drawing.Size(411, 81); - this._uapDebugGroup.TabIndex = 0; - this._uapDebugGroup.TabStop = false; - this._uapDebugGroup.Text = "Debug Settings"; + this._uwpDebugGroup.AutoSize = true; + this._uwpDebugGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._uwpDebugGroup.Controls.Add(_uwpRemoteSettingsPanel); + this._uwpDebugGroup.Dock = System.Windows.Forms.DockStyle.Fill; + this._uwpDebugGroup.Location = new System.Drawing.Point(6, 8); + this._uwpDebugGroup.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uwpDebugGroup.Name = "_uwpDebugGroup"; + this._uwpDebugGroup.Padding = new System.Windows.Forms.Padding(6, 8, 6, 8); + this._uwpDebugGroup.Size = new System.Drawing.Size(411, 81); + this._uwpDebugGroup.TabIndex = 0; + this._uwpDebugGroup.TabStop = false; + this._uwpDebugGroup.Text = "Debug Settings"; // - // PythonUapPropertyPageControl + // PythonUwpPropertyPageControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -157,15 +157,15 @@ private void InitializeComponent() { this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.Controls.Add(tableLayoutPanel1); this.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); - this.Name = "PythonUapPropertyPageControl"; + this.Name = "PythonUwpPropertyPageControl"; this.Size = new System.Drawing.Size(423, 117); - _uapRemoteSettingsPanel.ResumeLayout(false); - _uapRemoteSettingsPanel.PerformLayout(); + _uwpRemoteSettingsPanel.ResumeLayout(false); + _uwpRemoteSettingsPanel.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this._remotePort)).EndInit(); tableLayoutPanel1.ResumeLayout(false); tableLayoutPanel1.PerformLayout(); - this._uapDebugGroup.ResumeLayout(false); - this._uapDebugGroup.PerformLayout(); + this._uwpDebugGroup.ResumeLayout(false); + this._uwpDebugGroup.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -173,7 +173,7 @@ private void InitializeComponent() { #endregion - private System.Windows.Forms.GroupBox _uapDebugGroup; + private System.Windows.Forms.GroupBox _uwpDebugGroup; private System.Windows.Forms.ToolTip _toolTip; private System.Windows.Forms.Label _remoteDeviceLabel; private System.Windows.Forms.NumericUpDown _remotePort; diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs b/Python/Product/Uwp/Project/PythonUwpPropertyPageControl.cs similarity index 72% rename from Python/Product/Uap/Project/PythonUapPropertyPageControl.cs rename to Python/Product/Uwp/Project/PythonUwpPropertyPageControl.cs index a053bf3d1a..07197893bf 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPageControl.cs +++ b/Python/Product/Uwp/Project/PythonUwpPropertyPageControl.cs @@ -15,20 +15,20 @@ using System; using System.Windows.Forms; -namespace Microsoft.PythonTools.Uap.Project { - public partial class PythonUapPropertyPageControl : UserControl { - private readonly PythonUapPropertyPage _properties; +namespace Microsoft.PythonTools.Uwp.Project { + public partial class PythonUwpPropertyPageControl : UserControl { + private readonly PythonUwpPropertyPage _properties; - private PythonUapPropertyPageControl() { + private PythonUwpPropertyPageControl() { InitializeComponent(); - _toolTip.SetToolTip(_remoteDevice, Resources.UapRemoteDeviceHelp); - _toolTip.SetToolTip(_remoteDeviceLabel, Resources.UapRemoteDeviceHelp); - _toolTip.SetToolTip(_remotePort, Resources.UapRemotePortHelp); - _toolTip.SetToolTip(_remotePortLabel, Resources.UapRemotePortHelp); + _toolTip.SetToolTip(_remoteDevice, Resources.UwpRemoteDeviceHelp); + _toolTip.SetToolTip(_remoteDeviceLabel, Resources.UwpRemoteDeviceHelp); + _toolTip.SetToolTip(_remotePort, Resources.UwpRemotePortHelp); + _toolTip.SetToolTip(_remotePortLabel, Resources.UwpRemotePortHelp); } - internal PythonUapPropertyPageControl(PythonUapPropertyPage properties) + internal PythonUwpPropertyPageControl(PythonUwpPropertyPage properties) : this() { _properties = properties; } diff --git a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx b/Python/Product/Uwp/Project/PythonUwpPropertyPageControl.resx similarity index 98% rename from Python/Product/Uap/Project/PythonUapPropertyPageControl.resx rename to Python/Product/Uwp/Project/PythonUwpPropertyPageControl.resx index 0b5f6c5ca7..2cf6ae1709 100644 --- a/Python/Product/Uap/Project/PythonUapPropertyPageControl.resx +++ b/Python/Product/Uwp/Project/PythonUwpPropertyPageControl.resx @@ -117,7 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + False diff --git a/Python/Product/Uap/Properties/AssemblyInfo.cs b/Python/Product/Uwp/Properties/AssemblyInfo.cs similarity index 95% rename from Python/Product/Uap/Properties/AssemblyInfo.cs rename to Python/Product/Uwp/Properties/AssemblyInfo.cs index 0e203b3dd7..5a1a86e587 100644 --- a/Python/Product/Uap/Properties/AssemblyInfo.cs +++ b/Python/Product/Uwp/Properties/AssemblyInfo.cs @@ -21,7 +21,7 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Python Tools for Visual Studio - UAP Integration")] +[assembly: AssemblyTitle("Python Tools for Visual Studio - UWP Integration")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: ComVisible(false)] diff --git a/Python/Product/Uap/PythonUapPackage.cs b/Python/Product/Uwp/PythonUwpPackage.cs similarity index 85% rename from Python/Product/Uap/PythonUapPackage.cs rename to Python/Product/Uwp/PythonUwpPackage.cs index 50cf58e2a5..277102ec67 100644 --- a/Python/Product/Uap/PythonUapPackage.cs +++ b/Python/Product/Uwp/PythonUwpPackage.cs @@ -20,14 +20,14 @@ using System.Runtime.InteropServices; using EnvDTE90a; using Microsoft.PythonTools.Debugger.DebugEngine; -using Microsoft.PythonTools.Uap.Interpreter; -using Microsoft.PythonTools.Uap.Project; +using Microsoft.PythonTools.Uwp.Interpreter; +using Microsoft.PythonTools.Uwp.Project; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TextManager.Interop; -namespace Microsoft.PythonTools.Uap { +namespace Microsoft.PythonTools.Uwp { /// /// This is the class that implements the package exposed by this assembly. /// @@ -43,16 +43,16 @@ namespace Microsoft.PythonTools.Uap { [PackageRegistration(UseManagedResourcesOnly = true)] // This attribute is needed to let the shell know that this package exposes some menus. - [Guid(GuidList.guidUapPkgString)] + [Guid(GuidList.guidUwpPkgString)] - [ProvideObject(typeof(PythonUapPropertyPage))] - [ProvideObject(typeof(PythonUapProject))] + [ProvideObject(typeof(PythonUwpPropertyPage))] + [ProvideObject(typeof(PythonUwpProject))] [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasAppContainerProject_string)] - [Description("Python Tools UAP Interpreter")] - [ProvideProjectFactory(typeof(PythonUapProjectFactory), null, null, null, null, ".\\NullPath", LanguageVsTemplate = PythonConstants.LanguageName)] - [ProvidePythonInterpreterFactoryProvider(PythonUapInterpreterFactory.InterpreterGuidString, typeof(PythonUapInterpreterFactoryProvider))] - public sealed class PythonUapPackage : Package { - internal static PythonUapPackage Instance; + [Description("Python Tools Uwp Interpreter")] + [ProvideProjectFactory(typeof(PythonUwpProjectFactory), null, null, null, null, ".\\NullPath", LanguageVsTemplate = PythonConstants.LanguageName)] + [ProvidePythonInterpreterFactoryProvider(PythonUwpInterpreterFactory.InterpreterGuidString, typeof(PythonUwpInterpreterFactoryProvider))] + public sealed class PythonUwpPackage : Package { + internal static PythonUwpPackage Instance; /// /// Default constructor of the package. @@ -61,7 +61,7 @@ public sealed class PythonUapPackage : Package { /// not sited yet inside Visual Studio environment. The place to do all the other /// initialization is the Initialize method. /// - public PythonUapPackage() { + public PythonUwpPackage() { Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this)); Instance = this; } @@ -78,7 +78,7 @@ protected override void Initialize() { Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); base.Initialize(); - RegisterProjectFactory(new PythonUapProjectFactory(this)); + RegisterProjectFactory(new PythonUwpProjectFactory(this)); } #endregion @@ -93,7 +93,7 @@ public EnvDTE.DTE DTE { } } - internal static PythonUapProject GetProject(IServiceProvider serviceProvider, string filename) { + internal static PythonUwpProject GetProject(IServiceProvider serviceProvider, string filename) { IVsHierarchy hierarchy; IVsRunningDocumentTable rdt = serviceProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; uint itemid; @@ -110,7 +110,7 @@ internal static PythonUapProject GetProject(IServiceProvider serviceProvider, st if (ErrorHandler.Succeeded(hr)) { rdt.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, cookie); } - var res = hierarchy as PythonUapProject; + var res = hierarchy as PythonUwpProject; if (res != null) { return res; } diff --git a/Python/Product/Uap/Resources.Designer.cs b/Python/Product/Uwp/Resources.Designer.cs similarity index 86% rename from Python/Product/Uap/Resources.Designer.cs rename to Python/Product/Uwp/Resources.Designer.cs index 5d3d3e81c3..09c4e5e444 100644 --- a/Python/Product/Uap/Resources.Designer.cs +++ b/Python/Product/Uwp/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.PythonTools.Uap { +namespace Microsoft.PythonTools.Uwp { using System; @@ -39,7 +39,7 @@ internal Resources() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PythonTools.Uap.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PythonTools.Uwp.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -61,29 +61,29 @@ internal Resources() { } /// - /// Looks up a localized string similar to UAP Project Settings. + /// Looks up a localized string similar to UWP Project Settings. /// - internal static string UapPropertyPageTitle { + internal static string UwpPropertyPageTitle { get { - return ResourceManager.GetString("UapPropertyPageTitle", resourceCulture); + return ResourceManager.GetString("UwpPropertyPageTitle", resourceCulture); } } /// /// Looks up a localized string similar to Remote device name or IP address. /// - internal static string UapRemoteDeviceHelp { + internal static string UwpRemoteDeviceHelp { get { - return ResourceManager.GetString("UapRemoteDeviceHelp", resourceCulture); + return ResourceManager.GetString("UwpRemoteDeviceHelp", resourceCulture); } } /// /// Looks up a localized string similar to Remote debug port to use for Python debugger. /// - internal static string UapRemotePortHelp { + internal static string UwpRemotePortHelp { get { - return ResourceManager.GetString("UapRemotePortHelp", resourceCulture); + return ResourceManager.GetString("UwpRemotePortHelp", resourceCulture); } } } diff --git a/Python/Product/Uap/Resources.resx b/Python/Product/Uwp/Resources.resx similarity index 96% rename from Python/Product/Uap/Resources.resx rename to Python/Product/Uwp/Resources.resx index 53fa505ec8..897ff0146f 100644 --- a/Python/Product/Uap/Resources.resx +++ b/Python/Product/Uwp/Resources.resx @@ -117,13 +117,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - UAP Project Settings + + UWP Project Settings - + Remote device name or IP address - + Remote debug port to use for Python debugger \ No newline at end of file diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx b/Python/Product/Uwp/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx similarity index 100% rename from Python/Product/Uap/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx rename to Python/Product/Uwp/Templates/Projects/BackgroundService/Application_TemporaryKey.pfx diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest b/Python/Product/Uwp/Templates/Projects/BackgroundService/Package.appxmanifest similarity index 83% rename from Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest rename to Python/Product/Uwp/Templates/Projects/BackgroundService/Package.appxmanifest index e8175443d1..562bb40923 100644 --- a/Python/Product/Uap/Templates/Projects/BackgroundService/Package.appxmanifest +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/Package.appxmanifest @@ -3,9 +3,9 @@ + IgnorableNamespaces="uwp mp iot"> - - - + + - + @@ -59,8 +59,8 @@ - pyuapbackgroundservice.dll - + pyuwpbackgroundservice.dll + diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj similarity index 91% rename from Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj rename to Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj index 773e701a54..a96000f480 100644 --- a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj @@ -44,15 +44,15 @@ - $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets - $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUAP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UWP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUWP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets 14.0 2.2 - $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets - $(MSBuildProgramFiles32)\Microsoft Visual Studio $(VisualStudioVersion)\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio - UAP\$(PtvsVersion)\Microsoft.PythonTools.Uap.targets + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python Tools for Visual Studio - UWP\$(PtvsVersion)\Microsoft.PythonTools.Uwp.targets + $(MSBuildProgramFiles32)\Microsoft Visual Studio $(VisualStudioVersion)\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio - UWP\$(PtvsVersion)\Microsoft.PythonTools.Uwp.targets diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate similarity index 96% rename from Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate rename to Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate index 406eb323ac..c7b3c9e202 100644 --- a/Python/Product/Uap/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate @@ -7,7 +7,7 @@ 20 2 true - Microsoft.Ptvs.WinRT.UAP.IOT.BackgroundApplication + Microsoft.Ptvs.WinRT.UWP.IOT.BackgroundApplication BackgroundApplication true Windows diff --git a/Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py b/Python/Product/Uwp/Templates/Projects/BackgroundService/StartupTask.py similarity index 100% rename from Python/Product/Uap/Templates/Projects/BackgroundService/StartupTask.py rename to Python/Product/Uwp/Templates/Projects/BackgroundService/StartupTask.py diff --git a/Python/Product/Uap/Uap.csproj b/Python/Product/Uwp/Uwp.csproj similarity index 94% rename from Python/Product/Uap/Uap.csproj rename to Python/Product/Uwp/Uwp.csproj index 3baed11995..d2350a6b6d 100644 --- a/Python/Product/Uap/Uap.csproj +++ b/Python/Product/Uwp/Uwp.csproj @@ -31,8 +31,8 @@ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties - Microsoft.PythonTools.Uap - Microsoft.PythonTools.Uap + Microsoft.PythonTools.Uwp + Microsoft.PythonTools.Uwp true SAK SAK @@ -198,21 +198,21 @@ - - - - - - - - + + + + + + + + usercontrol - - PythonUapPropertyPageControl.cs + + PythonUwpPropertyPageControl.cs - + True True @@ -238,7 +238,7 @@ - + true PreserveNewest Designer @@ -252,8 +252,8 @@ - - PythonUapPropertyPageControl.cs + + PythonUwpPropertyPageControl.cs ResXFileCodeGenerator diff --git a/Python/Product/Uap/VSPackage.resx b/Python/Product/Uwp/VSPackage.resx similarity index 97% rename from Python/Product/Uap/VSPackage.resx rename to Python/Product/Uwp/VSPackage.resx index 25ba4c515b..0782e37a03 100644 --- a/Python/Product/Uap/VSPackage.resx +++ b/Python/Product/Uwp/VSPackage.resx @@ -118,9 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Python Tools for Visual Studio - UAP Integration + Python Tools for Visual Studio - UWP Integration - Provides templates and integration for the UAP framework. + Provides templates and integration for the UWP framework. \ No newline at end of file diff --git a/Python/Product/Uap/source.extension.vsixmanifest b/Python/Product/Uwp/source.extension.vsixmanifest similarity index 95% rename from Python/Product/Uap/source.extension.vsixmanifest rename to Python/Product/Uwp/source.extension.vsixmanifest index b42b5127d1..6b9b6df1db 100644 --- a/Python/Product/Uap/source.extension.vsixmanifest +++ b/Python/Product/Uwp/source.extension.vsixmanifest @@ -1,10 +1,10 @@  - Python Tools for Visual Studio - UAP + Python Tools for Visual Studio - UWP Microsoft 2.2 - Provides templates and integration for the UAP framework. + Provides templates and integration for the UWP framework. http://pytools.codeplex.com http://pytools.codeplex.com PythonProject.ico diff --git a/Python/PythonTools.sln b/Python/PythonTools.sln index 6203b53bbce35a1f1aeba196dd2d33db7ab4eeeb..601babe4223a5768a61cb305fbfa74c443f8142f 100644 GIT binary patch delta 91 zcmX?hg7?@7-VL{6naddpCJXXQa)&YiSqw1@K)RVRuAMQC5r~<#GsZE$XW@lNfh8FV irY9OOM^BGPX3k(NpMEfz*%4&MbWRp#?db+7%whoanHi!0 delta 91 zcmX?hg7?@7-VL{6nG+cbCJXXQa)&YiSqw1@K)RVRuAMQC5r~<#GsZE$XW@lNfh8FV irY9OOM^BGPX3k(toPIEw*%4&MbWRp#?db+7%who8l^F;C diff --git a/Python/Setup/BuildRelease.ps1 b/Python/Setup/BuildRelease.ps1 index 2cc4b5b279..0c6292331c 100644 --- a/Python/Setup/BuildRelease.ps1 +++ b/Python/Setup/BuildRelease.ps1 @@ -261,7 +261,7 @@ $managed_files = ( "Microsoft.PythonTools.WebRole.dll", "Microsoft.PythonTools.Django.dll", "Microsoft.PythonTools.VsLogger.dll", - "Microsoft.PythonTools.Uap.dll", + "Microsoft.PythonTools.Uwp.dll", "Microsoft.PythonTools.AzureSetup.exe", "Microsoft.IronPythonTools.Resolver.dll" ) diff --git a/Python/Setup/MergeModule.wxi b/Python/Setup/MergeModule.wxi index 19a5beaa17..2cbeab0b78 100644 --- a/Python/Setup/MergeModule.wxi +++ b/Python/Setup/MergeModule.wxi @@ -48,7 +48,7 @@ - + diff --git a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wixproj b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wixproj index 0266a2265e..72fdf85814 100644 --- a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wixproj +++ b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wixproj @@ -12,7 +12,7 @@ $(DefineConstants); IncludeVsLogger=$(IncludeVsLogger); IncludeHpc=$(IncludeHpc); - IncludeUap=$(IncludeUap); + IncludeUwp=$(IncludeUwp); $(DefineConstants); @@ -86,8 +86,8 @@ DjangoMsm {16671BE6-DD23-41D9-841A-0B80D47A090D} - - UapMsm + + UwpMsm {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} diff --git a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs index e9c9a3ede2..390ba4a706 100644 --- a/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs +++ b/Python/Setup/PythonToolsInstaller/PythonToolsInstaller.wxs @@ -197,8 +197,8 @@ - - + + @@ -261,9 +261,9 @@ - - - + + + diff --git a/Python/Setup/Uap/Uap.wixproj b/Python/Setup/Uwp/Uwp.wixproj similarity index 76% rename from Python/Setup/Uap/Uap.wixproj rename to Python/Setup/Uwp/Uwp.wixproj index 5213c9c543..09ad5fa127 100644 --- a/Python/Setup/Uap/Uap.wixproj +++ b/Python/Setup/Uwp/Uwp.wixproj @@ -5,21 +5,21 @@ 3.5 {83946E81-7A1B-4D3B-927F-4CD67AC95BE7} 2.0 - Uap + Uwp Module false - $(DefineConstants);ProductSuffix=Uap + $(DefineConstants);ProductSuffix=Uwp SAK SAK SAK SAK - + - - Microsoft.PythonTools.Uap + + Microsoft.PythonTools.Uwp {D28045D6-232C-4101-A5CB-AC4A68E92211} @@ -27,7 +27,7 @@ MergeModule.wxi - + \ No newline at end of file diff --git a/Python/Setup/Uap/Uap.wxs b/Python/Setup/Uwp/Uwp.wxs similarity index 90% rename from Python/Setup/Uap/Uap.wxs rename to Python/Setup/Uwp/Uwp.wxs index d65abdcad1..af2426b816 100644 --- a/Python/Setup/Uap/Uap.wxs +++ b/Python/Setup/Uwp/Uwp.wxs @@ -1,6 +1,6 @@ - + @@ -15,6 +15,6 @@ - + diff --git a/Python/Setup/Uap/UapFiles.proj b/Python/Setup/Uwp/UwpFiles.proj similarity index 71% rename from Python/Setup/Uap/UapFiles.proj rename to Python/Setup/Uwp/UwpFiles.proj index 4060e61055..91bace6e8b 100644 --- a/Python/Setup/Uap/UapFiles.proj +++ b/Python/Setup/Uwp/UwpFiles.proj @@ -2,7 +2,7 @@ - UapFiles + UwpFiles @@ -16,16 +16,16 @@ - - + + - + - + - UapProjectTemplates + UwpProjectTemplates diff --git a/Python/Setup/setup.proj b/Python/Setup/setup.proj index 80bf0fc743..b241889ce3 100644 --- a/Python/Setup/setup.proj +++ b/Python/Setup/setup.proj @@ -17,7 +17,7 @@ - + diff --git a/Python/products.settings b/Python/products.settings index 564ffddf21..5ffc66a868 100644 --- a/Python/products.settings +++ b/Python/products.settings @@ -1,8 +1,8 @@ - - true - false + + true + false false From 5d860aef684888653ae3f878c799a8ac6e6a80a6 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Thu, 7 May 2015 23:51:26 -0700 Subject: [PATCH 08/30] Removing XML for JSON for debug params via exception handler --- .../Uwp/Debugger/PythonRemoteDebugEvents.cs | 25 +++++++++---------- .../Uwp/Project/PythonUwpProjectConfig.cs | 17 ++++++------- Python/Product/Uwp/Uwp.csproj | 1 + 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs index d629ee5b3d..34d2691278 100644 --- a/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs +++ b/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs @@ -13,7 +13,6 @@ * ***************************************************************************/ using System; -using System.Text; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; @@ -40,7 +39,7 @@ public static PythonRemoteDebugEvents Instance { public Func AttachRemoteProcessFunction { get; set; } - public string AttachRemoteDebugXml { get; set; } + public byte[] RemoteDebugCommandInfo { get; set; } public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { if (riidEvent == typeof(IDebugProgramCreateEvent2).GUID) { @@ -65,31 +64,31 @@ public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 } void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTriggerHit hit, DkmEventDescriptorS eventDescriptor) { - var remoteProcessTask = default(System.Threading.Tasks.Task); - ThreadHelper.Generic.Invoke(() => { var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration // of the debugger - const int exceptionParameterCount = 2; + const int exceptionParameterCount = 3; if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { // If we have a port and debug id, we'll go ahead and tell the client we are present - if (Instance.AttachRemoteProcessFunction != null && Instance.AttachRemoteDebugXml != null) { - // Write back that debugger is present - hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); + if (Instance.AttachRemoteProcessFunction != null && Instance.RemoteDebugCommandInfo != null) { + if (Instance.RemoteDebugCommandInfo.Length <= (int)exceptionInfo.ExceptionParameters[2]) { + // Write back that debugger is present + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); + + // Write back the details of the debugger arguments + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Instance.RemoteDebugCommandInfo); - // Write back the details of the debugger arguments - hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Encoding.Unicode.GetBytes(Instance.AttachRemoteDebugXml)); + // Start the task to attach to the remote Python debugger session + System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); + } } } }); eventDescriptor.Suppress(); - - // Start the task to attach to the remote Python debugger session - remoteProcessTask = System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); } public int OnModeChange(DBGMODE dbgmodeNew) { diff --git a/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs b/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs index c6da2e7f88..0c68915085 100644 --- a/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs +++ b/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs @@ -18,9 +18,10 @@ using System.Globalization; using System.IO; using System.Runtime.InteropServices; +using System.Text; using System.Web; +using System.Web.Script.Serialization; using System.Windows.Forms; -using System.Xml.Linq; using Microsoft.PythonTools.Debugger.DebugEngine; using Microsoft.PythonTools.Debugger.Remote; using Microsoft.VisualStudio; @@ -448,13 +449,10 @@ public int DebugLaunch(uint grfLaunch) { var targetDir = Path.GetFullPath(this.LayoutDir).Trim('\\'); var debugPort = PythonConfig.GetProjectProperty("RemoteDebugPort") ?? DefaultRemoteDebugPort; var debugId = Guid.NewGuid(); - var debugXml = new XDocument(new XElement("dbg", - new XElement("arg", @"visualstudio_py_remote_launcher.py"), - new XElement("arg", debugPort), - new XElement("arg", debugId), - new XElement("arg", "--redirect-output"))); + var serializer = new JavaScriptSerializer(); + var debugCmdJson = serializer.Serialize(new string[] { "visualstudio_py_remote_launcher.py", debugPort.ToString(), debugId.ToString(), "--redirect-output" }); - Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteDebugXml = debugXml.ToString(); + Debugger.PythonRemoteDebugEvents.Instance.RemoteDebugCommandInfo = Encoding.Unicode.GetBytes(debugCmdJson); Debugger.PythonRemoteDebugEvents.Instance.AttachRemoteProcessFunction = () => { return RemoteProcessAttachAsync( appPackageDebugTarget[0].bstrRemoteMachine, @@ -606,7 +604,7 @@ public int StartDeploy(IVsOutputWindowPane pIVsOutputWindowPane, uint dwOptions) try { VsDebugTargetInfo2[] targets; - uint deployFlags = (uint)(_AppContainerDeployOptions.ACDO_NetworkLoopbackEnable | _AppContainerDeployOptions.ACDO_SetNetworkLoopback | _AppContainerDeployOptions.ACDO_ForceCleanLayout); + uint deployFlags = (uint)(_AppContainerDeployOptions.ACDO_NetworkLoopbackEnable | _AppContainerDeployOptions.ACDO_SetNetworkLoopback); string recipeFile = null; string layoutDir = null; var pythonProject = PythonConfig; @@ -622,7 +620,6 @@ public int StartDeploy(IVsOutputWindowPane pIVsOutputWindowPane, uint dwOptions) get_CanonicalName(out canonicalName); bps.GetPropertyValue("AppxPackageRecipe", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out recipeFile); - bps.GetPropertyValue("LayoutDir", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out layoutDir); string projectUniqueName = null; IVsSolution vsSolution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution; @@ -632,6 +629,8 @@ public int StartDeploy(IVsOutputWindowPane pIVsOutputWindowPane, uint dwOptions) IVsAppContainerProjectDeploy deployHelper = (IVsAppContainerProjectDeploy)Package.GetGlobalService(typeof(SVsAppContainerProjectDeploy)); if (String.IsNullOrEmpty(targets[0].bstrRemoteMachine)) { + bps.GetPropertyValue("LayoutDir", canonicalName, (uint)_PersistStorageType.PST_PROJECT_FILE, out layoutDir); + deployOp = deployHelper.StartDeployAsync(deployFlags, recipeFile, layoutDir, projectUniqueName, this); } else { IVsDebuggerDeploy deploy = (IVsDebuggerDeploy)Package.GetGlobalService(typeof(SVsShellDebugger)); diff --git a/Python/Product/Uwp/Uwp.csproj b/Python/Product/Uwp/Uwp.csproj index d2350a6b6d..b344158db8 100644 --- a/Python/Product/Uwp/Uwp.csproj +++ b/Python/Product/Uwp/Uwp.csproj @@ -98,6 +98,7 @@ + From ef08303cc53549fa2f95dbd6cbb56244be2c9afa Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Mon, 11 May 2015 12:38:54 -0700 Subject: [PATCH 09/30] Moved logic from Uwp CPython SDK targets to Uwp targets when referencing PTVSD Cleared up slow connection on PTVSD servers when DNS name specified Moved remote launcher script to Uwp project --- .../Debugger/Transports/TcpTransport.cs | 7 +- Python/Product/PythonTools/PythonTools.csproj | 4 - .../Uwp/Debugger/PythonRemoteDebugEvents.cs | 39 +++++-- .../RemoteDebugServerComponent.vsdconfigxml | 2 +- .../Uwp/Microsoft.PythonTools.Uwp.targets | 104 ++++++++++++++++++ .../Uwp/Project/PythonUwpProjectConfig.cs | 8 +- .../PythonBackgroundService.pyproj | 4 +- Python/Product/Uwp/Uwp.csproj | 4 + .../visualstudio_py_remote_launcher.py | 10 +- 9 files changed, 157 insertions(+), 25 deletions(-) rename Python/Product/{PythonTools => Uwp}/visualstudio_py_remote_launcher.py (89%) diff --git a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs index e1fba47486..5bde5bd3d0 100644 --- a/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs +++ b/Python/Product/Debugger/Debugger/Transports/TcpTransport.cs @@ -14,6 +14,8 @@ using System; using System.IO; +using System.Linq; +using System.Net; using System.Net.Sockets; namespace Microsoft.PythonTools.Debugger.Transports { @@ -32,7 +34,10 @@ public virtual Stream Connect(Uri uri, bool requireAuthentication) { uri = new UriBuilder(uri) { Port = DefaultPort }.Uri; } - var tcpClient = new TcpClient(uri.Host, uri.Port); + // PTVSD is using AF_INET by default, so lets make sure to try the IPv4 address in lieu of IPv6 address + var ipHostEntry = Dns.GetHostEntry(uri.Host); + var tcpClient = new TcpClient(ipHostEntry.AddressList.First(i => i.AddressFamily == AddressFamily.InterNetwork).ToString(), uri.Port); + try { var stream = tcpClient.GetStream(); tcpClient = null; diff --git a/Python/Product/PythonTools/PythonTools.csproj b/Python/Product/PythonTools/PythonTools.csproj index 3e7ec34a19..f99d53b296 100644 --- a/Python/Product/PythonTools/PythonTools.csproj +++ b/Python/Product/PythonTools/PythonTools.csproj @@ -1153,10 +1153,6 @@ true PreserveNewest - - true - PreserveNewest - diff --git a/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs b/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs index 34d2691278..1dc49c2829 100644 --- a/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs +++ b/Python/Product/Uwp/Debugger/PythonRemoteDebugEvents.cs @@ -29,6 +29,7 @@ internal class PythonRemoteDebugEvents : IVsDebuggerEvents, IDebugEventCallback2 // i.e. whatever remote process is launching the debug server, should throw and catch this exception code before starting // remote Python debug server to have automatic attach work internal const uint RemoteDebugStartExceptionCode = 0xEDCBA987; + internal const uint RemoteDebugAttachExceptionCode = 0xEDCBA988; public const string RemoteDebugExceptionId = "E7DD0845-FB1A-4A45-8192-44953C0ACC51"; public static readonly Guid RemoteDebugExceptionGuid = new Guid(RemoteDebugExceptionId); @@ -52,9 +53,11 @@ public int Event(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 if (dkmProcess != null) { var debugTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugStartExceptionCode); + var attachTrigger = DkmExceptionCodeTrigger.Create(DkmExceptionProcessingStage.Thrown, null, DkmExceptionCategory.Win32, RemoteDebugAttachExceptionCode); // Try to add exception trigger for when a remote debugger server is started for Python dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, debugTrigger); + dkmProcess.AddExceptionTrigger(RemoteDebugExceptionGuid, attachTrigger); } } } @@ -67,20 +70,34 @@ void IDkmExceptionTriggerHitNotification.OnExceptionTriggerHit(DkmExceptionTrigg ThreadHelper.Generic.Invoke(() => { var exceptionInfo = hit.Exception as VisualStudio.Debugger.Native.DkmWin32ExceptionInformation; - // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration - // of the debugger - const int exceptionParameterCount = 3; - - if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { - // If we have a port and debug id, we'll go ahead and tell the client we are present - if (Instance.AttachRemoteProcessFunction != null && Instance.RemoteDebugCommandInfo != null) { - if (Instance.RemoteDebugCommandInfo.Length <= (int)exceptionInfo.ExceptionParameters[2]) { + if (exceptionInfo.Code == RemoteDebugStartExceptionCode) { + // Parameters expected are the flag to indicate the debugger is present and JSON to write to the target for configuration + // of the debugger + const int exceptionParameterCount = 3; + + if (exceptionInfo.ExceptionParameters.Count == exceptionParameterCount) { + // If we have a port and debug id, we'll go ahead and tell the client we are present + if (Instance.AttachRemoteProcessFunction != null && Instance.RemoteDebugCommandInfo != null) { + if (Instance.RemoteDebugCommandInfo.Length <= (int)exceptionInfo.ExceptionParameters[2]) { + // Write back that debugger is present + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); + + // Write back the details of the debugger arguments + hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Instance.RemoteDebugCommandInfo); + } + } + } + } else if (exceptionInfo.Code == RemoteDebugAttachExceptionCode) { + // Parameters expected are the flag to indicate the debugger is present and XML to write to the target for configuration + // of the debugger + const int exceptionAttachParameterCount = 1; + + if (exceptionInfo.ExceptionParameters.Count == exceptionAttachParameterCount) { + // If we have a port and debug id, we'll go ahead and tell the client we are present + if (Instance.AttachRemoteProcessFunction != null) { // Write back that debugger is present hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[0], BitConverter.GetBytes(true)); - // Write back the details of the debugger arguments - hit.Process.WriteMemory(exceptionInfo.ExceptionParameters[1], Instance.RemoteDebugCommandInfo); - // Start the task to attach to the remote Python debugger session System.Threading.Tasks.Task.Factory.StartNew(Instance.AttachRemoteProcessFunction); } diff --git a/Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml b/Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml index ba112d0d49..557d98144c 100644 --- a/Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml +++ b/Python/Product/Uwp/Debugger/RemoteDebugServerComponent.vsdconfigxml @@ -18,7 +18,7 @@ diff --git a/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets b/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets index 5480c6c45d..eef253a815 100644 --- a/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets +++ b/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets @@ -7,8 +7,112 @@ $(MSBuildProjectDirectory) $([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(ProjectHome))))) $(QualifiedProjectHome)\ + + $(MSBuildThisFileDirectory)..\..\Python Tools for Visual Studio\$(PtvsVersion)\ + $(MSBuildThisFileDirectory)..\..\Python Tools for Visual Studio\$(PtvsVersion)\ + + $(StartupFile) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BuiltProjectOutputGroupFast; diff --git a/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs b/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs index 0c68915085..f8f4d8a979 100644 --- a/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs +++ b/Python/Product/Uwp/Project/PythonUwpProjectConfig.cs @@ -114,7 +114,7 @@ private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remote HttpUtility.UrlEncode(AD7Engine.TargetUwp)); var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); - var debugger = (EnvDTE90.Debugger3)dte.Debugger; + var debugger = (EnvDTE100.Debugger5)dte.Debugger; var transport = default(EnvDTE80.Transport); var transports = debugger.Transports; @@ -124,6 +124,7 @@ private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remote Guid tid; if (Guid.TryParse(t.ID, out tid) && tid == PythonRemoteDebugPortSupplier.PortSupplierGuid) { transport = t; + break; } } @@ -148,10 +149,9 @@ private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remote var processes = debugger.GetProcesses(transport, qualifierString); if (processes.Count > 0) { - foreach (EnvDTE.Process process in processes) { + foreach (EnvDTE90.Process3 process in processes) { process.Attach(); attached = true; - System.Diagnostics.Debug.WriteLine("Successfully attached to a python remote process"); break; } @@ -168,7 +168,7 @@ private async System.Threading.Tasks.Task RemoteProcessAttachAsync(string remote } catch (COMException comException) { // In the case where the debug client does not setup the PTVSD server in time for the Attach to work, we will // get this exception. We retry a few times to ensure client has time to start the Python debug server - System.Diagnostics.Debug.WriteLine("Failure during attach to remote Python process:\r\n{0}", comException); + System.Diagnostics.Debug.WriteLine("Non-fatal failure during attach to remote Python process:\r\n{0}", comException); } await System.Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(100)); diff --git a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj index a96000f480..c51fca841c 100644 --- a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj @@ -44,8 +44,8 @@ - $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UWP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets - $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUWP\3.5\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UWP\$(InterpreterVersion)\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUWP\$(InterpreterVersion)\DesignTime\CommonConfiguration\Neutral\CPython.targets diff --git a/Python/Product/Uwp/Uwp.csproj b/Python/Product/Uwp/Uwp.csproj index b344158db8..36b3583551 100644 --- a/Python/Product/Uwp/Uwp.csproj +++ b/Python/Product/Uwp/Uwp.csproj @@ -244,6 +244,10 @@ PreserveNewest Designer + + PreserveNewest + true + Designer diff --git a/Python/Product/PythonTools/visualstudio_py_remote_launcher.py b/Python/Product/Uwp/visualstudio_py_remote_launcher.py similarity index 89% rename from Python/Product/PythonTools/visualstudio_py_remote_launcher.py rename to Python/Product/Uwp/visualstudio_py_remote_launcher.py index 1ded935b52..23d4bbfeac 100644 --- a/Python/Product/PythonTools/visualstudio_py_remote_launcher.py +++ b/Python/Product/Uwp/visualstudio_py_remote_launcher.py @@ -37,10 +37,16 @@ def debug_remote( BREAK_ON_SYSTEMEXIT_ZERO = break_on_systemexit_zero DEBUG_STDLIB = debug_stdlib - print('Remote launcher starting ptvsd attach wait with File: %s, Port: %d, Id: %s\n' % (file, port_num, debug_id)) + import datetime + print('%s: Remote launcher starting ptvsd attach wait with File: %s, Port: %d, Id: %s\n' % (datetime.datetime.now(), file, port_num, debug_id)) ptvsd.enable_attach(debug_id, address = ('0.0.0.0', port_num), redirect_output = redirect_output) - ptvsd.wait_for_attach() + try: + import _ptvsdhelper + if _ptvsdhelper.ping_debugger_for_attach(): + ptvsd.wait_for_attach() + except ImportError: + _ptvsdhelper = None # now execute main file globals_obj = {'__name__': '__main__'} From 5ed5c16b964b9eae69f923d6c276c30859966762 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Mon, 11 May 2015 13:26:21 -0700 Subject: [PATCH 10/30] Moving visualstudio_py_remote_launcher.py in setup files from PythonTools to Uwp project --- Python/Setup/PythonTools/PythonToolsFiles.proj | 1 - Python/Setup/Uwp/UwpFiles.proj | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Setup/PythonTools/PythonToolsFiles.proj b/Python/Setup/PythonTools/PythonToolsFiles.proj index fa819f1fe9..a76c0a51cd 100644 --- a/Python/Setup/PythonTools/PythonToolsFiles.proj +++ b/Python/Setup/PythonTools/PythonToolsFiles.proj @@ -44,7 +44,6 @@ !(bindpath.bin)\PyDebugAttachX86.dll; visualstudio_py_repl.py; visualstudio_ipython_repl.py; - visualstudio_py_remote_launcher.py; visualstudio_py_launcher.py; visualstudio_py_debugger.py; visualstudio_py_util.py" /> diff --git a/Python/Setup/Uwp/UwpFiles.proj b/Python/Setup/Uwp/UwpFiles.proj index 91bace6e8b..cd8b51000c 100644 --- a/Python/Setup/Uwp/UwpFiles.proj +++ b/Python/Setup/Uwp/UwpFiles.proj @@ -21,6 +21,7 @@ + From 515bd123dcf83f9369365876be8dfba78c7c0448 Mon Sep 17 00:00:00 2001 From: int19h Date: Tue, 12 May 2015 17:40:19 -0700 Subject: [PATCH 11/30] Fix #133: Debugger doesn't handle sys.prefix=='' well Enable stdlib debugging (overriding the default and the user settings) in that case, since we can't reliably distinguish between stdlib and user code. --- Python/Product/PythonTools/visualstudio_py_debugger.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Python/Product/PythonTools/visualstudio_py_debugger.py b/Python/Product/PythonTools/visualstudio_py_debugger.py index 5470c07cd9..efa2716f6f 100644 --- a/Python/Product/PythonTools/visualstudio_py_debugger.py +++ b/Python/Product/PythonTools/visualstudio_py_debugger.py @@ -527,6 +527,10 @@ def probe_stack(depth = 10): PREFIXES.append(path.normcase(sys.base_prefix)) if hasattr(sys, 'real_prefix'): PREFIXES.append(path.normcase(sys.real_prefix)) +# If one or more of the prefixes are empty, we can't reliably distinguish stdlib +# from user code, so override stdlib-only mode and allow to debug everything. +if '' in PREFIXES: + DEBUG_STDLIB = True def should_debug_code(code): if not code or not code.co_filename: @@ -2391,7 +2395,8 @@ def debug( global BREAK_ON_SYSTEMEXIT_ZERO, DEBUG_STDLIB, DJANGO_DEBUG BREAK_ON_SYSTEMEXIT_ZERO = break_on_systemexit_zero - DEBUG_STDLIB = debug_stdlib + if not DEBUG_STDLIB: + DEBUG_STDLIB = debug_stdlib DJANGO_DEBUG = django_debugging def _excepthook(exc_type, exc_value, exc_tb): From 4cd2b8f8ee71d07c3d49149ebe746b486ffcb1fa Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 13 May 2015 11:21:48 -0700 Subject: [PATCH 12/30] Removes UWP source control bindings. --- Python/Product/Uwp/Uwp.csproj | 4 ---- Python/PythonTools.sln | Bin 222278 -> 197706 bytes Python/Setup/Uwp/Uwp.wixproj | 4 ---- 3 files changed, 8 deletions(-) diff --git a/Python/Product/Uwp/Uwp.csproj b/Python/Product/Uwp/Uwp.csproj index 36b3583551..ccf63bee82 100644 --- a/Python/Product/Uwp/Uwp.csproj +++ b/Python/Product/Uwp/Uwp.csproj @@ -34,10 +34,6 @@ Microsoft.PythonTools.Uwp Microsoft.PythonTools.Uwp true - SAK - SAK - SAK - SAK 1762 diff --git a/Python/PythonTools.sln b/Python/PythonTools.sln index 601babe4223a5768a61cb305fbfa74c443f8142f..86e39e2229937e925a86daa15750498db0e6a3d2 100644 GIT binary patch delta 20 bcmX?hg7;JhPeTi13)2>66PD=)Da>L3R#yhN literal 222278 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 diff --git a/Python/Setup/Uwp/Uwp.wixproj b/Python/Setup/Uwp/Uwp.wixproj index 09ad5fa127..b60b97c5f1 100644 --- a/Python/Setup/Uwp/Uwp.wixproj +++ b/Python/Setup/Uwp/Uwp.wixproj @@ -9,10 +9,6 @@ Module false $(DefineConstants);ProductSuffix=Uwp - SAK - SAK - SAK - SAK From 93a55abd96b83842eeeda2ed528cdd5b04a4e5b3 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 13 May 2015 13:42:53 -0700 Subject: [PATCH 13/30] Updates skip verification scripts for UWP. --- .../Prerequisites/DisableSkipVerification.reg | Bin 27422 -> 27882 bytes .../DisableSkipVerificationX86.reg | Bin 13032 -> 13250 bytes .../Prerequisites/EnableSkipVerification.reg | Bin 27182 -> 27638 bytes .../EnableSkipVerificationX86.reg | Bin 12912 -> 13128 bytes Python/Prerequisites/generate.py | 2 ++ 5 files changed, 2 insertions(+) diff --git a/Python/Prerequisites/DisableSkipVerification.reg b/Python/Prerequisites/DisableSkipVerification.reg index fbb40f6b4794cee02611170537e053c5c5af8037..4e4f04323816387a3b67e2c5de3f4a1ad63438aa 100644 GIT binary patch delta 43 wcmbPtjq%k@#tk!MSwk7h844yZ^asi?MD%u5v diff --git a/Python/Prerequisites/EnableSkipVerification.reg b/Python/Prerequisites/EnableSkipVerification.reg index 7f1e566182c943bb110b2fd0833f75d1cf1e4fad..e8a57d3f82cb899981d3b534738478f0cbe82c8b 100644 GIT binary patch delta 39 rcmZ2?h4I^U#tk8|tf36$32C{qph delta 62 zcmex%opIe2#tk8|n|0*;OegEe)lD`qjhQ5;H(5r$0K}YpL4FRH-k`7qOwUlPm>dut Kx0xsMl@I^|!WdHk diff --git a/Python/Prerequisites/EnableSkipVerificationX86.reg b/Python/Prerequisites/EnableSkipVerificationX86.reg index c6f08c9ae0e971f4057730532d0fa16de59e756e..be4c4efef4d9b62d935f81e50d8e370ac9efc0a4 100644 GIT binary patch delta 18 Zcmey6aw2U*h%9R;Lpeji=0=(Kq5w+!2RQ%$ delta 12 TcmX?+_910Mi0oz^IT=v^C;|l1 diff --git a/Python/Prerequisites/generate.py b/Python/Prerequisites/generate.py index 33fc1247c5..dd87136e15 100644 --- a/Python/Prerequisites/generate.py +++ b/Python/Prerequisites/generate.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 '''Regenerates the strong name verification scripts based on the list of assemblies stored in this file. @@ -38,6 +39,7 @@ "Microsoft.PythonTools.PyKinect", "Microsoft.PythonTools.Pyvot", "Microsoft.PythonTools.TestAdapter", + "Microsoft.PythonTools.Uwp", "Microsoft.PythonTools.VSInterpreters", "Microsoft.PythonTools.VsLogger", "Microsoft.PythonTools.WebRole", From 9fc4dc7ed11418a62428077653797e20233ee15a Mon Sep 17 00:00:00 2001 From: int19h Date: Wed, 13 May 2015 15:35:45 -0700 Subject: [PATCH 14/30] Add a test for #133. --- Python/Tests/DebuggerTests/DebuggerTests.cs | 49 ++++++++++++++++++- .../DebuggerProject/InfiniteRunBlankPrefix.py | 7 +++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 Python/Tests/TestData/DebuggerProject/InfiniteRunBlankPrefix.py diff --git a/Python/Tests/DebuggerTests/DebuggerTests.cs b/Python/Tests/DebuggerTests/DebuggerTests.cs index c7cbdd45a1..6bd09ac064 100644 --- a/Python/Tests/DebuggerTests/DebuggerTests.cs +++ b/Python/Tests/DebuggerTests/DebuggerTests.cs @@ -1985,7 +1985,6 @@ public virtual void AttachThreadingStartNewThread() { } } - [TestMethod, Priority(0)] public virtual void AttachReattach() { Process p = Process.Start(Version.InterpreterPath, "\"" + TestData.GetPath(@"TestData\DebuggerProject\InfiniteRun.py") + "\""); @@ -2541,6 +2540,54 @@ private void AttachTest(string hostCode) { } } + [TestMethod, Priority(0)] + public void AttachAndStepWithBlankSysPrefix() { + string script = TestData.GetPath(@"TestData\DebuggerProject\InfiniteRunBlankPrefix.py"); + var p = Process.Start(Version.InterpreterPath, "\"" + script + "\""); + try { + Thread.Sleep(1000); + var proc = PythonProcess.Attach(p.Id); + try { + var attached = new AutoResetEvent(false); + proc.ProcessLoaded += (sender, args) => { + Console.WriteLine("Process loaded"); + proc.Resume(); + attached.Set(); + }; + proc.StartListening(); + Assert.IsTrue(attached.WaitOne(20000), "Failed to attach within 20s"); + + var bpHit = new AutoResetEvent(false); + PythonThread thread = null; + PythonStackFrame oldFrame = null; + proc.BreakpointHit += (sender, args) => { + Console.WriteLine("Breakpoint hit"); + thread = args.Thread; + oldFrame = args.Thread.Frames[0]; + bpHit.Set(); + }; + var bp = proc.AddBreakPoint(script, 6); + bp.Add(); + Assert.IsTrue(bpHit.WaitOne(20000), "Failed to hit breakpoint within 20s"); + + var stepComplete = new AutoResetEvent(false); + PythonStackFrame newFrame = null; + proc.StepComplete += (sender, args) => { + newFrame = args.Thread.Frames[0]; + stepComplete.Set(); + }; + thread.StepOver(); + Assert.IsTrue(stepComplete.WaitOne(20000), "Failed to complete the step within 20s"); + + Assert.AreEqual(oldFrame.FileName, newFrame.FileName); + Assert.IsTrue(oldFrame.LineNo + 1 == newFrame.LineNo); + } finally { + DetachProcess(proc); + } + } finally { + DisposeProcess(p); + } + } class TraceRedirector : Redirector { private readonly string _prefix; diff --git a/Python/Tests/TestData/DebuggerProject/InfiniteRunBlankPrefix.py b/Python/Tests/TestData/DebuggerProject/InfiniteRunBlankPrefix.py new file mode 100644 index 0000000000..e37def78f2 --- /dev/null +++ b/Python/Tests/TestData/DebuggerProject/InfiniteRunBlankPrefix.py @@ -0,0 +1,7 @@ +import sys +sys.prefix = '' + +i = 0 +while True: + i += 1 + print(i) From 46836b1f3e9004a7d0f131ce4b9ac27edf458c8e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 13 May 2015 15:52:12 -0700 Subject: [PATCH 15/30] Updates skip verifications scripts to be generated in UTF-8. --- .../Prerequisites/DisableSkipVerification.reg | Bin 27882 -> 13940 bytes .../DisableSkipVerificationX86.reg | Bin 13250 -> 6624 bytes .../Prerequisites/EnableSkipVerification.reg | Bin 27638 -> 13818 bytes .../EnableSkipVerificationX86.reg | Bin 13128 -> 6563 bytes Python/Prerequisites/generate.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Prerequisites/DisableSkipVerification.reg b/Python/Prerequisites/DisableSkipVerification.reg index 4e4f04323816387a3b67e2c5de3f4a1ad63438aa..381e3582a4d373307b9b24368497170cafdae711 100644 GIT binary patch literal 13940 zcmc(l%WmQ@6o&UB^&NPCnTV3o&Ngj9LDoxzrHpIv_t?1KF0tJHTEOP8~ z*s#p!--S^#{>JM* z_Pm>p-)~KW`}V`c8@8rB9!a6Zvz`W8iY)1eb2iOC!Jl~)>ij!XFALL^;_87Z{r$S` zwwvv%_WAj3v+lXxU%TH|D1eWpWP{~X%GHc#$;dB~ppb6a(=thzLti*`%WDZzwDl`~{Yq;E`Otdc$TGhRU?13B9&Lr-+B zWnM-JJ9U%wgOsRs7|l*W{Ppvaa7c?vA27XK&@r!rsK7; zH=bA8Im@)_=Es}Uk)4j9#CH}ET~>d-5K>S0M<`=L>TU}?1=H~}&E!I|{Fjd3>k_gS zJecu09dIDUGf(rQH7W9iMW0ibu<{O8cRb6%gh^L#f)H1nYP~pIkgkr=JK=^s4oVe( zaO#XzR}_~+x~Vf^PeYM1T;Q0iWtawf8FP_eP!{QA{Vst5718T>{SJXLY`ZdscsniK zt$KMHv^!*pL%~4ovHg^ygfyBM6Zj}g1LbmbjJYC7 zGTVY#C|R7>=;GXmR?|wyA5=yVMT0PU4HJ~zxoG-G=SOnQ8Om090B|;&4aWTXii7%8 zcnBQ%7@-aipal^yMicRVdEqwtB4+3g2hPF|X36bTi@1G%BN@9b41D~ln9aohKu|=v zjJ!~_TqSKovAFlNLwA>ZxobH+NPTtpNHf8%+VZEE`}muVtkL29C=v-^_Q}XbZ%B zS>ZxGm<{;uG5Iug@bjHyqju46VPBsA@@E04E3+~HFmGl9U0e&n4$VdbKt9a|`0#1t zG*ch$*=+Ox*ttRB*8E$=2AH_GYN%Z)C3-0+-C+$JUB!%>zmyiYc9KV>QbwX)J(5z` z)!_&TaeCE8@fZp4d!|T$eKABryq_r&-~dgL03T?IbjcTq3-E-7DWT4=8rFtCtVS#y r14(Ln0WtB4)zcX`MgthwH<~lao|lWU-1V*l@sQQR9v*C#Yyavym)qJ zEaXJC6KBt!-I>|hIsE<8rXKa_oML*Q5iQX^b?E}vMl_}&J>tq1?)GpN<9vx)xIe_* z9?ttXU!{na@%NVAB*#yBPhV&U-}*r(^p$pLgVykVLQTAH;9i5i;qEaV;NCVJQXALz zlPgU;^$bss@%)&6<2U(J+z$S&3w*CbxA^_z=T`La8U98GpGn(ujA!+6euI{C8%KD4 z7q4S_N9$;19lyAW;}eb#_}j$UGQLyC@hj-|j}#$zqC0Tbr9Hf#f(z#160gkR5%_nU z=)trJG^apwfj=Jq`Vna5;R>^x$5E`3gRJ#B`+pm~&(?GU4D?p8L4@}}*2eKZ#I3j&!bZShPU1 z>lvFEvuN8QzAZ$r_ZY2d6 z8-TeXnB$D(6RhtLR@f%i{93IlIC-VYR*ED00S=BOtVa1wSYnI3Ho;sEd3nz0-kLC^!^dBvY9gzL&@s5Q?zrm2%1lZxT&K?(9TG!qwdiXnwlvzbyH|o454XYg|mwq z%0qc$x=wE8^Y%#n9 zt84N#3A%JRK5$uJ%+>gXq1!2w#o78dJM(T9nSA!AT;8&pK1-ZssBH1&PcKyFC@fan zq&idmejxAt!_*g*$#790Os)zJ5i6zIDv78w45WTm4xwWAp;HR4FXZ$~;b527HHC^@ zPVW>>54bgwtPUy^{J!*2q2QlgT~*2ZZ&J2OsS@rGbXK8^|9O2OtG^1RSLAeAp&<6D z*9xZ({OGts#_v<#6-vKI>Au3jE3F3$1$n+Qxk_=-bUn$WYS(5Cv^f8lRtre$%EFle zl6tdHP`jO{=}M$3&3!q2S}2^qR<%(j=6&edLKy?{IyY(L;pyMX?Ezo9xH3`Yqf%Qn z1;ufgA01t}JZ4p>*?zN&J@m)e#XQ;?tMipo+GB_L2&Hs-WzNM#P|E!0N8c!Av@d>9 zO6mQ4qm1?38sy|M5NPOuP mCAjmWWAuYeUf&o3PJ7kMZeHfcuACmSOlDupxI65NTi1X1^-&}M diff --git a/Python/Prerequisites/DisableSkipVerificationX86.reg b/Python/Prerequisites/DisableSkipVerificationX86.reg index e4c67904c230a1e837b6a2ec000f55d64b1408a1..2dfc4954215fc744d0bd23066f12c6060e8bdf97 100644 GIT binary patch literal 6624 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 literal 27638 zcmd^H+fLg+5S`~r{RcjvvS@B<-}a2n;v{xSUOc-q z7IGrniL+CTt7&z zH1W)HJUhnIWBQF>s*H2#B(8Fi=8y$QmZOsXu)yMfQ+RZH-;rU&> zj_Ct!ppA9>;wp|c93Szwg|lURr;g)Sz^xytY5z0bgRd^_Oc-z~U`5)aNE^p+kHo27%TBBaF#t>KAsQHy}m%Q_*F#^ey7j$$>dmdar+^d93$Qx7(}@NTs?Tj3q%1N)!g8)XLn{- zEEW-FF||E(+lW#hFiz7LkMkXU%*YXPC}~cndWa)rjW{QyC5y9oB+JMK@SpZLZ6H%O z0Bb|A#u>(ESll5juSKN!)mSx9@=BC#6UXlZ92_~=jOClKzV`nd&PQS9+8+L~6f=89i;{_M@iHoV3eb_;l?psU#4wBQj&$nEgHKi3P~C-n;P z%TCAuS0`&6wD1_iqu-#?A**4~GRyLDm&TpX;=QLPW()J;e3>3jCig(yUZD*X8b=pb zScEyvC?64=W|k1v-`6MmNhfH)KSG}WsKEPmuLq|%@CHlAuKCqur#oO*+Xq` z4OP85<_%|Dc{iksb;@-9PFpUUQbA>phD>rDRopRc>SrS z!s!7YYO0XY`%_tk(kn9Rt8mcDsMIn?ZVzd?cBndYUP>hv3g2(lTU3X2c~w~`{7b9Nq;ZF)LMs=)A9Y%pnDX(brHX*! zn9GNnty~VXTG4FB*hL!JW9ecJEsfIoswLf#!hC!(YPmA!;360${)eJ(j1p=TzZhjy zcfK)7sPTMblu+sU##k57!uZB0q3ZLCQC96&Ce|=ifMp_darbnF&t26XjXNBERD%+{ k`A`%3!6mIS3<0IJ`eHX9^P^Tul~^XLZ)MLN^2IIcKOC+*vH$=8 diff --git a/Python/Prerequisites/EnableSkipVerificationX86.reg b/Python/Prerequisites/EnableSkipVerificationX86.reg index be4c4efef4d9b62d935f81e50d8e370ac9efc0a4..2e0fa35859df95826936a5ef0f5179cbcc844769 100644 GIT binary patch literal 6563 zcmc(j+iv185Qgt7^&NPCRm5T0?rk`Us2rjM!mgH8WfGH233g;VRPglc(3S?Zmpj8v zF6Z;_%=|Os6^KN>=tjU(&_*2_!vu^}jfg7^QZ%|wvw44afA?qnIQ;!InfKkv)70&c zz1eUb&PR)t8w}S|h?UfGYu2GrQlvAs=W81Qwh%L8!>nDw@}ma5;M#oaH9Mp3=wZ}q z4Z2OYnOLZgS8Wh;->yIxs*3zA4j`EA04)3Z9urCEEGV(eX|{V%qdm zKuS~37KeW{nG{&}bE!n9#*#AEPQbOy4_qNs#|QLHTm_`!IPca%4s?3TIw#$YzA8OY z0*zsDb_3wApFik+XXWi7H)TPF?3Iuw3#NB;Rr20g$TSYlPECdziUU}8-E(2|#7;6d z13P0$NFNt5nNn|GNM%-VVk)77URvf2kd7w|SB2vCONO6L=uicO8SKe;zLHzW?Ep@2T4b(rdKy%1!Y+*U+DUC6xIr#E^8t^k=61?mNhHB31rSu zc@gN6<)JAPkah_v^40mp0fQ;yU37k7!1!es@4{O?YunS*YQfqOYZw9q-gSK>6CtDB zMabMwO4=Cf&u7X5g_cf}gwi6Rv`Hu(63TZ9iWkghZHzn%Gp2ZAUAda?uzajG=wKqk xIQCh*V<|=}t`5F5cI-C5F&g7leCM^fAY|587|gf7Yu?gBkaRs}R=_X+&p&gQt8xGU literal 13128 zcmd^_+iu!G5QgWvQr{sDP+2K7O>YyDs3@gDp+KurML=BQK(K{Ls_^h_zp-%$hqhdH z23c!scYT=O|L)A}?EL!KmPp2OEwTKNRQBXjLK)DO%2X!uNXsd`5w)25p0wzn&>K-7 zQ$LVE_Bme4i>!Q=_j1lNU*t+Y$c0qoi0hTqxK`+^%13%T(xC55K1rMQ%dDlwJNvvl z=Iyb3=ZkumN}*pG@LX5!_J|TkEKt|$Ul>N_zI=YwFeK%!3}5SuL<`BA65{ZfTahP0f&xz>+;*= z;FdY9BPcfb{}-}b{P$=4dDWj4{(kiI_ibp?>fCbFu?ADLkXjLI^LO1Q258aTA}-^K z!8d}dAhY0VK}YhC=`0&;Z9=*kp*K9in%CkVVSqoTqgZ`TwX2>H&)g!MqF1|k;5R#h z@Uy&=w=TzO;kP$na*Ds3I7E32t_Z7mjTfL^&5+_x@2sv^JR)59)c(+&;-x+?PIDhm z;~isWWJ(MrkIC#F;((};#)Payaklql6PJ6Ijy-;|z^ELGyY zdh~5I9(0~@nd9}y=Of-<443|}6|vVN{D3aH$E&qy64mm!er{naHd}m;x~7$_x#Fjt zKY#PFjn(L}TcA4yp<#W~!Xl)^c68rg^9Am+e1&+v6Oz#SJjcNbk1-Z~NT$Oghr!A$ zTlc#x?tE_Ed#W*8l$XMm+2Q2*9%!*w=mUkt(WO~dtD4CtjuJUmHgxX4IVqXh{n-Y~ zp&MSxWbt_!V`cIZ{a`6M!cumG<&7IGRaP(;>a{U%G~%keAtCFO<@lX7f2@#q zIigdvTJenwc^i24eHhV Date: Wed, 13 May 2015 17:09:24 -0700 Subject: [PATCH 16/30] Makes it possible to hide environments or make them ineligible to be the default environment. Makes the UWP environment ineligible to be the default. --- Python/Product/Analysis/Analysis.csproj | 1 + .../Interpreter/InterpreterConfiguration.cs | 39 ++++++++++++++- .../Analysis/Interpreter/InterpreterUIMode.cs | 37 ++++++++++++++ .../PythonInterpreterFactoryExtensions.cs | 33 +++++++++++++ .../EnvironmentsList/EnvironmentView.cs | 2 + .../EnvironmentsList/ToolWindow.xaml.cs | 7 ++- .../PythonTools/InterpreterView.cs | 1 + .../PythonInterpreterOptionsControl.cs | 4 ++ .../PythonUwpInterpreterFactoryProvider.cs | 12 ++++- .../InterpreterOptionsService.cs | 23 +++++---- Python/Tests/Core/EnvironmentListTests.cs | 48 ++++++++++++++++++- 11 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 Python/Product/Analysis/Interpreter/InterpreterUIMode.cs diff --git a/Python/Product/Analysis/Analysis.csproj b/Python/Product/Analysis/Analysis.csproj index b5125f5d28..794fe516bb 100644 --- a/Python/Product/Analysis/Analysis.csproj +++ b/Python/Product/Analysis/Analysis.csproj @@ -93,6 +93,7 @@ + diff --git a/Python/Product/Analysis/Interpreter/InterpreterConfiguration.cs b/Python/Product/Analysis/Interpreter/InterpreterConfiguration.cs index 47e2d5b102..87295edb21 100644 --- a/Python/Product/Analysis/Interpreter/InterpreterConfiguration.cs +++ b/Python/Product/Analysis/Interpreter/InterpreterConfiguration.cs @@ -26,6 +26,7 @@ public sealed class InterpreterConfiguration { readonly string _pathEnvironmentVariable; readonly ProcessorArchitecture _architecture; readonly Version _version; + readonly InterpreterUIMode _uiMode; /// /// Creates a blank configuration with the specified language version. @@ -55,6 +56,27 @@ public InterpreterConfiguration( string pathVar, ProcessorArchitecture arch, Version version + ) : this(prefixPath, path, winPath, libraryPath, pathVar, arch, version, InterpreterUIMode.Normal) { + } + /// + /// Constructs a new interpreter configuration based on the + /// provided values. + /// No validation is performed on the parameters. + /// If winPath is null or empty, + /// will be set to path. + /// If libraryPath is null or empty and prefixPath is a valid + /// file system path, will be set to + /// prefixPath plus "Lib". + /// + public InterpreterConfiguration( + string prefixPath, + string path, + string winPath, + string libraryPath, + string pathVar, + ProcessorArchitecture arch, + Version version, + InterpreterUIMode uiMode ) { _prefixPath = prefixPath; _interpreterPath = path; @@ -71,6 +93,7 @@ Version version _version = version; Debug.Assert(string.IsNullOrEmpty(_interpreterPath) || !string.IsNullOrEmpty(_prefixPath), "Anyone providing an interpreter should also specify the prefix path"); + _uiMode = uiMode; } /// @@ -127,6 +150,16 @@ public Version Version { get { return _version; } } + /// + /// The UI behavior of the interpreter. + /// + /// + /// New in 2.2 + /// + public InterpreterUIMode UIMode { + get { return _uiMode; } + } + public override bool Equals(object obj) { var other = obj as InterpreterConfiguration; if (other == null) { @@ -140,7 +173,8 @@ public override bool Equals(object obj) { cmp.Equals(LibraryPath, other.LibraryPath) && cmp.Equals(PathEnvironmentVariable, other.PathEnvironmentVariable) && Architecture == other.Architecture && - Version == other.Version; + Version == other.Version && + UIMode == other.UIMode; } public override int GetHashCode() { @@ -151,7 +185,8 @@ public override int GetHashCode() { cmp.GetHashCode(LibraryPath) ^ cmp.GetHashCode(PathEnvironmentVariable) ^ Architecture.GetHashCode() ^ - Version.GetHashCode(); + Version.GetHashCode() ^ + UIMode.GetHashCode(); } } } diff --git a/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs b/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs new file mode 100644 index 0000000000..4e89f2d6f9 --- /dev/null +++ b/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.PythonTools.Interpreter { + /// + /// Specifies the interpreter's behavior in the UI. + /// + /// New in 2.2 + [Flags] + public enum InterpreterUIMode : int { + /// + /// Interpreter can be set or selected as the default, and is visible to + /// the user. + /// + Normal = 0x00, + + /// + /// Interpreter is not displayed in the user interface, but can still be + /// added to a project if the ID is known. + /// + Hidden = 0x01, + + /// + /// Interpreter cannot be selected as the default. Implies + /// . + /// + CannotBeDefault = 0x02, + + /// + /// Interpreter cannot be automatically selected as the default. + /// + CannotBeAutoDefault = 0x04 + } +} diff --git a/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs b/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs index 071e1c7ed9..7abb843c16 100644 --- a/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs +++ b/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs @@ -101,5 +101,38 @@ GenerateDatabaseOptions options factory.GenerateDatabase(options, tcs.SetResult); return tcs.Task; } + + /// + /// Returns true if the factory should appear in the UI. + /// + /// New in 2.2 + public static bool IsUIVisible(this IPythonInterpreterFactory factory) { + return factory != null && + factory.Configuration != null && + !factory.Configuration.UIMode.HasFlag(InterpreterUIMode.Hidden); + } + + /// + /// Returns true if the factory can ever be the default + /// interpreter. + /// + /// New in 2.2 + public static bool CanBeDefault(this IPythonInterpreterFactory factory) { + return factory != null && + factory.Configuration != null && + !factory.Configuration.UIMode.HasFlag(InterpreterUIMode.CannotBeDefault); + } + + /// + /// Returns true if the factory can be automatically selected as + /// the default interpreter. + /// + /// New in 2.2 + public static bool CanBeAutoDefault(this IPythonInterpreterFactory factory) { + return factory != null && + factory.Configuration != null && + !factory.Configuration.UIMode.HasFlag(InterpreterUIMode.CannotBeDefault) && + !factory.Configuration.UIMode.HasFlag(InterpreterUIMode.CannotBeAutoDefault); + } } } diff --git a/Python/Product/EnvironmentsList/EnvironmentView.cs b/Python/Product/EnvironmentsList/EnvironmentView.cs index e17bde6bd1..d3907ccca8 100644 --- a/Python/Product/EnvironmentsList/EnvironmentView.cs +++ b/Python/Product/EnvironmentsList/EnvironmentView.cs @@ -105,6 +105,8 @@ Redirector redirector if (IsConfigurable) { Extensions.Add(new ConfigurationExtensionProvider(configurableProvider)); } + + CanBeDefault = Factory.CanBeDefault(); } public override string ToString() { diff --git a/Python/Product/EnvironmentsList/ToolWindow.xaml.cs b/Python/Product/EnvironmentsList/ToolWindow.xaml.cs index eb61e577ba..06f4e06565 100644 --- a/Python/Product/EnvironmentsList/ToolWindow.xaml.cs +++ b/Python/Product/EnvironmentsList/ToolWindow.xaml.cs @@ -161,7 +161,7 @@ public ICollectionView Environments { private void MakeGlobalDefault_CanExecute(object sender, CanExecuteRoutedEventArgs e) { var view = e.Parameter as EnvironmentView; - e.CanExecute = view != null && !view.IsDefault; + e.CanExecute = view != null && !view.IsDefault && view.Factory.CanBeDefault(); } private void MakeGlobalDefault_Executed(object sender, ExecutedRoutedEventArgs e) { @@ -295,7 +295,10 @@ private void UpdateEnvironments(IPythonInterpreterFactory select = null) { } _environments.Merge( - _service.Interpreters.Distinct().Select(f => { + _service.Interpreters + .Distinct() + .Where(f => f.IsUIVisible()) + .Select(f => { var view = new EnvironmentView(_service, f, null); OnViewCreated(view); return view; diff --git a/Python/Product/PythonTools/PythonTools/InterpreterView.cs b/Python/Product/PythonTools/PythonTools/InterpreterView.cs index 1daebaf11e..2b3929c620 100644 --- a/Python/Product/PythonTools/PythonTools/InterpreterView.cs +++ b/Python/Product/PythonTools/PythonTools/InterpreterView.cs @@ -46,6 +46,7 @@ public static IEnumerable GetInterpreters( return interpreterService.KnownProviders .Where(p => !(p is LoadedProjectInterpreterFactoryProvider)) .SelectMany(p => p.GetInterpreterFactories()) + .Where(Microsoft.PythonTools.Interpreter.PythonInterpreterFactoryExtensions.IsUIVisible) .OrderBy(fact => fact.Description) .ThenBy(fact => fact.Configuration.Version) .Select(i => new InterpreterView(i, i.Description, i == interpreterService.DefaultInterpreter)); diff --git a/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs b/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs index f867c541bb..00491d86b2 100644 --- a/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs +++ b/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs @@ -60,6 +60,10 @@ internal void UpdateInterpreters() { _defaultInterpreter.Items.Clear(); foreach (var interpreter in _serviceProvider.GetPythonToolsService().InterpreterOptions.Select(x => x.Key).OrderBy(f => f.Description)) { + if (!Microsoft.PythonTools.Interpreter.PythonInterpreterFactoryExtensions.IsUIVisible(interpreter)) { + continue; + } + InterpreterOptions opts; if (_serviceProvider.GetPythonToolsService().TryGetInterpreterOptions(interpreter, out opts) && !opts.Removed) { _showSettingsFor.Items.Add(interpreter); diff --git a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs index 0217028235..1cb1368a70 100644 --- a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Reflection; using Microsoft.PythonTools.Interpreter; namespace Microsoft.PythonTools.Uwp.Interpreter { @@ -36,7 +37,16 @@ public string PythonVersion { private void DiscoverFactories() { if (_factories == null) { - var defaultConfig = new InterpreterConfiguration(new Version(PythonVersion)); + var defaultConfig = new InterpreterConfiguration( + null, + null, + null, + null, + null, + ProcessorArchitecture.None, + new Version(PythonVersion), + InterpreterUIMode.CannotBeDefault + ); _factories = new HashSet(); diff --git a/Python/Product/VSInterpreters/InterpreterOptionsService.cs b/Python/Product/VSInterpreters/InterpreterOptionsService.cs index 9f1ae43dcc..ce761a8f13 100644 --- a/Python/Product/VSInterpreters/InterpreterOptionsService.cs +++ b/Python/Product/VSInterpreters/InterpreterOptionsService.cs @@ -154,15 +154,16 @@ private void Initialize(IServiceProvider serviceProvider) { try { var store = _settings.GetReadOnlySettingsStore(SettingsScope.Configuration); _providers = LoadProviders(store, serviceProvider); + + + foreach (var provider in _providers) { + provider.InterpreterFactoriesChanged += Provider_InterpreterFactoriesChanged; + } + + LoadDefaultInterpreter(suppressChangeEvent: true); } finally { EndSuppressInterpretersChangedEvent(); } - - foreach (var provider in _providers) { - provider.InterpreterFactoriesChanged += Provider_InterpreterFactoriesChanged; - } - - LoadDefaultInterpreter(suppressChangeEvent: true); } private void Provider_InterpreterFactoriesChanged(object sender, EventArgs e) { @@ -175,7 +176,7 @@ private void Provider_InterpreterFactoriesChanged(object sender, EventArgs e) { // May have removed the default interpreter, so select a new default if (FindInterpreter(DefaultInterpreter.Id, DefaultInterpreter.Configuration.Version) == null) { - DefaultInterpreter = Interpreters.LastOrDefault(); + DefaultInterpreter = Interpreters.LastOrDefault(fact => fact.CanBeAutoDefault()); } var evt = InterpretersChanged; @@ -344,10 +345,7 @@ IServiceProvider serviceProvider internal IPythonInterpreterFactoryProvider[] SetProviders(IPythonInterpreterFactoryProvider[] providers) { var oldProviders = _providers; _providers = providers; - var evt = InterpretersChanged; - if (evt != null) { - evt(this, EventArgs.Empty); - } + Provider_InterpreterFactoriesChanged(this, EventArgs.Empty); return oldProviders; } @@ -423,7 +421,8 @@ private void LoadDefaultInterpreter(bool suppressChangeEvent = false) { version = store.GetString(DefaultInterpreterOptionsCollection, DefaultInterpreterVersionSetting, string.Empty); } - var newDefault = FindInterpreter(id, version) ?? Interpreters.LastOrDefault(); + var newDefault = FindInterpreter(id, version) ?? + Interpreters.LastOrDefault(fact => fact.CanBeAutoDefault()); if (suppressChangeEvent) { _defaultInterpreter = newDefault; diff --git a/Python/Tests/Core/EnvironmentListTests.cs b/Python/Tests/Core/EnvironmentListTests.cs index ec8fb2c4f4..845372b9d6 100644 --- a/Python/Tests/Core/EnvironmentListTests.cs +++ b/Python/Tests/Core/EnvironmentListTests.cs @@ -45,6 +45,10 @@ public static void DoDeployment(TestContext context) { } + private static InterpreterConfiguration MockInterpreterConfiguration(Version version, InterpreterUIMode uiMode) { + return new InterpreterConfiguration(null, null, null, null, null, ProcessorArchitecture.None, version, uiMode); + } + private static InterpreterConfiguration MockInterpreterConfiguration(Version version) { return new InterpreterConfiguration(version); } @@ -55,6 +59,8 @@ private static InterpreterConfiguration MockInterpreterConfiguration(string path [TestMethod, Priority(0)] public void HasInterpreters() { + var sp = new MockServiceProvider(); + var service = new InterpreterOptionsService(sp); var mockService = new MockInterpreterOptionsService(); mockService.AddProvider(new MockPythonInterpreterFactoryProvider("Test Provider 1", new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 1", MockInterpreterConfiguration(new Version(2, 7))), @@ -64,7 +70,8 @@ public void HasInterpreters() { mockService.AddProvider(new MockPythonInterpreterFactoryProvider("Test Provider 2", new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 4", MockInterpreterConfiguration(new Version(2, 7))), new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 5", MockInterpreterConfiguration(new Version(3, 0))), - new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 6", MockInterpreterConfiguration(new Version(3, 3))) + new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 6", MockInterpreterConfiguration(new Version(3, 3))), + new MockPythonInterpreterFactory(Guid.NewGuid(), "Hidden Factory 7", MockInterpreterConfiguration(new Version(3, 3), InterpreterUIMode.Hidden)) )); using (var wpf = new WpfProxy()) @@ -80,6 +87,45 @@ public void HasInterpreters() { } } + [TestMethod, Priority(0)] + public void NonDefaultInterpreter() { + var mockProvider = new MockPythonInterpreterFactoryProvider("Test Provider 1", + new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 1", MockInterpreterConfiguration(new Version(2, 7))), + new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 2", MockInterpreterConfiguration(new Version(3, 0), InterpreterUIMode.CannotBeDefault)), + new MockPythonInterpreterFactory(Guid.NewGuid(), "Test Factory 3", MockInterpreterConfiguration(new Version(3, 3), InterpreterUIMode.CannotBeAutoDefault)) + ); + + using (var wpf = new WpfProxy()) + using (var list = new EnvironmentListProxy(wpf)) { + var service = GetInterpreterOptionsService(); + var oldDefault = service.DefaultInterpreter; + var oldProviders = ((InterpreterOptionsService)service).SetProviders(new[] { mockProvider }); + try { + list.Service = service; + var environments = list.Environments; + + AssertUtil.AreEqual( + wpf.Invoke(() => environments.Select(ev => ev.Description).ToList()), + "Test Factory 1", "Test Factory 2", "Test Factory 3" + ); + // TF 1 and 3 can be set as default + AssertUtil.AreEqual( + wpf.Invoke(() => environments.Select(ev => ev.CanBeDefault).ToList()), + true, false, true + ); + + // TF 1 should have been selected as the default + Assert.AreEqual( + "Test Factory 1", + wpf.Invoke(() => environments.First(ev => ev.IsDefault).Description) + ); + } finally { + ((InterpreterOptionsService)service).SetProviders(oldProviders); + service.DefaultInterpreter = oldDefault; + } + } + } + [TestMethod, Priority(0)] public void AddFactories() { var mockService = new MockInterpreterOptionsService(); From e42d91aa242d76ba886aad5fd495fe2d54bae635 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 13 May 2015 17:10:49 -0700 Subject: [PATCH 17/30] Adds missing copyright header and cleans up usings. --- .../Analysis/Interpreter/InterpreterUIMode.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs b/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs index 4e89f2d6f9..50460ca2e5 100644 --- a/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs +++ b/Python/Product/Analysis/Interpreter/InterpreterUIMode.cs @@ -1,8 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; namespace Microsoft.PythonTools.Interpreter { /// From 17438b56d99b1d654444bef32b0cb86adefb26e4 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 09:05:10 -0700 Subject: [PATCH 18/30] Resolves collisions between PythonInterpreterFactoryExtensions classes. --- .../Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs | 2 +- Python/Product/PythonTools/PythonTools.csproj | 3 --- Python/Product/PythonTools/PythonTools/InterpreterView.cs | 2 +- .../PythonTools/Options/PythonInterpreterOptionsControl.cs | 2 +- .../PythonTools/PythonInterpreterFactoryExtensions.cs | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs b/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs index 7abb843c16..a01a02467a 100644 --- a/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs +++ b/Python/Product/Analysis/Interpreter/PythonInterpreterFactoryExtensions.cs @@ -54,7 +54,7 @@ public static bool IsEqual(this IPythonInterpreterFactory x, IPythonInterpreterF /// modules. /// /// The names of the modules that were found. - internal static async Task> FindModulesAsync(this IPythonInterpreterFactory factory, params string[] moduleNames) { + public static async Task> FindModulesAsync(this IPythonInterpreterFactory factory, params string[] moduleNames) { var withDb = factory as PythonInterpreterFactoryWithDatabase; if (withDb != null && withDb.IsCurrent) { var db = withDb.GetCurrentDatabase(); diff --git a/Python/Product/PythonTools/PythonTools.csproj b/Python/Product/PythonTools/PythonTools.csproj index 3cb83de031..8fc45eda06 100644 --- a/Python/Product/PythonTools/PythonTools.csproj +++ b/Python/Product/PythonTools/PythonTools.csproj @@ -271,9 +271,6 @@ IEnumerableExtensions.cs - - PythonInterpreterFactoryExtensions.cs - diff --git a/Python/Product/PythonTools/PythonTools/InterpreterView.cs b/Python/Product/PythonTools/PythonTools/InterpreterView.cs index 2b3929c620..640dd3ef66 100644 --- a/Python/Product/PythonTools/PythonTools/InterpreterView.cs +++ b/Python/Product/PythonTools/PythonTools/InterpreterView.cs @@ -46,7 +46,7 @@ public static IEnumerable GetInterpreters( return interpreterService.KnownProviders .Where(p => !(p is LoadedProjectInterpreterFactoryProvider)) .SelectMany(p => p.GetInterpreterFactories()) - .Where(Microsoft.PythonTools.Interpreter.PythonInterpreterFactoryExtensions.IsUIVisible) + .Where(PythonInterpreterFactoryExtensions.IsUIVisible) .OrderBy(fact => fact.Description) .ThenBy(fact => fact.Configuration.Version) .Select(i => new InterpreterView(i, i.Description, i == interpreterService.DefaultInterpreter)); diff --git a/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs b/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs index 00491d86b2..5e012dabae 100644 --- a/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs +++ b/Python/Product/PythonTools/PythonTools/Options/PythonInterpreterOptionsControl.cs @@ -60,7 +60,7 @@ internal void UpdateInterpreters() { _defaultInterpreter.Items.Clear(); foreach (var interpreter in _serviceProvider.GetPythonToolsService().InterpreterOptions.Select(x => x.Key).OrderBy(f => f.Description)) { - if (!Microsoft.PythonTools.Interpreter.PythonInterpreterFactoryExtensions.IsUIVisible(interpreter)) { + if (!interpreter.IsUIVisible()) { continue; } diff --git a/Python/Product/PythonTools/PythonTools/PythonInterpreterFactoryExtensions.cs b/Python/Product/PythonTools/PythonTools/PythonInterpreterFactoryExtensions.cs index c5a8ad0d58..ea78013e0a 100644 --- a/Python/Product/PythonTools/PythonTools/PythonInterpreterFactoryExtensions.cs +++ b/Python/Product/PythonTools/PythonTools/PythonInterpreterFactoryExtensions.cs @@ -23,7 +23,7 @@ using Microsoft.VisualStudioTools.Project; namespace Microsoft.PythonTools { - static class PythonInterpreterFactoryExtensions { + static class PythonInterpreterFactoryRunnableExtensions { /// /// Returns true if the factory can be run. This checks whether the /// configured InterpreterPath value is an actual file. From 787f336b8375a541facec52fe30794eb047be53b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 10:04:25 -0700 Subject: [PATCH 19/30] Removes extra space. --- Python/Product/VSInterpreters/InterpreterOptionsService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/Product/VSInterpreters/InterpreterOptionsService.cs b/Python/Product/VSInterpreters/InterpreterOptionsService.cs index ce761a8f13..08933c7b2c 100644 --- a/Python/Product/VSInterpreters/InterpreterOptionsService.cs +++ b/Python/Product/VSInterpreters/InterpreterOptionsService.cs @@ -155,7 +155,6 @@ private void Initialize(IServiceProvider serviceProvider) { var store = _settings.GetReadOnlySettingsStore(SettingsScope.Configuration); _providers = LoadProviders(store, serviceProvider); - foreach (var provider in _providers) { provider.InterpreterFactoriesChanged += Provider_InterpreterFactoriesChanged; } From 9d6ba15bec24838f3a0d8664d14cf58ae5389b52 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 10:55:22 -0700 Subject: [PATCH 20/30] Prevents XAML dependencies being updated while iterating over them. Prevents deadlock when updating object browser. --- Python/Product/Analysis/XamlProjectEntry.cs | 26 ++++++++++++++++--- .../Navigation/PythonLibraryManager.cs | 5 +++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Python/Product/Analysis/XamlProjectEntry.cs b/Python/Product/Analysis/XamlProjectEntry.cs index adeee88adc..3f41877ad8 100644 --- a/Python/Product/Analysis/XamlProjectEntry.cs +++ b/Python/Product/Analysis/XamlProjectEntry.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using Microsoft.PythonTools.Interpreter; @@ -25,7 +26,7 @@ sealed class XamlProjectEntry : IXamlProjectEntry { private string _content; private IAnalysisCookie _cookie; private Dictionary _properties; - private HashSet _dependencies = new HashSet(); + private readonly HashSet _dependencies = new HashSet(); public XamlProjectEntry(string filename) { _filename = filename; @@ -37,7 +38,9 @@ public void ParseContent(TextReader content, IAnalysisCookie fileCookie) { } public void AddDependency(IProjectEntry projectEntry) { - _dependencies.Add(projectEntry); + lock (_dependencies) { + _dependencies.Add(projectEntry); + } } #region IProjectEntry Members @@ -61,12 +64,27 @@ public void Analyze(CancellationToken cancel) { _version++; // update any .py files which depend upon us. - foreach (var dep in _dependencies) { - dep.Analyze(cancel); + for (var deps = GetNewDependencies(null); deps.Any(); deps = GetNewDependencies(deps)) { + foreach (var dep in deps) { + dep.Analyze(cancel); + } } } } + private HashSet GetNewDependencies(HashSet oldDependencies) { + HashSet deps; + lock (_dependencies) { + deps = new HashSet(_dependencies); + } + + if (oldDependencies != null) { + deps.ExceptWith(oldDependencies); + } + + return deps; + } + public string FilePath { get { return _filename; } } public int AnalysisVersion { diff --git a/Python/Product/PythonTools/PythonTools/Navigation/PythonLibraryManager.cs b/Python/Product/PythonTools/PythonTools/Navigation/PythonLibraryManager.cs index e80ac5bdd9..580d37b410 100644 --- a/Python/Product/PythonTools/PythonTools/Navigation/PythonLibraryManager.cs +++ b/Python/Product/PythonTools/PythonTools/Navigation/PythonLibraryManager.cs @@ -19,6 +19,7 @@ using Microsoft.VisualStudioTools; using Microsoft.VisualStudioTools.Navigation; using Microsoft.VisualStudioTools.Project; +using SR = Microsoft.PythonTools.Project.SR; namespace Microsoft.PythonTools.Navigation { @@ -71,7 +72,9 @@ protected override void OnNewFile(LibraryTask task) { // object browser (for example we could include base type information with // links elsewhere in the object browser). item.OnNewAnalysis += (sender, args) => { - _package.GetUIThread().Invoke(() => FileParsed(task, new AstScopeNode(item.Tree, item))); + _package.GetUIThread().InvokeAsync(() => FileParsed(task, new AstScopeNode(item.Tree, item))) + .HandleAllExceptions(SR.ProductName, GetType()) + .DoNotWait(); }; } } From 811b9e32c6cd79221f8b6a6133be75b228387a60 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 10:57:37 -0700 Subject: [PATCH 21/30] #34 XAML designer doesn't work in VS 2015 RC Implements RefType so that the XAML designer no longer aborts initialization when getting our references. --- .../SharedProject/Automation/VSProject/OAReferenceBase.cs | 8 ++++++-- .../Project/Automation/OAPythonExtensionReference.cs | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Common/Product/SharedProject/Automation/VSProject/OAReferenceBase.cs b/Common/Product/SharedProject/Automation/VSProject/OAReferenceBase.cs index 6aabfc0546..e13ff61d39 100644 --- a/Common/Product/SharedProject/Automation/VSProject/OAReferenceBase.cs +++ b/Common/Product/SharedProject/Automation/VSProject/OAReferenceBase.cs @@ -167,8 +167,12 @@ public virtual bool Isolated { } } - public uint RefType { - get { throw new NotImplementedException(); } + public virtual uint RefType { + get { + // Default to native reference to help prevent callers from + // making incorrect assumptions + return (uint)__PROJECTREFERENCETYPE.PROJREFTYPE_NATIVE; + } } public virtual bool Resolved { diff --git a/Python/Product/PythonTools/PythonTools/Project/Automation/OAPythonExtensionReference.cs b/Python/Product/PythonTools/PythonTools/Project/Automation/OAPythonExtensionReference.cs index 909fb56c4d..cba9a5ce61 100644 --- a/Python/Product/PythonTools/PythonTools/Project/Automation/OAPythonExtensionReference.cs +++ b/Python/Product/PythonTools/PythonTools/Project/Automation/OAPythonExtensionReference.cs @@ -16,6 +16,7 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudioTools.Project.Automation; using VSLangProj; +using VSLangProj80; namespace Microsoft.PythonTools.Project.Automation { [ComVisible(true)] @@ -37,6 +38,12 @@ public override prjReferenceType Type { } } + public override uint RefType { + get { + return (uint)__PROJECTREFERENCETYPE.PROJREFTYPE_ASSEMBLY; + } + } + public override bool CopyLocal { get { return false; From 10f152991287116d05ee52f41666fb3e6fbe187b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 11:36:09 -0700 Subject: [PATCH 22/30] Prevents exceptions in projection buffers when suggesting missing imports. --- .../PythonSuggestedActionsSource.cs | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Python/Product/PythonTools/PythonTools/Intellisense/PythonSuggestedActionsSource.cs b/Python/Product/PythonTools/PythonTools/Intellisense/PythonSuggestedActionsSource.cs index c50099297d..241402cd28 100644 --- a/Python/Product/PythonTools/PythonTools/Intellisense/PythonSuggestedActionsSource.cs +++ b/Python/Product/PythonTools/PythonTools/Intellisense/PythonSuggestedActionsSource.cs @@ -1,11 +1,25 @@ -using Microsoft.VisualStudio.Language.Intellisense; +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; +using Microsoft.PythonTools.Editor.Core; +using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; -using System.Threading; using Microsoft.VisualStudio.Text.Editor; namespace Microsoft.PythonTools.Intellisense { @@ -48,13 +62,14 @@ public IEnumerable GetSuggestedActions(ISuggestedActionCateg public async Task HasSuggestedActionsAsync(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) { var textBuffer = _textBuffer; var pos = _view.Caret.Position.BufferPosition; - var lineStart = pos.GetContainingLine().Start; - if (pos.Position < pos.Snapshot.Length - 1) { + pos = _view.BufferGraph.MapDownToFirstMatch(pos, PointTrackingMode.Positive, EditorExtensions.IsPythonContent, PositionAffinity.Successor) ?? pos; + var line = pos.GetContainingLine(); + if (pos.Position < line.End.Position) { pos += 1; } var span = pos.Snapshot.CreateTrackingSpan( - lineStart.Position, - pos.Position - lineStart.Position, + line.Start.Position, + pos.Position - line.Start.Position, SpanTrackingMode.EdgePositive, TrackingFidelityMode.Forward ); From abb6bb73f696a47a32b250979487ff4d07c103b7 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 13:04:21 -0700 Subject: [PATCH 23/30] #129 Python Package icon appears with white background Removes failed attempt at correctly handling the background colour. --- Python/Product/EnvironmentsList/images14.0/images.xaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Python/Product/EnvironmentsList/images14.0/images.xaml b/Python/Product/EnvironmentsList/images14.0/images.xaml index 6fc2e4bf18..bd91205f79 100644 --- a/Python/Product/EnvironmentsList/images14.0/images.xaml +++ b/Python/Product/EnvironmentsList/images14.0/images.xaml @@ -143,9 +143,7 @@ - - - + From dd2c516f6445673173b174283246345de99caf22 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 13:54:47 -0700 Subject: [PATCH 24/30] Prevents progress bar from getting stuck when pip is not installed. Improves detection of whether pip is installed so we don't have to check as often. --- .../EnvironmentsList/PipExtension.xaml.cs | 34 ++-- .../EnvironmentsList/PipExtensionProvider.cs | 174 ++++++++++++------ Python/Tests/Core/EnvironmentListTests.cs | 5 +- 3 files changed, 131 insertions(+), 82 deletions(-) diff --git a/Python/Product/EnvironmentsList/PipExtension.xaml.cs b/Python/Product/EnvironmentsList/PipExtension.xaml.cs index fa9881479b..2748d04e6e 100644 --- a/Python/Product/EnvironmentsList/PipExtension.xaml.cs +++ b/Python/Product/EnvironmentsList/PipExtension.xaml.cs @@ -190,6 +190,7 @@ PipExtensionProvider provider _provider = provider; _provider.UpdateStarted += PipExtensionProvider_UpdateStarted; _provider.UpdateComplete += PipExtensionProvider_UpdateComplete; + _provider.IsPipInstalledChanged += PipExtensionProvider_IsPipInstalledChanged; _installCommandView = new InstallPackageView(this); _matcher = new FuzzyStringMatcher(FuzzyMatchMode.FuzzyIgnoreCase); @@ -207,6 +208,10 @@ PipExtensionProvider provider FinishInitialization(); } + private async void PipExtensionProvider_IsPipInstalledChanged(object sender, EventArgs e) { + await Dispatcher.InvokeAsync(() => { IsPipInstalled = _provider.IsPipInstalled ?? true; }); + } + private void InstalledView_CurrentChanged(object sender, EventArgs e) { if (_installedView.View.CurrentItem != null) { _installableView.View.MoveCurrentTo(null); @@ -221,23 +226,8 @@ private void InstallableView_CurrentChanged(object sender, EventArgs e) { private async void FinishInitialization() { try { - try { - if (!(IsPipInstalled = await _provider.IsPipInstalled())) { - // pip is not installed, so no point refreshing packages. - return; - } - } catch (InvalidOperationException) { - IsPipInstalled = false; - return; - } catch (OperationCanceledException) { - IsPipInstalled = false; - return; - } - - try { - await RefreshPackages(); - } catch (OperationCanceledException) { - } + await RefreshPackages(); + } catch (OperationCanceledException) { } catch (Exception ex) { if (ex.IsCriticalException()) { throw; @@ -400,10 +390,12 @@ await Dispatcher.InvokeAsync(() => { CommandManager.InvalidateRequerySuggested(); }); try { - await Task.WhenAll( - RefreshInstalledPackages(), - RefreshInstallablePackages() - ); + if (IsPipInstalled) { + await Task.WhenAll( + RefreshInstalledPackages(), + RefreshInstallablePackages() + ); + } } finally { Dispatcher.Invoke(() => { IsListRefreshing = false; diff --git a/Python/Product/EnvironmentsList/PipExtensionProvider.cs b/Python/Product/EnvironmentsList/PipExtensionProvider.cs index c5a7017709..666faad8ad 100644 --- a/Python/Product/EnvironmentsList/PipExtensionProvider.cs +++ b/Python/Product/EnvironmentsList/PipExtensionProvider.cs @@ -33,6 +33,7 @@ public sealed class PipExtensionProvider : IEnvironmentViewExtension { private readonly Redirector _output; private FrameworkElement _wpfObject; + private bool? _isPipInstalled; private PipPackageCache _cache; private readonly CancellationTokenSource _cancelAll = new CancellationTokenSource(); @@ -148,16 +149,27 @@ internal async Task> GetInstalledPackagesAsync() { args = new [] { "-m", "pip", "list", "--no-index" }; } - PipPackageView[] packages; + PipPackageView[] packages = null; - using (var output = ProcessOutput.RunHiddenAndCapture(_factory.Configuration.InterpreterPath, args)) { - if ((await output) != 0) { - throw new PipException(Resources.ListFailed); - } + try { + using (var output = ProcessOutput.RunHiddenAndCapture(_factory.Configuration.InterpreterPath, args)) { + if ((await output) != 0) { + throw new PipException(Resources.ListFailed); + } - packages = output.StandardOutputLines - .Select(s => new PipPackageView(_cache, s)) - .ToArray(); + packages = output.StandardOutputLines + .Select(s => new PipPackageView(_cache, s)) + .ToArray(); + } + } catch (IOException) { + } finally { + if (packages == null) { + // pip is obviously not installed + IsPipInstalled = false; + } else { + // pip is obviously installed + IsPipInstalled = true; + } } return packages; @@ -167,14 +179,44 @@ internal async Task> GetAvailablePackagesAsync() { return await _cache.GetAllPackagesAsync(_cancelAll.Token); } - internal async Task IsPipInstalled() { - AbortOnInvalidConfiguration(); + internal bool? IsPipInstalled { + get { + return _isPipInstalled; + } + private set { + if (_isPipInstalled != value) { + _isPipInstalled = value; - using (var output = ProcessOutput.RunHiddenAndCapture( - _factory.Configuration.InterpreterPath, - "-E", "-c", "import pip" - )) { - return (await output) == 0; + var evt = IsPipInstalledChanged; + if (evt != null) { + evt(this, EventArgs.Empty); + } + } + } + } + + internal event EventHandler IsPipInstalledChanged; + + internal async Task CheckPipInstalledAsync() { + if (!CanExecute) { + // Don't cache the result in case our configuration gets fixed + IsPipInstalled = false; + return; + } + + if (!_isPipInstalled.HasValue) { + try { + using (var output = ProcessOutput.RunHiddenAndCapture( + _factory.Configuration.InterpreterPath, + "-E", "-c", "import pip" + )) { + IsPipInstalled = (await output) == 0; + } + } catch (IOException) { + IsPipInstalled = false; + } catch (OperationCanceledException) { + IsPipInstalled = false; + } } } @@ -240,6 +282,10 @@ public async Task InstallPip() { } ToolWindow.UnhandledException.Execute(ExceptionDispatchInfo.Capture(ex), WpfObject); } finally { + if (success) { + IsPipInstalled = true; + } + OnOperationFinished( success ? Resources.InstallingPipSuccess : Resources.InstallingPipFailed ); @@ -277,34 +323,39 @@ public async Task InstallPackage(string package, bool upgrade) { } using (await WaitAndLockPip()) { + bool success = false; OnOperationStarted(string.Format(Resources.InstallingPackageStarted, package)); - using (var output = ProcessOutput.Run( - _factory.Configuration.InterpreterPath, - QuotedArgumentsWithPackageName(args, package), - _factory.Configuration.PrefixPath, - UnbufferedEnv, - false, - _output, - quoteArgs: false, - elevate: ShouldElevate - )) { - if (!output.IsStarted) { - OnOperationFinished(string.Format(Resources.InstallingPackageFailed, package)); - return; - } - bool success = true; - try { + try { + using (var output = ProcessOutput.Run( + _factory.Configuration.InterpreterPath, + QuotedArgumentsWithPackageName(args, package), + _factory.Configuration.PrefixPath, + UnbufferedEnv, + false, + _output, + quoteArgs: false, + elevate: ShouldElevate + )) { + if (!output.IsStarted) { + return; + } var exitCode = await output; if (exitCode != 0) { - success = false; throw new PipException(Resources.InstallationFailed); } - } finally { - OnOperationFinished(string.Format( - success ? Resources.InstallingPackageSuccess : Resources.InstallingPackageFailed, - package - )); + success = true; + } + } catch (IOException) { + } finally { + if (!success) { + // Check whether we failed because pip is missing + await CheckPipInstalledAsync(); } + + OnOperationFinished(string.Format( + success ? Resources.InstallingPackageSuccess : Resources.InstallingPackageFailed, + package + )); } } } @@ -320,22 +371,21 @@ public async Task UninstallPackage(string package) { using (await WaitAndLockPip()) { OnOperationStarted(string.Format(Resources.UninstallingPackageStarted, package)); - using (var output = ProcessOutput.Run( - _factory.Configuration.InterpreterPath, - QuotedArgumentsWithPackageName(args, package), - _factory.Configuration.PrefixPath, - UnbufferedEnv, - false, - _output, - quoteArgs: false, - elevate: ShouldElevate - )) { - if (!output.IsStarted) { - OnOperationFinished(string.Format(Resources.UninstallingPackageFailed, package)); - return; - } - bool success = true; - try { + bool success = false; + try { + using (var output = ProcessOutput.Run( + _factory.Configuration.InterpreterPath, + QuotedArgumentsWithPackageName(args, package), + _factory.Configuration.PrefixPath, + UnbufferedEnv, + false, + _output, + quoteArgs: false, + elevate: ShouldElevate + )) { + if (!output.IsStarted) { + return; + } var exitCode = await output; if (exitCode != 0) { // Double check whether the package has actually @@ -343,16 +393,22 @@ public async Task UninstallPackage(string package) { // where, for all practical purposes, there is no // error. if ((await GetInstalledPackagesAsync()).Any(p => p.Name == package)) { - success = false; throw new PipException(Resources.UninstallationFailed); } } - } finally { - OnOperationFinished(string.Format( - success ? Resources.UninstallingPackageSuccess : Resources.UninstallingPackageFailed, - package - )); + success = true; + } + } catch (IOException) { + } finally { + if (!success) { + // Check whether we failed because pip is missing + await CheckPipInstalledAsync(); } + + OnOperationFinished(string.Format( + success ? Resources.UninstallingPackageSuccess : Resources.UninstallingPackageFailed, + package + )); } } } diff --git a/Python/Tests/Core/EnvironmentListTests.cs b/Python/Tests/Core/EnvironmentListTests.cs index 845372b9d6..409d8f81c6 100644 --- a/Python/Tests/Core/EnvironmentListTests.cs +++ b/Python/Tests/Core/EnvironmentListTests.cs @@ -527,11 +527,12 @@ public void PipExtension() { var environment = list.Environments.Single(); var pip = (PipExtensionProvider)list.GetExtensionOrAssert(environment); - Assert.IsFalse(pip.IsPipInstalled().GetAwaiter().GetResult(), "venv should not install pip"); + pip.CheckPipInstalledAsync().GetAwaiter().GetResult(); + Assert.AreEqual(false, pip.IsPipInstalled, "venv should not install pip"); var task = wpf.Invoke(() => pip.InstallPip().ContinueWith(LogException)); Assert.IsTrue(task.Wait(TimeSpan.FromSeconds(120.0)), "pip install timed out"); Assert.IsTrue(task.Result, "pip install failed"); - Assert.IsTrue(pip.IsPipInstalled().GetAwaiter().GetResult(), "pip was not installed"); + Assert.AreEqual(true, pip.IsPipInstalled, "pip was not installed"); var packages = pip.GetInstalledPackagesAsync().GetAwaiter().GetResult(); AssertUtil.ContainsExactly(packages.Select(pv => pv.Name), "pip", "setuptools"); From 00ca8e1a8d10d3eac4bf9c95b1dcc98bb3145617 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 14 May 2015 13:56:30 -0700 Subject: [PATCH 25/30] Unhook event on dispose. --- Python/Product/EnvironmentsList/PipExtension.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/Product/EnvironmentsList/PipExtension.xaml.cs b/Python/Product/EnvironmentsList/PipExtension.xaml.cs index 2748d04e6e..1c407db631 100644 --- a/Python/Product/EnvironmentsList/PipExtension.xaml.cs +++ b/Python/Product/EnvironmentsList/PipExtension.xaml.cs @@ -239,6 +239,7 @@ private async void FinishInitialization() { public void Dispose() { _provider.UpdateStarted -= PipExtensionProvider_UpdateStarted; _provider.UpdateComplete -= PipExtensionProvider_UpdateComplete; + _provider.IsPipInstalledChanged -= PipExtensionProvider_IsPipInstalledChanged; _installableViewRefreshTimer.Dispose(); } From 8a3b36f71483711c1869af29ec004c39dc75c0c5 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Thu, 14 May 2015 22:32:14 -0700 Subject: [PATCH 26/30] Scanning for UWP interpreters based on existing installed SDKs Moving CPython UWP targets include to the PTVS UWP targets file instead of pyproj template file --- .../PythonUwpInterpreterFactory.cs | 6 +- .../PythonUwpInterpreterFactoryProvider.cs | 77 +++++++++++++------ .../Uwp/Microsoft.PythonTools.Uwp.targets | 7 ++ .../PythonBackgroundService.pyproj | 9 +-- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs index c18e25f307..fe94d4f265 100644 --- a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactory.cs @@ -20,10 +20,10 @@ class PythonUwpInterpreterFactory : PythonInterpreterFactoryWithDatabase { public const string InterpreterGuidString = "{86767848-40B4-4007-8BCC-A3835EDF0E69}"; public static readonly Guid InterpreterGuid = new Guid(InterpreterGuidString); - public PythonUwpInterpreterFactory(InterpreterConfiguration configuration) + public PythonUwpInterpreterFactory(InterpreterConfiguration configuration, string description) : base( - InterpreterGuid, - "Python UWP", + InterpreterGuid, + description, configuration, true) { } diff --git a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs index 1cb1368a70..737de35fbf 100644 --- a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs @@ -15,6 +15,8 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.IO; +using System.Linq; using System.Reflection; using Microsoft.PythonTools.Interpreter; @@ -23,39 +25,68 @@ namespace Microsoft.PythonTools.Uwp.Interpreter { class PythonUwpInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { private HashSet _factories = null; - public const string DefaultVersion = "3.5"; - public event EventHandler InterpreterFactoriesChanged; public PythonUwpInterpreterFactoryProvider() { - PythonVersion = DefaultVersion; - } - - public string PythonVersion { - get; set; } private void DiscoverFactories() { if (_factories == null) { - var defaultConfig = new InterpreterConfiguration( - null, - null, - null, - null, - null, - ProcessorArchitecture.None, - new Version(PythonVersion), - InterpreterUIMode.CannotBeDefault - ); + var userSdkInstallDir = new DirectoryInfo(Environment.ExpandEnvironmentVariables(@"%LOCALAPPDATA%\Microsoft\VisualStudio\%VISUALSTUDIOVERSION%Exp\Extensions\Microsoft\Python UWP")); + var sdkInstallDir = new DirectoryInfo(Environment.ExpandEnvironmentVariables(@"%ProgramFiles(x86)%\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\Python UWP")); _factories = new HashSet(); - _factories.Add(new PythonUwpInterpreterFactory(defaultConfig)); - - var factoriesChanged = InterpreterFactoriesChanged; - - if (factoriesChanged != null) { - factoriesChanged(this, new EventArgs()); + if (userSdkInstallDir.Exists) { + foreach (var dirInfo in userSdkInstallDir.EnumerateDirectories()) { + var targetsFile = dirInfo.GetFiles("CPython.targets", SearchOption.AllDirectories).FirstOrDefault(); + + if (targetsFile != null) { + _factories.Add( + new PythonUwpInterpreterFactory( + new InterpreterConfiguration( + dirInfo.FullName, + targetsFile.FullName, + null, + null, + null, + ProcessorArchitecture.None, + new Version(dirInfo.Name), + InterpreterUIMode.CannotBeDefault + ), + string.Join(" ", userSdkInstallDir.Name, dirInfo.Name))); + } + } + } + + if (sdkInstallDir.Exists) { + foreach (var dirInfo in sdkInstallDir.EnumerateDirectories()) { + var targetsFile = dirInfo.GetFiles("CPython.targets", SearchOption.AllDirectories).FirstOrDefault(); + + if (targetsFile != null) { + _factories.Add( + new PythonUwpInterpreterFactory( + new InterpreterConfiguration( + dirInfo.FullName, + targetsFile.FullName, + null, + null, + null, + ProcessorArchitecture.None, + new Version(dirInfo.Name), + InterpreterUIMode.CannotBeDefault + ), + string.Join(" ", sdkInstallDir.Name, dirInfo.Name))); + } + } + } + + if (_factories.Count > 0) { + var factoriesChanged = InterpreterFactoriesChanged; + + if (factoriesChanged != null) { + factoriesChanged(this, new EventArgs()); + } } } } diff --git a/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets b/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets index eef253a815..fc3c9a666d 100644 --- a/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets +++ b/Python/Product/Uwp/Microsoft.PythonTools.Uwp.targets @@ -1,6 +1,13 @@  + + $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UWP\$(InterpreterVersion)\DesignTime\CommonConfiguration\Neutral\CPython.targets + $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\Python UWP\$(InterpreterVersion)\DesignTime\CommonConfiguration\Neutral\CPython.targets + + + + ARM,x86,x64 diff --git a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj index c51fca841c..5f776442bd 100644 --- a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.pyproj @@ -43,11 +43,7 @@ - - $(LocalAppData)\Microsoft\VisualStudio\$(VisualStudioVersion)Exp\Extensions\Microsoft\Python UWP\$(InterpreterVersion)\DesignTime\CommonConfiguration\Neutral\CPython.targets - $(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\PyUWP\$(InterpreterVersion)\DesignTime\CommonConfiguration\Neutral\CPython.targets - - + 14.0 2.2 @@ -55,7 +51,6 @@ $(MSBuildProgramFiles32)\Microsoft Visual Studio $(VisualStudioVersion)\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio - UWP\$(PtvsVersion)\Microsoft.PythonTools.Uwp.targets - - + \ No newline at end of file From 88b54dfc70fe3253867975c3ff82c37c531b9f0c Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Fri, 15 May 2015 12:43:49 -0700 Subject: [PATCH 27/30] Consolidating UWP SDK search for interpreter factory and looking for particular relative targets file path --- .../PythonUwpInterpreterFactoryProvider.cs | 99 ++++++++++--------- 1 file changed, 54 insertions(+), 45 deletions(-) diff --git a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs index 737de35fbf..499075f83f 100644 --- a/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs +++ b/Python/Product/Uwp/Interpreter/PythonUwpInterpreterFactoryProvider.cs @@ -25,63 +25,72 @@ namespace Microsoft.PythonTools.Uwp.Interpreter { class PythonUwpInterpreterFactoryProvider : IPythonInterpreterFactoryProvider { private HashSet _factories = null; + private const string PythonUwpSdkTargetsFile = @"DesignTime\CommonConfiguration\Neutral\CPython.targets"; + public event EventHandler InterpreterFactoriesChanged; - public PythonUwpInterpreterFactoryProvider() { + public PythonUwpInterpreterFactoryProvider() { } - private void DiscoverFactories() { - if (_factories == null) { - var userSdkInstallDir = new DirectoryInfo(Environment.ExpandEnvironmentVariables(@"%LOCALAPPDATA%\Microsoft\VisualStudio\%VISUALSTUDIOVERSION%Exp\Extensions\Microsoft\Python UWP")); - var sdkInstallDir = new DirectoryInfo(Environment.ExpandEnvironmentVariables(@"%ProgramFiles(x86)%\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\Python UWP")); - - _factories = new HashSet(); - - if (userSdkInstallDir.Exists) { - foreach (var dirInfo in userSdkInstallDir.EnumerateDirectories()) { - var targetsFile = dirInfo.GetFiles("CPython.targets", SearchOption.AllDirectories).FirstOrDefault(); + private IDictionary FindFactoriesFromDirectories(params string[] directories) { + var factoryMap = new Dictionary(); + + try { + if (directories != null) { + for (int i = 0; i < directories.Length; i++) { + var rootDirectoryInfo = new DirectoryInfo(directories[i]); + + if (rootDirectoryInfo.Exists) { + foreach (var dirInfo in rootDirectoryInfo.EnumerateDirectories()) { + Version pythonUwpVersion; - if (targetsFile != null) { - _factories.Add( - new PythonUwpInterpreterFactory( - new InterpreterConfiguration( - dirInfo.FullName, - targetsFile.FullName, - null, - null, - null, - ProcessorArchitecture.None, - new Version(dirInfo.Name), - InterpreterUIMode.CannotBeDefault - ), - string.Join(" ", userSdkInstallDir.Name, dirInfo.Name))); + if (Version.TryParse(dirInfo.Name, out pythonUwpVersion)) { + var targetsFile = dirInfo.GetFiles(PythonUwpSdkTargetsFile).FirstOrDefault(); + var factoryName = string.Join(" ", rootDirectoryInfo.Name, dirInfo.Name); + + if (targetsFile != null) { + // This will add a new or overwrite factory with new factory found + // Ordering of the directories means that the last directory specified will + // win the battle of conflicting factory names + factoryMap[factoryName] = new PythonUwpInterpreterFactory( + new InterpreterConfiguration( + dirInfo.FullName, + targetsFile.FullName, + null, + null, + null, + ProcessorArchitecture.None, + pythonUwpVersion, + InterpreterUIMode.CannotBeDefault + ), + factoryName); + } + } + } } } } + } catch (IOException) { + // IOException is not critical here, just means we cannot interrogate for factories at this point + } + + return factoryMap; + } + + private void DiscoverFactories() { + if (_factories == null) { + _factories = new HashSet(); + + var userSdkInstallDir = Environment.ExpandEnvironmentVariables(@"%LOCALAPPDATA%\Microsoft\VisualStudio\%VISUALSTUDIOVERSION%Exp\Extensions\Microsoft\Python UWP"); + var sdkInstallDir = Environment.ExpandEnvironmentVariables(@"%ProgramFiles(x86)%\Microsoft SDKs\Windows\v10.0\ExtensionSDKs\Python UWP"); - if (sdkInstallDir.Exists) { - foreach (var dirInfo in sdkInstallDir.EnumerateDirectories()) { - var targetsFile = dirInfo.GetFiles("CPython.targets", SearchOption.AllDirectories).FirstOrDefault(); + var factoryMap = FindFactoriesFromDirectories(sdkInstallDir, userSdkInstallDir); - if (targetsFile != null) { - _factories.Add( - new PythonUwpInterpreterFactory( - new InterpreterConfiguration( - dirInfo.FullName, - targetsFile.FullName, - null, - null, - null, - ProcessorArchitecture.None, - new Version(dirInfo.Name), - InterpreterUIMode.CannotBeDefault - ), - string.Join(" ", sdkInstallDir.Name, dirInfo.Name))); - } + if (factoryMap.Count > 0) { + foreach (var factory in factoryMap.Values) { + _factories.Add(factory); } - } - if (_factories.Count > 0) { var factoriesChanged = InterpreterFactoriesChanged; if (factoriesChanged != null) { From 086c55af37e4b86c262a54f6787d99f96b9835ce Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 15 May 2015 14:33:26 -0700 Subject: [PATCH 28/30] #21 Types in annotations are not highlighted Ensures AST walkers will walk parameter decorators. Also fixes IntelliSense in functions that include lambda functions. --- .../MockClassificationTypeRegistryService.cs | 2 +- Python/Product/Analysis/ModuleAnalysis.cs | 13 ++++++++++++- .../Product/Analysis/Parsing/Ast/Parameter.cs | 3 +++ .../PythonToolsMockTests/ClassifierTests.cs | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Common/Tests/Utilities/Mocks/MockClassificationTypeRegistryService.cs b/Common/Tests/Utilities/Mocks/MockClassificationTypeRegistryService.cs index 882d6de549..17f160b8e1 100644 --- a/Common/Tests/Utilities/Mocks/MockClassificationTypeRegistryService.cs +++ b/Common/Tests/Utilities/Mocks/MockClassificationTypeRegistryService.cs @@ -38,7 +38,7 @@ public MockClassificationTypeRegistryService([ImportMany]IEnumerable()) { type.AddBaseType(GetClasificationType(baseType)); } } diff --git a/Python/Product/Analysis/ModuleAnalysis.cs b/Python/Product/Analysis/ModuleAnalysis.cs index 01992c4c3e..eae3856fb2 100644 --- a/Python/Product/Analysis/ModuleAnalysis.cs +++ b/Python/Product/Analysis/ModuleAnalysis.cs @@ -960,7 +960,18 @@ private static InterpreterScope FindScope(InterpreterScope parent, PythonAst tre } // Recurse to check children of candidate scope - return FindScope(candidate, tree, location); + var child = FindScope(candidate, tree, location); + + var funcChild = child as FunctionScope; + if (funcChild != null && + funcChild.Function.FunctionDefinition.IsLambda && + child.GetStop(tree) < location.Index) { + // Do not want to extend a lambda function's scope to the end of + // the parent scope. + return parent; + } + + return child; } diff --git a/Python/Product/Analysis/Parsing/Ast/Parameter.cs b/Python/Product/Analysis/Parsing/Ast/Parameter.cs index 5d12f1951c..83ef931c04 100644 --- a/Python/Product/Analysis/Parsing/Ast/Parameter.cs +++ b/Python/Product/Analysis/Parsing/Ast/Parameter.cs @@ -85,6 +85,9 @@ internal ParameterKind Kind { public override void Walk(PythonWalker walker) { if (walker.Walk(this)) { + if (_annotation != null) { + _annotation.Walk(walker); + } if (_defaultValue != null) { _defaultValue.Walk(walker); } diff --git a/Python/Tests/PythonToolsMockTests/ClassifierTests.cs b/Python/Tests/PythonToolsMockTests/ClassifierTests.cs index 5ad1eb2f9d..b9ae19aa07 100644 --- a/Python/Tests/PythonToolsMockTests/ClassifierTests.cs +++ b/Python/Tests/PythonToolsMockTests/ClassifierTests.cs @@ -174,6 +174,23 @@ public void ParameterClassification() { } } + [TestMethod, Priority(0)] + public void ParameterAnnotationClassification() { + var code = @"class A: pass +class B: pass + +def f(a = A, b : B): + pass +"; + using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) { + helper.CheckAstClassifierSpans("ki:k ki:k ki(i=i,i:i): k"); + + helper.Analyze(); + + helper.CheckAnalysisClassifierSpans("ccfpcpc"); + } + } + [TestMethod, Priority(0)] public void TrueFalseClassification() { var code = "True False"; From 5c486e85617553668ac53b3dc0523e4ac2b58558 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Fri, 15 May 2015 14:37:18 -0700 Subject: [PATCH 29/30] Move Priority of IoT UWP template to bottom of list --- .../BackgroundService/PythonBackgroundService.vstemplate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate index c7b3c9e202..9c4eba32f1 100644 --- a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate @@ -4,7 +4,7 @@ A project for creating an Python IoT Background Application Python - 20 + 2000 2 true Microsoft.Ptvs.WinRT.UWP.IOT.BackgroundApplication From a298d0882cbf5da3a59a5c92c1fc9c6795f3efa8 Mon Sep 17 00:00:00 2001 From: Juanya Williams Date: Fri, 15 May 2015 19:31:31 -0700 Subject: [PATCH 30/30] Changing Python UWP template rollup to 0 --- .../BackgroundService/PythonBackgroundService.vstemplate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate index 9c4eba32f1..1cf131d5e8 100644 --- a/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate +++ b/Python/Product/Uwp/Templates/Projects/BackgroundService/PythonBackgroundService.vstemplate @@ -5,7 +5,7 @@ Python 2000 - 2 + 0 true Microsoft.Ptvs.WinRT.UWP.IOT.BackgroundApplication BackgroundApplication