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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
|
'''
This file gets data from Keil project and creates:
- base Makefile which can be used with VS Code STM32 IDE ideScripts
- VS Code workspace
'''
import copy
import json
import os
import shutil
import subprocess
import sys
from xml.dom import minidom
import templateStrings as tmpStr
import updateMakefile as mkf
import utilities as utils
from updateMakefile import MakefileStrings as mkfStr
__version__ = '1.0'
class Paths():
def __init__(self):
self.rootFolder = None # path where ideScripts folder is placed
self.cubeMxExe = None # path to STM32CubeMX executable
self.tmpCubeMxFolder = None # path to temporary folder, there CubeMx performs its magic
self.tmpCubeMxScript = None # path to temporary script file for CubeMx
self.tmpMakefile = None # tempory Makefile that is later modified and copied to ideScripts root folder
self.outputMakefile = None # final clean Makefile that is later used by ideScripts
self.keilProjectFolder = None # path to Keil project file directory
self.keilProject = None # path to Keil project file
class KeilProjectData:
def __init__(self):
self.projName = None
self.cpuName = None
self.stmExactCpuName = None
self.svdFile = None
self.cDefines = []
self.asmDefines = []
self.cIncludes = [] # relative paths
self.asmIncludes = []
self.allSources = []
self.cSources = []
self.asmSources = []
self.cCompilerSettings = []
self.asmCompilerSettings = []
self.linkerSettings = []
def getCubeMxExePath():
'''
Get absolute path to STM32CubeMX.exe either by windows default associated program or user input.
'''
cubeMxPath = utils.findExecutablePath('ioc', raiseException=False)
if cubeMxPath is not None:
if os.path.exists(cubeMxPath):
cubeMxPath = utils.pathWithForwardSlashes(cubeMxPath)
print("STM32CubeMX.exe path automatically updated.")
return cubeMxPath
else:
while cubeMxPath is None:
cubeMxPath = utils.getUserPath('STM32CubeMX.exe')
if os.path.exists(cubeMxPath):
cubeMxPath = utils.pathWithForwardSlashes(cubeMxPath)
return cubeMxPath
else:
cubeMxPath = None
def getKeilProjectPath(paths: Paths):
'''
Try to find Keil *.uvprojx file. If found, this file is used as project file.
If not found, throw error.
If multiple files found, user is asked to enter specific file path.
Return files absolute paths: *.uvprojx
'''
KEIL_PROJECT_FILE_EXTENSION = '.uvprojx'
# Get the list of all files in directory tree at given path
allFiles = utils.getAllFilesInFolderTree(paths.rootFolder)
keilProjectFiles = []
for theFile in allFiles:
if theFile.find(KEIL_PROJECT_FILE_EXTENSION) != -1:
keilProjectFiles.append(theFile)
if len(keilProjectFiles) == 0:
errorMsg = "Unable to find any Keil project files ending with " + KEIL_PROJECT_FILE_EXTENSION + ". "
errorMsg += "Is folder structure correct?\n\t"
errorMsg += "Searched files in folder tree: " + paths.rootFolder
raise Exception(errorMsg)
elif len(keilProjectFiles) == 1:
# only one keil project file available, take this one
print("Keil project file found:", keilProjectFiles[0])
return keilProjectFiles[0]
else:
print("More than one Keil project files available. Select the right one.")
keilProjectPath = None
while keilProjectPath is None:
keilProjectPath = utils.getUserPath('Keil project (.uvprojx)')
if os.path.exists(keilProjectPath):
break
else:
keilProjectPath = None
print("Keil project path updated.")
return keilProjectPath
def getKeilProjectData(paths: Paths) -> KeilProjectData:
'''
Read Keil project file and return filled KeilProjectData class.
Some blocks are placed in try...except statements - error is thrown if xml field does not contain any items.
'''
projData = KeilProjectData()
_, fileName = os.path.split(paths.keilProject)
projData.projName, _ = os.path.splitext(fileName)
projFileData = minidom.parse(paths.keilProject)
projData.cpuName = projFileData.getElementsByTagName('Device')[0].firstChild.data
svdFile = projFileData.getElementsByTagName('SFDFile')[0].firstChild.data
_, projData.svdFile = os.path.split(svdFile)
# c stuff
_cads = projFileData.getElementsByTagName('Cads')[0]
try: # c defines
cDefines = _cads.getElementsByTagName('Define')[0].firstChild.data
projData.cDefines = utils.stringToList(cDefines, ',')
except Exception as err:
print("WARNING: unable to get C Defines: error or no items")
try: # c include folders
cIncludes = _cads.getElementsByTagName('IncludePath')[0].firstChild.data
cIncludesList = utils.stringToList(cIncludes, ';')
projData.cIncludes = _fixRelativePaths(paths, cIncludesList)
except Exception as err:
print("WARNING: unable to get C Includes (folders): error or no items")
try: # c miscelaneous controls
cMiscControls = _cads.getElementsByTagName('MiscControls')[0].firstChild.data
projData.cCompilerSettings = utils.stringToList(cMiscControls, ',')
except Exception as err:
print("WARNING: unable to get C Miscelaneous settings: error or no items")
# asm stuff
_aads = projFileData.getElementsByTagName('Aads')[0]
try: # asm defines
asmDefines = _aads.getElementsByTagName('Define')[0].firstChild.data
projData.asmDefines = utils.stringToList(asmDefines, ',')
except Exception as err:
print("WARNING: unable to get Asm Defines: error or no items")
try: # asm include folders
asmIncludes = _aads.getElementsByTagName('IncludePath')[0].firstChild.data
asmIncludes = utils.stringToList(asmIncludes, ';')
projData.asmIncludes = _fixRelativePaths(paths, asmIncludes)
except Exception as err:
print("WARNING: unable to get Asm Includes (folders): error or no items")
try: # asm miscelaneous controls
asmMiscControls = _aads.getElementsByTagName('MiscControls')[0].firstChild.data
projData.asmCompilerSettings = utils.stringToList(asmMiscControls, ',')
except Exception as err:
print("WARNING: unable to get Asm Miscelaneous settings: error or no items")
# get linker misc controls
_lads = projFileData.getElementsByTagName('Cads')[0]
try: # asm miscelaneous controls
linkerMiscControls = _lads.getElementsByTagName('MiscControls')[0].firstChild.data
projData.linkerSettings = utils.stringToList(linkerMiscControls, ',')
except Exception as err:
print("WARNING: unable to get Linker Miscelaneous settings: error or no items")
# get all source files. Add only '.c' and '.s' files. Throw error on exception, this data is mandatory.
files = projFileData.getElementsByTagName('FilePath')
cSourceFiles = []
asmSourceFiles = []
for fileData in files:
filePathList = _fixRelativePaths(paths, [fileData.firstChild.data])
if len(filePathList) == 1:
filePath = filePathList[0]
projData.allSources.append(filePath)
_, extension = os.path.splitext(filePath)
if extension == '.c':
cSourceFiles.append(filePath)
elif extension == '.s':
asmSourceFiles.append(filePath)
else:
msg = "WARNING: this file is not '.c' or '.s'. Not added to project (user must handle this manually).\n"
msg += "\t" + filePath
print(msg)
else:
# missing file reported in _fixRelativePaths
msg = "WARNING: seems like none or more than one file is specified. This is not a valid Keil project syntax: "
msg += str(filePathList)
print(msg)
projData.cSources = cSourceFiles
print("\nC source files added:\n\t" + '\n\t'.join(cSourceFiles))
projData.asmSources = asmSourceFiles
print("\nAsm source files added:\n\t" + '\n\t'.join(asmSourceFiles) + '\n')
return projData
def _fixRelativePaths(paths: Paths, relativePaths: list):
'''
Correct relative paths according to the folder structure as it is expected.
Relative paths in Keil project file are relative to the keil file path,
while we need paths relative to root folder where 'ideScripts' is.
Return list of a VALID relative paths paths.
'''
keilProjectAbsPath = os.path.normpath(os.path.join(paths.rootFolder, paths.keilProject))
allPaths = []
for relativePath in relativePaths:
if os.path.isabs(relativePath):
relativePath = os.path.normpath(relativePath)
relativePath = utils.pathWithForwardSlashes(relativePath)
allPaths.append(relativePath)
continue
absolutePath = os.path.normpath(os.path.join(paths.keilProjectFolder, relativePath))
if os.path.exists(absolutePath):
# path is valid, build correct relative path
try:
newRelativePath = os.path.relpath(absolutePath, paths.rootFolder)
newRelativePath = utils.pathWithForwardSlashes(newRelativePath)
allPaths.append(newRelativePath)
except:
absolutePath = utils.pathWithForwardSlashes(absolutePath)
allPaths.append(absolutePath)
else:
print("WARNING: unable to find file/folder:", absolutePath)
print("\tBuilt from relative path:", relativePath)
return allPaths
def _getAbsolutePaths(relativePaths):
'''
Get list of relative paths and try to build absolute paths.
If any path does not exist, print warning message.
Return list of valid absolute paths.
'''
absolutePaths = []
for relativePath in relativePaths:
relativePath = relativePath.strip()
relativePath = os.path.normpath(os.path.join(paths.keilProjectFolder, relativePath))
if os.path.exists(relativePath):
relativePath = utils.pathWithForwardSlashes(relativePath)
absolutePaths.append(relativePath)
else:
print("WARNING: unable to find file/folder:", relativePath)
return absolutePaths
def createMakefileTemplate(paths: Paths, keilProjData: KeilProjectData):
'''
Create Makefile template with CubeMX.
'''
# create script that CubeMX executes
paths.tmpCubeMxFolder = os.path.join(paths.rootFolder, tmpStr.cubeMxTmpFolderName)
paths.tmpCubeMxFolder = utils.pathWithForwardSlashes(paths.tmpCubeMxFolder)
if not os.path.exists(paths.tmpCubeMxFolder):
try:
os.mkdir(paths.tmpCubeMxFolder)
except Exception as err:
errorMsg = "Unable to create existing temporary folder:\n" + str(err)
print(errorMsg)
# even if any error occured, try to create files anyway
_createCubeMxTmpScript(paths, keilProjData)
# run CubeMX as subprocess with this script as a parameter
cmd = ['java', '-jar', paths.cubeMxExe, '-s', paths.tmpCubeMxScript]
if _checkCubeMxFirmwarePackage(paths, keilProjData):
cmd.append('-q') # no-gui mode
print("\tSTM32CubeMX GUI set to non-visible mode.")
else:
print("\tSTM32CubeMX GUI set to visible because of repository warning.")
try:
print("Generating template Makefile with STM32CubeMX...")
proc = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
if proc.returncode == 0:
print("\tSTM32CubeMX project generated.")
else:
errorMsg = "CubeMx returned non-zero exit code. Something went wrong:\n"
errorMsg += str(proc.stderr) + '\n'
errorMsg += str(proc.stdout)
utils.printAndQuit(errorMsg)
except Exception as err:
errorMsg = "Exception error while creating template Makefile with STM32CubeMX:\n" + str(err)
utils.printAndQuit(errorMsg)
# get makefile path
allGeneratedFiles = utils.getAllFilesInFolderTree(paths.tmpCubeMxFolder)
for theFile in allGeneratedFiles:
_, fileName = os.path.split(theFile)
if fileName == 'Makefile':
paths.tmpMakefile = theFile
print("\tMakefile found: " + paths.tmpMakefile)
_copyStartupFile(paths, keilProjData)
return
else:
errorMsg = "Unable to find template Makefile generated by STM32CubeMX. Was project really generated?"
utils.printAndQuit(errorMsg)
def _copyStartupFile(paths: Paths, keilProjData: KeilProjectData):
'''
Get '*.s' startup file in the same folder as CubeMX template Makefile file and
copy it into the same location as current startup file is.
'''
# find CubeMX temporary generated startup file
filesInMakefileDir = os.listdir(os.path.dirname(paths.tmpMakefile))
for theFile in filesInMakefileDir:
name, ext = os.path.splitext(theFile)
if ext == '.s':
startupFile = os.path.join(os.path.dirname(paths.tmpMakefile), theFile)
newStartupFilePath = os.path.join(paths.rootFolder, theFile)
try:
shutil.copy(startupFile, newStartupFilePath)
print("Default STM32CubeMX startup file copied to:", newStartupFilePath)
relativeStartupFilePath = os.path.relpath(newStartupFilePath, paths.rootFolder)
relativeStartupFilePath = utils.pathWithForwardSlashes(relativeStartupFilePath)
break
except Exception as err:
pass
#print("Seems like default STM32CubeMX startup file already exist:", newStartupFilePath)
# find startup file in current keil project data and replace it with this one
if len(keilProjData.asmSources) == 1:
# no problem only one '*.s' file, assume this is the startup file
originalStartupFile = keilProjData.asmSources[0]
keilProjData.asmSources = [relativeStartupFilePath]
msg = "Default " + originalStartupFile + " source was replaced with CubeMX one: " + relativeStartupFilePath
print(msg)
return
else:
# more than one assembler file found, try to find file with 'startup' string or throw error
possibleStartupFiles = []
for startupFileListIndex, asmFile in enumerate(keilProjData.asmSources):
_, fileName = os.path.split(asmFile)
if fileName.lower().find('startup') != -1:
possibleStartupFiles.append((asmFile, startupFileListIndex)) # asm file, file index in list
if len(possibleStartupFiles) == 1:
# OK, only one file with startup string
originalStartupFile = keilProjData.asmSources[possibleStartupFiles[0][1]]
keilProjData.asmSources[possibleStartupFiles[0][1]] = relativeStartupFilePath
msg = "WARNING: Multiple '*.s' files found. "
msg += originalStartupFile + " source file was replaced with CubeMX one: " + relativeStartupFilePath
print(msg)
else:
errorMsg = "Multiple '*.s' source files listed. Can't determine startup file (searched with 'startup' string)."
errorMsg += "\n\tAsm files: " + str(keilProjData.asmSources)
utils.printAndQuit(errorMsg)
def cleanTempMakefile(paths: Paths):
'''
Clean default generated Makefile data (sources, includes, names, ...).
'''
makefile = mkf.Makefile()
try:
with open(paths.tmpMakefile, 'r') as makefileHandler:
data = makefileHandler.readlines()
# do not change project name intentionally
# data = makefile.searchAndCleanData(data, makefile.mkfStr.projectName)
data = makefile.searchAndCleanData(data, makefile.mkfStr.cSources)
data = makefile.searchAndCleanData(data, makefile.mkfStr.asmSources)
data = makefile.searchAndCleanData(data, makefile.mkfStr.cDefines)
data = makefile.searchAndCleanData(data, makefile.mkfStr.asmDefines)
data = makefile.searchAndCleanData(data, makefile.mkfStr.cIncludes)
data = makefile.searchAndCleanData(data, makefile.mkfStr.asmIncludes)
data = makefile.searchAndCleanData(data, makefile.mkfStr.cIncludes)
print("Makefile template prepared.")
return data
except Exception as err:
errorMsg = "Exception during Makefile template preparation:\n" + str(err)
utils.printAndQuit(errorMsg)
def createNewMakefile(paths: Paths, keilProjData: KeilProjectData, newMakefileData):
'''
Fill and write new makefile with data from Keil project.
'''
makefile = mkf.Makefile()
try:
# sources
data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.cSources, keilProjData.cSources)
data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.asmSources, keilProjData.asmSources)
# includes
data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.cIncludes, keilProjData.cIncludes, preappend='-I')
data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.asmIncludes, keilProjData.asmIncludes, preappend='-I')
# defines
data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.cDefines, keilProjData.cDefines, preappend='-D')
data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.asmDefines, keilProjData.asmDefines, preappend='-D')
# compiler flags
# TODO should import?
# data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.cFlags, keilProjData.cCompilerSettings)
# data = makefile.searchAndAppend(newMakefileData, makefile.mkfStr.asmFlags, keilProjData.asmCompilerSettings)
if keilProjData.cCompilerSettings:
print("WARNING: C compiler settings not imported (user must handle manualy):", str(keilProjData.cCompilerSettings))
if keilProjData.asmCompilerSettings:
print("WARNING: Asm compiler settings not imported (user must handle manualy):", str(keilProjData.asmCompilerSettings))
if keilProjData.linkerSettings:
print("WARNING: Linker settings not imported (user must handle manualy):", str(keilProjData.linkerSettings))
with open(paths.outputMakefile, 'w+') as newMakefileHandler:
newMakefileHandler.writelines(data)
print("Makefile created in: " + paths.outputMakefile)
except Exception as err:
errorMsg = "Exception during creating new Makefile:\n" + str(err)
utils.printAndQuit(errorMsg)
def _getCPUName(paths: Paths, keilProjData: KeilProjectData):
'''
Try to get correct CPU family name from Keil project device tag.
STM32 CPU name, passed to CubeMX is not the same as Keil device name.
CubeMX device firmware pack must be installed so CubeMX is able to generate template Makefile.
'''
cubeMxMcuFolderPath = os.path.join(os.path.dirname(paths.cubeMxExe), 'db', 'mcu')
allFamiliesFilePath = os.path.join(cubeMxMcuFolderPath, 'families.xml')
allFiles = os.listdir(cubeMxMcuFolderPath)
for theFile in allFiles:
theFilePath = os.path.join(cubeMxMcuFolderPath, theFile)
if os.path.isfile(theFilePath):
if theFile.find(keilProjData.cpuName) != -1:
fileName, ext = os.path.splitext(theFile)
return fileName
errorMsg = "Unable to find matching STM32 CPU name for Keil project device: " + keilProjData.cpuName
utils.printAndQuit(errorMsg)
stm32McuData = minidom.parse(allFamiliesFilePath)
# build possible device family name search strings. Search order is important
allMcuData = stm32McuData.getElementsByTagName('Mcu')
minimumSearchStringLenght = len('STM32xx')
numOfStrippedCharacters = len(keilProjData.cpuName) - minimumSearchStringLenght
possibleDeviceSearchString = []
possibleDeviceSearchString.append(keilProjData.cpuName)
for charIndexFromBack in range(-1, -numOfStrippedCharacters-1, -1):
possibleDeviceSearchString.append(keilProjData.cpuName[:charIndexFromBack])
# find possible mcu ref names
allPossibleMcu = [None] * len(allMcuData)
subFamilyMcuData = None
for thisDeviceSearchString in possibleDeviceSearchString:
thisSearchStringPossibleMcu = []
for mcuData in allMcuData:
thisMcuName = mcuData.attributes._attrs['RPN'].value
if thisMcuName.find(thisDeviceSearchString) != -1:
thisSearchStringPossibleMcu.append(thisMcuName)
if thisSearchStringPossibleMcu:
if len(thisSearchStringPossibleMcu) <= len(allPossibleMcu):
allPossibleMcu = copy.copy(thisSearchStringPossibleMcu)
break
if not allPossibleMcu:
errorMsg = "Unable to find any (even partly) matching device name:" + keilProjData.cpuName
utils.printAndQuit(errorMsg)
allPossibleMcu = list(set(allPossibleMcu)) # remove cuplicates
# all possible MCUs are listed, ask user to select correct one
if len(allPossibleMcu) == 1:
keilProjData.stmExactCpuName = allPossibleMcu[0]
return allPossibleMcu[0]
else:
msg = "\n\n??? Please select exact CPU..."
for mcuIndex, mcu in enumerate(allPossibleMcu):
msg += '\n\t' + str(mcuIndex) + ': ' + mcu
limits = list(range(0, len(allPossibleMcu)))
askMsg = "Type number (0 - " + str(len(allPossibleMcu)) + ") and press Enter:"
print(msg + '\n' + askMsg)
while(True):
userAnswer = input()
try:
userNumber = int(userAnswer)
except:
print(askMsg)
continue
if userNumber not in limits:
print(askMsg)
else:
print("--> " + allPossibleMcu[userNumber] + " selected.")
keilProjData.stmExactCpuName = allPossibleMcu[userNumber]
return allPossibleMcu[userNumber]
def _checkCubeMxFirmwarePackage(paths: Paths, keilProjData: KeilProjectData):
'''
Check if this cpu family firmware package can be found inside CubeMX local repository.
Returns True if found, False otherwise.
'''
errorMsg = ''
try:
# get all files inside local repository
appDataFolder = os.path.expandvars(os.environ['APPDATA'])
stm32CubeRepositoryFolder = os.path.join(appDataFolder, '..', '..', 'STM32Cube', 'Repository')
stm32CubeRepositoryFolder = os.path.normpath(stm32CubeRepositoryFolder)
# get start of package name
cpuFamilyName = keilProjData.cpuName[len('STM32'):len('STM32xx')]
fwPackageName = 'STM32Cube_FW_' + cpuFamilyName
# search if any folder name contains fwPackageName
for item in os.listdir(stm32CubeRepositoryFolder):
if os.path.isdir(os.path.join(stm32CubeRepositoryFolder, item)):
if item.find(fwPackageName) != -1:
print("Seems like STM32CubeMX " + fwPackageName + "* package is installed.")
return True
except Exception as err:
errorMsg = "\nException:\n" + str(err)
msg = "WARNING: unable to check if STM32Cube " + keilProjData.cpuName + " firmware package is installed."
msg += errorMsg
print(msg)
return False
def _createCubeMxTmpScript(paths: Paths, keilProjData: KeilProjectData):
'''
Create tempory script for CubeMX Makefile generation.
Raises exception on error.
'''
paths.tmpCubeMxScript = os.path.join(paths.tmpCubeMxFolder, tmpStr.cubeMxTmpFileName)
paths.tmpCubeMxScript = utils.pathWithForwardSlashes(paths.tmpCubeMxScript)
dataToWrite = "// Temporary script for generating Base Makefile with STM32CubeMX.\n"
dataToWrite += "load " + _getCPUName(paths, keilProjData) + "\n"
dataToWrite += "project name " + keilProjData.projName + "\n"
dataToWrite += "project toolchain Makefile\n"
dataToWrite += "project path \"" + paths.tmpCubeMxFolder + "\"\n"
dataToWrite += "project generate\n"
dataToWrite += "exit"
with open(paths.tmpCubeMxScript, 'w+') as scriptHandler:
scriptHandler.write(dataToWrite)
print("Temporary STM32CubeMX script created.")
def deleteTemporaryFiles(paths: Paths):
'''
Delete (clean) CubeMX temporary files.
'''
try:
shutil.rmtree(paths.tmpCubeMxFolder)
print("STM32CubeMX temporary files deleted.")
except Exception as err:
errorMsg = "Exception while deleting STM32CubeMX temporary files:\n" + str(err)
raise Exception(err)
def _separateAbsoluteAndRelativePaths(pathsListToSeparate: list):
'''
This function splits pathsListToSeparate to relative and absolute paths.
Returns two lists: absolutePaths, relativePaths
'''
absPaths = []
relPaths = []
for path in pathsListToSeparate:
if os.path.isabs(path):
absPaths.append(path)
else:
relPaths.append(path)
return absPaths, relPaths
def createVSCodeWorkspace(paths: Paths, keilProjData: KeilProjectData):
'''
Create VS Code workspace so user can easily run 'update.py' from ideScripts.
'''
# add non-relative source folders to VS Code workspace folders.
allPaths = []
# TODO are c and asm includes folders needed in Code workspace?
# allPaths.extend(keilProjData.cIncludes)
# allPaths.extend(keilProjData.asmIncludes)
cSourcesFolders = [os.path.dirname(source) for source in keilProjData.cSources]
allPaths.extend(list(set(cSourcesFolders)))
asmSourcesFolders = [os.path.dirname(source) for source in keilProjData.asmSources]
allPaths.extend(list(set(asmSourcesFolders)))
absPaths, relPaths = _separateAbsoluteAndRelativePaths(allPaths)
dataToWrite = """
{
"folders": [
{
"path": "."
}
"""
for absPath in absPaths:
addToFoldersStr = ",{ \"path\": \"" + absPath + "\"}"
dataToWrite += addToFoldersStr
dataToWrite += "]"
dataToWrite += ",\"settings\": { }"
dataToWrite += "}"
data = json.loads(dataToWrite)
data = json.dumps(data, indent=4, sort_keys=False)
codeWorkspaceFileName = keilProjData.projName + '.code-workspace'
codeWorkspaceFilePath = os.path.join(paths.rootFolder, codeWorkspaceFileName)
with open(codeWorkspaceFilePath, 'w+') as fileHandler:
fileHandler.write(data)
print("VS Code workspace file created:", codeWorkspaceFilePath)
if __name__ == "__main__":
paths = Paths()
thisFileAbsPath = os.path.abspath(sys.argv[0])
paths.rootFolder = os.path.dirname(os.path.dirname(thisFileAbsPath))
paths.rootFolder = utils.pathWithForwardSlashes(paths.rootFolder)
paths.cubeMxExe = getCubeMxExePath()
paths.keilProject = getKeilProjectPath(paths)
paths.keilProjectFolder = utils.pathWithForwardSlashes(os.path.dirname(paths.keilProject))
paths.outputMakefile = utils.pathWithForwardSlashes(os.path.join(paths.rootFolder, 'Makefile'))
keilProjData = getKeilProjectData(paths)
createMakefileTemplate(paths, keilProjData)
cleanMakefileData = cleanTempMakefile(paths)
createNewMakefile(paths, keilProjData, cleanMakefileData)
deleteTemporaryFiles(paths)
createVSCodeWorkspace(paths, keilProjData)
|