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
|
'''
Update/generate 'buildData.json' file in '.vscode' subfolder from new Makefile.
This file also handles 'toolsPaths.json' file.
New Makefile is not updated by this script - it is updated with 'updateMakefile.py' or 'updateWorkspaceSources.py'
'''
import os
import json
import datetime
import utilities as utils
import templateStrings as tmpStr
import updatePaths as pth
import updateMakefile as mkf
import updateWorkspaceSources as wks
__version__ = utils.__version__
class BuildDataStrings():
# project sources, includes, defines, ....
cSources = 'cSources'
asmSources = 'asmSources'
ldSources = 'ldSources'
cIncludes = 'cIncludes'
asmIncludes = 'asmIncludes'
ldIncludes = 'ldIncludes'
cDefines = 'cDefines'
asmDefines = 'asmDefines'
cFlags = 'cFlags'
asmFlags = 'asmFlags'
ldFlags = 'ldFlags'
buildDirPath = 'buildDir'
# build/interface tools paths, configuration files
gccInludePath = 'gccInludePath' # GCC standard libraries root folder path
gccExePath = 'gccExePath' # path to 'gcc.exe'
buildToolsPath = 'buildToolsPath' # path to 'make.exe'
targetExecutablePath = 'targetExecutablePath' # path to downloadable '*.elf' file
pythonExec = 'pythonExec'
openOcdPath = 'openOcdPath' # path to 'openocd.exe'
openOcdInterfacePath = "openOcdInterfacePath" # path to OpenOCD interface cofniguration file (currently 'stlink.cfg')
openOcdConfig = 'openOcdConfig' # path to target '*.cfg' file
stm32SvdPath = 'stm32SvdPath' # path to target '*.svd' file
cubeMxProjectPath = 'cubeMxProjectPath'
# list of paths that are automatically built (default, system or once their 'parent' paths are valid)
derivedPaths = [
pythonExec,
gccInludePath
]
# list of target-specific configuration paths that must exist in 'buildData.json'
targetConfigurationPaths = [
openOcdConfig,
stm32SvdPath
]
# list of paths that can be cached in 'toolsPaths.json'
toolsPaths = [
gccExePath,
buildToolsPath,
pythonExec,
openOcdPath,
openOcdInterfacePath
]
class BuildData():
def __init__(self):
self.mkfStr = mkf.MakefileStrings()
self.cPStr = wks.CPropertiesStrings()
self.bStr = BuildDataStrings()
def prepareBuildData(self, request=False):
'''
This function is used in all 'update*.py' scripts and makes sure, that 'toolsPaths.json' and 'buildData.json' with a
valid tools/target cofniguration paths exist. Invalid paths are updated (requested from the user).
Returns available, valid build data.
Note: tools paths listed in 'BuildDataStrings.toolsPaths' are stored in system local 'toolsPaths.json' file, and are
copied (overwritten) to 'buildData.json' on first 'Update' task run. This makes it possible for multiple code contributors.
'''
paths = pth.UpdatePaths()
self.checkBuildDataFile()
buildData = self.getBuildData()
if self.checkToolsPathFile(): # a valid toolsPaths.json exists
toolsPathsData = self.getToolsPathsData()
else:
# no valid data from 'toolsPaths.json' file
# try to get data from current 'buildData.json' - backward compatibility for paths that already exist in 'buildData.json'
toolsPathsData = json.loads(tmpStr.toolsPathsTemplate)
for path in self.bStr.toolsPaths:
if path in buildData:
if utils.pathExists(buildData[path]):
toolsPathsData[path] = buildData[path]
# update/overwrite tools paths file. Don't mind if paths are already valid.
toolsPathsData = paths.verifyToolsPaths(toolsPathsData, request)
self.createUserToolsFile(toolsPathsData)
buildData = self.addToolsPathsToBuildData(buildData, toolsPathsData)
templateBuildData = json.loads(tmpStr.buildDataTemplate)
buildData = utils.mergeCurrentDataWithTemplate(buildData, templateBuildData)
buildData = paths.verifyTargetConfigurationPaths(buildData, request)
buildData = paths.copyTargetConfigurationFiles(buildData)
return buildData
def checkToolsPathFile(self):
'''
Returns True if 'toolsPaths.json' file exists and is a valid JSON file.
If it is not a valid JSON, delete it and return False.
'''
if utils.pathExists(utils.toolsPaths):
# file exists, check if it loads OK
try:
with open(utils.toolsPaths, 'r') as toolsFileHandler:
json.load(toolsFileHandler)
print("Valid 'toolsPaths.json' file found.")
return True
except Exception as err:
errorMsg = "Invalid 'toolsPaths.json' file. Error:\n" + str(err)
print(errorMsg)
try:
os.remove(utils.toolsPaths)
msg = "\tDeleted. New 'toolsPaths.json' will be created on first workspace update."
print(msg)
except Exception as err:
errorMsg = "Error deleting 'toolsPaths.json'. Error:\n" + str(err)
utils.printAndQuit(errorMsg)
# else: toolsPaths.json does not exist
return False
def checkBuildDataFile(self):
'''
This function makes sure 'buildData.json' is available.
If existing 'buildData.json' file is a valid JSON, it returns immediately.
If it is not a valid JSON file OR it does not exist, new 'buildData.json' file is created from template.
Note: There is no backup file for buildData.json, since it is always regenerated on Update task.
'''
if utils.pathExists(utils.buildDataPath):
# file exists, check if it loads OK
try:
with open(utils.buildDataPath, 'r') as buildDataFileHandler:
json.load(buildDataFileHandler)
print("Valid 'buildData.json' file found.")
return
except Exception as err:
errorMsg = "Invalid 'buildData.json' file. Error:\n" + str(err)
print(errorMsg)
try:
os.remove(utils.buildDataPath)
msg = "\tDeleted. New 'buildData.json' will be created on first workspace update."
print(msg)
except Exception as err:
errorMsg = "Error deleting 'buildData.json'. Error:\n" + str(err)
utils.printAndQuit(errorMsg)
# else: buildData.json does not exist
self.createBuildDataFile()
def createUserToolsFile(self, toolsPaths):
'''
Create 'toolsPaths.json' file with current tools paths.
This pats are absolute and not project-specific.
'''
data = json.loads(tmpStr.toolsPathsTemplate)
try:
data["VERSION"] = __version__
data["LAST_RUN"] = str(datetime.datetime.now())
for path in self.bStr.toolsPaths:
data[path] = toolsPaths[path]
data = json.dumps(data, indent=4, sort_keys=False)
with open(utils.toolsPaths, 'w+') as toolsPathsFile:
toolsPathsFile.write(data)
print("'toolsPaths.json' file updated!")
except Exception as err:
errorMsg = "Exception error overwriting 'toolsPaths.json' file:\n"
errorMsg += str(err)
print("WARNING:", errorMsg)
def createBuildDataFile(self):
'''
Create fresh 'buildData.json' file.
'''
try:
data = json.loads(tmpStr.buildDataTemplate)
dataToWrite = json.dumps(data, indent=4, sort_keys=False)
with open(utils.buildDataPath, 'w+') as buildDataFile:
buildDataFile.truncate()
buildDataFile.write(dataToWrite)
print("New template 'buildData.json' file created.")
except Exception as err:
errorMsg = "Exception error creating new 'buildData.json' file:\n"
errorMsg += str(err)
utils.printAndQuit(errorMsg)
def getToolsPathsData(self):
'''
Get data from current 'toolsPaths.json' file.
File existance is previoulsy checked in 'checkToolsPathFile()'.
'''
with open(utils.toolsPaths, 'r') as toolsPathsFile:
data = json.load(toolsPathsFile)
return data
def getBuildData(self):
'''
Get data from current 'buildData.json' file.
File existance is previoulsy checked in 'checkBuildDataFile()'.
'''
with open(utils.buildDataPath, 'r') as buildDataFile:
data = json.load(buildDataFile)
return data
def addToolsPathsToBuildData(self, buildData, toolsPaths):
'''
Get tools paths from 'toolsPaths.json' and add it to buildData
Returns new data.
'''
allToolsPaths = []
allToolsPaths.extend(self.bStr.toolsPaths)
allToolsPaths.extend(self.bStr.derivedPaths)
for path in allToolsPaths:
try:
buildData[path] = toolsPaths[path]
except Exception as err:
errorMsg = "Missing '" + path + "' key in tools paths data:\n" + str(toolsPaths)
print("Warning:", errorMsg)
return buildData
def addMakefileDataToBuildDataFile(self, buildData, makefileData):
'''
This function fills buildData.json file with data from 'Makefile'.
Returns new data.
'''
# sources
cSources = makefileData[self.mkfStr.cSources]
buildData[self.bStr.cSources] = cSources
asmSources = makefileData[self.mkfStr.asmSources]
buildData[self.bStr.ldSources] = asmSources
ldSources = makefileData[self.mkfStr.ldSources]
buildData[self.bStr.ldSources] = ldSources
# includes
cIncludes = makefileData[self.mkfStr.cIncludes]
buildData[self.bStr.cIncludes] = cIncludes
asmIncludes = makefileData[self.mkfStr.asmIncludes]
buildData[self.bStr.asmIncludes] = asmIncludes
ldIncludes = makefileData[self.mkfStr.ldIncludes]
buildData[self.bStr.ldIncludes] = ldIncludes
# defines
cDefines = makefileData[self.mkfStr.cDefines]
buildData[self.bStr.cDefines] = cDefines
asmDefines = makefileData[self.mkfStr.asmDefines]
buildData[self.bStr.asmDefines] = asmDefines
# compiler flags and paths
cFlags = makefileData[self.mkfStr.cFlags]
buildData[self.bStr.cFlags] = cFlags
asmFlags = makefileData[self.mkfStr.asmFlags]
buildData[self.bStr.asmFlags] = asmFlags
ldFlags = makefileData[self.mkfStr.ldFlags]
buildData[self.bStr.ldFlags] = ldFlags
# build folder must be always inside workspace folder
buildDirPath = makefileData[self.mkfStr.buildDir]
buildData[self.bStr.buildDirPath] = buildDirPath
# Target executable '.elf' file
projectName = makefileData[self.mkfStr.projectName]
targetExecutablePath = utils.getBuildElfFilePath(buildDirPath, projectName)
buildData[self.bStr.targetExecutablePath] = targetExecutablePath
return buildData
def addCubeMxProjectPathToBuildData(self, buildData):
'''
If utils.cubeMxProjectFilePath is not None, add/update 'cubeMxProjectPath' field to 'buildData.json'.
'''
if utils.cubeMxProjectFilePath is not None:
buildData[self.bStr.cubeMxProjectPath] = utils.cubeMxProjectFilePath
else:
buildData.pop(self.bStr.cubeMxProjectPath)
return buildData
def overwriteBuildDataFile(self, data):
'''
Overwrite existing 'buildData.json' file with new data.
'''
try:
with open(utils.buildDataPath, 'r+') as buildDataFile:
data["VERSION"] = __version__
data["LAST_RUN"] = str(datetime.datetime.now())
buildDataFile.seek(0)
buildDataFile.truncate()
dataToWrite = json.dumps(data, indent=4, sort_keys=False)
buildDataFile.write(dataToWrite)
print("'buildData.json' file updated!")
except Exception as err:
errorMsg = "Exception error overwriting 'buildData.json' file:\n"
errorMsg += str(err)
utils.printAndQuit(errorMsg)
########################################################################################################################
if __name__ == "__main__":
utils.verifyFolderStructure()
paths = pth.UpdatePaths()
makefile = mkf.Makefile()
bData = BuildData()
# Makefile must exist - # point in continuing if Makefile does not exist
makefile.checkMakefileFile()
# build data (update tools paths if neccessary)
buildData = bData.prepareBuildData()
# data from current Makefile
makeExePath = buildData[bData.bStr.buildToolsPath]
gccExePath = buildData[bData.bStr.gccExePath]
makefileData = makefile.getMakefileData(makeExePath, gccExePath)
# try to add CubeMX project file path
buildData = bData.addCubeMxProjectPathToBuildData(buildData)
buildData = bData.addMakefileDataToBuildDataFile(buildData, makefileData)
bData.overwriteBuildDataFile(buildData)
|