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
|
'''
Update/generate 'tasks.json' file in .vscode subfolder.
'tasks.json' fields description:
https://code.visualstudio.com/docs/editor/tasks
'''
import os
import json
import utilities as utils
import templateStrings as tmpStr
import updatePaths as pth
import updateWorkspaceSources as wks
import updateMakefile as mkf
import updateBuildData as build
__version__ = utils.__version__
class Tasks():
def __init__(self):
self.cPStr = wks.CPropertiesStrings()
self.mkfStr = mkf.MakefileStrings()
self.bStr = build.BuildDataStrings()
def checkTasksFile(self):
'''
Check if 'tasks.json' file exists. If it does, check if it is a valid JSON file.
If it doesn't exist, create new according to template.
'''
if utils.pathExists(utils.tasksPath):
# file exists, check if it loads OK
try:
with open(utils.tasksPath, 'r') as tasksFile:
json.load(tasksFile)
print("Existing 'tasks.json' file found.")
return
except Exception as err:
errorMsg = "Invalid 'tasks.json' file. Creating backup and new one.\n"
errorMsg += "Possible cause: invalid json format or comments (not supported by this scripts). Error:\n"
errorMsg += str(err)
print(errorMsg)
utils.copyAndRename(utils.tasksPath, utils.tasksBackupPath)
self.createTasksFile()
else: # 'tasks.json' file does not exist jet, create it according to template string
self.createTasksFile()
def createTasksFile(self):
'''
Create fresh 'tasks.json' file.
'''
try:
with open(utils.tasksPath, 'w') as tasksFile:
data = json.loads(tmpStr.tasksFileTemplate)
dataToWrite = json.dumps(data, indent=4, sort_keys=False)
tasksFile.seek(0)
tasksFile.truncate()
tasksFile.write(dataToWrite)
print("New 'tasks.json' file created.")
except Exception as err:
errorMsg = "Exception error creating new 'tasks.json' file:\n"
errorMsg += str(err)
utils.printAndQuit(errorMsg)
def getTasksData(self):
'''
Get data from current 'tasks.json' file.
File existance is previoulsy checked in 'checkTasksFile()'.
'''
with open(utils.tasksPath, 'r') as tasksFile:
data = json.load(tasksFile)
return data
def overwriteTasksFile(self, data):
'''
Overwrite existing 'tasks.json' file with new data.
'''
try:
with open(utils.tasksPath, 'r+') as tasksFile:
tasksFile.seek(0)
tasksFile.truncate()
dataToWrite = json.dumps(data, indent=4, sort_keys=False)
tasksFile.write(dataToWrite)
print("'tasks.json' file updated!")
except Exception as err:
errorMsg = "Exception error overwriting 'tasks.json' file:\n"
errorMsg += str(err)
utils.printAndQuit(errorMsg)
def addOrReplaceTask(self, data, taskData):
'''
Check wether tasks with this "label" already exists. If it doesn't, create new task, overwrite otherwise.
'''
thisTaskName = taskData["label"]
taskExist = False
listOfTasks = data["tasks"]
for taskIndex, task in enumerate(listOfTasks):
if task["label"] == thisTaskName:
# task with this name already exist, replace it's content
data["tasks"][taskIndex] = taskData
taskExist = True
if not taskExist:
data["tasks"].append(taskData)
return data
def addAllTasks(self, tasksData):
'''
Merge and return all combined tasks data.
'''
# building and compiling project tasks
task = self.getBuildTask()
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getCompileTask()
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getDeleteBuildFolderTask()
tasksData = self.addOrReplaceTask(tasksData, task)
# debugging and target control tasts
task = self.getBuildDownloadAndRunTask()
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getDownloadAndRunTask()
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getResetAndRunTask()
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getHaltTask()
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getRunTask()
tasksData = self.addOrReplaceTask(tasksData, task)
# update IDE workspace tasks
task = self.getRunCurrentPythonFileTask() # common "run python file" task
tasksData = self.addOrReplaceTask(tasksData, task)
if utils.cubeMxProjectFilePath is not None:
task = self.getOpenCubeMXTask() # open CubeMX project
tasksData = self.addOrReplaceTask(tasksData, task)
task = self.getUpdateTask() # update all files for VS Code so it can be used as IDE
tasksData = self.addOrReplaceTask(tasksData, task)
# TODO USER: User can add other custom tasks here
# - copy any of getXTask() functions below, edit
# - add this function here as other tasks above
return tasksData
########################################################################################################################
# Build, compile and clean tasks
########################################################################################################################
def getBuildTask(self):
'''
Add build task (execute 'make' command). Also the VS Code default 'build' task.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"group": {
"kind": "build",
"isDefault": true
},
"type": "shell",
"command": "specified below",
"args": ["specified below"],
"problemMatcher": {
"pattern": {
"regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(warning|error):\\\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
},
"presentation": {
"focus": true
}
}
"""
jsonTaskData = json.loads(taskData)
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_build
jsonTaskData["command"] = buildData[self.bStr.buildToolsPath]
gccFolderPath = os.path.dirname(buildData[self.bStr.gccExePath])
gccFolderPath = utils.pathWithForwardSlashes(gccFolderPath)
jsonTaskData["args"] = ["GCC_PATH=" + gccFolderPath] # specify compiler path to make command
numOfCores = os.cpu_count()
parallelJobsNumber = int(numOfCores * 1.5) # https://stackoverflow.com/questions/15289250/make-j4-or-j8/15295032
parallelJobsStr = "-j" + str(parallelJobsNumber)
jsonTaskData["args"].append(parallelJobsStr) # set 'make' parallel job execution
return jsonTaskData
def getCompileTask(self):
'''
Create compile current file task (execute gcc compile command).
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "will be replaced with GCC path below",
"args": ["will be replaced with path from buildData.json"],
"problemMatcher": {
"pattern": {
"regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(warning|error):\\\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
},
"presentation": {
"focus": true
}
}
"""
jsonTaskData = json.loads(taskData)
# get compiler C flags, defines, includes, ... from 'buildData.json'
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_compile
# defines
cDefines = buildData[self.bStr.cDefines]
cDefines = utils.preappendString(cDefines, '-D')
# includes
cIncludes = buildData[self.bStr.cIncludes]
cIncludes = utils.preappendString(cIncludes, '-I')
# build directory
buildDir = buildData[self.bStr.buildDirPath]
# c flags
cFlags = buildData[self.bStr.cFlags]
for flagIndex, flag in enumerate(cFlags):
if flag == "-MF":
newFlagString = "-MF'" + buildDir + "/${fileBasenameNoExtension}.d'"
cFlags[flagIndex] = newFlagString
continue
# output file
outputFilePath = "'" + buildDir + "/${fileBasenameNoExtension}.o'"
outputFile = ["-o"]
outputFile.append(outputFilePath)
# compile file string
fileString = "'${relativeFile}'"
fileString = [fileString]
jsonTaskData["command"] = buildData[self.bStr.gccExePath]
jsonTaskData["args"] = ["-c"] # only compile switch
jsonTaskData["args"].extend(cDefines)
jsonTaskData["args"].extend(cIncludes)
jsonTaskData["args"].extend(cFlags)
jsonTaskData["args"].extend(fileString)
jsonTaskData["args"].extend(outputFile)
return jsonTaskData
def getDeleteBuildFolderTask(self):
'''
Create delete task (execute 'make clean' command).
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": ["clean"],
"problemMatcher": [],
"presentation": {
"focus": false
}
}
"""
jsonTaskData = json.loads(taskData)
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_clean
jsonTaskData["command"] = buildData[self.bStr.buildToolsPath]
return jsonTaskData
########################################################################################################################
# Debugging and target control tasks
########################################################################################################################
def getBuildDownloadAndRunTask(self):
'''
Create Build + Download and run task. Use 'dependsOn' feature to avoid doubling code.
Note: If multiple 'dependOn' tasks are defined, these tasks are launched simultaneously,
not chained one after another.
'''
jsonTaskData = self.getDownloadAndRunTask()
jsonTaskData["label"] = tmpStr.taskName_CPU_buildDownloadRun
jsonTaskData["dependsOn"] = tmpStr.taskName_build
return jsonTaskData
def getDownloadAndRunTask(self):
'''
Create Download and run task.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": ["specified below"],
"problemMatcher": []
}
"""
jsonTaskData = json.loads(taskData)
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_CPU_downloadRun
jsonTaskData["command"] = buildData[self.bStr.openOcdPath]
jsonTaskData["args"] = []
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(buildData[self.bStr.openOcdInterfacePath])
for arg in buildData[self.bStr.openOcdConfig]:
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(arg)
# -c program filename [verify] [reset] [exit] [offset] ([] are optional arguments)
# Note: due problems with VS Code OpenOCD Tasks in case of workspace path containing spaces, target executable is passed
# as relative path.
workspacePath = utils.workspacePath
jsonTaskData["args"].append("-c")
programString = "program " + buildData[self.bStr.targetExecutablePath] + " verify reset exit"
jsonTaskData["args"].append(programString)
return jsonTaskData
def getResetAndRunTask(self):
'''
Create CPU: Reset and run task.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": ["specified below"],
"problemMatcher": []
}
"""
jsonTaskData = json.loads(taskData)
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_CPU_resetRun
jsonTaskData["command"] = buildData[self.bStr.openOcdPath]
jsonTaskData["args"] = []
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(buildData[self.bStr.openOcdInterfacePath])
for arg in buildData[self.bStr.openOcdConfig]:
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(arg)
jsonTaskData["args"].append("-c init") # init must be executed before other commands!
jsonTaskData["args"].append("-c reset")
jsonTaskData["args"].append("-c exit")
return jsonTaskData
def getHaltTask(self):
'''
Create Halt/stop task.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": ["specified below"],
"problemMatcher": []
}
"""
jsonTaskData = json.loads(taskData)
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_CPU_halt
jsonTaskData["command"] = buildData[self.bStr.openOcdPath]
jsonTaskData["args"] = []
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(buildData[self.bStr.openOcdInterfacePath])
for arg in buildData[self.bStr.openOcdConfig]:
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(arg)
jsonTaskData["args"].append("-c init") # init must be executed before other commands!
jsonTaskData["args"].append("-c halt")
jsonTaskData["args"].append("-c exit")
return jsonTaskData
def getRunTask(self):
'''
Create Run task.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": ["specified below"],
"problemMatcher": []
}
"""
jsonTaskData = json.loads(taskData)
buildData = build.BuildData().getBuildData()
jsonTaskData["label"] = tmpStr.taskName_CPU_run
jsonTaskData["command"] = buildData[self.bStr.openOcdPath]
jsonTaskData["args"] = []
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(buildData[self.bStr.openOcdInterfacePath])
for arg in buildData[self.bStr.openOcdConfig]:
jsonTaskData["args"].append("-f")
jsonTaskData["args"].append(arg)
jsonTaskData["args"].append("-c init") # init must be executed before other commands!
jsonTaskData["args"].append("-c resume")
jsonTaskData["args"].append("-c exit")
return jsonTaskData
########################################################################################################################
# Other tasks
########################################################################################################################
def getRunCurrentPythonFileTask(self):
'''
Create Run Python file task, which runs current active Python file.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": [
"${file}"
],
"presentation": {
"focus": true
},
"problemMatcher": []
}
"""
buildData = build.BuildData().getBuildData()
jsonTaskData = json.loads(taskData)
jsonTaskData["label"] = tmpStr.taskName_Python
jsonTaskData["command"] = buildData[self.bStr.pythonExec]
return jsonTaskData
def getOpenCubeMXTask(self):
'''
Create Open CubeMX project task. Starts with default program.
Method of starting CubeMX differs across systems:
- WIN: use standard 'start' cmd command to start default program for '.ioc' files
- LINUX: does not associate itself with files by default.
Use a program like "Main Menu" for GNOME to add CubeMX to the applications list,
and then it can be selected as the default program for .ioc files.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": ["specified below"],
"presentation": {
"focus": false
},
"problemMatcher": []
}
"""
osIs = utils.detectOs()
if osIs == "unix":
openCubeCommand = "xdg-open"
elif osIs == "osx":
openCubeCommand = "/Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX"
else:
openCubeCommand = "start"
jsonTaskData = json.loads(taskData)
jsonTaskData["label"] = tmpStr.taskName_OpenCubeMX
jsonTaskData["command"] = openCubeCommand
jsonTaskData["args"] = [utils.cubeMxProjectFilePath] # opens with default program
return jsonTaskData
def getUpdateTask(self):
'''
Create Update workspace task, which runs update.py script.
'''
taskData = """
{
"label": "will be replaced with templateStrings string",
"type": "shell",
"command": "specified below",
"args": [
"${workspaceFolder}/ideScripts/update.py"
],
"presentation": {
"focus": true
},
"problemMatcher": []
}
"""
buildData = build.BuildData().getBuildData()
jsonTaskData = json.loads(taskData)
jsonTaskData["label"] = tmpStr.taskName_updateWorkspace
jsonTaskData["command"] = buildData[self.bStr.pythonExec]
return jsonTaskData
########################################################################################################################
if __name__ == "__main__":
utils.verifyFolderStructure()
paths = pth.UpdatePaths()
bData = build.BuildData()
cP = wks.CProperties()
makefile = mkf.Makefile()
tasks = Tasks()
# build data (update tools paths if neccessary)
buildData = bData.prepareBuildData()
# create taks file
tasks.checkTasksFile()
tasksData = tasks.getTasksData()
tasksData = tasks.addAllTasks(tasksData)
tasks.overwriteTasksFile(tasksData)
|