Last active
January 23, 2020 01:07
-
-
Save nistath/9921d854c27d078424f85225ed15380a to your computer and use it in GitHub Desktop.
cubemxcpy is a python script to copy CubeMX generated files in a nice format into your project.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
from pathlib import Path | |
INP_DIR = Path('./cubemx/') | |
OUT_DIR = Path('./') | |
comment = r'/\*{}([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/' | |
any_comment = re.compile(comment.format('')) | |
user_comment = re.compile(comment.format(' USER')) | |
empty_comment_title = re.compile(comment.format('') + r'[\s\t\r\n]+(?=/\*)') | |
bad_comment_space = re.compile(r'/\*(?=\**[^\*\s\t\r\n])') | |
brackets = re.compile(r'[\s\t\r\n]*{') | |
multi_newline = re.compile(r'\n[\s\t\r\n]*\n') | |
trailing_white = re.compile(r'[\s\t]+$') | |
def fix_style(contents: str) -> str: | |
contents = contents.replace('{0}', '{}') | |
contents = contents.replace('/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/', '', 1) | |
contents = user_comment.sub('', contents) | |
copy_notice = empty_comment_title.match(contents, pos=1).group(0) | |
contents = empty_comment_title.sub('', contents) | |
contents = multi_newline.sub('\n\n', contents) | |
contents = bad_comment_space.sub('/* ', contents) | |
contents = brackets.sub(' {', contents) | |
contents = contents.strip() + '\n' * 3 + copy_notice.strip() | |
contents = trailing_white.sub('', contents) | |
contents += '\n' | |
return contents | |
def append_after(contents: str, string: str, append: str) -> str: | |
loc = contents.find(string) | |
if loc == -1: | |
raise ValueError('didn\'t find given string') | |
split = loc + len(string) | |
return contents[:split] + append + contents[split:] | |
def padded_comment(comment: str, linelen: int=80) -> str: | |
return ('/* ' + comment + ' ').ljust(linelen - 2, '-') + '*/' | |
def get_section(contents: str, begin: str=None, end: str=None, lines: bool=False) -> slice: | |
start, len_begin = (0, 0) if begin is None else (contents.find(begin), len(begin)) | |
stop = None if end is None else contents.find(end, start + len_begin) + len(end) | |
if lines: | |
cstart = contents.rfind('\n', None, start) | |
start = cstart + 1 if cstart >= 0 else start | |
if stop: | |
cstop = contents.find('\n', stop) or stop | |
stop = cstop if cstop >= 0 else stop | |
return slice(start, stop) | |
def sub_section(contents: str, section: slice, sub: str='') -> str: | |
return contents[:section.start] + sub + contents[section.stop:] | |
class CubeMX: | |
entry_function = 'void MX_Init(void)' | |
it_stem = 'stm32f4xx_it' | |
msp_file = 'stm32f4xx_hal_msp.c' | |
system_file = 'system_stm32f4xx.c' | |
conf_file = 'stm32f4xx_hal_conf.h' | |
def __init__(self, mxdir: Path, stem: str): | |
self.mxdir = mxdir | |
self.stem = stem | |
with self.mxdir.joinpath('Core', 'Src', 'main.c').open('r', newline=None) as f: | |
self.source = self.process_main_c(f.read()) | |
with self.mxdir.joinpath('Core', 'Inc', 'main.h').open('r', newline=None) as f: | |
self.header = self.process_main_h(f.read()) | |
with self.mxdir.joinpath('Core', 'Inc', self.it_stem).with_suffix('.h').open('r', newline=None) as f: | |
self.it_h = self.process_common(f.read()) | |
with self.mxdir.joinpath('Core', 'Src', self.it_stem).with_suffix('.c').open('r', newline=None) as f: | |
self.it_c = self.process_common(f.read()) | |
with self.mxdir.joinpath('Core', 'Src', self.msp_file).open('r', newline=None) as f: | |
self.msp = self.process_common(f.read()) | |
with self.mxdir.joinpath('Core', 'Src', self.system_file).open('r', newline=None) as f: | |
self.system = f.read() | |
with self.mxdir.joinpath('Core', 'Inc', self.conf_file).open('r', newline=None) as f: | |
self.conf = f.read() | |
def replace_stems(self, contents: str, original: str='main') -> str: | |
file_f = '{}.' | |
contents = contents.replace(file_f.format(original), file_f.format(self.stem)) | |
contents = contents.replace(original.upper(), self.stem.upper()) | |
return contents | |
def process_common(self, contents: str) -> str: | |
contents = fix_style(contents) | |
contents = self.replace_stems(contents) | |
return contents | |
def process_main_c(self, contents: str) -> str: | |
contents = contents.rsplit('\n', 31)[0] # last 30 lines have Error_Handler and assert_failed | |
contents = contents.replace('static ', '') | |
contents = sub_section(contents, get_section(contents, '/* Infinite loop */', '/* USER CODE END 3 */')) | |
self.exports = contents[get_section(contents, '/* Private variables', '/* USER', True)] | |
self.exports = any_comment.sub('', self.exports).strip() | |
function_section = get_section(contents, '/* Private function', '/* USER', True) | |
self.functions = contents[function_section] | |
self.functions = any_comment.sub('', self.functions).strip() | |
self.functions += '\n' + self.entry_function + ';' | |
contents = sub_section(contents, function_section) | |
contents = contents.replace('int main(void)', self.entry_function) | |
return self.process_common(contents) | |
def process_main_h(self, contents: str) -> str: | |
contents = append_after(contents, '#include "stm32f4xx_hal.h"\n', '#include "error.h"\n') | |
pin_section = get_section(contents, '/* Private defines', '*/') | |
contents = sub_section(contents, pin_section, padded_comment('Pin defines')) | |
function_section = get_section(contents, '/* Exported functions', '\n', lines=True) | |
functions = contents[function_section].split('\n')[0] + '\n' | |
functions += self.functions | |
contents = sub_section(contents, function_section, functions) | |
function_section = get_section(contents, '/* Exported functions', '/*') | |
export_section = slice(function_section.stop - 2, function_section.stop - 2) | |
exports = '\n'.join('extern ' + line if line else '' for line in self.exports.split('\n')) | |
exports = padded_comment('Exported variables') + '\n' + exports | |
contents = sub_section(contents, export_section, exports) | |
return self.process_common(contents) | |
def save_as(self, outdir: Path): | |
with outdir.joinpath('src', self.stem).with_suffix('.c').open('w') as f: | |
f.write(self.source) | |
with outdir.joinpath('inc', self.stem).with_suffix('.h').open('w') as f: | |
f.write(self.header) | |
with outdir.joinpath('src', self.it_stem).with_suffix('.c').open('w') as f: | |
f.write(self.it_c) | |
with outdir.joinpath('inc', self.it_stem).with_suffix('.h').open('w') as f: | |
f.write(self.it_h) | |
with outdir.joinpath('src', self.msp_file).open('w') as f: | |
f.write(self.msp) | |
with outdir.joinpath('src', self.system_file).open('w') as f: | |
f.write(self.system) | |
with outdir.joinpath('inc', self.conf_file).open('w') as f: | |
f.write(self.conf) | |
if __name__ == '__main__': | |
cubemx = CubeMX(INP_DIR, 'cubemx') | |
cubemx.save_as(OUT_DIR) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment