Beispiel #1
0
        /// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
#pragma warning disable VSTHRD100
        private async void Execute(object sender, EventArgs e)
#pragma warning restore VSTHRD100
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            if (dte.Solution == null)
            {
                return;
            }

            var project = PackageHelper.GetStartupProject(dte.Solution);

            if (project == null)
            {
                MessageBoxEx.Show(
                    "Please select a startup project using the Project/Set as Startup project menu or by right clicking a project in the Solution Explorer and enabling this.",
                    "Startup Project not found",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return;
            }

            var targetFrameworkMonikers = (string)project.Properties.Item("TargetFrameworkMoniker").Value;
            var outputType = (int)project.Properties.Item("OutputType").Value;
            var monikers   = targetFrameworkMonikers.Split(',');
            var isNetCore  = monikers[0] == ".NETCoreApp";
            var sdkVersion = monikers[1].StartsWith("Version=v") ? monikers[1].Substring("Version=v".Length) : null;

            if (!isNetCore ||
                outputType != 1 /* EXE */ ||
                sdkVersion == null ||
                SemanticVersion.Parse(sdkVersion) < SemanticVersion.Parse("3.1"))
            {
                MessageBoxEx.Show(
                    "Raspberry debugging is not supported by this project type.  Only .NET Core applications targeting .NET Core 3.1 or greater are supported.",
                    "Unsupported Project Type",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Warning);

                return;
            }

            var raspberryProjects = PackageHelper.ReadRaspberryProjects(dte.Solution);
            var projectSettings   = raspberryProjects[project.UniqueName];
            var settingsDialog    = new SettingsDialog(projectSettings);

            if (settingsDialog.ShowDialog() == DialogResult.OK)
            {
                PackageHelper.WriteRaspberryProjects(dte.Solution, raspberryProjects);
            }
        }
        //--------------------------------------------------------------------
        // 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();

            var projectFolder = Path.GetDirectoryName(project.FullName);
            var projectFile   = File.ReadAllText(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);

#if DOESNT_WORK
            // 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

            // We're going to execute [dotnet --info] in the project directory, to obtain the
            // SDK version which will be on the second line which will look something like:
            //
            //      Version:   3.1.301
            //
            // The cool thing is that this will honor any [global.json] files in the solution.

            var orgDirectory = Environment.CurrentDirectory;

            Environment.CurrentDirectory = projectFolder;

            try
            {
                var response = NeonHelper.ExecuteCapture("dotnet", new object[] { "--info" });

                // Note that we'll stick with the version we extracted from the TargetFrameworkMoniker
                // above on the off chance that this call fails.

                if (response.ExitCode == 0)
                {
                    using (var reader = new StringReader(response.OutputText))
                    {
                        var versionLine = reader.Lines().Skip(1).Take(1).FirstOrDefault().Trim();

                        Covenant.Assert(versionLine != null);
                        Covenant.Assert(versionLine.StartsWith("Version:"));

                        sdkName = versionLine.Split(':')[1];
                    }
                }
            }
            finally
            {
                Environment.CurrentDirectory = orgDirectory;
            }
#else
            // 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.SdkCatalog.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);;
                }
            }

            if (targetSdk == null)
            {
                // I don't believe we'll ever see this because the project shouldn't build
                // when the required SDK isn't present.

                Log.Error($"Unable to locate an SDK for [{targetFrameworkMonikers}].");
                Covenant.Assert(false, $"Unable to locate an SDK for [{targetFrameworkMonikers}].");
            }

            sdkName = targetSdk.Name;
#endif

            // 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 = PackageHelper.SdkCatalog.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;

            // Return the properties.

            return(new ProjectProperties()
            {
                Name = project.Name,
                FullPath = project.FullName,
                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
            });
        }