├── settings.gradle.kts
├── path_settings.PNG
├── src
└── main
│ ├── resources
│ ├── messages
│ │ └── MyBundle.properties
│ ├── esp32
│ │ └── embedded
│ │ │ └── clion
│ │ │ └── openocd
│ │ │ ├── ocd.png
│ │ │ ├── reset.png
│ │ │ └── ocd_run.png
│ └── META-INF
│ │ ├── plugin.xml
│ │ └── pluginIcon.svg
│ └── java
│ └── esp32
│ └── embedded
│ └── clion
│ └── openocd
│ ├── OpenOcdConfigurationType.java
│ ├── OpenOcdSettingsState.java
│ ├── Informational.java
│ ├── OpenOcdSettings.java
│ ├── OpenOcdLauncher.java
│ ├── OpenOcdConfigurationEditor.java
│ ├── OpenOcdComponent.java
│ ├── FileChooseInput.java
│ └── OpenOcdConfiguration.java
├── configure_openocd_path.PNG
├── configure_debug_settings.PNG
├── .gitignore
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .github
└── issue_template.md
├── .run
├── Run IDE for UI Tests.run.xml
├── Run Plugin Tests.run.xml
├── Run Qodana.run.xml
├── Run IDE with Plugin.run.xml
└── Run Plugin Verification.run.xml
├── LICENSE.txt
├── gradle.properties
├── README.md
├── CONTRIBUTING.md
├── USAGE.md
├── gradlew.bat
├── CODE_OF_CONDUCT.md
├── CHANGELOG.md
└── gradlew
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "OpenOCD + ESP32 Support for embedded development"
2 |
--------------------------------------------------------------------------------
/path_settings.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/path_settings.PNG
--------------------------------------------------------------------------------
/src/main/resources/messages/MyBundle.properties:
--------------------------------------------------------------------------------
1 | name = OpenOCD + ESP32 Support for Embedded Development
2 |
--------------------------------------------------------------------------------
/configure_openocd_path.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/configure_openocd_path.PNG
--------------------------------------------------------------------------------
/configure_debug_settings.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/configure_debug_settings.PNG
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea/
3 | .intellijPlatform/
4 | .qodana
5 | build
6 | release.properties
7 | .DS_Store
8 | gradle/.DS_Store
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/src/main/resources/esp32/embedded/clion/openocd/ocd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/src/main/resources/esp32/embedded/clion/openocd/ocd.png
--------------------------------------------------------------------------------
/src/main/resources/esp32/embedded/clion/openocd/reset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/src/main/resources/esp32/embedded/clion/openocd/reset.png
--------------------------------------------------------------------------------
/src/main/resources/esp32/embedded/clion/openocd/ocd_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ThexXTURBOXx/clion-embedded-esp32/HEAD/src/main/resources/esp32/embedded/clion/openocd/ocd_run.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | changelog = "2.4.0"
3 | intelliJPlatform = "2.9.0"
4 | kotlin = "2.1.20"
5 |
6 | [plugins]
7 | changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
8 | intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" }
9 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
10 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | ## Expected Behavior
2 |
3 |
4 | ## Actual Behavior
5 |
6 |
7 | ## Steps to Reproduce the Problem
8 |
9 | 1.
10 | 1.
11 | 1.
12 |
13 | ## Specifications
14 |
15 | - Plugin Version:
16 | - CLion version:
17 | - OpenOCD version:
18 | - GDB Version:
19 | - STM32CubeMX version:
20 | - Toolchain:
21 | - Platform:
22 | - Hardware configuration:
23 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | de.femtopedia.openocd
4 | OpenOCD + ESP32 Support for Embedded Development
5 | Nico Mexis
6 |
7 | com.intellij.modules.clion
8 |
9 |
10 |
12 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.run/Run IDE for UI Tests.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | true
18 | true
19 | false
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.run/Run Plugin Tests.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.run/Run Qodana.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | true
22 | true
23 | false
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.run/Run IDE with Plugin.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 | SPDX short identifier: MIT
3 |
4 | Further resources on the MIT License
5 | Copyright 2017, Elmot
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
9 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
10 | persons to whom the Software is furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/.run/Run Plugin Verification.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # IntelliJ Platform Artifacts Repositories
2 | # -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
3 |
4 | pluginGroup = de.femtopedia.openocd
5 | pluginName = OpenOCD + ESP32 Support for embedded development
6 | pluginRepositoryUrl = https://github.com/ThexXTURBOXx/clion-embedded-esp32
7 | pluginVersion = 0.4.4
8 |
9 | # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
10 | # for insight into build numbers and IntelliJ Platform versions.
11 | pluginSinceBuild = 252
12 | pluginUntilBuild = 253.*
13 |
14 | # See https://jb.gg/intellij-platform-builds-list for available build versions.
15 | platformType = CL
16 | platformVersion = 253.17525-EAP-CANDIDATE-SNAPSHOT
17 |
18 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
19 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
20 | platformPlugins =
21 | # Example: platformBundledPlugins = com.intellij.java
22 | platformBundledPlugins = com.intellij.clion, com.intellij.cidr.lang, com.intellij.clion.cmake, com.intellij.nativeDebug
23 |
24 | gradleVersion = 8.14.3
25 |
26 | # Opt-out flag for bundling Kotlin standard library.
27 | # See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
28 | # suppress inspection "UnusedProperty"
29 | kotlin.stdlib.default.dependency = false
30 |
31 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
32 | org.gradle.configuration-cache = true
33 |
34 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
35 | org.gradle.caching = true
36 |
37 | # Assign more memory to Gradle
38 | org.gradle.jvmargs = -Xmx4G
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ESP32 MCU development plugin for JetBrains CLion
2 | ====
3 |
4 |
5 | This project is a modified version of Elmot's excellent OpenOCD +
6 | STM / ARM CLion plugin modified to work with the ESP32 series of MCU. All thanks go to them!
7 |
8 | This is a plugin to support complete ESP32 development from within CLion.
9 |
10 | If there are any features missing, anything that you'd like to see, or of course bugs, please raise an issue and I'll
11 | try and have a look at it.
12 |
13 | Plugin page
14 | at [Jetbrains Repository](https://plugins.jetbrains.com/plugin/18760-openocd--esp32-support-for-embedded-development).
15 |
16 | 
17 |
18 | The plugin is able to:
19 | ---
20 |
21 | * Download project binaries via JTAG using *[OpenOCD](https://openocd.org/)*
22 | * Debug project on chip including support for initial breakpoints.
23 |
24 | Disclaimer
25 | ---
26 |
27 | * No warranties, you are using the plugin at your own risk.
28 | * Beware bugs! This is very early version.
29 |
30 |
31 |
32 | License
33 | ---
34 | [MIT](https://github.com/ThexXTURBOXx/clion-embedded-esp32/blob/master/LICENSE.txt)
35 |
36 | How To Use
37 | ---
38 | See [USAGE.md](https://github.com/ThexXTURBOXx/clion-embedded-esp32/blob/master/USAGE.md).
39 |
40 | Contributions
41 | ===
42 | First, please have a look at
43 | our [code of conduct](https://github.com/ThexXTURBOXx/clion-embedded-esp32/blob/master/CODE_OF_CONDUCT.md). Well, it's
44 | standard stuff, I believe you won't do wrong things. Then read
45 | our [contribution guide](https://github.com/ThexXTURBOXx/clion-embedded-esp32/blob/master/CONTRIBUTING.md).
46 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to contribute to *clion-embedded-esp32* plugin
2 |
3 | #### **Did you find a bug?**
4 |
5 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues).
6 |
7 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
8 |
9 | * Please note your platform (linux, windows, mac), CLion version, OpenOCD version, STM32CubeMX version (if applicable), type of your MCU and/or development board
10 |
11 | #### **Did you write a patch that fixes a bug?**
12 |
13 | * Open a new GitHub pull request with the patch.
14 |
15 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
16 |
17 | #### **Did you fix whitespace, format code, or make a purely cosmetic patch?**
18 |
19 | See above. Please note that formatting fixes may merged, or may be rejected without any explanations.
20 |
21 | #### **Do you intend to add a new feature or change an existing one?**
22 |
23 | * [Open an issue](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/new) on GitHub, add `feature request` label to it.
24 |
25 | #### **Do you have questions about the source code?**
26 |
27 | * [Open an issue](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/new) on GitHub, add `question` label to it.
28 |
29 | #### **Do you want to contribute to the documentation?**
30 |
31 | This is the best contribution to the project. Go right ahead, open a pull request! You can do it [online](https://help.github.com/articles/editing-files-in-another-user-s-repository/)
32 |
33 | Thanks! :heartbeat:
34 |
35 | Elmot
36 |
--------------------------------------------------------------------------------
/USAGE.md:
--------------------------------------------------------------------------------
1 | Purpose
2 | ===
3 |
4 | The plugin supports two different, almost unrelated features:
5 | * Downloading and debugging binaries onto MCU chips using [OpenOCD](https://openocd.org/)
6 |
7 | Disclaimer
8 | ===
9 | You are doing everything at your own risk. Nor me, nor JetBrains, nor anybody else takes any
10 | responsibility in case of any direct or indirect damages or losses.
11 |
12 | Prerequisites
13 | ===
14 | You will need following tools being installed and configured:
15 |
16 | * Compatible hardware. This guide was written for, and tested against the ESP-WROOM32 (DevKit C) and FTDI C232HM-DDHSL JTag USB Adaptor
17 | * [CLion](https://www.jetbrains.com/clion/). This project has been tested against CLion 2018.3.
18 | * ESP32 toolchain installed on your platform as per [the getting started guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started-cmake/index.html)
19 |
20 | Install Plugin
21 | ===
22 | In CLion, go to **File -> Settings ... -> Plugins -> Browse repositories ...** and install the plugin **"OpenOCD + ESP32 Support for embedded development"**.
23 |
24 | Running Examples
25 | ===
26 | Detailed guide coming soon!
27 |
28 | Basic steps:
29 | 1. Install the ESP32 toolchain for your platform as well as the latest (git master) ESP-IDF (Required for Cmake scripts). Ensure that all of the paths have been set (IDF_PATH etc.)
30 | 
31 | 2. Load an example project (recommended as a base) into Clion. Ensure that the initial Cmake build succeeds. If it fails, there is probably a path issue.
32 | 3. Configure the path to OpenOCD as below (this is for the Windows version of the ESP toolchain)
33 | 
34 | 4. Create a new Run / Debug configuration as below.
35 | 
36 | 5. Try running / debugging your project!
37 |
38 | Bugs, Feature requests, Questions, and Contribution
39 | ===
40 |
41 | Please read [CONTRIBUTING.md](CONTRIBUTING.md)
42 |
43 | Likes and Donations
44 | ===
45 |
46 | If you like the plugin, you may :star: the project at github (button at top-right of the page) and at [jetbrains plugins repository](https://plugins.jetbrains.com/plugin/11284).
47 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ESP 32
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdConfigurationType.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.execution.configurations.ConfigurationFactory;
4 | import com.intellij.execution.configurations.RunConfiguration;
5 | import com.intellij.execution.configurations.RunConfigurationSingletonPolicy;
6 | import com.intellij.ide.ui.ProductIcons;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.util.IconLoader;
9 | import com.intellij.openapi.util.NotNullLazyValue;
10 | import com.jetbrains.cidr.cpp.execution.CMakeRunConfigurationType;
11 | import javax.swing.Icon;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | /**
15 | * (c) elmot on 29.9.2017.
16 | */
17 | public class OpenOcdConfigurationType extends CMakeRunConfigurationType {
18 |
19 | private static final String FACTORY_ID = "elmot.embedded.openocd.conf.factory";
20 | public static final String TYPE_ID = "elmot.embedded.openocd.conf.type";
21 | public static final NotNullLazyValue ICON = NotNullLazyValue.lazy(() -> {
22 | final Icon icon = IconLoader.findIcon("esp32/embedded/clion/openocd/ocd_run.png",
23 | OpenOcdConfigurationType.class);
24 | return icon == null ? ProductIcons.getInstance().getProductIcon() : icon;
25 | });
26 | private final ConfigurationFactory factory;
27 |
28 | public OpenOcdConfigurationType() {
29 | super(TYPE_ID,
30 | FACTORY_ID,
31 | "OpenOCD Download & Run (ESP32)",
32 | "Downloads and Runs Embedded Applications using OpenOCD",
33 | ICON
34 | );
35 | factory = new ConfigurationFactory(this) {
36 | @NotNull
37 | @Override
38 | public RunConfiguration createTemplateConfiguration(@NotNull Project project) {
39 | return new OpenOcdConfiguration(project, factory, "");
40 | }
41 |
42 | @NotNull
43 | @Override
44 | public RunConfigurationSingletonPolicy getSingletonPolicy() {
45 | return RunConfigurationSingletonPolicy.SINGLE_INSTANCE_ONLY;
46 | }
47 |
48 | @NotNull
49 | @Override
50 | public String getId() {
51 | return FACTORY_ID;
52 | }
53 | };
54 | }
55 |
56 | @Override
57 | public OpenOcdConfigurationEditor createEditor(@NotNull Project project) {
58 | return new OpenOcdConfigurationEditor(project, getHelper(project));
59 | }
60 |
61 | @NotNull
62 | @Override
63 | protected OpenOcdConfiguration createRunConfiguration(@NotNull Project project,
64 | @NotNull ConfigurationFactory configurationFactory) {
65 | return new OpenOcdConfiguration(project, factory, "");
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at elijah.mot@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdSettingsState.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.execution.configurations.PathEnvironmentVariableUtil;
4 | import com.intellij.openapi.components.PersistentStateComponent;
5 | import com.intellij.openapi.components.Service;
6 | import com.intellij.openapi.components.State;
7 | import com.intellij.openapi.util.SystemInfo;
8 | import com.intellij.openapi.vfs.VfsUtil;
9 | import com.intellij.openapi.vfs.VirtualFile;
10 | import java.io.File;
11 | import org.jdesktop.swingx.util.OS;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | /**
16 | * (c) elmot on 21.10.2017.
17 | */
18 | @Service(Service.Level.PROJECT)
19 | @State(name = "elmot.OpenOcdPlugin")
20 | public final class OpenOcdSettingsState implements PersistentStateComponent {
21 |
22 | public String openOcdHome;
23 | public boolean shippedGdb;
24 | public boolean autoUpdateCmake;
25 |
26 | public OpenOcdSettingsState() {
27 | openOcdHome = defOpenOcdLocation();
28 | shippedGdb = true;
29 | autoUpdateCmake = false;
30 | }
31 |
32 | public static VirtualFile findOcdScripts(VirtualFile ocdHomeVFile) {
33 | VirtualFile ocdScripts = null;
34 | if (ocdHomeVFile != null) {
35 | ocdScripts = ocdHomeVFile.findFileByRelativePath(OpenOcdComponent.SCRIPTS_PATH_LONG);
36 | if (ocdScripts == null) {
37 | ocdScripts = ocdHomeVFile.findFileByRelativePath(OpenOcdComponent.SCRIPTS_PATH_SHORT);
38 | if (ocdScripts == null) {
39 | ocdScripts = ocdHomeVFile.findFileByRelativePath(OpenOcdComponent.SCRIPTS_PATH_MEDIUM);
40 | }
41 | }
42 | }
43 | return ocdScripts;
44 | }
45 |
46 | @NotNull
47 | @Override
48 | public OpenOcdSettingsState getState() {
49 | return this;
50 | }
51 |
52 | @Override
53 | public void loadState(@NotNull OpenOcdSettingsState state) {
54 | openOcdHome = state.openOcdHome;
55 | shippedGdb = state.shippedGdb;
56 | autoUpdateCmake = state.autoUpdateCmake;
57 | }
58 |
59 | @Override
60 | public void noStateLoaded() {
61 | openOcdHome = defOpenOcdLocation();
62 | File openocd = findExecutableInPath("openocd");
63 | if (openocd != null) {
64 | File folder = openocd.getParentFile();
65 | if (folder != null) {
66 | folder = folder.getParentFile();
67 | if (folder != null) {
68 | openOcdHome = folder.getAbsolutePath();
69 | }
70 | }
71 | }
72 | }
73 |
74 | @NotNull
75 | private String defOpenOcdLocation() {
76 | if (!OS.isWindows()) return "/usr";
77 | VirtualFile defDir = VfsUtil.getUserHomeDir();
78 | if (defDir != null) {
79 | return defDir.getPath();
80 | }
81 | return "C:\\";
82 | }
83 |
84 | @Nullable
85 | private File findExecutableInPath(String name) {
86 | if (SystemInfo.isWindows) {
87 | for (String ext : PathEnvironmentVariableUtil.getWindowsExecutableFileExtensions()) {
88 | File file = PathEnvironmentVariableUtil.findInPath(name + ext);
89 | if (file != null) return null;
90 | }
91 | return null;
92 | } else {
93 | return PathEnvironmentVariableUtil.findInPath(name);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/Informational.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.CommonBundle;
4 | import com.intellij.ide.BrowserUtil;
5 | import com.intellij.openapi.application.ApplicationManager;
6 | import com.intellij.openapi.options.ConfigurationException;
7 | import com.intellij.openapi.options.ShowSettingsUtil;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.openapi.ui.MessageType;
10 | import com.intellij.openapi.ui.Messages;
11 | import com.intellij.openapi.wm.ToolWindowId;
12 | import com.intellij.openapi.wm.ToolWindowManager;
13 | import com.intellij.ui.HyperlinkAdapter;
14 | import javax.swing.event.HyperlinkEvent;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | /**
18 | * (c) elmot on 20.10.2017.
19 | */
20 | public class Informational {
21 | public static final String SETTINGS_PROTOCOL = "settings://";
22 | public static final String HELP_URL = "https://github.com/ThexXTURBOXx/clion-embedded-esp32/blob/master/USAGE.md";
23 |
24 | private Informational() {
25 | }
26 |
27 | public static void showSuccessfulDownloadNotification(Project project) {
28 | showMessage(project, MessageType.INFO, "Firmware Download Success");
29 | }
30 |
31 | public static void showMessage(Project project, MessageType messageType, String message) {
32 | ApplicationManager.getApplication().invokeLater(() -> {
33 | ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
34 | if (toolWindowManager.canShowNotification(ToolWindowId.RUN)) {
35 | toolWindowManager.notifyByBalloon(ToolWindowId.RUN, messageType, message,
36 | OpenOcdConfigurationType.ICON.getValue(),
37 | new HyperlinkHandler(project)
38 | );
39 | }
40 | }
41 | );
42 | }
43 |
44 | public static void showFailedDownloadNotification(Project project) {
45 | showMessage(project, MessageType.ERROR,
46 | "MCU Communication FAILURE.\nCheck OpenOCD configuration and connection. " +
50 | "Plugin documentation is located here ");
51 | }
52 |
53 | public static void showPluginError(Project project, ConfigurationException e) {
54 | ApplicationManager.getApplication().invokeLater(() -> {
55 | int optionNo = Messages.showDialog(project, e.getLocalizedMessage(), e.getTitle(),
56 | new String[]{Messages.getOkButton(), CommonBundle.settingsAction(),
57 | CommonBundle.getHelpButtonText()},
58 | 0, Messages.getErrorIcon());
59 | switch (optionNo) {
60 | case 1 -> ShowSettingsUtil.getInstance().showSettingsDialog(project, OpenOcdSettings.class);
61 | case 2 -> BrowserUtil.browse(HELP_URL);
62 | default -> {//nothing to do
63 | }
64 | }
65 | });
66 | }
67 |
68 | private static class HyperlinkHandler extends HyperlinkAdapter {
69 | private final Project project;
70 |
71 | public HyperlinkHandler(Project project) {
72 | this.project = project;
73 | }
74 |
75 | @Override
76 | protected void hyperlinkActivated(@NotNull HyperlinkEvent e) {
77 | /*String link = Objects.toString(e.getDescription(), "");
78 | if (link.toLowerCase().startsWith(SETTINGS_PROTOCOL)) {
79 | try {
80 | String className = link.substring(SETTINGS_PROTOCOL.length());
81 | ShowSettingsUtil.getInstance().showSettingsDialog(project, (Class) Class.forName(className));
82 | } catch (ClassNotFoundException ignored) {
83 | }
84 | } else {
85 | BrowserUtil.browse(link);
86 | }*/
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # OpenOCD + ESP32 Support for embedded development Changelog
2 |
3 | ## [Unreleased]
4 |
5 | ## [0.4.4] - 2025-09-16
6 |
7 | ### Fixed
8 |
9 | - Updated to 2025.3 EAP
10 |
11 | ## [0.4.3] - 2025-05-06
12 |
13 | ### Fixed
14 |
15 | - Updated to 2025.2 EAP
16 |
17 | ## [0.4.2]
18 |
19 | ### Fixed
20 |
21 | - Also support 2025.1 EAP
22 |
23 | ## [0.4.1]
24 |
25 | ### Fixed
26 |
27 | - Fix error messages being shown in wrong thread ([#25](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/25))
28 |
29 | ## [0.4.0]
30 |
31 | ### Added
32 |
33 | - Add additional program parameters ([#22](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/22)) through PR ([#23](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/23))
34 |
35 | ### Changed
36 |
37 | - Update IntelliJ platform plugin
38 |
39 | ### Fixed
40 |
41 | - Fix OpenOCD launching in wrong thread ([#21](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/21)) through PR ([#24](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/24))
42 |
43 | ## [0.3.8]
44 |
45 | ### Fixed
46 |
47 | - Updated to 2024.2 EAP
48 |
49 | ## [0.3.7]
50 |
51 | ### Fixed
52 |
53 | - Updated to 2024.1 EAP
54 |
55 | ## [0.3.6]
56 |
57 | ### Fixed
58 |
59 | - Fixed program offsets not properly updating ([#15](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/15))
60 |
61 | ## [0.3.5]
62 |
63 | ### Fixed
64 |
65 | - Fixed content roots
66 |
67 | ## [0.3.4]
68 |
69 | ### Added
70 |
71 | - Add debugger configuration ([#12](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/12))
72 |
73 | ## [0.3.3]
74 |
75 | ### Fixed
76 |
77 | - Updated to 2023.3 EAP
78 | - Remove deprecations
79 |
80 | ## [0.3.2]
81 |
82 | ### Fixed
83 |
84 | - Fix root path ([#10](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/10))
85 |
86 | ## [0.3.1]
87 |
88 | ### Fixed
89 |
90 | - Fix typo when restoring saved run configs
91 |
92 | ## [0.3.0]
93 |
94 | ### Fixed
95 |
96 | - Fix deadlock ([#4](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/4)) through PR ([#9](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/9))
97 | - Allow not specifying a few config entries
98 | - Support more reset types
99 | - Cleanup (migrate away from obsolete and deprecated entities)
100 |
101 | ## [0.2.4]
102 |
103 | ### Fixed
104 |
105 | - Fix deadlock ([#4](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/4)) through PR ([#7](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/7))
106 |
107 | ## [0.2.3]
108 |
109 | ### Fixed
110 |
111 | - Fix file path parameter ([#6](https://github.com/ThexXTURBOXx/clion-embedded-esp32/issues/6))
112 | - Fix icons
113 | - Add ESP32 tag to run configurations to differentiate them better
114 | - Change default command to `program_esp`
115 | - Support new xpack OpenOCD format
116 | - Fix changelog
117 |
118 | ## [0.2.2]
119 |
120 | ### Fixed
121 |
122 | - Updated to 2023.2 EAP
123 |
124 | ## [0.2.1]
125 |
126 | ### Fixed
127 |
128 | - Updated to 2023.1 EAP
129 |
130 | ## [0.2.0]
131 |
132 | ### Added
133 |
134 | - Support for both `program_esp[32]` commands ([#3](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/3))
135 | - Support for `offset` parameters ([#3](https://github.com/ThexXTURBOXx/clion-embedded-esp32/pull/3))
136 | - Support for `verify` parameter
137 |
138 | ## [0.1.9]
139 |
140 | ### Fixed
141 |
142 | - Updated to 2022.3 EAP
143 |
144 | ## [0.1.8]
145 |
146 | ### Fixed
147 |
148 | - Updated to 2022.2 EAP
149 |
150 | ## [0.1.7]
151 |
152 | ### Fixed
153 |
154 | - Updated to 2022.1 EAP again
155 |
156 | ## [0.1.6]
157 |
158 | ### Fixed
159 |
160 | - Fixed support for CLion 2020.3 and 2021.1
161 |
162 | ## [0.1.5]
163 |
164 | ### Added
165 |
166 | - Support for CLion 2022.1 EAP
167 |
168 | ## [0.1.4]
169 |
170 | ### Changed
171 |
172 | - Changed the icon
173 |
174 | ## [0.1.3]
175 |
176 | ### Changed
177 |
178 | - Migrate to Gradle
179 |
180 | ### Fixed
181 |
182 | - Fixed `NoClassDefFoundError` (#6)
183 |
184 | ## [0.1.2]
185 |
186 | ### Added
187 |
188 | - Updated for CLion 2019.1+
189 |
190 | ## [0.1.1]
191 |
192 | ### Added
193 |
194 | - STM32G0 and STM32L5 experimental support added
195 |
196 | [Unreleased]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.4.4...HEAD
197 | [0.4.4]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.4.3...v0.4.4
198 | [0.4.3]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.4.2...v0.4.3
199 | [0.4.2]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.4.1...v0.4.2
200 | [0.4.1]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.4.0...v0.4.1
201 | [0.4.0]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.8...v0.4.0
202 | [0.3.8]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.7...v0.3.8
203 | [0.3.7]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.6...v0.3.7
204 | [0.3.6]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.5...v0.3.6
205 | [0.3.5]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.4...v0.3.5
206 | [0.3.4]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.3...v0.3.4
207 | [0.3.3]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.2...v0.3.3
208 | [0.3.2]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.1...v0.3.2
209 | [0.3.1]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.3.0...v0.3.1
210 | [0.3.0]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.2.4...v0.3.0
211 | [0.2.4]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.2.3...v0.2.4
212 | [0.2.3]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.2.2...v0.2.3
213 | [0.2.2]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.2.1...v0.2.2
214 | [0.2.1]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.2.0...v0.2.1
215 | [0.2.0]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.9...v0.2.0
216 | [0.1.9]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.8...v0.1.9
217 | [0.1.8]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.7...v0.1.8
218 | [0.1.7]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.6...v0.1.7
219 | [0.1.6]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.5...v0.1.6
220 | [0.1.5]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.4...v0.1.5
221 | [0.1.4]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.3...v0.1.4
222 | [0.1.3]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.2...v0.1.3
223 | [0.1.2]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/compare/v0.1.1...v0.1.2
224 | [0.1.1]: https://github.com/ThexXTURBOXx/clion-embedded-esp32/commits/v0.1.1
225 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdSettings.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.openapi.options.Configurable;
4 | import com.intellij.openapi.options.ConfigurationException;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.vfs.VfsUtil;
7 | import com.intellij.ui.components.JBCheckBox;
8 | import com.intellij.uiDesigner.core.GridConstraints;
9 | import com.intellij.uiDesigner.core.GridLayoutManager;
10 | import com.intellij.uiDesigner.core.Spacer;
11 | import com.jetbrains.cidr.cpp.toolchains.CPPToolchains;
12 | import java.awt.FlowLayout;
13 | import java.io.File;
14 | import java.util.Objects;
15 | import javax.swing.ButtonGroup;
16 | import javax.swing.JComponent;
17 | import javax.swing.JLabel;
18 | import javax.swing.JPanel;
19 | import javax.swing.JRadioButton;
20 | import org.jetbrains.annotations.Nls;
21 | import org.jetbrains.annotations.NotNull;
22 | import org.jetbrains.annotations.Nullable;
23 |
24 | import static com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER;
25 | import static com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST;
26 | import static com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL;
27 | import static com.intellij.uiDesigner.core.GridConstraints.FILL_NONE;
28 | import static com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED;
29 | import static com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW;
30 |
31 | /**
32 | * (c) elmot on 20.10.2017.
33 | */
34 | public class OpenOcdSettings implements Configurable {
35 | protected final Project project;
36 | private OpenOcdSettingsPanel panel = null;
37 |
38 | public OpenOcdSettings(Project project) {
39 | this.project = project;
40 | }
41 |
42 | @Nls
43 | @Override
44 | public String getDisplayName() {
45 | return "ARM/OpenOCD support";
46 | }
47 |
48 | @Override
49 | public boolean isModified() {
50 | OpenOcdSettingsState state = project.getService(OpenOcdSettingsState.class);
51 | if (state == null) return true;
52 | return !(
53 | Objects.equals(panel.openOcdHome.getText(), state.openOcdHome) &&
54 | panel.shippedRadioButton.isSelected() == state.shippedGdb &&
55 | panel.autoUpdateCmake.isSelected() == state.autoUpdateCmake);
56 | }
57 |
58 | @Override
59 | public void apply() throws ConfigurationException {
60 | panel.openOcdHome.validateContent();
61 |
62 | OpenOcdSettingsState state = project.getService(OpenOcdSettingsState.class);
63 | if (state != null) {
64 | state.openOcdHome = panel.openOcdHome.getText();
65 | state.shippedGdb = panel.shippedRadioButton.isSelected();
66 | state.autoUpdateCmake = panel.autoUpdateCmake.isSelected();
67 | }
68 | }
69 |
70 | @Nullable
71 | @Override
72 | public JComponent createComponent() {
73 |
74 | panel = new OpenOcdSettingsPanel();
75 | return panel;
76 | }
77 |
78 | @Override
79 | public void reset() {
80 | OpenOcdSettingsState state = project.getService(OpenOcdSettingsState.class);
81 | panel.openOcdHome.setText(state.openOcdHome);
82 | panel.shippedRadioButton.setSelected(state.shippedGdb);
83 | panel.toolchainRadioButton.setSelected(!state.shippedGdb);
84 | panel.updateToolchainGdbName();
85 | panel.autoUpdateCmake.setSelected(state.autoUpdateCmake);
86 | }
87 |
88 | /**
89 | * (c) elmot on 20.10.2017.
90 | */
91 | public static class OpenOcdSettingsPanel extends JPanel {
92 |
93 | // private final FileChooseInput boardConfigFile;
94 | private final FileChooseInput openOcdHome;
95 | private final JBCheckBox autoUpdateCmake;
96 | private JRadioButton toolchainRadioButton;
97 | private JRadioButton shippedRadioButton;
98 |
99 | public OpenOcdSettingsPanel() {
100 | super(new GridLayoutManager(6, 3), true);
101 | ((GridLayoutManager) getLayout()).setColumnStretch(1, 10);
102 | openOcdHome = addValueRow(0, new FileChooseInput.OpenOcdHome("OpenOCD Home", VfsUtil.getUserHomeDir()));
103 |
104 | addValueRow(2, "Use GDB", setupGdbButtonGroup());
105 |
106 | autoUpdateCmake = addValueRow(4, "CMake Project Update", new JBCheckBox("Automatic"));
107 |
108 | add(new Spacer(), new GridConstraints(5, 0, 1, 1, ANCHOR_CENTER, FILL_NONE,
109 | SIZEPOLICY_FIXED, SIZEPOLICY_WANT_GROW, null, null, null));
110 | }
111 |
112 | @NotNull
113 | protected JPanel setupGdbButtonGroup() {
114 | shippedRadioButton = new JRadioButton("Shipped with CLion");
115 |
116 | toolchainRadioButton = new JRadioButton();
117 | updateToolchainGdbName();
118 | ButtonGroup buttonGroup = new ButtonGroup();
119 | buttonGroup.add(shippedRadioButton);
120 | buttonGroup.add(toolchainRadioButton);
121 | JPanel gdbPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
122 |
123 | gdbPanel.add(shippedRadioButton);
124 | gdbPanel.add(toolchainRadioButton);
125 | return gdbPanel;
126 | }
127 |
128 | private void updateToolchainGdbName() {
129 | CPPToolchains.Toolchain toolchain = CPPToolchains.getInstance().getDefaultToolchain();
130 | File debugger = toolchain == null ? null : toolchain.getDebugger().getGdbExecutable();
131 | if (debugger == null) {
132 | toolchainRadioButton.setText("From Toolchain");
133 | toolchainRadioButton.setToolTipText(null);
134 | } else {
135 | toolchainRadioButton.setText(String.format("From Toolchain (%s)", debugger.getName()));
136 | toolchainRadioButton.setToolTipText(debugger.getAbsolutePath());
137 | }
138 | }
139 |
140 | private T addValueRow(int row, @NotNull T component) {
141 | return addValueRow(row, component.getValueName(), component);
142 | }
143 |
144 | private T addValueRow(int row, @Nullable String labelText, @NotNull T component) {
145 | add(component, new GridConstraints(row, 1, 1, 1, ANCHOR_WEST,
146 | FILL_HORIZONTAL, SIZEPOLICY_WANT_GROW, SIZEPOLICY_FIXED, null, null, null));
147 | if (labelText != null) {
148 | JLabel label = new JLabel(labelText);
149 | add(label, new GridConstraints(row, 0, 1, 1, ANCHOR_WEST, FILL_NONE,
150 | SIZEPOLICY_FIXED, SIZEPOLICY_FIXED, null, null, null));
151 | label.setLabelFor(component);
152 | }
153 | return component;
154 | }
155 |
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH="\\\"\\\""
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | if ! command -v java >/dev/null 2>&1
137 | then
138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
139 |
140 | Please set the JAVA_HOME variable in your environment to match the
141 | location of your Java installation."
142 | fi
143 | fi
144 |
145 | # Increase the maximum file descriptors if we can.
146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
147 | case $MAX_FD in #(
148 | max*)
149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
150 | # shellcheck disable=SC2039,SC3045
151 | MAX_FD=$( ulimit -H -n ) ||
152 | warn "Could not query maximum file descriptor limit"
153 | esac
154 | case $MAX_FD in #(
155 | '' | soft) :;; #(
156 | *)
157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
158 | # shellcheck disable=SC2039,SC3045
159 | ulimit -n "$MAX_FD" ||
160 | warn "Could not set maximum file descriptor limit to $MAX_FD"
161 | esac
162 | fi
163 |
164 | # Collect all arguments for the java command, stacking in reverse order:
165 | # * args from the command line
166 | # * the main class name
167 | # * -classpath
168 | # * -D...appname settings
169 | # * --module-path (only if needed)
170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
171 |
172 | # For Cygwin or MSYS, switch paths to Windows format before running java
173 | if "$cygwin" || "$msys" ; then
174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
176 |
177 | JAVACMD=$( cygpath --unix "$JAVACMD" )
178 |
179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
180 | for arg do
181 | if
182 | case $arg in #(
183 | -*) false ;; # don't mess with options #(
184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
185 | [ -e "$t" ] ;; #(
186 | *) false ;;
187 | esac
188 | then
189 | arg=$( cygpath --path --ignore --mixed "$arg" )
190 | fi
191 | # Roll the args list around exactly as many times as the number of
192 | # args, so each arg winds up back in the position where it started, but
193 | # possibly modified.
194 | #
195 | # NB: a `for` loop captures its iteration list before it begins, so
196 | # changing the positional parameters here affects neither the number of
197 | # iterations, nor the values presented in `arg`.
198 | shift # remove old arg
199 | set -- "$@" "$arg" # push replacement arg
200 | done
201 | fi
202 |
203 |
204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
206 |
207 | # Collect all arguments for the java command:
208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
209 | # and any embedded shellness will be escaped.
210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
211 | # treated as '${Hostname}' itself on the command line.
212 |
213 | set -- \
214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
215 | -classpath "$CLASSPATH" \
216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
217 | "$@"
218 |
219 | # Stop when "xargs" is not available.
220 | if ! command -v xargs >/dev/null 2>&1
221 | then
222 | die "xargs is not available"
223 | fi
224 |
225 | # Use "xargs" to parse quoted args.
226 | #
227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
228 | #
229 | # In Bash we could simply go:
230 | #
231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
232 | # set -- "${ARGS[@]}" "$@"
233 | #
234 | # but POSIX shell has neither arrays nor command substitution, so instead we
235 | # post-process each arg (as a line of input to sed) to backslash-escape any
236 | # character that might be a shell metacharacter, then use eval to reverse
237 | # that process (while maintaining the separation between arguments), and wrap
238 | # the whole thing up as a single "set" statement.
239 | #
240 | # This will of course break if any of these variables contains a newline or
241 | # an unmatched quote.
242 | #
243 |
244 | eval "set -- $(
245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
246 | xargs -n1 |
247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
248 | tr '\n' ' '
249 | )" '"$@"'
250 |
251 | exec "$JAVACMD" "$@"
252 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdLauncher.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.execution.ExecutionException;
4 | import com.intellij.execution.configurations.CommandLineState;
5 | import com.intellij.execution.configurations.GeneralCommandLine;
6 | import com.intellij.execution.filters.Filter;
7 | import com.intellij.execution.process.OSProcessHandler;
8 | import com.intellij.execution.process.ProcessEvent;
9 | import com.intellij.execution.process.ProcessHandler;
10 | import com.intellij.execution.process.ProcessListener;
11 | import com.intellij.execution.ui.ExecutionConsole;
12 | import com.intellij.openapi.actionSystem.AnAction;
13 | import com.intellij.openapi.actionSystem.AnActionEvent;
14 | import com.intellij.openapi.application.ApplicationManager;
15 | import com.intellij.openapi.options.ConfigurationException;
16 | import com.intellij.openapi.progress.ProgressManager;
17 | import com.intellij.openapi.project.Project;
18 | import com.intellij.openapi.util.IconLoader;
19 | import com.intellij.openapi.util.Key;
20 | import com.intellij.openapi.util.ThrowableComputable;
21 | import com.intellij.xdebugger.XDebugProcess;
22 | import com.intellij.xdebugger.XDebugSession;
23 | import com.jetbrains.cidr.cpp.execution.CMakeAppRunConfiguration;
24 | import com.jetbrains.cidr.cpp.execution.debugger.backend.CLionGDBDriverConfiguration;
25 | import com.jetbrains.cidr.cpp.toolchains.CPPDebugger;
26 | import com.jetbrains.cidr.cpp.toolchains.CPPToolchains;
27 | import com.jetbrains.cidr.execution.CidrLauncher;
28 | import com.jetbrains.cidr.execution.debugger.CidrDebugProcess;
29 | import com.jetbrains.cidr.execution.debugger.CidrDebuggerPathManager;
30 | import com.jetbrains.cidr.execution.debugger.backend.DebuggerCommandException;
31 | import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
32 | import com.jetbrains.cidr.execution.debugger.remote.CidrRemoteDebugParameters;
33 | import com.jetbrains.cidr.execution.debugger.remote.CidrRemoteGDBDebugProcess;
34 | import esp32.embedded.clion.openocd.OpenOcdConfiguration.DownloadType;
35 | import java.io.File;
36 | import java.util.List;
37 | import java.util.concurrent.Future;
38 | import java.util.concurrent.TimeUnit;
39 | import java.util.concurrent.TimeoutException;
40 | import java.util.concurrent.atomic.AtomicReference;
41 | import org.jetbrains.annotations.NotNull;
42 |
43 | /**
44 | * (c) elmot on 19.10.2017.
45 | */
46 |
47 | class OpenOcdLauncher extends CidrLauncher {
48 |
49 | private static final Key RESTART_KEY = Key.create(OpenOcdLauncher.class.getName() + "#restartAction");
50 | private final OpenOcdConfiguration openOcdConfiguration;
51 |
52 | OpenOcdLauncher(OpenOcdConfiguration openOcdConfiguration) {
53 | this.openOcdConfiguration = openOcdConfiguration;
54 | }
55 |
56 | @Override
57 | protected ProcessHandler createProcess(@NotNull CommandLineState commandLineState) throws ExecutionException {
58 | File runFile = findRunFile(commandLineState);
59 | findOpenOcdAction(commandLineState.getEnvironment().getProject()).stopOpenOcd();
60 | try {
61 | GeneralCommandLine commandLine = OpenOcdComponent
62 | .createOcdCommandLine(openOcdConfiguration,
63 | runFile, "reset", true);
64 | OSProcessHandler osProcessHandler = new OSProcessHandler(commandLine);
65 | osProcessHandler.addProcessListener(new ProcessListener() {
66 | @Override
67 | public void processTerminated(@NotNull ProcessEvent event) {
68 | Project project = commandLineState.getEnvironment().getProject();
69 | if (event.getExitCode() == 0) {
70 | Informational.showSuccessfulDownloadNotification(project);
71 | } else {
72 | Informational.showFailedDownloadNotification(project);
73 | }
74 | }
75 | });
76 | return osProcessHandler;
77 | } catch (ConfigurationException e) {
78 | Informational.showPluginError(getProject(), e);
79 | throw new ExecutionException(e);
80 | }
81 | }
82 |
83 | @NotNull
84 | @Override
85 | protected CidrDebugProcess createDebugProcess(@NotNull CommandLineState commandLineState,
86 | @NotNull XDebugSession xDebugSession) throws ExecutionException {
87 | Project project = commandLineState.getEnvironment().getProject();
88 | OpenOcdSettingsState ocdSettings = project.getService(OpenOcdSettingsState.class);
89 | CidrRemoteDebugParameters remoteDebugParameters = new CidrRemoteDebugParameters();
90 |
91 | remoteDebugParameters.setSymbolFile(findRunFile(commandLineState).getAbsolutePath());
92 | remoteDebugParameters.setRemoteCommand("tcp:localhost:" + openOcdConfiguration.getGdbPort());
93 |
94 | CPPToolchains.Toolchain toolchain = openOcdConfiguration.getDebuggerData().getOrCreateDebuggerToolchain();
95 | if (ocdSettings.shippedGdb) {
96 | toolchain = toolchain.copy();
97 | File gdbFile = CidrDebuggerPathManager.getBundledGDBBinary();
98 | String gdbPath = gdbFile.getAbsolutePath();
99 | CPPDebugger cppDebugger = CPPDebugger.create(CPPDebugger.Kind.CUSTOM_GDB, gdbPath);
100 | toolchain.setDebugger(cppDebugger);
101 | }
102 | CLionGDBDriverConfiguration gdbDriverConfiguration = new CLionGDBDriverConfiguration(getProject(), toolchain);
103 |
104 | xDebugSession.stop();
105 |
106 | AtomicReference debugProcessRef = new AtomicReference<>();
107 | ApplicationManager.getApplication().invokeAndWait(() -> {
108 | try {
109 | debugProcessRef.set(new CidrRemoteGDBDebugProcess(gdbDriverConfiguration,
110 | remoteDebugParameters,
111 | xDebugSession,
112 | commandLineState.getConsoleBuilder(),
113 | project1 -> new Filter[0]));
114 | } catch (ExecutionException e) {
115 | throw new RuntimeException(e);
116 | }
117 | });
118 | CidrRemoteGDBDebugProcess debugProcess = debugProcessRef.get();
119 |
120 | debugProcess.getProcessHandler().addProcessListener(new ProcessListener() {
121 | @Override
122 | public void processWillTerminate(@NotNull ProcessEvent event, boolean willBeDestroyed) {
123 | findOpenOcdAction(project).stopOpenOcd();
124 | }
125 | });
126 |
127 | debugProcess.getProcessHandler().putUserData(RESTART_KEY,
128 | new AnAction("Reset", "MCU reset",
129 | IconLoader.findIcon("esp32/embedded/clion/openocd/reset.png", OpenOcdLauncher.class)) {
130 | @Override
131 | public void actionPerformed(@NotNull AnActionEvent e) {
132 | XDebugSession session = debugProcess.getSession();
133 | session.pause();
134 | debugProcess.postCommand(drv -> {
135 | try {
136 | ProgressManager.getInstance().runProcess(() -> {
137 | while (drv.getState() != DebuggerDriver.TargetState.SUSPENDED) {
138 | Thread.yield();
139 | }
140 | }, null);
141 | drv.executeInterpreterCommand("monitor reset init");
142 | session.resume();
143 | } catch (DebuggerCommandException exception) {
144 | Informational.showFailedDownloadNotification(e.getProject());
145 | }
146 | });
147 | }
148 | }
149 | );
150 |
151 | new Thread(() -> {
152 | while (!debugProcess.getCurrentStateMessage().equals("Connected")) {
153 | try {
154 | Thread.onSpinWait();
155 | } catch (Throwable ignored) {
156 | }
157 | }
158 |
159 | // Connected. Perform initialisation
160 | XDebugSession session = debugProcess.getSession();
161 |
162 | // Check if we need any init
163 | if (openOcdConfiguration.getResetType().needsInit()) {
164 | session.pause();
165 | debugProcess.postCommand(drv -> {
166 | try {
167 | ProgressManager.getInstance().runProcess(() -> {
168 | while (drv.getState() != DebuggerDriver.TargetState.SUSPENDED) {
169 | Thread.yield();
170 | }
171 | }, null);
172 |
173 | // Determine which commands need to be run
174 | if (openOcdConfiguration.getFlushRegs()) {
175 | drv.executeInterpreterCommand("flushregs");
176 | }
177 |
178 | if (openOcdConfiguration.getInitialBreak() && !openOcdConfiguration.getInitialBreakName().isEmpty()) {
179 | drv.executeInterpreterCommand("thb " + openOcdConfiguration.getInitialBreakName());
180 | }
181 |
182 | session.resume();
183 |
184 | } catch (DebuggerCommandException ignored) {
185 | }
186 | });
187 | }
188 |
189 | }).start();
190 |
191 | return debugProcess;
192 | }
193 |
194 | @NotNull
195 | private File findRunFile(CommandLineState commandLineState) throws ExecutionException {
196 | String targetProfileName = commandLineState.getExecutionTarget().getDisplayName();
197 | CMakeAppRunConfiguration.BuildAndRunConfigurations runConfigurations = openOcdConfiguration
198 | .getBuildAndRunConfigurations(targetProfileName);
199 | if (runConfigurations == null) {
200 | throw new ExecutionException("Target is not defined");
201 | }
202 | File runFile = runConfigurations.getRunFile(getProject());
203 | if (runFile == null) {
204 | throw new ExecutionException("Run file is not defined for " + runConfigurations);
205 | }
206 | if (!runFile.exists() || !runFile.isFile()) {
207 | throw new ExecutionException("Invalid run file " + runFile.getAbsolutePath());
208 | }
209 | return runFile;
210 | }
211 |
212 |
213 | @NotNull
214 | @Override
215 | public XDebugProcess startDebugProcess(@NotNull CommandLineState commandLineState,
216 | @NotNull XDebugSession xDebugSession) throws ExecutionException {
217 |
218 | File runFile = null;
219 | if (openOcdConfiguration.getDownloadType() != DownloadType.NONE) {
220 | runFile = findRunFile(commandLineState);
221 | if (openOcdConfiguration.getDownloadType() == DownloadType.UPDATED_ONLY &&
222 | OpenOcdComponent.isLatestUploaded(runFile)) {
223 | runFile = null;
224 | }
225 | }
226 |
227 | try {
228 | xDebugSession.stop();
229 | OpenOcdComponent openOcdComponent = findOpenOcdAction(commandLineState.getEnvironment().getProject());
230 | openOcdComponent.stopOpenOcd();
231 | Future downloadResult = openOcdComponent.startOpenOcd(openOcdConfiguration,
232 | runFile);
233 |
234 | ProgressManager progressManager = ProgressManager.getInstance();
235 | ThrowableComputable process = () -> {
236 | try {
237 | progressManager.getProgressIndicator().setIndeterminate(true);
238 | while (true) {
239 | try {
240 | return downloadResult.get(500, TimeUnit.MILLISECONDS);
241 | } catch (TimeoutException ignored) {
242 | ProgressManager.checkCanceled();
243 | }
244 | }
245 | } catch (InterruptedException | java.util.concurrent.ExecutionException e) {
246 | throw new ExecutionException(e);
247 | }
248 | };
249 | String progressTitle = runFile == null ? "Start OpenOCD" : "Firmware Download";
250 | OpenOcdComponent.Status downloadStatus = progressManager.runProcessWithProgressSynchronously(
251 | process, progressTitle, true, getProject());
252 | if (downloadStatus == OpenOcdComponent.Status.FLASH_ERROR) {
253 | downloadResult.cancel(true);
254 | throw new ExecutionException("OpenOCD cancelled");
255 | }
256 | return super.startDebugProcess(commandLineState, xDebugSession);
257 | } catch (ConfigurationException e) {
258 | Informational.showPluginError(getProject(), e);
259 | throw new ExecutionException(e);
260 | }
261 | }
262 |
263 | @Override
264 | protected void collectAdditionalActions(@NotNull CommandLineState state, @NotNull ProcessHandler processHandler,
265 | @NotNull ExecutionConsole console,
266 | @NotNull List super AnAction> actions) throws ExecutionException {
267 | super.collectAdditionalActions(state, processHandler, console, actions);
268 | AnAction restart = processHandler.getUserData(RESTART_KEY);
269 | if (restart != null) {
270 | actions.add(restart);
271 | }
272 | }
273 |
274 | private OpenOcdComponent findOpenOcdAction(Project project) {
275 | return project.getService(OpenOcdComponent.class);
276 | }
277 |
278 | @NotNull
279 | @Override
280 | public Project getProject() {
281 | return openOcdConfiguration.getProject();
282 | }
283 |
284 | }
285 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdConfigurationEditor.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.execution.ui.CommonProgramParametersPanel;
4 | import com.intellij.openapi.module.ModuleManager;
5 | import com.intellij.openapi.options.ConfigurationException;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.roots.ModuleRootManager;
8 | import com.intellij.openapi.vfs.VfsUtil;
9 | import com.intellij.openapi.vfs.VirtualFile;
10 | import com.intellij.ui.components.fields.ExtendableTextField;
11 | import com.intellij.ui.components.fields.IntegerField;
12 | import com.intellij.util.ui.GridBag;
13 | import com.jetbrains.cidr.cpp.execution.CMakeAppRunConfiguration;
14 | import com.jetbrains.cidr.cpp.execution.CMakeAppRunConfigurationSettingsEditor;
15 | import com.jetbrains.cidr.cpp.execution.CMakeBuildConfigurationHelper;
16 | import com.jetbrains.cidr.cpp.execution.remote.DebuggerData;
17 | import com.jetbrains.cidr.cpp.execution.remote.DebuggersComboBoxWithModel;
18 | import esp32.embedded.clion.openocd.OpenOcdConfiguration.DownloadType;
19 | import esp32.embedded.clion.openocd.OpenOcdConfiguration.ProgramType;
20 | import java.awt.Component;
21 | import java.awt.FlowLayout;
22 | import java.awt.GridLayout;
23 | import java.awt.event.ItemEvent;
24 | import java.util.Objects;
25 | import javax.swing.Box;
26 | import javax.swing.JCheckBox;
27 | import javax.swing.JLabel;
28 | import javax.swing.JPanel;
29 | import org.jdesktop.swingx.JXRadioGroup;
30 | import org.jetbrains.annotations.NotNull;
31 |
32 | public class OpenOcdConfigurationEditor extends CMakeAppRunConfigurationSettingsEditor {
33 | public static final String BOOTLOADER_FILE = "Bootloader file";
34 | public static final String PART_TABLE_FILE = "Partition Table file";
35 | private DebuggersComboBoxWithModel debuggers;
36 | private IntegerField gdbPort;
37 | private IntegerField telnetPort;
38 | private ExtendableTextField offset;
39 | private JXRadioGroup resetGroup;
40 | private JCheckBox flushRegsCheck;
41 | private JCheckBox initialBreakpointCheck;
42 | private ExtendableTextField initialBreakpointName;
43 | private FileChooseInput boardConfigFile;
44 | private FileChooseInput interfaceConfigFile;
45 |
46 | private FileChooseInput.BinFile bootloaderFile;
47 | private ExtendableTextField bootloaderOffset;
48 | private FileChooseInput.BinFile partitionTableFile;
49 | private ExtendableTextField partitionTableOffset;
50 | private String openocdHome;
51 | private JXRadioGroup downloadGroup;
52 |
53 | private JXRadioGroup programType;
54 | private JCheckBox appendVerify;
55 |
56 | private ExtendableTextField additionalProgramParameters;
57 |
58 |
59 | public OpenOcdConfigurationEditor(Project project,
60 | @NotNull CMakeBuildConfigurationHelper cMakeBuildConfigurationHelper) {
61 | super(project, cMakeBuildConfigurationHelper);
62 | }
63 |
64 | @Override
65 | protected void applyEditorTo(@NotNull CMakeAppRunConfiguration cMakeAppRunConfiguration) throws ConfigurationException {
66 | super.applyEditorTo(cMakeAppRunConfiguration);
67 |
68 | OpenOcdConfiguration ocdConfiguration = (OpenOcdConfiguration) cMakeAppRunConfiguration;
69 |
70 | String boardConfig = boardConfigFile.getText().trim();
71 | ocdConfiguration.setBoardConfigFile(boardConfig.isEmpty() ? null : boardConfig);
72 |
73 | String interfaceConfig = interfaceConfigFile.getText().trim();
74 | ocdConfiguration.setInterfaceConfigFile(interfaceConfig.isEmpty() ? null : interfaceConfig);
75 |
76 | String bootPath = bootloaderFile.getPath().trim();
77 | ocdConfiguration.setBootBinPath(bootPath.isEmpty() ? null : bootPath);
78 |
79 | String partPath = partitionTableFile.getPath().trim();
80 | ocdConfiguration.setPartitionBinPath(partPath.isEmpty() ? null : partPath);
81 |
82 | gdbPort.validateContent();
83 | telnetPort.validateContent();
84 |
85 | DebuggerData selectedDebugger = debuggers.getSelectedDebugger();
86 | ocdConfiguration.setDebuggerData(selectedDebugger);
87 |
88 | ocdConfiguration.setGdbPort(gdbPort.getValue());
89 | ocdConfiguration.setTelnetPort(telnetPort.getValue());
90 | ocdConfiguration.setDownloadType(downloadGroup.getSelectedValue());
91 | ocdConfiguration.setProgramType(programType.getSelectedValue());
92 | ocdConfiguration.setAppendVerify(appendVerify.isSelected());
93 | ocdConfiguration.setAdditionalProgramParameters(additionalProgramParameters.getText());
94 |
95 | ocdConfiguration.setOffset(offset.getText());
96 | ocdConfiguration.setBootOffset(bootloaderOffset.getText());
97 | ocdConfiguration.setPartitionOffset(partitionTableOffset.getText());
98 | ocdConfiguration.setResetType(resetGroup.getSelectedValue());
99 | ocdConfiguration.setFlushRegs(flushRegsCheck.isSelected());
100 | ocdConfiguration.setInitialBreak(initialBreakpointCheck.isSelected());
101 | ocdConfiguration.setInitialBreakName(initialBreakpointName.getText());
102 | }
103 |
104 | @Override
105 | protected void resetEditorFrom(@NotNull CMakeAppRunConfiguration cMakeAppRunConfiguration) {
106 | super.resetEditorFrom(cMakeAppRunConfiguration);
107 |
108 | OpenOcdConfiguration ocd = (OpenOcdConfiguration) cMakeAppRunConfiguration;
109 |
110 | openocdHome = ocd.getProject().getService(OpenOcdSettingsState.class).openOcdHome;
111 |
112 | boardConfigFile.setText(ocd.getBoardConfigFile());
113 | interfaceConfigFile.setText(ocd.getInterfaceConfigFile());
114 |
115 | ModuleRootManager manager = ModuleRootManager.getInstance(ModuleManager.getInstance(myProject).getModules()[0]);
116 | String root = Objects.requireNonNull(getContentRoot(manager)).getPath();
117 |
118 | String bootBinPath = ocd.getBootBinPath();
119 | if (bootBinPath != null)
120 | bootBinPath = bootBinPath.replaceAll(root + "/", "");
121 | bootloaderFile.setText(bootBinPath);
122 |
123 | String partitionPath = ocd.getPartitionBinPath();
124 | if (partitionPath != null)
125 | partitionPath = partitionPath.replaceAll(root + "/", "");
126 | partitionTableFile.setText(partitionPath);
127 |
128 | debuggers.resetModel(ocd.getDebuggerData());
129 |
130 | gdbPort.setText(String.valueOf(ocd.getGdbPort()));
131 |
132 | telnetPort.setText(String.valueOf(ocd.getTelnetPort()));
133 | downloadGroup.setSelectedValue(ocd.getDownloadType());
134 | programType.setSelectedValue(ocd.getProgramType());
135 | appendVerify.setSelected(ocd.getAppendVerify());
136 | if (ocd.getAdditionalProgramParameters() != null) {
137 | additionalProgramParameters.setText(ocd.getAdditionalProgramParameters());
138 | } else {
139 | additionalProgramParameters.setText("");
140 | }
141 |
142 | offset.setText(ocd.getOffset());
143 | bootloaderOffset.setText(ocd.getBootOffset());
144 | partitionTableOffset.setText(ocd.getPartitionOffset());
145 | resetGroup.setSelectedValue(ocd.getResetType());
146 | flushRegsCheck.setSelected(ocd.getFlushRegs());
147 | initialBreakpointCheck.setSelected(ocd.getInitialBreak());
148 | initialBreakpointName.setText(ocd.getInitialBreakName());
149 | }
150 |
151 | @Override
152 | protected void createEditorInner(JPanel panel, GridBag gridBag) {
153 | super.createEditorInner(panel, gridBag);
154 |
155 | for (Component component : panel.getComponents()) {
156 | if (component instanceof CommonProgramParametersPanel) {
157 | component.setVisible(false);//todo get rid of this hack
158 | }
159 | }
160 |
161 | this.debuggers = new DebuggersComboBoxWithModel(this.myProject, true, true);
162 | panel.add(new JLabel("Debugger:"), gridBag.nextLine().next());
163 | panel.add(debuggers.getComponent(), gridBag.next().coverLine());
164 |
165 | panel.add(new JLabel("Board config file:"), gridBag.nextLine().next());
166 | boardConfigFile = new FileChooseInput.BoardCfg("Board config", VfsUtil.getUserHomeDir(),
167 | this::getOpenocdHome);
168 | panel.add(boardConfigFile, gridBag.next().coverLine());
169 |
170 | panel.add(new JLabel("Interface config file:"), gridBag.nextLine().next());
171 | interfaceConfigFile = new FileChooseInput.InterfaceCfg("Interface config", VfsUtil.getUserHomeDir(),
172 | this::getOpenocdHome);
173 | panel.add(interfaceConfigFile, gridBag.next().coverLine());
174 |
175 | ModuleRootManager manager = ModuleRootManager.getInstance(ModuleManager.getInstance(myProject).getModules()[0]);
176 | VirtualFile contentRoot = getContentRoot(manager);
177 |
178 | JPanel bootloaderPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
179 | bootloaderPanel.add(new JLabel("Bootloader binary:"));
180 | bootloaderFile = new FileChooseInput.BinFile(BOOTLOADER_FILE, VfsUtil.getUserHomeDir(), contentRoot);
181 | bootloaderPanel.add(bootloaderFile);
182 |
183 | bootloaderPanel.add(new JLabel("Bootloader offset:"));
184 | bootloaderOffset = addOffsetInput(OpenOcdConfiguration.DEF_BOOT_OFFSET);
185 | bootloaderPanel.add(bootloaderOffset);
186 |
187 | panel.add(bootloaderPanel, gridBag.nextLine().next().coverLine());
188 |
189 | JPanel partitionPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
190 | partitionPanel.add(new JLabel("Partition Table binary:"));
191 | partitionTableFile = new FileChooseInput.BinFile(PART_TABLE_FILE, VfsUtil.getUserHomeDir(), contentRoot);
192 | partitionPanel.add(partitionTableFile);
193 |
194 | partitionPanel.add(new JLabel("Partition Table offset:"));
195 | partitionTableOffset = addOffsetInput(OpenOcdConfiguration.DEF_PART_OFFSET);
196 | partitionPanel.add(partitionTableOffset);
197 |
198 | panel.add(partitionPanel, gridBag.nextLine().next().coverLine());
199 |
200 | panel.add(new JLabel("OpenOCD command:"), gridBag.nextLine().next());
201 | programType = new JXRadioGroup<>(ProgramType.values());
202 | panel.add(programType, gridBag.next().coverLine());
203 |
204 | appendVerify = new JCheckBox("Append verify parameter", OpenOcdConfiguration.DEF_APPEND_VERIFY);
205 | panel.add(appendVerify, gridBag.nextLine().next());
206 |
207 | panel.add(new JLabel("Additional program parameters:"), gridBag.nextLine().next());
208 | additionalProgramParameters = new ExtendableTextField(OpenOcdConfiguration.DEF_ADD_PROG_PARAM);
209 | panel.add(additionalProgramParameters, gridBag.next().coverLine());
210 |
211 | JPanel portsPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
212 |
213 | gdbPort = addPortInput(portsPanel, "GDB port", OpenOcdConfiguration.DEF_GDB_PORT);
214 | portsPanel.add(Box.createHorizontalStrut(10));
215 |
216 | telnetPort = addPortInput(portsPanel, "Telnet port", OpenOcdConfiguration.DEF_TELNET_PORT);
217 |
218 | panel.add(portsPanel, gridBag.nextLine().next().coverLine());
219 |
220 | panel.add(new JLabel("Download Options"), gridBag.nextLine().next());
221 |
222 | panel.add(createDownloadSelector(), gridBag.nextLine().coverLine());
223 |
224 | panel.add(createGDBSettingsSelector(), gridBag.nextLine().coverLine());
225 | }
226 |
227 | @NotNull
228 | private JPanel createDownloadSelector() {
229 | JPanel downloadPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
230 | GridLayout downloadGrid = new GridLayout(3, 2);
231 | downloadPanel.setLayout(downloadGrid);
232 |
233 | downloadPanel.add(new JLabel("Offset:"));
234 | offset = addOffsetInput(OpenOcdConfiguration.DEF_PROGRAM_OFFSET);
235 | downloadPanel.add(offset);
236 |
237 | downloadPanel.add(new JLabel("Perform:"));
238 | downloadGroup = new JXRadioGroup<>(DownloadType.values());
239 | downloadPanel.add(downloadGroup);
240 |
241 | downloadPanel.add(new JLabel("Reset:"));
242 | resetGroup = new JXRadioGroup<>(OpenOcdConfiguration.ResetType.values());
243 | resetGroup.addActionListener(e -> {
244 | if (resetGroup.getSelectedValue().supportsBreakpoints()) {
245 | initialBreakpointCheck.setVisible(true);
246 | if (initialBreakpointCheck.isSelected()) initialBreakpointName.setVisible(true);
247 | } else {
248 | initialBreakpointCheck.setVisible(false);
249 | initialBreakpointName.setVisible(false);
250 | }
251 | });
252 | downloadPanel.add(resetGroup);
253 |
254 | return downloadPanel;
255 | }
256 |
257 | private VirtualFile getContentRoot(ModuleRootManager manager) {
258 | VirtualFile[] contentRoots = manager.getContentRoots();
259 | if (contentRoots.length > 0) return contentRoots[0];
260 |
261 | VirtualFile[] excludeRoots = manager.getExcludeRoots();
262 | if (excludeRoots.length > 0) return excludeRoots[0].getParent();
263 |
264 | throw new IllegalStateException("Cannot find content root!");
265 | }
266 |
267 | private JPanel createGDBSettingsSelector() {
268 | JPanel settingsPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
269 | GridLayout settingsGrid = new GridLayout(2, 4);
270 | settingsPanel.setLayout(settingsGrid);
271 |
272 | flushRegsCheck = new JCheckBox("Flush registers", OpenOcdConfiguration.DEF_FLUSH_REGS);
273 | settingsPanel.add(flushRegsCheck);
274 |
275 | settingsPanel.add(new JPanel()); // Placeholder
276 |
277 | initialBreakpointCheck = new JCheckBox("Break on function", OpenOcdConfiguration.DEF_BREAK_FUNCTION);
278 | initialBreakpointCheck.addItemListener(e -> initialBreakpointName.setVisible(e.getStateChange() == ItemEvent.SELECTED));
279 | settingsPanel.add(initialBreakpointCheck);
280 |
281 | initialBreakpointName = new ExtendableTextField(OpenOcdConfiguration.DEF_BREAK_FUNCTION_NAME);
282 | settingsPanel.add(initialBreakpointName);
283 |
284 | return settingsPanel;
285 | }
286 |
287 | private IntegerField addPortInput(JPanel portsPanel, String label, int defaultValue) {
288 | portsPanel.add(new JLabel(label + ": "));
289 | IntegerField field = new IntegerField(label, 1024, 65535);
290 | field.setDefaultValue(defaultValue);
291 | field.setColumns(5);
292 | portsPanel.add(field);
293 | return field;
294 | }
295 |
296 | private ExtendableTextField addOffsetInput(String defaultValue) {
297 | ExtendableTextField field = new ExtendableTextField(defaultValue);
298 | field.setColumns(5);
299 | return field;
300 | }
301 |
302 | private String getOpenocdHome() {
303 | return openocdHome;
304 | }
305 |
306 | }
307 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdComponent.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.execution.ExecutionException;
4 | import com.intellij.execution.RunContentExecutor;
5 | import com.intellij.execution.configurations.GeneralCommandLine;
6 | import com.intellij.execution.configurations.PtyCommandLine;
7 | import com.intellij.execution.filters.Filter;
8 | import com.intellij.execution.process.OSProcessHandler;
9 | import com.intellij.execution.process.ProcessEvent;
10 | import com.intellij.execution.process.ProcessListener;
11 | import com.intellij.execution.ui.ConsoleViewContentType;
12 | import com.intellij.execution.util.ExecutionErrorDialog;
13 | import com.intellij.openapi.application.ApplicationManager;
14 | import com.intellij.openapi.components.Service;
15 | import com.intellij.openapi.diagnostic.Logger;
16 | import com.intellij.openapi.editor.colors.EditorColorsManager;
17 | import com.intellij.openapi.editor.colors.EditorColorsScheme;
18 | import com.intellij.openapi.editor.markup.HighlighterLayer;
19 | import com.intellij.openapi.options.ConfigurationException;
20 | import com.intellij.openapi.progress.ProgressManager;
21 | import com.intellij.openapi.project.Project;
22 | import com.intellij.openapi.util.Key;
23 | import com.intellij.openapi.util.text.StringUtil;
24 | import com.intellij.openapi.vfs.LocalFileSystem;
25 | import com.intellij.openapi.vfs.VfsUtil;
26 | import com.intellij.openapi.vfs.VirtualFile;
27 | import java.io.File;
28 | import java.util.Objects;
29 | import java.util.concurrent.CompletableFuture;
30 | import java.util.concurrent.Future;
31 | import org.jdesktop.swingx.util.OS;
32 | import org.jetbrains.annotations.NotNull;
33 | import org.jetbrains.annotations.Nullable;
34 |
35 | @Service(Service.Level.PROJECT)
36 | public final class OpenOcdComponent {
37 |
38 | public static final String SCRIPTS_PATH_SHORT = "scripts";
39 | public static final String SCRIPTS_PATH_MEDIUM = "openocd/" + SCRIPTS_PATH_SHORT;
40 | public static final String SCRIPTS_PATH_LONG = "share/openocd/" + SCRIPTS_PATH_SHORT;
41 | public static final String BIN_OPENOCD;
42 | private static final Key UPLOAD_LOAD_COUNT_KEY = new Key<>(OpenOcdConfiguration.class.getName() +
43 | "#LAST_DOWNLOAD_MOD_COUNT");
44 | private static final String ERROR_PREFIX = "Error: ";
45 | private static final String[] IGNORED_STRINGS = {
46 | "clearing lockup after double fault",
47 | "LIB_USB_NOT_SUPPORTED"};
48 |
49 | private final static String[] FAIL_STRINGS = {
50 | "** Programming Failed **", "communication failure", "** OpenOCD init failed **"};
51 | private static final String FLASH_SUCCESS_TEXT = "** Program Flash Complete! **";
52 | private static final Logger LOG = Logger.getInstance(OpenOcdComponent.class);
53 | private static final String ADAPTER_SPEED = "adapter speed";
54 |
55 | static {
56 | BIN_OPENOCD = "bin/openocd" + (OS.isWindows() ? ".exe" : "");
57 | }
58 |
59 | private final EditorColorsScheme myColorsScheme;
60 | private OSProcessHandler process;
61 |
62 | public OpenOcdComponent() {
63 | myColorsScheme = EditorColorsManager.getInstance().getGlobalScheme();
64 | }
65 |
66 | @NotNull
67 | public static GeneralCommandLine createOcdCommandLine(OpenOcdConfiguration config, File fileToLoad,
68 | @Nullable String additionalCommand, boolean shutdown) throws ConfigurationException {
69 | Project project = config.getProject();
70 | OpenOcdSettingsState ocdSettings = project.getService(OpenOcdSettingsState.class);
71 | if (StringUtil.isEmpty(config.getBoardConfigFile())) {
72 | throw new ConfigurationException("Board Config file is not defined.", "OpenOCD Run Error");
73 | }
74 | VirtualFile ocdHome = require(LocalFileSystem.getInstance().findFileByPath(ocdSettings.openOcdHome));
75 | VirtualFile ocdBinary = require(ocdHome.findFileByRelativePath(BIN_OPENOCD));
76 | File ocdBinaryIo = VfsUtil.virtualToIoFile(ocdBinary);
77 | GeneralCommandLine commandLine = new PtyCommandLine()
78 | .withWorkDirectory(ocdBinaryIo.getParentFile())
79 | .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
80 | .withParameters("-c", "tcl_port disabled")
81 | .withExePath(ocdBinaryIo.getAbsolutePath());
82 |
83 | VirtualFile ocdScripts = require(OpenOcdSettingsState.findOcdScripts(ocdHome));
84 | commandLine.addParameters("-s", VfsUtil.virtualToIoFile(ocdScripts).getAbsolutePath());
85 | if (config.getGdbPort() != OpenOcdConfiguration.DEF_GDB_PORT) {
86 | commandLine.addParameters("-c", "gdb_port " + config.getGdbPort());
87 | }
88 | if (config.getTelnetPort() != OpenOcdConfiguration.DEF_TELNET_PORT) {
89 | commandLine.addParameters("-c", "telnet_port " + config.getTelnetPort());
90 | }
91 |
92 | if (!StringUtil.isEmpty(config.getInterfaceConfigFile())) {
93 | commandLine.addParameters("-f", config.getInterfaceConfigFile());
94 | }
95 |
96 | commandLine.addParameters("-f", config.getBoardConfigFile());
97 |
98 | if (!StringUtil.isEmpty(config.getBootBinPath())) {
99 | commandLine.addParameters("-c",
100 | config.getProgramType().toString() + " " + config.getBootBinPath() + " "
101 | + config.getBootOffset() + (config.getAppendVerify() ? " verify" : "") +
102 | (config.getAdditionalProgramParameters() != null ?
103 | " " + config.getAdditionalProgramParameters() : ""));
104 | }
105 | if (!StringUtil.isEmpty(config.getPartitionBinPath())) {
106 | commandLine.addParameters("-c",
107 | config.getProgramType().toString() + " " + config.getPartitionBinPath() + " "
108 | + config.getPartitionOffset() + (config.getAppendVerify() ? " verify" : "") +
109 | (config.getAdditionalProgramParameters() != null ?
110 | " " + config.getAdditionalProgramParameters() : ""));
111 | }
112 |
113 | if (fileToLoad != null) { // Program Command
114 | String command =
115 | config.getProgramType().toString() + " " + fileToLoad.getAbsolutePath()
116 | .replace(File.separatorChar, '/')
117 | .replace(".elf", ".bin");
118 | if (config.getOffset() != null && !config.getOffset().isEmpty())
119 | command = command + " " + config.getOffset();
120 |
121 | if (config.getAppendVerify()) {
122 | command += " verify";
123 | }
124 |
125 | if (config.getAdditionalProgramParameters() != null){
126 | command += " " + config.getAdditionalProgramParameters();
127 | }
128 |
129 | commandLine.addParameters("-c", command);
130 | }
131 |
132 | if (additionalCommand != null && !additionalCommand.isEmpty())
133 | commandLine.addParameters("-c", additionalCommand);
134 |
135 | commandLine.addParameters("-c", config.getResetType().getCommand());
136 |
137 | commandLine.addParameters("-c", "echo \"" + FLASH_SUCCESS_TEXT + "\"");
138 |
139 | if (shutdown) {
140 | commandLine.addParameters("-c", "shutdown");
141 | }
142 | return commandLine;
143 | }
144 |
145 | @NotNull
146 | public static VirtualFile require(VirtualFile fileToCheck) throws ConfigurationException {
147 | if (fileToCheck == null) {
148 | openOcdNotFound();
149 | }
150 | return fileToCheck;
151 | }
152 |
153 | private static void openOcdNotFound() throws ConfigurationException {
154 | throw new ConfigurationException("Please open settings dialog and fix OpenOCD home", "OpenOCD Config "
155 | + "Error");
156 | }
157 |
158 | public void stopOpenOcd() {
159 | if (process == null || process.isProcessTerminated() || process.isProcessTerminating())
160 | return;
161 | ProgressManager.getInstance().executeNonCancelableSection(() -> {
162 | process.destroyProcess();
163 | process.waitFor(1000);
164 | });
165 | }
166 |
167 | public Future startOpenOcd(OpenOcdConfiguration config, @Nullable File fileToLoad) throws
168 | ConfigurationException {
169 | CompletableFuture ret = new CompletableFuture<>();
170 | if (config == null) {
171 | ret.obtrudeValue(Status.FLASH_ERROR);
172 | return ret;
173 | }
174 | GeneralCommandLine commandLine = createOcdCommandLine(config, fileToLoad, null, false);
175 | if (process != null && !process.isProcessTerminated()) {
176 | LOG.info("OpenOCD is already run");
177 | ret.obtrudeValue(Status.FLASH_ERROR);
178 | return ret;
179 | }
180 | VirtualFile virtualFile = fileToLoad != null ? VfsUtil.findFileByIoFile(fileToLoad, true) : null;
181 | Project project = config.getProject();
182 | try {
183 | process = new OSProcessHandler(commandLine) {
184 | @Override
185 | public boolean isSilentlyDestroyOnClose() {
186 | return true;
187 | }
188 | };
189 | DownloadFollower downloadFollower = new DownloadFollower(virtualFile);
190 | process.addProcessListener(downloadFollower);
191 | RunContentExecutor openOCDConsole = new RunContentExecutor(project, process)
192 | .withTitle("OpenOCD Console")
193 | .withActivateToolWindow(true)
194 | .withFilter(new ErrorFilter(project))
195 | .withStop(process::destroyProcess,
196 | () -> !process.isProcessTerminated() && !process.isProcessTerminating());
197 |
198 | ApplicationManager.getApplication().invokeLater(openOCDConsole::run);
199 | ret.obtrudeValue(null); // Unneeded. Complete anyway.
200 | return downloadFollower;
201 | } catch (ExecutionException e) {
202 | ExecutionErrorDialog.show(e, "OpenOCD Start Failed", project);
203 | ret.obtrudeValue(Status.FLASH_ERROR);
204 | return ret;
205 | }
206 | }
207 |
208 | public enum Status {
209 | FLASH_SUCCESS,
210 | FLASH_WARNING,
211 | FLASH_ERROR,
212 | }
213 |
214 | public enum FlashedStatus {
215 | INITIALIZED, APP_OK;
216 |
217 | private FlashedStatus nextState;
218 |
219 | static {
220 | INITIALIZED.nextState = APP_OK;
221 | APP_OK.nextState = APP_OK;
222 | }
223 |
224 | public FlashedStatus getNextState() {
225 | return nextState;
226 | }
227 | }
228 |
229 | private class ErrorFilter implements Filter {
230 | private final Project project;
231 |
232 | ErrorFilter(Project project) {
233 | this.project = project;
234 | }
235 |
236 | /**
237 | * Filters line by creating an instance of {@link Result}.
238 | *
239 | * @param line The line to be filtered. Note that the line must contain a line
240 | * separator at the end.
241 | * @param entireLength The length of the entire text including the line passed for filtration.
242 | * @return null , if there was no match, otherwise, an instance of {@link Result}
243 | */
244 | @Nullable
245 | @Override
246 | public Result applyFilter(@NotNull String line, int entireLength) {
247 | if (containsOneOf(line, FAIL_STRINGS)) {
248 | Informational.showFailedDownloadNotification(project);
249 | return new Result(0, line.length(), null,
250 | myColorsScheme.getAttributes(ConsoleViewContentType.ERROR_OUTPUT_KEY)) {
251 | @Override
252 | public int getHighlighterLayer() {
253 | return HighlighterLayer.ERROR;
254 | }
255 | };
256 | } else if (line.equals(FLASH_SUCCESS_TEXT)) {
257 | Informational.showSuccessfulDownloadNotification(project);
258 | }
259 | return null;
260 | }
261 | }
262 |
263 | private class DownloadFollower extends CompletableFuture implements ProcessListener {
264 | @Nullable
265 | private final VirtualFile vRunFile;
266 |
267 | private FlashedStatus flashedStatusListen = FlashedStatus.INITIALIZED;
268 |
269 | DownloadFollower(@Nullable VirtualFile vRunFile) {
270 | this.vRunFile = vRunFile;
271 | }
272 |
273 | @Override
274 | public void startNotified(@NotNull ProcessEvent event) {
275 | //nothing to do
276 | }
277 |
278 | @Override
279 | public void processTerminated(@NotNull ProcessEvent event) {
280 | try {
281 | if (!isDone()) {
282 | obtrudeValue(Status.FLASH_ERROR);
283 | }
284 | } catch (Exception e) {
285 | obtrudeValue(Status.FLASH_ERROR);
286 | }
287 | }
288 |
289 | @Override
290 | public void processWillTerminate(@NotNull ProcessEvent event, boolean willBeDestroyed) {
291 | //nothing to do
292 | }
293 |
294 | @Override
295 | public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
296 | String text = event.getText().trim();
297 | if (containsOneOf(text, FAIL_STRINGS)) {
298 | obtrudeValue(Status.FLASH_ERROR);
299 | } else if (vRunFile == null && text.startsWith(ADAPTER_SPEED)) {
300 | obtrudeValue(Status.FLASH_SUCCESS);
301 | } else if (text.contains(FLASH_SUCCESS_TEXT)) {
302 | if (flashedStatusListen == FlashedStatus.APP_OK) {
303 | if (vRunFile != null) {
304 | UPLOAD_LOAD_COUNT_KEY.set(vRunFile, vRunFile.getModificationCount());
305 | }
306 | obtrudeValue(Status.FLASH_SUCCESS);
307 | }
308 | flashedStatusListen = flashedStatusListen.getNextState();
309 | } else if (text.startsWith(ERROR_PREFIX) && !containsOneOf(text, IGNORED_STRINGS)) {
310 | obtrudeValue(Status.FLASH_WARNING);
311 | }
312 | }
313 | }
314 |
315 | private boolean containsOneOf(String text, String[] sampleStrings) {
316 | if (text == null || text.isEmpty()) {
317 | return false;
318 | }
319 | for (String sampleString : sampleStrings) {
320 | if (text.contains(sampleString)) return true;
321 | }
322 | return false;
323 | }
324 |
325 | public static boolean isLatestUploaded(File runFile) {
326 | VirtualFile vRunFile = VfsUtil.findFileByIoFile(runFile, true);
327 | Long latestDownloadModCount = UPLOAD_LOAD_COUNT_KEY.get(vRunFile);
328 | return vRunFile != null && Objects.equals(latestDownloadModCount, vRunFile.getModificationCount());
329 | }
330 |
331 | }
332 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/FileChooseInput.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.openapi.fileChooser.FileChooser;
4 | import com.intellij.openapi.fileChooser.FileChooserDescriptor;
5 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
6 | import com.intellij.openapi.options.ConfigurationException;
7 | import com.intellij.openapi.ui.TextFieldWithBrowseButton;
8 | import com.intellij.openapi.util.InvalidDataException;
9 | import com.intellij.openapi.util.SystemInfo;
10 | import com.intellij.openapi.util.text.StringUtil;
11 | import com.intellij.openapi.vfs.LocalFileSystem;
12 | import com.intellij.openapi.vfs.VfsUtil;
13 | import com.intellij.openapi.vfs.VirtualFile;
14 | import com.intellij.ui.components.JBTextField;
15 | import com.intellij.ui.components.fields.valueEditors.TextFieldValueEditor;
16 | import java.io.File;
17 | import java.util.function.Supplier;
18 | import org.jetbrains.annotations.NotNull;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | public abstract class FileChooseInput extends TextFieldWithBrowseButton {
22 |
23 | public static final String BOARD_FOLDER = "board";
24 | public static final String INTERFACE_FOLDER = "interface";
25 | public static final String BOOT_BIN_FOLDER = "bootloader";
26 | public static final String PART_BIN_FOLDER = "partition_table";
27 | protected final TextFieldValueEditor editor;
28 | private final FileChooserDescriptor fileDescriptor;
29 |
30 | protected FileChooseInput(String valueName, VirtualFile defValue) {
31 | super(new JBTextField());
32 |
33 | editor = new FileTextFieldValueEditor(valueName, defValue);
34 | fileDescriptor = createFileChooserDescriptor().withFileFilter(this::validateFile);
35 | installPathCompletion(fileDescriptor);
36 | addActionListener(e -> {
37 | VirtualFile virtualFile = null;
38 | String text = getTextField().getText();
39 | if (text != null && !text.isEmpty())
40 | try {
41 | virtualFile = parseTextToFile(text);
42 | } catch (InvalidDataException ignored) {
43 | virtualFile = LocalFileSystem.getInstance().findFileByPath(text);
44 | }
45 | if (virtualFile == null) {
46 | virtualFile = getDefaultLocation();
47 | }
48 | VirtualFile chosenFile = FileChooser.chooseFile(fileDescriptor, null, virtualFile);
49 | if (chosenFile != null) {
50 | getTextField().setText(fileToTextValue(chosenFile));
51 | }
52 | });
53 | }
54 |
55 | protected VirtualFile getDefaultLocation() {
56 | return VfsUtil.getUserHomeDir();
57 | }
58 |
59 | protected String fileToTextValue(VirtualFile file) {
60 | return file.getCanonicalPath();
61 | }
62 |
63 | protected abstract boolean validateFile(VirtualFile virtualFile);
64 |
65 | protected abstract FileChooserDescriptor createFileChooserDescriptor();
66 |
67 | public String getValueName() {
68 | return editor.getValueName();
69 | }
70 |
71 | public void validateContent() throws ConfigurationException {
72 | editor.validateContent();
73 | }
74 |
75 | @NotNull
76 | protected VirtualFile parseTextToFile(@Nullable String text) {
77 | VirtualFile file = text == null ? editor.getDefaultValue() :
78 | LocalFileSystem.getInstance().findFileByPath(text);
79 | if (file == null || !validateFile(file)) {
80 | throw new InvalidDataException("is invalid");
81 | }
82 | return file;
83 | }
84 |
85 | public static class BoardCfg extends FileChooseInput {
86 |
87 | private final Supplier ocdHome;
88 |
89 | public BoardCfg(String valueName, VirtualFile defValue, Supplier ocdHome) {
90 | super(valueName, defValue);
91 | this.ocdHome = ocdHome;
92 | }
93 |
94 | @Override
95 | protected VirtualFile getDefaultLocation() {
96 | VirtualFile ocdScripts = findOcdScripts();
97 | if (ocdScripts != null) {
98 | VirtualFile ocdBoards = ocdScripts.findFileByRelativePath(BOARD_FOLDER);
99 | if (ocdBoards != null) {
100 | return ocdBoards;
101 | }
102 | }
103 | return super.getDefaultLocation();
104 | }
105 |
106 | @NotNull
107 | @Override
108 | protected VirtualFile parseTextToFile(@Nullable String text) {
109 | VirtualFile file;
110 | if (text == null) {
111 | file = editor.getDefaultValue();
112 | } else {
113 | file = LocalFileSystem.getInstance().findFileByPath(text);
114 | if (file == null) {
115 | VirtualFile ocdScripts = findOcdScripts();
116 | if (ocdScripts != null) {
117 | file = ocdScripts.findFileByRelativePath(text);
118 | }
119 | }
120 | }
121 | if (file == null || !validateFile(file)) {
122 | throw new InvalidDataException("is invalid");
123 | }
124 | return file;
125 | }
126 |
127 | private VirtualFile getOpenOcdHome() {
128 | return LocalFileSystem.getInstance().findFileByPath(ocdHome.get());
129 | }
130 |
131 | @Override
132 | protected boolean validateFile(VirtualFile virtualFile) {
133 | return virtualFile.exists() && !virtualFile.isDirectory();
134 | }
135 |
136 | @Override
137 | protected FileChooserDescriptor createFileChooserDescriptor() {
138 | return FileChooserDescriptorFactory.singleFile();
139 | }
140 |
141 | @Override
142 | protected String fileToTextValue(VirtualFile file) {
143 | String completeFileName = super.fileToTextValue(file);
144 | VirtualFile ocdScripts = findOcdScripts();
145 | if (ocdScripts != null) {
146 | String relativePath = VfsUtil.getRelativePath(file, ocdScripts);
147 | if (relativePath != null) {
148 | return relativePath;
149 | }
150 | }
151 | return completeFileName;
152 | }
153 |
154 | @Nullable
155 | private VirtualFile findOcdScripts() {
156 | return OpenOcdSettingsState.findOcdScripts(getOpenOcdHome());
157 | }
158 |
159 | }
160 |
161 | public static class InterfaceCfg extends FileChooseInput {
162 |
163 | private final Supplier ocdHome;
164 |
165 | public InterfaceCfg(String valueName, VirtualFile defValue, Supplier ocdHome) {
166 | super(valueName, defValue);
167 | this.ocdHome = ocdHome;
168 | }
169 |
170 | @Override
171 | protected VirtualFile getDefaultLocation() {
172 | VirtualFile ocdScripts = findOcdScripts();
173 | if (ocdScripts != null) {
174 | VirtualFile ocdInterfaces = ocdScripts.findFileByRelativePath(INTERFACE_FOLDER);
175 | if (ocdInterfaces != null) {
176 | return ocdInterfaces;
177 | }
178 | }
179 | return super.getDefaultLocation();
180 | }
181 |
182 | @NotNull
183 | @Override
184 | protected VirtualFile parseTextToFile(@Nullable String text) {
185 | VirtualFile file;
186 | if (text == null) {
187 | file = editor.getDefaultValue();
188 | } else {
189 | file = LocalFileSystem.getInstance().findFileByPath(text);
190 | if (file == null) {
191 | VirtualFile ocdScripts = findOcdScripts();
192 | if (ocdScripts != null) {
193 | file = ocdScripts.findFileByRelativePath(text);
194 | }
195 | }
196 | }
197 | if (file == null || !validateFile(file)) {
198 | throw new InvalidDataException("is invalid");
199 | }
200 | return file;
201 | }
202 |
203 | private VirtualFile getOpenOcdHome() {
204 | return LocalFileSystem.getInstance().findFileByPath(ocdHome.get());
205 | }
206 |
207 | @Override
208 | protected boolean validateFile(VirtualFile virtualFile) {
209 | return virtualFile.exists() && !virtualFile.isDirectory();
210 | }
211 |
212 | @Override
213 | protected FileChooserDescriptor createFileChooserDescriptor() {
214 | return FileChooserDescriptorFactory.singleFile();
215 | }
216 |
217 | @Override
218 | protected String fileToTextValue(VirtualFile file) {
219 | String completeFileName = super.fileToTextValue(file);
220 | VirtualFile ocdScripts = findOcdScripts();
221 | if (ocdScripts != null) {
222 | String relativePath = VfsUtil.getRelativePath(file, ocdScripts);
223 | if (relativePath != null) {
224 | return relativePath;
225 | }
226 | }
227 | return completeFileName;
228 | }
229 |
230 | @Nullable
231 | private VirtualFile findOcdScripts() {
232 | return OpenOcdSettingsState.findOcdScripts(getOpenOcdHome());
233 | }
234 |
235 | }
236 |
237 | public static class BinFile extends FileChooseInput {
238 |
239 | private final VirtualFile projectHome;
240 |
241 | public BinFile(String valueName, VirtualFile defValue, VirtualFile projectHome) {
242 | super(valueName, defValue);
243 | this.projectHome = projectHome;
244 | }
245 |
246 | public String getPath() {
247 | String text = getText();
248 | if (StringUtil.isEmpty(text)) {
249 | return "";
250 | }
251 | if (new File(text).isAbsolute()) {
252 | return text;
253 | }
254 | // return Objects.requireNonNull(projectHome.findFileByRelativePath(text)).getPath();
255 | return projectHome.getPath() + "/" + text;
256 | }
257 |
258 | @Override
259 | protected VirtualFile getDefaultLocation() {
260 | if (projectHome != null) {
261 | String valueName = getValueName();
262 | if (valueName.equals(OpenOcdConfigurationEditor.BOOTLOADER_FILE)) {
263 | VirtualFile bin = projectHome.findFileByRelativePath(BOOT_BIN_FOLDER);
264 | if (bin != null) return bin;
265 | }
266 | if (valueName.equals(OpenOcdConfigurationEditor.PART_TABLE_FILE)) {
267 | VirtualFile bin = projectHome.findFileByRelativePath(PART_BIN_FOLDER);
268 | if (bin != null) return bin;
269 | }
270 | }
271 | return super.getDefaultLocation();
272 | }
273 |
274 | @NotNull
275 | @Override
276 | protected VirtualFile parseTextToFile(@Nullable String text) {
277 | VirtualFile file;
278 | if (text == null) {
279 | file = editor.getDefaultValue();
280 | } else {
281 | file = LocalFileSystem.getInstance().findFileByPath(text);
282 | if (file == null) {
283 | if (projectHome != null) {
284 | file = projectHome.findFileByRelativePath(text);
285 | }
286 | }
287 | }
288 | if (file == null || !validateFile(file)) {
289 | throw new InvalidDataException("is invalid");
290 | }
291 | return file;
292 | }
293 |
294 | @Override
295 | protected boolean validateFile(VirtualFile virtualFile) {
296 | return virtualFile.exists() && !virtualFile.isDirectory();
297 | }
298 |
299 | @Override
300 | protected FileChooserDescriptor createFileChooserDescriptor() {
301 | if (SystemInfo.isWindows) {
302 | return FileChooserDescriptorFactory.createSingleFileDescriptor("bin");
303 | } else {
304 | return FileChooserDescriptorFactory.singleFile();
305 | }
306 | }
307 |
308 | @Override
309 | protected String fileToTextValue(VirtualFile file) {
310 | String completeFileName = super.fileToTextValue(file);
311 | if (projectHome != null) {
312 | String relativePath = VfsUtil.getRelativePath(file, projectHome);
313 | if (relativePath != null) {
314 | return relativePath;
315 | }
316 | }
317 | return completeFileName;
318 | }
319 | }
320 |
321 | // Might be used in future
322 | public static class ExeFile extends FileChooseInput {
323 |
324 | public ExeFile(String valueName, VirtualFile defValue) {
325 | super(valueName, defValue);
326 | }
327 |
328 | @Override
329 | public boolean validateFile(VirtualFile virtualFile) {
330 | return virtualFile.exists() && !virtualFile.isDirectory()
331 | && VfsUtil.virtualToIoFile(virtualFile).canExecute();
332 | }
333 |
334 | @Override
335 | protected FileChooserDescriptor createFileChooserDescriptor() {
336 | if (SystemInfo.isWindows) {
337 | return FileChooserDescriptorFactory.createSingleFileDescriptor("exe");
338 | } else {
339 | return FileChooserDescriptorFactory.singleFile();
340 | }
341 | }
342 | }
343 |
344 | public static class OpenOcdHome extends FileChooseInput {
345 |
346 | public OpenOcdHome(String valueName, VirtualFile defValue) {
347 | super(valueName, defValue);
348 | }
349 |
350 | @Override
351 | public boolean validateFile(VirtualFile virtualFile) {
352 | if (!virtualFile.isDirectory()) return false;
353 | VirtualFile openOcdBinary = virtualFile.findFileByRelativePath(OpenOcdComponent.BIN_OPENOCD);
354 | if (openOcdBinary == null || openOcdBinary.isDirectory()
355 | || !VfsUtil.virtualToIoFile(openOcdBinary).canExecute()) return false;
356 | VirtualFile ocdScripts = OpenOcdSettingsState.findOcdScripts(virtualFile);
357 | if (ocdScripts != null) {
358 | VirtualFile ocdBoard = ocdScripts.findFileByRelativePath(BOARD_FOLDER);
359 | return ocdBoard != null && ocdBoard.isDirectory();
360 | }
361 | return false;
362 | }
363 |
364 | @Override
365 | protected FileChooserDescriptor createFileChooserDescriptor() {
366 | return FileChooserDescriptorFactory.createSingleFolderDescriptor();
367 | }
368 | }
369 |
370 | private class FileTextFieldValueEditor extends TextFieldValueEditor {
371 | FileTextFieldValueEditor(String valueName, VirtualFile defValue) {
372 | super(FileChooseInput.this.getTextField(), valueName, defValue);
373 | }
374 |
375 | @NotNull
376 | @Override
377 | public VirtualFile parseValue(@Nullable String text) {
378 | return parseTextToFile(text);
379 | }
380 |
381 | @Override
382 | public String valueToString(@NotNull VirtualFile value) {
383 | return value.getPath();
384 | }
385 |
386 | @Override
387 | public boolean isValid(@NotNull VirtualFile virtualFile) {
388 | return FileChooseInput.this.validateFile(virtualFile);
389 | }
390 | }
391 | }
392 |
--------------------------------------------------------------------------------
/src/main/java/esp32/embedded/clion/openocd/OpenOcdConfiguration.java:
--------------------------------------------------------------------------------
1 | package esp32.embedded.clion.openocd;
2 |
3 | import com.intellij.execution.Executor;
4 | import com.intellij.execution.configurations.ConfigurationFactory;
5 | import com.intellij.execution.configurations.RuntimeConfigurationException;
6 | import com.intellij.execution.runners.ExecutionEnvironment;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.util.InvalidDataException;
9 | import com.intellij.openapi.util.WriteExternalException;
10 | import com.intellij.openapi.util.text.StringUtil;
11 | import com.jetbrains.cidr.cpp.execution.CMakeAppRunConfiguration;
12 | import com.jetbrains.cidr.cpp.execution.debugger.CLionDebuggerKind;
13 | import com.jetbrains.cidr.cpp.execution.remote.DebuggerData;
14 | import com.jetbrains.cidr.execution.CidrCommandLineState;
15 | import com.jetbrains.cidr.execution.CidrExecutableDataHolder;
16 | import java.util.Objects;
17 | import org.jdom.Element;
18 | import org.jetbrains.annotations.NotNull;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | /**
22 | * (c) elmot on 29.9.2017.
23 | */
24 | public class OpenOcdConfiguration extends CMakeAppRunConfiguration implements CidrExecutableDataHolder {
25 | public static final DebuggerData DEF_DEBUGGER = new DebuggerData(CLionDebuggerKind.Bundled.GDB.INSTANCE);
26 | public static final int DEF_GDB_PORT = 3333;
27 | public static final int DEF_TELNET_PORT = 4444;
28 | public static final DownloadType DEF_DOWNLOAD_TYPE = DownloadType.ALWAYS;
29 | public static final String DEF_PROGRAM_OFFSET = "0x10000";
30 | public static final String DEF_BOOT_OFFSET = "0x0";
31 | public static final String DEF_BOOT_BIN_PATH = "build/bootloader/bootloader.bin";
32 | public static final boolean DEF_BOOT_BIN_PATH_SET = false;
33 |
34 | public static final String DEF_PART_OFFSET = "0x8000";
35 | public static final String DEF_PART_BIN_PATH = "build/partition_table/partition-table.bin";
36 | public static final boolean DEF_PART_BIN_PATH_SET = false;
37 | public static final ResetType DEF_RESET_TYPE = ResetType.HALT;
38 | public static final boolean DEF_FLUSH_REGS = true;
39 | public static final boolean DEF_BREAK_FUNCTION = true;
40 | public static final String DEF_BREAK_FUNCTION_NAME = "app_main";
41 |
42 | public static final ProgramType DEF_PROGRAM_TYPE = ProgramType.PROGRAM_ESP;
43 | public static final boolean DEF_APPEND_VERIFY = true;
44 | public static final String DEF_ADD_PROG_PARAM = "";
45 | public static final boolean DEF_ADD_PROG_PARAM_SET = false;
46 |
47 | private static final String ATTR_DEBUGGER = "gdb_debugger";
48 | private static final String ATTR_GDB_PORT = "gdb_port";
49 | private static final String ATTR_TELNET_PORT = "telnet_port";
50 | private static final String ATTR_BOARD_CONFIG = "board_config";
51 | private static final String ATTR_INTERFACE_CONFIG = "interface_config";
52 | private static final String ATTR_BOOT_PATH_SET_CONFIG = "boot_path_set_cfg";
53 | private static final String ATTR_BOOT_PATH_CONFIG = "boot_path_cfg";
54 | private static final String ATTR_BOOT_OFFSET_CONFIG = "boot_offset_cfg";
55 |
56 | private static final String ATTR_PART_PATH_SET_CONFIG = "part_path_set_cfg";
57 | private static final String ATTR_PART_PATH_CONFIG = "part_path_cfg";
58 | private static final String ATTR_PART_OFFSET_CONFIG = "part_offset_cfg";
59 | private static final String ATTR_PROGRAM_OFFSET_CONFIG = "program_offset_cfg";
60 | public static final String ATTR_DOWNLOAD_TYPE = "download_type";
61 | public static final String ATTR_RESET_TYPE = "reset_type";
62 | public static final String ATTR_FLUSH_REGS = "flush_regs";
63 | public static final String ATTR_BREAK_FUNCTION = "break";
64 | public static final String ATTR_BREAK_FUNCTION_NAME = "break_function";
65 |
66 | public static final String ATTR_PROGRAM_TYPE_CONFIG = "prog_type_cfg";
67 | public static final String ATTR_APPEND_VERIFY_CONFIG = "app_verify_cfg";
68 | public static final String ATTR_ADD_PROG_PARAM_CONFIG = "add_prog_param_cfg";
69 | public static final String ATTR_ADD_PROG_PARAM_SET_CONFIG = "add_prog_param_set_cfg";
70 |
71 |
72 | private DebuggerData debuggerData = DEF_DEBUGGER;
73 | private int gdbPort = DEF_GDB_PORT;
74 | private int telnetPort = DEF_TELNET_PORT;
75 | private String boardConfigFile;
76 | private String interfaceConfigFile;
77 | private DownloadType downloadType = DEF_DOWNLOAD_TYPE;
78 | private String offset = DEF_PROGRAM_OFFSET;
79 | private ResetType resetType = DEF_RESET_TYPE;
80 | private boolean flushRegs = DEF_FLUSH_REGS;
81 | private boolean initialBreak = DEF_BREAK_FUNCTION;
82 | private String initialBreakName = DEF_BREAK_FUNCTION_NAME;
83 |
84 | private String bootloaderOffset = DEF_BOOT_OFFSET;
85 | private String bootloaderBinPath = DEF_BOOT_BIN_PATH;
86 | private boolean bootloaderBinPathSet = DEF_BOOT_BIN_PATH_SET;
87 | private String partitionOffset = DEF_PART_OFFSET;
88 | private String partitionBinPath = DEF_PART_BIN_PATH;
89 | private boolean partitionBinPathSet = DEF_PART_BIN_PATH_SET;
90 |
91 | private ProgramType programType = DEF_PROGRAM_TYPE;
92 | private boolean appendVerify = DEF_APPEND_VERIFY;
93 | private String additionalProgramParameters = DEF_ADD_PROG_PARAM;
94 | private boolean additionalProgramParametersSet = DEF_ADD_PROG_PARAM_SET;
95 |
96 | public enum DownloadType {
97 |
98 | ALWAYS,
99 | UPDATED_ONLY,
100 | NONE;
101 |
102 | @Override
103 | public String toString() {
104 | return toBeautyString(super.toString());
105 | }
106 | }
107 |
108 | public enum ProgramType {
109 | PROGRAM_ESP,
110 | PROGRAM_ESP32;
111 |
112 | @Override
113 | public String toString() {
114 | return super.toString().toLowerCase();
115 | }
116 | }
117 |
118 | public static String toBeautyString(String obj) {
119 | return StringUtil.toTitleCase(obj.toLowerCase().replace("_", " "));
120 | }
121 |
122 | public enum ResetType {
123 | HALT("init; reset halt", true, true),
124 | RESET("init; reset"),
125 | RUN("init; reset run;"),
126 | INIT("init; reset init;"),
127 | NONE("");
128 |
129 | @Override
130 | public String toString() {
131 | return toBeautyString(super.toString());
132 | }
133 |
134 | ResetType(String command) {
135 | this(command, false, false);
136 | }
137 |
138 | ResetType(String command, boolean supportsBreakpoints, boolean needsInit) {
139 | this.command = command;
140 | this.supportsBreakpoints = supportsBreakpoints;
141 | this.needsInit = needsInit;
142 | }
143 |
144 | private final String command;
145 | private final boolean supportsBreakpoints;
146 | private final boolean needsInit;
147 |
148 | public final String getCommand() {
149 | return command;
150 | }
151 |
152 | public final boolean supportsBreakpoints() {
153 | return supportsBreakpoints;
154 | }
155 |
156 | public final boolean needsInit() {
157 | return needsInit;
158 | }
159 |
160 | }
161 |
162 | public OpenOcdConfiguration(Project project, ConfigurationFactory configurationFactory, String targetName) {
163 | super(project, configurationFactory, targetName);
164 | }
165 |
166 | @Nullable
167 | @Override
168 | public CidrCommandLineState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
169 | return new CidrCommandLineState(environment, new OpenOcdLauncher(this));
170 | }
171 |
172 | @Override
173 | public void readExternal(@NotNull Element element) throws InvalidDataException {
174 | super.readExternal(element);
175 | boardConfigFile = element.getAttributeValue(ATTR_BOARD_CONFIG);
176 | interfaceConfigFile = element.getAttributeValue(ATTR_INTERFACE_CONFIG);
177 |
178 | Element debuggerElement = element.getChild(ATTR_DEBUGGER);
179 | if (debuggerElement != null) {
180 | debuggerData.readExternal(debuggerElement);
181 | }
182 |
183 | gdbPort = readIntAttr(element, ATTR_GDB_PORT, DEF_GDB_PORT);
184 | telnetPort = readIntAttr(element, ATTR_TELNET_PORT, DEF_TELNET_PORT);
185 | downloadType = readEnumAttr(element, ATTR_DOWNLOAD_TYPE, DownloadType.ALWAYS);
186 | resetType = readEnumAttr(element, ATTR_RESET_TYPE, DEF_RESET_TYPE);
187 | flushRegs = readBoolAttr(element, ATTR_FLUSH_REGS, DEF_FLUSH_REGS);
188 | initialBreak = readBoolAttr(element, ATTR_BREAK_FUNCTION, DEF_BREAK_FUNCTION);
189 | initialBreakName = element.getAttributeValue(ATTR_BREAK_FUNCTION_NAME, null, DEF_BREAK_FUNCTION_NAME);
190 |
191 | offset = element.getAttributeValue(ATTR_PROGRAM_OFFSET_CONFIG, null, DEF_PROGRAM_OFFSET);
192 |
193 | bootloaderBinPathSet = readBoolAttr(element, ATTR_BOOT_PATH_SET_CONFIG, DEF_BOOT_BIN_PATH_SET);
194 | bootloaderBinPath = element.getAttributeValue(ATTR_BOOT_PATH_CONFIG, null,
195 | bootloaderBinPathSet ? null : DEF_BOOT_BIN_PATH);
196 | bootloaderOffset = element.getAttributeValue(ATTR_BOOT_OFFSET_CONFIG, null, DEF_BOOT_OFFSET);
197 |
198 | partitionBinPathSet = readBoolAttr(element, ATTR_PART_PATH_SET_CONFIG, DEF_PART_BIN_PATH_SET);
199 | partitionBinPath = element.getAttributeValue(ATTR_PART_PATH_CONFIG, null,
200 | partitionBinPathSet ? null : DEF_PART_BIN_PATH);
201 | partitionOffset = element.getAttributeValue(ATTR_PART_OFFSET_CONFIG, null, DEF_PART_OFFSET);
202 |
203 | String programTypeStr = element.getAttributeValue(ATTR_PROGRAM_TYPE_CONFIG);
204 | programType = programTypeStr != null ? ProgramType.valueOf(programTypeStr) : DEF_PROGRAM_TYPE;
205 | appendVerify = readBoolAttr(element, ATTR_APPEND_VERIFY_CONFIG, DEF_APPEND_VERIFY);
206 |
207 | additionalProgramParametersSet = readBoolAttr(element, ATTR_ADD_PROG_PARAM_SET_CONFIG, DEF_ADD_PROG_PARAM_SET);
208 | additionalProgramParameters = element.getAttributeValue(ATTR_ADD_PROG_PARAM_CONFIG, null,
209 | additionalProgramParametersSet ? null : DEF_ADD_PROG_PARAM);
210 | }
211 |
212 | private int readIntAttr(@NotNull Element element, String name, int def) {
213 | String s = element.getAttributeValue(name);
214 | if (StringUtil.isEmpty(s)) return def;
215 | return Integer.parseUnsignedInt(s);
216 | }
217 |
218 | @SuppressWarnings("unchecked")
219 | private > T readEnumAttr(@NotNull Element element, String name, T def) {
220 | String s = element.getAttributeValue(name);
221 | if (StringUtil.isEmpty(s)) return def;
222 | try {
223 | return (T) Enum.valueOf(def.getDeclaringClass(), s);
224 | } catch (Throwable t) {
225 | return def;
226 | }
227 | }
228 |
229 | private boolean readBoolAttr(@NotNull Element element, String name, boolean def) {
230 | String s = element.getAttributeValue(name);
231 | if (StringUtil.isEmpty(s)) return def;
232 | try {
233 | return Boolean.parseBoolean(s);
234 | } catch (Throwable t) {
235 | return def;
236 | }
237 | }
238 |
239 | @Override
240 | public void writeExternal(@NotNull Element element) throws WriteExternalException {
241 | super.writeExternal(element);
242 |
243 | Element debuggerElement = new Element(ATTR_DEBUGGER);
244 | element.addContent(debuggerElement);
245 | debuggerData.writeExternal(debuggerElement);
246 |
247 | element.setAttribute(ATTR_GDB_PORT, String.valueOf(gdbPort));
248 | element.setAttribute(ATTR_TELNET_PORT, String.valueOf(telnetPort));
249 | if (boardConfigFile != null) {
250 | element.setAttribute(ATTR_BOARD_CONFIG, boardConfigFile);
251 | }
252 | if (interfaceConfigFile != null) {
253 | element.setAttribute(ATTR_INTERFACE_CONFIG, interfaceConfigFile);
254 | }
255 | element.setAttribute(ATTR_DOWNLOAD_TYPE, downloadType.name());
256 | element.setAttribute(ATTR_RESET_TYPE, resetType.name());
257 | element.setAttribute(ATTR_FLUSH_REGS, String.valueOf(flushRegs));
258 | element.setAttribute(ATTR_BREAK_FUNCTION, String.valueOf(initialBreak));
259 | element.setAttribute(ATTR_BREAK_FUNCTION_NAME, initialBreakName);
260 |
261 | element.setAttribute(ATTR_BOOT_PATH_SET_CONFIG, String.valueOf(bootloaderBinPathSet));
262 | element.setAttribute(ATTR_BOOT_PATH_CONFIG, bootloaderBinPath == null ? "" : bootloaderBinPath);
263 | element.setAttribute(ATTR_BOOT_OFFSET_CONFIG, Objects.requireNonNullElse(bootloaderOffset, DEF_BOOT_OFFSET));
264 |
265 | element.setAttribute(ATTR_PART_PATH_SET_CONFIG, String.valueOf(partitionBinPathSet));
266 | element.setAttribute(ATTR_PART_PATH_CONFIG, partitionBinPath == null ? "" : partitionBinPath);
267 | element.setAttribute(ATTR_PART_OFFSET_CONFIG, Objects.requireNonNullElse(partitionOffset, DEF_PART_OFFSET));
268 |
269 | element.setAttribute(ATTR_PROGRAM_OFFSET_CONFIG, Objects.requireNonNullElse(offset, DEF_PROGRAM_OFFSET));
270 |
271 | element.setAttribute(ATTR_PROGRAM_TYPE_CONFIG, programType.name());
272 | element.setAttribute(ATTR_APPEND_VERIFY_CONFIG, String.valueOf(appendVerify));
273 |
274 | element.setAttribute(ATTR_ADD_PROG_PARAM_SET_CONFIG, String.valueOf(additionalProgramParametersSet));
275 | element.setAttribute(ATTR_ADD_PROG_PARAM_CONFIG, additionalProgramParameters == null ? "" : additionalProgramParameters);
276 | }
277 |
278 | @Override
279 | public void checkConfiguration() throws RuntimeConfigurationException {
280 | super.checkConfiguration();
281 | checkPort(gdbPort);
282 | checkPort(telnetPort);
283 | if (gdbPort == telnetPort) {
284 | throw new RuntimeConfigurationException("Port values should be different");
285 | }
286 | if (StringUtil.isEmpty(boardConfigFile)) {
287 | throw new RuntimeConfigurationException("Board config file is not defined");
288 | }
289 | }
290 |
291 | private void checkPort(int port) throws RuntimeConfigurationException {
292 | if (port <= 1024 || port > 65535)
293 | throw new RuntimeConfigurationException("Port value must be in the range [1025...65535]");
294 | }
295 |
296 | public DebuggerData getDebuggerData() {
297 | return debuggerData;
298 | }
299 |
300 | public void setDebuggerData(DebuggerData debuggerData) {
301 | this.debuggerData = debuggerData;
302 | }
303 |
304 | public int getGdbPort() {
305 | return gdbPort;
306 | }
307 |
308 | public void setGdbPort(int gdbPort) {
309 | this.gdbPort = gdbPort;
310 | }
311 |
312 | public int getTelnetPort() {
313 | return telnetPort;
314 | }
315 |
316 | public void setTelnetPort(int telnetPort) {
317 | this.telnetPort = telnetPort;
318 | }
319 |
320 | public String getBoardConfigFile() {
321 | return boardConfigFile;
322 | }
323 |
324 | public String getInterfaceConfigFile() {
325 | return interfaceConfigFile;
326 | }
327 |
328 | public void setBoardConfigFile(String boardConfigFile) {
329 | this.boardConfigFile = boardConfigFile;
330 | }
331 |
332 | public void setInterfaceConfigFile(String interfaceConfigFile) {
333 | this.interfaceConfigFile = interfaceConfigFile;
334 | }
335 |
336 |
337 | public DownloadType getDownloadType() {
338 | return downloadType;
339 | }
340 |
341 | public void setDownloadType(DownloadType downloadType) {
342 | this.downloadType = downloadType;
343 | }
344 |
345 | public String getOffset() {
346 | return offset;
347 | }
348 |
349 | public void setOffset(String offset) {
350 | this.offset = offset;
351 | }
352 |
353 | public ResetType getResetType() {
354 | return resetType;
355 | }
356 |
357 | public void setResetType(ResetType resetType) {
358 | this.resetType = resetType;
359 | }
360 |
361 | public boolean getFlushRegs() {
362 | return flushRegs;
363 | }
364 |
365 | public void setFlushRegs(boolean flushRegs) {
366 | this.flushRegs = flushRegs;
367 | }
368 |
369 | public boolean getInitialBreak() {
370 | return initialBreak;
371 | }
372 |
373 | public void setInitialBreak(boolean initialBreak) {
374 | this.initialBreak = initialBreak;
375 | }
376 |
377 | public String getInitialBreakName() {
378 | return initialBreakName;
379 | }
380 |
381 | public void setInitialBreakName(String initialBreakName) {
382 | this.initialBreakName = initialBreakName;
383 | }
384 |
385 | public String getBootOffset() {
386 | return bootloaderOffset;
387 | }
388 |
389 | public void setBootOffset(String offset) {
390 | this.bootloaderOffset = offset;
391 | }
392 |
393 | public String getPartitionOffset() {
394 | return partitionOffset;
395 | }
396 |
397 | public void setPartitionOffset(String offset) {
398 | this.partitionOffset = offset;
399 | }
400 |
401 | public String getBootBinPath() {
402 | return bootloaderBinPath;
403 | }
404 |
405 | public void setBootBinPath(String path) {
406 | this.bootloaderBinPath = path;
407 | this.bootloaderBinPathSet = true;
408 | }
409 |
410 | public String getPartitionBinPath() {
411 | return partitionBinPath;
412 | }
413 |
414 | public void setPartitionBinPath(String path) {
415 | this.partitionBinPath = path;
416 | this.partitionBinPathSet = true;
417 | }
418 |
419 | public ProgramType getProgramType() {
420 | return programType;
421 | }
422 |
423 | public void setProgramType(ProgramType programType) {
424 | this.programType = programType;
425 | }
426 |
427 | public boolean getAppendVerify() {
428 | return appendVerify;
429 | }
430 |
431 | public void setAppendVerify(boolean appendVerify) {
432 | this.appendVerify = appendVerify;
433 | }
434 |
435 | public String getAdditionalProgramParameters() {
436 | return additionalProgramParameters;
437 | }
438 |
439 | public void setAdditionalProgramParameters(String additionalProgramParameters) {
440 | this.additionalProgramParameters = additionalProgramParameters;
441 | this.additionalProgramParametersSet = additionalProgramParameters != null;
442 | }
443 | }
444 |
--------------------------------------------------------------------------------