From 2662bdc5d2832020b3ac986132b21a7fc9a1fa40 Mon Sep 17 00:00:00 2001 From: ussserrr Date: Wed, 7 Nov 2018 16:04:08 +0300 Subject: [PATCH] Release 0.7 --- CHANGELOG | 40 ++++++++++++++++++++++++++++-------- README.md | 23 ++++++++++++--------- settings.py | 14 ++++++------- stm32pio.py | 9 +++++---- tests.py | 19 +++++++++++++----- util.py | 58 ++++++++++++++++++++++++++++++----------------------- 6 files changed, 105 insertions(+), 58 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 84f0c45..c31cbb0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Changelog: +stm32pio changelog: ver. 0.1 (30.11.17): - Initial version @@ -15,24 +15,48 @@ Changelog: ver. 0.4 (03-04.04.18): - New: hide CubeMX and PlatformIO stdout output - New: shebang - - New: choose your favourite editor with --start-editor option (replaced --with-atom) + - New: choose your favourite editor with '--start-editor' option (replaces '--with-atom') - New: logging module - New: more checks - - New: settings.py file + - New: 'settings.py' file - New: cross-platform running - - New: debug output (verbose -v mode) - - New: readme.md and more comments + - New: debug output (verbose '-v' mode) + - New: 'README.md' and more comments - Fixed: remove unnecessary imports - - Fixed: command to init pio project (removed double quotation marks) + - Fixed: command to initialize PlatformIO project (remove double quotation marks) - Changed: many architectural improvements - Changed: documentation improvements ver. 0.45 (04-05.04.18): - New: introducing unit-tests for the app - - New: clean-up mode + - New: clean-up feature ver. 0.5 (07.04.18): - New: more comments - New: screenshots for the usage example - Fixed: many small fixes and improvements - - Changed: test now more isolated and uses ./stm32pio-test/stm32pio-test.ioc file + - Changed: test now is more isolated and uses ./stm32pio-test/stm32pio-test.ioc file + + ver. 0.7 (05-07.11.18): + - New: Windows support! + - New: new editors support (Sublime Text, gedit) + - New: more comments and docstrings + - New: more checks to improve robustness + - New: if __name__ == '__main__' block + - New: new test: build generated project + - New: new test: run editors + - New: new test: user's code preservation after the code regeneration + - New: clean run for test cases (implemented using decorator) + - Fixed: compatible with latest PlatformIO project structure (ver 3.6.1) + - Fixed: many small fixes and improvements + - Changed: 'java_cmd' parameter in 'settings.py' (simple 'java' by default) + - Changed: move to double-quoted strings + - Changed: remove '_getProjectNameByPath()' function (replaced by 'os.path.basename()') + - Changed: vast f-strings usage + - Changed: test '.ioc' file is updated to the latest STM32CubeMX version (4.27.0 at the moment) + - Changed: use 'os.path.join()' instead of manually composing of paths + - Changed: use 'with ... as ...' construction for opening files + - Changed: 120 chars line width + - Changed: PEP 8 conformity: variables and functions naming conventions + - Changed: PEP 8 conformity: multi-line imports + - Changed: 'miscs.py' module is renamed to 'util.py' diff --git a/README.md b/README.md index be2f8d3..9c57101 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # stm32pio -Small Python app that can create and update [PlatformIO](https://platformio.org) project from [STM32CubeMX](http://www.st.com/en/development-tools/stm32cubemx.html) `.ioc` file. +Small cross-platform Python app that can create and update [PlatformIO](https://platformio.org) projects from [STM32CubeMX](http://www.st.com/en/development-tools/stm32cubemx.html) `.ioc` files. ## Features - Start the new project in a single directory using only `.ioc` file @@ -9,15 +9,15 @@ Small Python app that can create and update [PlatformIO](https://platformio.org) ## Requirements: - For this app: - - Python 3.5+ + - Python 3.6+ - For usage: - - macOS or Linux OS + - macOS, Linux, Windows - STM32CubeMX (all recent versions) with downloaded necessary frameworks (F0, F1, etc.). Try to generate code in ordinary way (through the GUI) at least once before running stm32pio - Java CLI (JRE) (likely is already installed if STM32CubeMX works) - - PlatformIO CLI (from Atom you can run `Menubar -> PlatformIO -> Install Shell Commands`). Therefore, currently stm32pio doesn't support Windows due to the lack of the PlatformIO CLI. + - PlatformIO CLI. ## Usage -Check `settings.py` to make sure that all user-specific parameters (path to the STM32CubeMX executable) are valid. Run +Check `settings.py` to make sure that all user-specific parameters are valid. Run ```bash $ python3 stm32pio.py --help ``` @@ -29,17 +29,22 @@ to see help. ![Code Generator tab](/screenshots/tab_CodeGenerator.png) -3. Back in the first tab (Project) copy the "Toolchain Folder Location" string. Click OK +3. Back in the first tab (Project) copy the "Toolchain Folder Location" string. Click OK, close CubeMX ![Project tab](/screenshots/tab_Project.png) -4. Use copied string as a `-d` argument for stm32pio -5. Run `pio boards` to list all supported devices. Pick one and use its ID as a `-b` argument (for example, `nucleo_f031k6`) +4. Use copied string as a `-d` argument for stm32pio. So it is assumed that the name of the project folder matches the name of `.ioc` file +5. Run `platformio boards` (`pio boards`) to list all supported devices. Pick one and use its ID as a `-b` argument (for example, `nucleo_f031k6`) 6. All done. You can now run ```bash $ python3 stm32pio.py new -d /path/to/cubemx/project -b nucleo_f031k6 --start-editor=vscode ``` -to complete generation and start the Visual Studio Code editor with opened folder (as example, not required). +to complete generation and start the Visual Studio Code editor with opened folder (as example, not required). Make sure you have all tools in PATH (`java` (or set in `settings.py`), editor, Python) +7. If you will be in need to update hardware configuration in the future, make all necessary stuff in CubeMX and run `generate` command in a similar way: +```bash +$ python3 stm32pio.py generate -d /path/to/cubemx/project +``` +8. To clean-up the folder and keep only `.ioc` file run `clean` command ## Testing Since ver. 0.45 there are some unit-tests in file `tests.py` (based on the unittest module). Run diff --git a/settings.py b/settings.py index bfe190a..9e154a5 100644 --- a/settings.py +++ b/settings.py @@ -1,7 +1,6 @@ import logging import pathlib import platform -import sys my_os = platform.system() @@ -10,19 +9,20 @@ logger = logging.getLogger('') -# We trying to guess STM32CubeMX path. You can just avoid this and hard-code it +# How you start Java from command line? +java_cmd = 'java' +# We trying to guess STM32CubeMX location. You can just avoid this and hard-code it. Note that STM32CubeMX will be +# called as 'java -jar' # macOS default: 'Applications' folder if my_os == 'Darwin': cubemx_path = "/Applications/STMicroelectronics/STM32CubeMX.app/Contents/Resources/STM32CubeMX" -# not exactly default STM32CubeMX path on Linux but general convention on it +# Linux (Ubuntu) default: elif my_os == 'Linux': cubemx_path = "/usr/local/STMicroelectronics/STM32Cube/STM32CubeMX/STM32CubeMX" -# Windows is not implemented yet +# Windows default: elif my_os == 'Windows': - cubemx_path = "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeMX/STM32CubeMX.exe" - # logger.error("Windows is not supported!") - # sys.exit() + cubemx_path = "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeMX/STM32CubeMX.exe" cubemx_script_filename = 'cubemx-script' diff --git a/stm32pio.py b/stm32pio.py index 53889c0..425aece 100755 --- a/stm32pio.py +++ b/stm32pio.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -__version__ = 0.6 +__version__ = 0.7 if __name__ == '__main__': @@ -11,7 +11,7 @@ import sys parser = argparse.ArgumentParser(description="Automation of creating and updating STM32CubeMX-PlatformIO projects. " - "Requirements: Python 3.5+, STM32CubeMX, PlatformIO CLI. Edit " + "Requirements: Python 3.6+, STM32CubeMX, Java, PlatformIO CLI. Edit " "settings.py to set project path to the STM32CubeMX (if default " "doesn't work)") # global arguments (there is also automatically added '-h, --help' option) @@ -21,11 +21,12 @@ subparsers = parser.add_subparsers(dest='subcommand', title='subcommands', description="valid subcommands", help="modes of operation") - parser_new = subparsers.add_parser('new', help="generate CubeMX code, create PlatformIO project [and start editor]") + parser_new = subparsers.add_parser('new', + help="generate CubeMX code, create PlatformIO project [and start the editor]") parser_new.add_argument('-d', '--directory', dest='project_path', help="path to the project", required=True) parser_new.add_argument('-b', '--board', dest='board', help="PlatformIO name of the board", required=True) parser_new.add_argument('--start-editor', dest='editor', help="use specified editor to open PlatformIO project", - choices=['atom', 'vscode', 'sublime'], required=False) + choices=['atom', 'vscode', 'sublime', 'gedit'], required=False) parser_generate = subparsers.add_parser('generate', help="generate CubeMX code") parser_generate.add_argument('-d', '--directory', dest='project_path', help="path to the project", required=True) diff --git a/tests.py b/tests.py index f423c00..86f13a2 100755 --- a/tests.py +++ b/tests.py @@ -61,7 +61,8 @@ def test_patch_platformio_ini(self): with open(os.path.join(project_path, 'platformio.ini'), mode='rb') as platformio_ini: # '2' in seek() means that we count from the end of the file. This feature works only in binary file mode - # In Windows additional '\r' is appended to every '\n' so we need to count them for the correct calculation + # In Windows additional '\r' is appended to every '\n' (newline differences) so we need to count them + # for the correct calculation if settings.my_os == 'Windows': platformio_ini.seek(-(len(settings.platformio_ini_patch_text) + settings.platformio_ini_patch_text.count('\n')), 2) @@ -103,6 +104,7 @@ def test_run_editor(self): util.start_editor(project_path, 'atom') util.start_editor(project_path, 'vscode') util.start_editor(project_path, 'sublime') + util.start_editor(project_path, 'gedit') time.sleep(1) # wait a little bit for apps to start if settings.my_os == 'Windows': @@ -115,9 +117,16 @@ def test_run_editor(self): result = subprocess.run(['ps', '-A'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8') # Or, for Python 3.7 and above: # result = subprocess.run(['ps', '-A'], capture_output=True, encoding='utf-8') - self.assertIn('Atom', result.stdout) - self.assertIn('Visual Studio Code', result.stdout) - self.assertIn('Sublime', result.stdout) + if settings.my_os == 'Darwin': + self.assertIn('Atom', result.stdout) + self.assertIn('Visual Studio Code', result.stdout) + self.assertIn('Sublime', result.stdout) + if settings.my_os == 'Linux': + self.assertIn('atom', result.stdout) + self.assertIn('code', result.stdout) + self.assertIn('sublime', result.stdout) + self.assertIn('gedit', result.stdout) + @clean_run @@ -127,7 +136,7 @@ def test_regenerate_code(self): and some new files) """ - # Generate new project + # Generate a new project util.generate_code(project_path) util.pio_init(project_path, board) util.patch_platformio_ini(project_path) diff --git a/util.py b/util.py index 09de7df..ae4f705 100644 --- a/util.py +++ b/util.py @@ -43,12 +43,12 @@ def generate_code(project_path): logger.info("starting to generate a code from the CubeMX .ioc file...") if logger.level <= logging.DEBUG: - result = subprocess.run(['java', '-jar', settings.cubemx_path, '-q', cubemx_script_full_filename]) + result = subprocess.run([settings.java_cmd, '-jar', settings.cubemx_path, '-q', cubemx_script_full_filename]) else: - result = subprocess.run(['java', '-jar', settings.cubemx_path, '-q', cubemx_script_full_filename], + result = subprocess.run([settings.java_cmd, '-jar', settings.cubemx_path, '-q', cubemx_script_full_filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Or, for Python 3.7 and above: - # result = subprocess.run(['java', '-jar', settings.cubemx_path, '-q', cubemx_script_full_filename], + # result = subprocess.run([settings.java_cmd, '-jar', settings.cubemx_path, '-q', cubemx_script_full_filename], # capture_output=True) if result.returncode != 0: logger.error(f"code generation error (return code is {result.returncode}).\n" @@ -116,14 +116,16 @@ def patch_platformio_ini(project_path): logger.debug("patching 'platformio.ini' file...") - with open(os.path.join(project_path, 'platformio.ini'), mode='a') as platformio_ini_file: - platformio_ini_file.write(settings.platformio_ini_patch_text) + if os.path.isfile(os.path.join(project_path, 'platformio.ini')): + with open(os.path.join(project_path, 'platformio.ini'), mode='a') as platformio_ini_file: + platformio_ini_file.write(settings.platformio_ini_patch_text) + logger.info("'platformio.ini' patched") + else: + logger.warning("'platformio.ini' file not found") shutil.rmtree(os.path.join(project_path, 'include'), ignore_errors=True) shutil.rmtree(os.path.join(project_path, 'src'), ignore_errors=True) - logger.info("'platformio.ini' patched") - def start_editor(project_path, editor): @@ -135,24 +137,28 @@ def start_editor(project_path, editor): editor: editor keyword """ - # TODO: handle errors if there is no editor - logger.info("starting an editor...") - if settings.my_os == 'Windows': - if editor == 'atom': - subprocess.run(['atom', project_path], shell=True) - elif editor == 'vscode': - subprocess.run(['code', project_path], shell=True) - elif editor == 'sublime': - subprocess.run(['subl', project_path], shell=True) - else: - if editor == 'atom': - subprocess.run(['atom', project_path]) - elif editor == 'vscode': - subprocess.run(['code', project_path]) - elif editor == 'sublime': - subprocess.run(['subl', project_path]) + try: + if settings.my_os == 'Windows': + if editor == 'atom': + subprocess.run(['atom', project_path], check=True, shell=True) + elif editor == 'vscode': + subprocess.run(['code', project_path], check=True, shell=True) + elif editor == 'sublime': + subprocess.run(['subl', project_path], check=True, shell=True) + else: + if editor == 'atom': + subprocess.run(['atom', project_path], check=True) + elif editor == 'vscode': + subprocess.run(['code', project_path], check=True) + elif editor == 'sublime': + subprocess.run(['subl', project_path], check=True) + elif editor == 'gedit': + subprocess.run(['gedit', project_path], check=True) + + except subprocess.CalledProcessError as e: + logger.error(f"Failed to start the editor {editor}: {e.stdout}") @@ -164,16 +170,18 @@ def clean(project_path): project_path: path to the project """ + # Get folder content folder_content = os.listdir(project_path) + # Keep the '.ioc' file if (os.path.basename(project_path) + '.ioc') in folder_content: folder_content.remove(os.path.basename(project_path) + '.ioc') for item in folder_content: if os.path.isdir(os.path.join(project_path, item)): shutil.rmtree(os.path.join(project_path, item), ignore_errors=True) - logger.debug('del ./' + item) + logger.debug(f"del {item}/") elif os.path.isfile(os.path.join(project_path, item)): os.remove(os.path.join(project_path, item)) - logger.debug('del ' + item) + logger.debug(f"del {item}") logger.info("project has been cleaned")