├── .gitignore ├── CONTRIBUTORS ├── README.md ├── cubemximporter.py └── cubemximporter.sublime-build /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.DS_Store 3 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | fab672000 (https://github.com/fab672000) 2 | Christopher Wilson (https://github.com/cdwilson) 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CubeMXImporter 2 | This tool can be used to import projects generated by STM32CubeMX tool inside an Eclipse project created with the GNU ARM Eclipse plugin. This release supports also the import of Middleware libraries (FatFS, LwIP, FreeRTOS, etc). 3 | 4 | It can be easily used in this way: 5 | 6 | 1. Generate a new Eclipse project using the GNU ARM Eclipse plugin as described in [this blog post](http://www.carminenoviello.com/en/2015/06/04/stm32-applications-eclipse-gcc-stcube/) or in [this book](https://leanpub.com/mastering-stm32). 7 | 1. Close the prject once generated 8 | 2. Create a new CubeMX project for your MCU or development board. 9 | 2. Generate the C code from CubeMX project selecting **SW4STM32** as Tool-chain. 10 | 3. Launch the CubeMXImporter tool with the following command: 11 | ``` 12 | $ python cubemximporter.py 13 | ``` 14 | 4. Open again the Eclipse project and do a refresh of the source tree. 15 | 16 | The whole procedure is better described [here](http://www.carminenoviello.com/en/2015/11/02/quickly-import-stm32cubemx-project-eclipse-project/) 17 | 18 | CubeMXImporter works both with Python 2.7 and 3.x. It requires the `lxml` library. Linux and MacOS X users can install it using pip: 19 | 20 | ``` 21 | $ pip install lxml 22 | ``` 23 | 24 | while Windows users can download [this pre-compiled](http://bit.ly/1P4lxSO) package. -------------------------------------------------------------------------------- /cubemximporter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2015/2016 Carmine Noviello 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | from __future__ import print_function 24 | 25 | version = '0.2.3' # using semantic versioning 2.0 model, denote a patch change 26 | 27 | import os 28 | import argparse 29 | import copy 30 | import logging 31 | import shutil 32 | import re 33 | from lxml import etree 34 | 35 | 36 | class CubeMXImporter(object): 37 | """docstring for CubeMXImporter""" 38 | 39 | def __init__(self): 40 | super(CubeMXImporter, self).__init__() 41 | 42 | self.eclipseprojectpath = "" 43 | self.dryrun = 0 44 | self.logger = logging.getLogger(__name__) 45 | self.HAL_TYPE = None 46 | 47 | def setCubeMXProjectPath(self, path): 48 | """Set the path of CubeMX generated project folder""" 49 | 50 | if os.path.exists(os.path.join(path, ".mxproject")): 51 | if os.path.exists(os.path.join(path, "SW4STM32")): # For CubeMX < 4.14 52 | self.cubemxprojectpath = path 53 | self.sw4stm32projectpath = os.path.join(path, "SW4STM32") 54 | self.detectHALInfo() 55 | elif os.path.exists(os.path.join(path, ".cproject")): 56 | # Recent releases of CubeMX (from 4.14 and higher) allow to generate the 57 | # SW4STM32 project in the root folder. This means that project files are 58 | # stored in the root of the CubeMX project, but this is the 59 | # same behavior for TrueSTUDIO project. So we need to check if the project 60 | # is generated for the SW4STM32 toolchain by playing with the content of .cproject file 61 | 62 | if open(os.path.join(path, ".cproject")).read().find("ac6") < 0: # It is not an AC6 project 63 | raise InvalidSW4STM32Project( 64 | "The generated CubeMX project is not for SW4STM32 tool-chain. Please, regenerate the project again.") 65 | else: 66 | self.cubemxprojectpath = path 67 | self.sw4stm32projectpath = path 68 | self.detectHALInfo() 69 | 70 | else: 71 | raise InvalidSW4STM32Project( 72 | "The generated CubeMX project is not for SW4STM32 tool-chain. Please, regenerate the project again.") 73 | else: 74 | raise InvalidCubeMXFolder("The folder '%s' doesn't seem a CubeMX project" % path) 75 | 76 | def getCubeMXProjectPath(self): 77 | """Retrieve the path of CubeMX generated project folder""" 78 | return self.cubemxprojectpath 79 | 80 | cubeMXProjectPath = property(getCubeMXProjectPath, setCubeMXProjectPath) 81 | 82 | def setEclipseProjectPath(self, path): 83 | """Set the path of Eclipse generated project folder""" 84 | 85 | if os.path.exists(os.path.join(path, ".cproject")): 86 | self.eclipseprojectpath = path 87 | else: 88 | raise InvalidEclipseFolder("The folder '%s' doesn't seem an Eclipse project" % path) 89 | 90 | def getEclipseProjectPath(self): 91 | """Retrieve the path of Eclipse generated project folder""" 92 | return self.eclipseprojectpath 93 | 94 | eclipseProjectPath = property(getEclipseProjectPath, setEclipseProjectPath) 95 | 96 | def __addOptionValuesToProject(self, values, section, quote=True): 97 | if self.dryrun: return 98 | """Add a list of option values into a given section in the Eclipse project file""" 99 | options = self.projectRoot.xpath("//option[@superClass='%s']" % section) # Uses XPATH to retrieve the 'section' 100 | optionsValues = [o.attrib["value"] for o in 101 | options[0]] # List all available values to avoid reinsert again the same value 102 | for opt in options: 103 | for v in values: 104 | pattern = '"%s"' if quote else '%s' # The way how include paths and macros are stored differs. Include paths are quoted with "" 105 | if pattern % v not in optionsValues: # Avoid to place the same include again 106 | listOptionValue = copy.deepcopy(opt[0]) 107 | if quote: 108 | listOptionValue.attrib["value"] = ""%s"" % v # Quote the path 109 | else: 110 | listOptionValue.attrib["value"] = "%s" % v 111 | opt.append(listOptionValue) 112 | 113 | def addAssemblerIncludes(self, includes): 114 | """Add a list of include paths to the Assembler section in project settings""" 115 | self.__addOptionValuesToProject(includes, "ilg.gnuarmeclipse.managedbuild.cross.option.assembler.include.paths") 116 | 117 | def addCIncludes(self, includes): 118 | """Add a list of include paths to the C section in project settings""" 119 | self.__addOptionValuesToProject(includes, 120 | "ilg.gnuarmeclipse.managedbuild.cross.option.c.compiler.include.paths") 121 | 122 | def addCPPIncludes(self, includes): 123 | """Add a list of include paths to the CPP section in project settings""" 124 | self.__addOptionValuesToProject(includes, 125 | "ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.include.paths") 126 | 127 | def addAssemblerMacros(self, macros): 128 | """Add a list of macros to the CPP section in project settings""" 129 | self.__addOptionValuesToProject(macros, "ilg.gnuarmeclipse.managedbuild.cross.option.assembler.defs", False) 130 | 131 | def addCMacros(self, macros): 132 | """Add a list of macros to the CPP section in project settings""" 133 | self.__addOptionValuesToProject(macros, "ilg.gnuarmeclipse.managedbuild.cross.option.c.compiler.defs", False) 134 | 135 | def addCPPMacros(self, macros): 136 | """Add a list of macros to the CPP section in project settings""" 137 | self.__addOptionValuesToProject(macros, "ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.defs", False) 138 | 139 | def addSourceEntries(self, entries): 140 | """Add a list of directory to the source entries list in the eclipse project""" 141 | sources = self.projectRoot.xpath("//sourceEntries") # Uses XPATH to retrieve the 'section' 142 | for source in sources: 143 | for e in entries: 144 | logging.debug("Adding '%s' folder to source entries" % e) 145 | entry = copy.deepcopy(source[0]) 146 | entry.attrib["name"] = e 147 | source.append(entry) 148 | 149 | def copyTree(self, src, dst, ignore=None): 150 | """Copy 'src' directory in 'dst' folder""" 151 | logging.debug("Copying folder '%s' to '%s'" % (src, dst)) 152 | 153 | if not self.dryrun: 154 | shutil.copytree(src, dst, ignore) 155 | 156 | def copyTreeContent(self, src, dst): 157 | """Copy all files contsined in 'src' folder to 'dst' folder""" 158 | files = os.listdir(src) 159 | for f in files: 160 | fileToCopy = os.path.join(src, f) 161 | if os.path.isfile(fileToCopy): 162 | logging.debug("Copying %s to %s" % (fileToCopy, dst)) 163 | if not self.dryrun: 164 | shutil.copyfile(fileToCopy, os.path.join(dst, f)) 165 | elif os.path.isdir(fileToCopy): 166 | logging.debug("Copying folder %s to %s" % (fileToCopy, dst)) 167 | if not self.dryrun: 168 | shutil.copytree(fileToCopy, os.path.join(dst, f)) 169 | 170 | def purge(self, rootdir, pattern): 171 | for f in os.listdir(rootdir): 172 | if re.search(pattern, f): 173 | path = os.path.join(rootdir, f) 174 | logging.debug("Deleting %s" % path) 175 | if os.path.isfile(path): 176 | os.remove(path) 177 | elif os.path.isdir(path): 178 | shutil.rmtree(path) 179 | 180 | def deleteOriginalEclipseProjectFiles(self): 181 | """Deletes useless files generated by the GNU ARM Eclipse plugin""" 182 | 183 | # search for any original or alternate -stdperiph pattern: 184 | dirs = ["src", "include", "system/src/cmsis"] 185 | dirs2 = ["system/include/", "system/src/"] 186 | stm32_dir_pat = "stm32%s(xx|-stdperiph)" % self.HAL_TYPE.lower() 187 | stm32_h_pat = '(system_)*stm32%s.*.h$' % self.HAL_TYPE.lower() 188 | 189 | 190 | if not self.dryrun: 191 | [self.deleteTreeContent(os.path.join(self.eclipseprojectpath, d)) for d in dirs] 192 | [self.purge(os.path.join(self.eclipseprojectpath, d), stm32_dir_pat) for d in dirs2] 193 | self.purge(os.path.join(self.eclipseprojectpath, "system/include/cmsis/"), stm32_h_pat) 194 | rdirs = [os.path.join(self.eclipseprojectpath, d, "stm32%sxx" % self.HAL_TYPE.lower()) for d in dirs2] 195 | [os.makedirs(d ) for d in rdirs] 196 | self.fixDeviceInclude() 197 | self.logger.info("Deleted unneeded files generated by GNU Eclipse plugin") 198 | 199 | def deleteTreeContent(self, tree): 200 | """Delete all files contained in a given folder""" 201 | for f in os.listdir(tree): 202 | f = os.path.join(tree, f) 203 | logging.debug("Deleting %s" % f) 204 | if not self.dryrun: 205 | if os.path.isfile(f): 206 | os.unlink(f) 207 | elif os.path.isdir(f): 208 | shutil.rmtree(f) 209 | 210 | def detectHALInfo(self): 211 | """Scans the SW4STM32 project file looking for relevant informations about MCU and HAL types""" 212 | 213 | root = None 214 | 215 | for rootdir, dirs, files in os.walk(self.sw4stm32projectpath): 216 | if ".cproject" in files: 217 | root = etree.fromstring(open(os.path.join(rootdir, ".cproject")).read().encode('UTF-8')) 218 | 219 | if root is None: 220 | raise InvalidSW4STM32Project( 221 | "The generated CubeMX project is not for SW4STM32 tool-chain. Please, regenerate the project again.") 222 | 223 | options = root.xpath("//option[@superClass='gnu.c.compiler.option.preprocessor.def.symbols']")[0] 224 | 225 | for opt in options: 226 | if "STM32" in opt.attrib["value"]: 227 | self.HAL_MCU_TYPE = opt.attrib["value"] 228 | self.HAL_TYPE = re.search("([FL][0-9])", self.HAL_MCU_TYPE).group(1) 229 | self.logger.info("Detected MCU type: %s" % self.HAL_MCU_TYPE) 230 | self.logger.info("Detected HAL type: %s" % self.HAL_TYPE) 231 | 232 | def getAC6Includes(self): 233 | root = None 234 | 235 | for rootdir, dirs, files in os.walk(self.sw4stm32projectpath): 236 | if ".cproject" in files: 237 | root = etree.fromstring(open(os.path.join(rootdir, ".cproject")).read().encode('UTF-8')) 238 | 239 | if root is None: 240 | raise InvalidSW4STM32Project( 241 | "The generated CubeMX project is not for SW4STM32 tool-chain. Please, regenerate the project again.") 242 | 243 | options = root.xpath("//option[@superClass='gnu.c.compiler.option.include.paths']")[0] 244 | 245 | return [opt.attrib["value"] for opt in options] 246 | 247 | def importApplication(self): 248 | """Import generated application code inside the Eclipse project""" 249 | srcIncludeDir = os.path.join(self.cubemxprojectpath, "Inc") 250 | srcSourceDir = os.path.join(self.cubemxprojectpath, "Src") 251 | dstIncludeDir = os.path.join(self.eclipseprojectpath, "include") 252 | dstSourceDir = os.path.join(self.eclipseprojectpath, "src") 253 | 254 | locations = ((srcIncludeDir, dstIncludeDir), (srcSourceDir, dstSourceDir)) 255 | 256 | for loc in locations: 257 | self.copyTreeContent(loc[0], loc[1]) 258 | 259 | self.logger.info("Successfully imported application files") 260 | 261 | def importCMSIS(self): 262 | """Import CMSIS package and CMSIS-DEVICE adapter by ST inside the Eclipse project""" 263 | cubeMXVersion = 417 264 | 265 | srcIncludeDir = os.path.join(self.cubemxprojectpath, 266 | "Drivers/CMSIS/Device/ST/STM32%sxx/Include" % self.HAL_TYPE) 267 | dstIncludeDir = os.path.join(self.eclipseprojectpath, "system/include/cmsis/device") 268 | srcCMSISIncludeDir = os.path.join(self.cubemxprojectpath, "Drivers/CMSIS/Include") 269 | dstCMSISIncludeDir = os.path.join(self.eclipseprojectpath, "system/include/cmsis") 270 | dstSourceDir = os.path.join(self.eclipseprojectpath, "system/src/cmsis") 271 | 272 | try: 273 | if not self.dryrun: 274 | os.mkdir(dstIncludeDir) 275 | except OSError: 276 | pass 277 | 278 | # Add hal includes for variants with otehr folder names 279 | self.addCIncludes(("../system/include/stm32%sxx" % self.HAL_TYPE.lower(),)) 280 | self.addCPPIncludes(("../system/include/stm32%sxx" % self.HAL_TYPE.lower(),)) 281 | 282 | # Add includes to the project settings 283 | self.addCIncludes(("../system/include/cmsis/device",)) 284 | self.addCPPIncludes(("../system/include/cmsis/device",)) 285 | self.addAssemblerIncludes(("../system/include/cmsis/device",)) 286 | 287 | locations = ((srcIncludeDir, dstIncludeDir), (srcCMSISIncludeDir, dstCMSISIncludeDir)) 288 | 289 | for loc in locations: 290 | self.copyTreeContent(loc[0], loc[1]) 291 | 292 | systemFile = os.path.join(self.cubemxprojectpath, 293 | "Drivers/CMSIS/Device/ST/STM32%sxx/Source/Templates/system_stm32%sxx.c" % ( 294 | self.HAL_TYPE, self.HAL_TYPE.lower())) 295 | 296 | if not os.path.exists(systemFile): 297 | #CubeMX 4.18 moved the system_stm32XXxx.c file inside the main src folder 298 | cubeMXVersion = 418 299 | systemFile = os.path.join(self.cubemxprojectpath, 300 | "Src/system_stm32%sxx.c" % self.HAL_TYPE.lower()) 301 | 302 | startupFile = os.path.join(self.cubemxprojectpath, 303 | "Drivers/CMSIS/Device/ST/STM32%sxx/Source/Templates/gcc/startup_%s.s" % ( 304 | self.HAL_TYPE, self.HAL_MCU_TYPE.lower())) 305 | 306 | if not os.path.exists(startupFile): 307 | #CubeMX 4.19 moved the system_stm32XXxx.s file inside the startup folder 308 | cubeMXVersion = 419 309 | startupFile = os.path.join(self.cubemxprojectpath, 310 | "startup/startup_%s.s" % self.HAL_MCU_TYPE.lower()) 311 | 312 | locations = ((systemFile, dstSourceDir), (startupFile, dstSourceDir)) 313 | 314 | if not self.dryrun: 315 | for loc in locations: 316 | shutil.copy(loc[0], loc[1]) 317 | 318 | os.rename( 319 | os.path.join(self.eclipseprojectpath, "system/src/cmsis/startup_%s.s" % self.HAL_MCU_TYPE.lower()), 320 | os.path.join(self.eclipseprojectpath, "system/src/cmsis/startup_%s.S" % self.HAL_MCU_TYPE.lower())) 321 | 322 | if cubeMXVersion >= 418: 323 | os.unlink(os.path.join(self.eclipseprojectpath, "src/system_stm32%sxx.c" % self.HAL_TYPE.lower())) 324 | 325 | self.logger.info("Successfully imported CMSIS files") 326 | 327 | def importHAL(self): 328 | """Import the ST HAL inside the Eclipse project""" 329 | srcIncludeDir = os.path.join(self.cubemxprojectpath, "Drivers/STM32%sxx_HAL_Driver/Inc" % self.HAL_TYPE) 330 | srcSourceDir = os.path.join(self.cubemxprojectpath, "Drivers/STM32%sxx_HAL_Driver/Src" % self.HAL_TYPE) 331 | dstIncludeDir = os.path.join(self.eclipseprojectpath, "system/include/stm32%sxx" % self.HAL_TYPE.lower()) 332 | dstSourceDir = os.path.join(self.eclipseprojectpath, "system/src/stm32%sxx" % self.HAL_TYPE.lower()) 333 | 334 | locations = ((srcIncludeDir, dstIncludeDir), (srcSourceDir, dstSourceDir)) 335 | 336 | for loc in locations: 337 | self.copyTreeContent(loc[0], loc[1]) 338 | 339 | self.addAssemblerMacros((self.HAL_MCU_TYPE,)) 340 | self.addCMacros((self.HAL_MCU_TYPE,)) 341 | self.addCPPMacros((self.HAL_MCU_TYPE,)) 342 | 343 | if not self.dryrun: 344 | try: 345 | # Try to delete templete files, if generated 346 | os.unlink(os.path.join(self.eclipseprojectpath, "system/src/stm32%sxx/stm32%sxx_hal_msp_template.c" % ( 347 | self.HAL_TYPE.lower(), self.HAL_TYPE.lower()))) 348 | os.unlink(os.path.join(self.eclipseprojectpath, 349 | "system/src/stm32%sxx/stm32%sxx_hal_timebase_tim_template.c" % ( 350 | self.HAL_TYPE.lower(), self.HAL_TYPE.lower()))) 351 | except OSError: 352 | pass 353 | 354 | self.logger.info("Successfully imported the STCubeHAL") 355 | 356 | def importMiddlewares(self): 357 | """Import the ST HAL inside the Eclipse project""" 358 | 359 | foundFreeRTOS = False 360 | foundMiddlewares = False 361 | foundFF = False 362 | foundLwIP = False 363 | 364 | for rootdir, dirs, files in os.walk(self.cubemxprojectpath): 365 | if "Middlewares" in dirs: 366 | foundMiddlewares = True 367 | if "FreeRTOS" in dirs: 368 | foundFreeRTOS = True 369 | if "FatFs" in dirs: 370 | foundFF = True 371 | if "LwIP" in dirs: 372 | foundLwIP = True 373 | 374 | if not foundMiddlewares: 375 | return 376 | 377 | srcDir = os.path.join(self.cubemxprojectpath, "Middlewares") 378 | dstDir = os.path.join(self.eclipseprojectpath, "Middlewares") 379 | 380 | locations = ((srcDir, dstDir),) 381 | 382 | try: 383 | for loc in locations: 384 | self.copyTree(loc[0], loc[1]) 385 | except OSError as e: 386 | import errno 387 | if e.errno == errno.EEXIST: 388 | shutil.rmtree(dstDir) 389 | return self.importMiddlewares() 390 | 391 | # Adding Middleware library includes 392 | includes = [inc.replace("../../", "") for inc in self.getAC6Includes() if "Middlewares" in inc] 393 | 394 | self.addCIncludes(includes) 395 | self.addCPPIncludes(includes) 396 | self.addAssemblerIncludes(includes) 397 | self.addSourceEntries(("Middlewares",)) 398 | 399 | self.logger.info("Successfully imported Middlewares libraries") 400 | 401 | if foundLwIP: 402 | try: 403 | ethernetif_template = os.path.join(self.eclipseprojectpath, 404 | "Middlewares/Third_Party/LwIP/src/netif/ethernetif_template.c") 405 | os.unlink(ethernetif_template) 406 | except OSError: # CubeMX 4.14 no longer generates this file 407 | pass 408 | 409 | if foundFreeRTOS: 410 | print("#" * 100) 411 | print("####", end="") 412 | print("READ CAREFULLY".center(92), end="") 413 | print("####") 414 | print("#" * 100) 415 | print("""The original CubeMX project contains the FreeRTOS middleware library. 416 | This library was imported in the Eclipse project correctly, but you still need to 417 | configure your tool-chain 'Float ABI' and 'FPU Type' if your STM32 support hard float 418 | (e.g. for a STM32F4 MCU set 'Float ABI'='FP Instructions(hard)'' and 'FPU Type'='fpv4-sp-d16'. 419 | Moreover, exclude from build those MemManage files (heap_1.c, etc) not needed for your project.""") 420 | 421 | if foundFF: 422 | print("#" * 100) 423 | print("####", end="") 424 | print("READ CAREFULLY".center(92), end="") 425 | print("####") 426 | print("#" * 100) 427 | print("""The original CubeMX project contains the FatFs middleware library. 428 | This library was imported in the Eclipse project correctly, but you still need to 429 | exclude from build those uneeded codepage files (cc932.c, etc) not needed for your project.""") 430 | 431 | def patchMEM_LDFile(self): 432 | """ Fix the FLASH starting address if set to 0x00000000 """ 433 | 434 | memLD_File = os.path.join(self.eclipseprojectpath, "ldscripts", "mem.ld") 435 | 436 | fcontent = open(memLD_File, "r+").readlines() 437 | changed = False 438 | for i in range(len(fcontent)): 439 | if re.search("FLASH .([r,x])", fcontent[i]): 440 | fcontent[i] = fcontent[i].replace("00000000", "08000000") 441 | changed = True 442 | 443 | if changed and not self.dryrun: 444 | open(memLD_File, "w+").writelines(fcontent) 445 | 446 | if changed: 447 | self.logger.info("Changed the FLASH region starting address from 0x00000000 to 0x08000000") 448 | 449 | def parseEclipseProjectFile(self): 450 | """Parse the Eclipse XML project file""" 451 | projectFile = os.path.join(self.eclipseprojectpath, ".cproject") 452 | self.projectRoot = etree.fromstring(open(projectFile).read().encode('UTF-8')) 453 | 454 | def printEclipseProjectFile(self): 455 | """Do a pretty print of Eclipse project DOM""" 456 | xmlout = etree.tostring(self.projectRoot, pretty_print=True) 457 | # lxml correctly escapes the "&" to "&", as specified by the XML standard. 458 | # However, Eclipse expects that the " charachter is espressed as " So, 459 | # here we replace the "&" with "&" in the final XML file 460 | xmlout = xmlout.replace("&", "&") 461 | print(xmlout) 462 | 463 | def saveEclipseProjectFile(self): 464 | """Save the XML DOM of Eclipse project inside the .cproject file""" 465 | 466 | xmlout = '' + etree.tostring( 467 | self.projectRoot).decode('UTF-8') 468 | # lxml correctly escapes the "&" to "&", as specified by the XML standard. 469 | # However, Eclipse expects that the " charachter is espressed as " So, 470 | # here we replace the "&" with "&" in the final XML file 471 | xmlout = xmlout.replace("&", "&") 472 | projectFile = os.path.join(self.eclipseprojectpath, ".cproject") 473 | if not self.dryrun: 474 | open(projectFile, "w+").write(xmlout) 475 | 476 | def setDryRun(self, dryrun): 477 | """Enable dryrun mode: it does't execute operations on projects""" 478 | self.dryrun = dryrun 479 | if dryrun > 0: 480 | self.logger.debug("Running in DryRun mode: the Eclipse project will not be modified") 481 | 482 | def fixDeviceInclude(self): 483 | """Set the correct include file inside the cmsis device include if exists, this will work even if old naming was present""" 484 | filename_in = os.path.join(self.eclipseprojectpath, "system/include/cmsis/cmsis_device.h") 485 | filename_out = filename_in + '.tmp' 486 | if os.path.isfile(filename_in): 487 | with open(filename_out, "wt") as fout: 488 | with open(filename_in, "rt") as fin: 489 | for line in fin: 490 | if re.search('^#include .*stm32.*\.h.*$', line): 491 | fout.write('#include "stm32%sxx.h"\n' % self.HAL_TYPE.lower()) 492 | else: 493 | fout.write(line) 494 | os.remove(filename_in) 495 | os.rename(filename_out, filename_in) 496 | 497 | class InvalidCubeMXFolder(Exception): 498 | pass 499 | 500 | 501 | class InvalidEclipseFolder(Exception): 502 | pass 503 | 504 | 505 | class InvalidSW4STM32Project(Exception): 506 | pass 507 | 508 | 509 | if __name__ == "__main__": 510 | parser = argparse.ArgumentParser( 511 | description='Import a CubeMX generated project inside an existing Eclipse project generated with the GNU ARM plugin') 512 | 513 | parser.add_argument('eclipse_path', metavar='eclipse_dest_prj_path', type=str, 514 | help='eclipse destination project path') 515 | 516 | parser.add_argument('cubemx_path', metavar='cubemx_src_prj_path', type=str, 517 | help='cube_mx source project path') 518 | 519 | parser.add_argument('-v', '--verbose', type=int, action='store', 520 | help='Verbose level') 521 | 522 | parser.add_argument('--dryrun', action='store_true', 523 | help="Doesn't perform operations - for debug purpose") 524 | 525 | args = parser.parse_args() 526 | 527 | if args.verbose == 3: 528 | logging.basicConfig(level=logging.DEBUG) 529 | if args.verbose == 2: 530 | logging.basicConfig(level=logging.INFO) 531 | else: 532 | logging.basicConfig(level=logging.ERROR) 533 | 534 | cubeImporter = CubeMXImporter() 535 | cubeImporter.setDryRun(args.dryrun) 536 | cubeImporter.eclipseProjectPath = args.eclipse_path 537 | cubeImporter.cubeMXProjectPath = args.cubemx_path 538 | cubeImporter.parseEclipseProjectFile() 539 | cubeImporter.deleteOriginalEclipseProjectFiles() 540 | cubeImporter.importApplication() 541 | cubeImporter.importHAL() 542 | cubeImporter.importCMSIS() 543 | cubeImporter.importMiddlewares() 544 | cubeImporter.saveEclipseProjectFile() 545 | cubeImporter.patchMEM_LDFile() 546 | # cubeImporter.addCIncludes(["../middlewares/freertos"]) 547 | # cubeImporter.printEclipseProjectFile() 548 | -------------------------------------------------------------------------------- /cubemximporter.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["python", "$file", "-e", "/Users/cnoviello/STM32Toolchain/projects/empty", "-c", "/Users/cnoviello/STM32Toolchain/cubemx-out/boh", "-v3", "--dryrun"] 3 | } 4 | --------------------------------------------------------------------------------