Exemplo n.º 1
0
        /// <summary>
        /// Establishes a connection to the Raspberry and ensures that the Raspberry has
        /// the target SDK, <b>vsdbg</b> installed and also handles uploading of the project
        /// binaries.
        /// </summary>
        /// <param name="connectionInfo">The connection info.</param>
        /// <param name="targetSdk">The target SDK.</param>
        /// <param name="projectProperties">The project properties.</param>
        /// <param name="projectSettings">The project's Raspberry debug settings.</param>
        /// <returns>The <see cref="Connection"/> or <c>null</c> if there was an error.</returns>
        public static async Task <Connection> InitializeConnectionAsync(ConnectionInfo connectionInfo, Sdk targetSdk, ProjectProperties projectProperties, ProjectSettings projectSettings)
        {
            Covenant.Requires <ArgumentNullException>(connectionInfo != null, nameof(connectionInfo));
            Covenant.Requires <ArgumentNullException>(targetSdk != null, nameof(targetSdk));
            Covenant.Requires <ArgumentNullException>(projectProperties != null, nameof(projectProperties));
            Covenant.Requires <ArgumentNullException>(projectSettings != null, nameof(projectSettings));

            var connection = await Connection.ConnectAsync(connectionInfo, projectSettings : projectSettings);

            // .NET Core only supports Raspberry models 3 and 4.

            if (!connection.PiStatus.RaspberryModel.StartsWith("Raspberry Pi 3 Model") &&
                !connection.PiStatus.RaspberryModel.StartsWith("Raspberry Pi 4 Model"))
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                MessageBoxEx.Show(
                    $"Your [{connection.PiStatus.RaspberryModel}] is not supported.  .NET Core requires a Raspberry Model 3 or 4.",
                    $"Raspberry Not Supported",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                connection.Dispose();
                return(null);
            }

            // Ensure that the SDK is installed.

            if (!await connection.InstallSdkAsync(targetSdk.Version))
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                MessageBoxEx.Show(
                    $"Cannot install the .NET SDK [v{targetSdk.Version}] on the Raspberry.\r\n\r\nCheck the Debug Output for more details.",
                    "SDK Installation Failed",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                connection.Dispose();
                return(null);
            }

            // Ensure that the debugger is installed.

            if (!await connection.InstallDebuggerAsync())
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                MessageBoxEx.Show(
                    $"Cannot install the VSDBG debugger on the Raspberry.\r\n\r\nCheck the Debug Output for more details.",
                    "Debugger Installation Failed",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                connection.Dispose();
                return(null);
            }

            // Upload the program binaries.

            if (!await connection.UploadProgramAsync(projectProperties.Name, projectProperties.AssemblyName, projectProperties.PublishFolder))
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                MessageBoxEx.Show(
                    $"Cannot upload the program binaries to the Raspberry.\r\n\r\nCheck the Debug Output for more details.",
                    "Debugger Installation Failed",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                connection.Dispose();
                return(null);
            }

            return(connection);
        }
Exemplo n.º 2
0
        //--------------------------------------------------------------------
        // Static members

        /// <summary>
        /// Creates a <see cref="ProjectProperties"/> instance holding the
        /// necessary properties from a <see cref="Project"/>.  This must
        /// be called on a UI thread.
        /// </summary>
        /// <param name="solution">The current solution.</param>
        /// <param name="project">The source project.</param>
        /// <returns>The cloned <see cref="ProjectProperties"/>.</returns>
        public static ProjectProperties CopyFrom(Solution solution, Project project)
        {
            Covenant.Requires <ArgumentNullException>(solution != null, nameof(solution));
            Covenant.Requires <ArgumentNullException>(project != null, nameof(project));

            ThreadHelper.ThrowIfNotOnUIThread();

            if (string.IsNullOrEmpty(project.FullName))
            {
                // We'll see this for unsupported Visual Studio projects and will just
                // return project properties indicating this.

                return(new ProjectProperties()
                {
                    Name = project.Name,
                    FullPath = project.FullName,
                    Configuration = null,
                    IsNetCore = false,
                    SdkVersion = null,
                    OutputFolder = null,
                    OutputFileName = null,
                    IsExecutable = false,
                    AssemblyName = null,
                    DebugEnabled = false,
                    DebugConnectionName = null,
                    CommandLineArgs = new List <string>(),
                    EnvironmentVariables = new Dictionary <string, string>(),
                    IsSupportedSdkVersion = false,
                    IsRaspberryCompatible = false,
                    IsAspNet = false,
                    AspPort = 0,
                    AspLaunchBrowser = false,
                    AspRelativeBrowserUri = null
                });
            }

            var projectFolder = Path.GetDirectoryName(project.FullName);
            var isNetCore     = true;
            var netVersion    = (SemanticVersion)null;
            var sdkName       = (string)null;

            // Read the properties we care about from the project.

            var targetFrameworkMonikers = (string)project.Properties.Item("TargetFrameworkMoniker").Value;
            var outputType = (int)project.Properties.Item("OutputType").Value;

            var monikers = targetFrameworkMonikers.Split(',');

            isNetCore = monikers[0] == ".NETCoreApp";

            // Extract the version from the moniker.  This looks like: "Version=v5.0"

            var versionRegex = new Regex(@"(?<version>[0-9\.]+)$");

            netVersion = SemanticVersion.Parse(versionRegex.Match(monikers[1]).Groups["version"].Value);

            // The [dotnet --info] command doesn't work as I expected because it doesn't
            // appear to examine the project file when determining the SDK version.
            //
            //      https://github.com/nforgeio/RaspberryDebugger/issues/16
            //
            // So, we're just going to use the latest known SDK from our catalog instead.
            // This isn't ideal but should work fine for the vast majority of people.

            var targetSdk        = (Sdk)null;
            var targetSdkVersion = (SemanticVersion)null;

            foreach (var sdkItem in PackageHelper.SdkGoodCatalog.Items
                     .Where(item => item.IsStandalone && item.Architecture == SdkArchitecture.ARM32))
            {
                var sdkVersion = SemanticVersion.Parse(sdkItem.Version);

                if (sdkVersion.Major != netVersion.Major || sdkVersion.Minor != netVersion.Minor)
                {
                    continue;
                }

                if (targetSdkVersion == null || sdkVersion > targetSdkVersion)
                {
                    targetSdkVersion = sdkVersion;
                    targetSdk        = new Sdk(sdkItem.Name, sdkItem.Version);;
                }
            }

            sdkName = targetSdk?.Name;

            // Load [Properties/launchSettings.json] if present to obtain the command line
            // arguments and environment variables as well as the target connection.  Note
            // that we're going to use the profile named for the project and ignore any others.
            //
            // The launch settings for Console vs. WebApps are a bit different.  WebApps include
            // a top-level "iisSettings"" property and two profiles: "IIS Express" and the
            // profile with the project name.  We're going to use the presence of the "iisSettings"
            // property to determine that we're dealing with a WebApp and we'll do some additional
            // processing based off of the project profile:
            //
            //      1. Launch the browser if [launchBrowser=true]
            //      2. Extract the site port number from [applicationUrl]
            //      3. Have the app listen on all IP addresses by adding this environment
            //         variable when we :
            //
            //              ASPNETCORE_SERVER.URLS=http://0.0.0.0:<port>

            var launchSettingsPath   = Path.Combine(projectFolder, "Properties", "launchSettings.json");
            var commandLineArgs      = new List <string>();
            var environmentVariables = new Dictionary <string, string>();
            var isAspNet             = false;
            var aspPort               = 0;
            var aspLaunchBrowser      = false;
            var aspRelativeBrowserUri = "/";

            if (File.Exists(launchSettingsPath))
            {
                var settings = JObject.Parse(File.ReadAllText(launchSettingsPath));
                var profiles = settings.Property("profiles");

                if (profiles != null)
                {
                    foreach (var profile in ((JObject)profiles.Value).Properties())
                    {
                        if (profile.Name == project.Name)
                        {
                            var profileObject = (JObject)profile.Value;
                            var environmentVariablesObject = (JObject)profileObject.Property("environmentVariables")?.Value;

                            commandLineArgs = ParseArgs((string)profileObject.Property("commandLineArgs")?.Value);

                            if (environmentVariablesObject != null)
                            {
                                foreach (var variable in environmentVariablesObject.Properties())
                                {
                                    environmentVariables[variable.Name] = (string)variable.Value;
                                }
                            }

                            // Extract additional settings for ASPNET projects.

                            if (settings.Property("iisSettings") != null)
                            {
                                isAspNet = true;

                                // Note that we're going to fall back to port 5000 if there are any
                                // issues parsing the application URL.

                                const int fallbackPort = 5000;

                                var jProperty = profileObject.Property("applicationUrl");

                                if (jProperty != null && jProperty.Value.Type == JTokenType.String)
                                {
                                    try
                                    {
                                        var uri = new Uri((string)jProperty.Value);

                                        aspPort = uri.Port;

                                        if (!NetHelper.IsValidPort(aspPort))
                                        {
                                            aspPort = fallbackPort;
                                        }
                                    }
                                    catch
                                    {
                                        aspPort = fallbackPort;
                                    }
                                }
                                else
                                {
                                    aspPort = fallbackPort;
                                }

                                jProperty = profileObject.Property("launchBrowser");

                                if (jProperty != null && jProperty.Value.Type == JTokenType.Boolean)
                                {
                                    aspLaunchBrowser = (bool)jProperty.Value;
                                }
                            }
                        }
                        else if (profile.Name == "IIS Express")
                        {
                            // For ASPNET apps, this profile may include a "launchUrl" which
                            // specifies the absolute or relative URI to display in a debug
                            // browser launched during debugging.
                            //
                            // We're going to normalize this as a relative URI and save it
                            // so we'll be able to launch the browser on the correct page.

                            var profileObject = (JObject)profile.Value;
                            var jProperty     = profileObject.Property("launchUrl");

                            if (jProperty != null && jProperty.Value.Type == JTokenType.String)
                            {
                                var launchUri = (string)jProperty.Value;

                                if (!string.IsNullOrEmpty(launchUri))
                                {
                                    try
                                    {
                                        var uri = new Uri(launchUri, UriKind.RelativeOrAbsolute);

                                        if (uri.IsAbsoluteUri)
                                        {
                                            aspRelativeBrowserUri = uri.PathAndQuery;
                                        }
                                        else
                                        {
                                            aspRelativeBrowserUri = launchUri;
                                        }
                                    }
                                    catch
                                    {
                                        // We'll fall back to "/" for any URI parsing errors.

                                        aspRelativeBrowserUri = "/";
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Get the target Raspberry from the debug settings.

            var projects            = PackageHelper.ReadRaspberryProjects(solution);
            var projectSettings     = projects[project.UniqueName];
            var debugEnabled        = projectSettings.EnableRemoteDebugging;
            var debugConnectionName = projectSettings.RemoteDebugTarget;

            // Determine whether the referenced .NET Core SDK is currently supported.

            var sdk = sdkName == null ? null : PackageHelper.SdkGoodCatalog.Items.SingleOrDefault(item => SemanticVersion.Parse(item.Name) == SemanticVersion.Parse(sdkName) && item.Architecture == SdkArchitecture.ARM32);

            var isSupportedSdkVersion = sdk != null;

            // Determine whether the project is Raspberry compatible.

            var isRaspberryCompatible = isNetCore &&
                                        outputType == 1 && // 1=EXE
                                        isSupportedSdkVersion;

            // We need to jump through some hoops to obtain the project GUID.

            var solutionService = RaspberryDebuggerPackage.Instance.SolutionService;

            Covenant.Assert(solutionService.GetProjectOfUniqueName(project.UniqueName, out var hierarchy) == VSConstants.S_OK);
            Covenant.Assert(solutionService.GetGuidOfProject(hierarchy, out var projectGuid) == VSConstants.S_OK);

            // Return the properties.

            return(new ProjectProperties()
            {
                Name = project.Name,
                FullPath = project.FullName,
                Guid = projectGuid,
                Configuration = project.ConfigurationManager.ActiveConfiguration.ConfigurationName,
                IsNetCore = isNetCore,
                SdkVersion = sdk?.Version,
                OutputFolder = Path.Combine(projectFolder, project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString()),
                OutputFileName = (string)project.Properties.Item("OutputFileName").Value,
                IsExecutable = outputType == 1,              // 1=EXE
                AssemblyName = project.Properties.Item("AssemblyName").Value.ToString(),
                DebugEnabled = debugEnabled,
                DebugConnectionName = debugConnectionName,
                CommandLineArgs = commandLineArgs,
                EnvironmentVariables = environmentVariables,
                IsSupportedSdkVersion = isSupportedSdkVersion,
                IsRaspberryCompatible = isRaspberryCompatible,
                IsAspNet = isAspNet,
                AspPort = aspPort,
                AspLaunchBrowser = aspLaunchBrowser,
                AspRelativeBrowserUri = aspRelativeBrowserUri
            });
        }