-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpiocubemx.py
342 lines (300 loc) · 11.8 KB
/
piocubemx.py
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
# Author: Joachim Baumann
# Version: 1.0.2
#
# This script is based on the information for advanced scripting provided
# by platformio
# https://docs.platformio.org/en/latest/projectconf/advanced_scripting.html
#
# We try to get all needed information from the .cproject file, collect all
# source directories from that to create the src_filter and read platformio's
# own board definition to glean the cpu type.
import re
import xml.etree.ElementTree as ET
from os import path, rename, walk
import SCons.Errors
Import("env")
project_dir = env["PROJECT_DIR"]
log_name = "SETUP_CUBEMX"
# lib_directory = "lib/"
# The project option containing the directory in which CubeMX resides
# try:
# repository_location = env.GetProjectOption("custom_repo_location")
# except:
# repository_location = "~"
# pass
# repository_location = path.expanduser(repository_location)
# print("%s: Using the following repository location: '%s'"
# % (log_name, repository_location))
# set the project source dir
env["PROJECT_SRC_DIR"] = project_dir
# We simply take the first extra library dependency
# try:
# linked_resources_dir = path.join(lib_directory, env.GetProjectOption("lib_deps")[0])
# except:
# raise SCons.Errors.BuildError(
# errstr="%s Error: The option 'lib_deps' is not set" % log_name
# )
#################################################
# Open the .project file and parse it as XML
#################################################
try:
project_root = ET.parse(".project").getroot()
except IOError:
raise SCons.Errors.BuildError(
errstr="%s Error: Cannot open project file .project in directory '%s'"
% (log_name, project_dir)
)
# now create the directory and link all the needed files
# if not path.exists(lib_directory):
# print(
# "%s: Warning - Directory '%s' doesn't exist. Did you initialize platformio?"
# % (log_name, lib_directory)
# )
# mkdir(lib_directory)
#
# if not path.exists(linked_resources_dir):
# mkdir(linked_resources_dir)
third_party_includes = set()
# Get existing resources
# linked_resource_dict = {}
# if path.exists(linked_resources_dir):
# items = listdir(linked_resources_dir)
# for f in items:
# if not path.isdir(f):
# # print(f)
# linked_resource_dict[f] = True
# Collect the virtual dirs so that we later know not to add them
virtual_dirs = []
for linked_resource in project_root.findall(".//linkedResources/link"):
# Needed for Third Party directories
lib_target_dir = None
# Retrieve the complete link
linkedName = linked_resource.find(".//name").text
# with STMCubeIDE 1.13.2 the name changed from "locationURI" to "location"
linkedNode = linked_resource.find(".//locationURI")
if linkedNode is None:
linkedNode = linked_resource.find(".//location")
linkedURI = linkedNode.text
# It is a virtual folder?
if linkedURI == "virtual:/virtual":
# Add to virtual_dirs in case of virtual folder
virtual_dirs.append(linkedName)
continue
# It is a relative path?
m = re.match("PARENT-(\d+)-PROJECT_LOC(.*)$", linkedURI)
if m is not None:
parent_level = int(m.group(1))
current_dir = project_dir + "/.." * parent_level
resource = path.abspath(current_dir + m.group(2))
# Third_Party signals that we might need the directory structure
# if "Third_Party" in resource:
# # print(resource)
# m = re.match("^.*/Third_Party/(.+)/", resource)
# lib_target_dir = path.join(linked_resources_dir, m.group(1))
# (tp_dir, tp_file) = path.split(resource)
# third_party_includes.add(tp_dir)
# try:
# makedirs(lib_target_dir)
# except Exception:
# # ignore errors
# pass
else:
# we have a path type that we haven't seen yet
raise SCons.Errors.BuildError(
errstr="%s Error: Unexpected relative path type '%s'"
% (log_name, linkedURI)
)
# try:
# resource_name = path.basename(resource)
# # if lib_target_dir:
# # link_name = path.join(lib_target_dir, resource_name)
# # else:
# # link_name = path.join(linked_resources_dir, resource_name)
#
# # if symlink already created previously,
# # remove entry from dictionary
# # if resource_name in linked_resource_dict:
# # del linked_resource_dict[resource_name]
# # else:
# # # create symlink
# # # print(link_name)
# # symlink(resource, link_name)
# except OSError:
# if "Third_Party" not in resource:
# # print(resource)
# raise SCons.Errors.BuildError(
# errstr="%s Error: Cannot create symlink in directory '%s'"
# % (log_name, linked_resources_dir)
# )
# Remove previously linked resources that have disappeared
# for name in linked_resource_dict:
# link_name = path.join(linked_resources_dir, name)
# unlink(link_name)
#################################################
# Open the .cproject file and parse it as XML
#################################################
try:
cproject_root = ET.parse(".cproject").getroot()
except IOError:
raise SCons.Errors.BuildError(
errstr="%s Error: Cannot open project file .cproject in directory '%s'"
% (log_name, project_dir)
)
# In these lists we collect our directories
src_dirs = set()
include_dirs = []
#################################################
# read the source directories from .cproject
# and scan their subdirectories for source files
#################################################
cubemx_directories = set()
cubemx_directories.add("Core/Startup")
config = cproject_root.find(".//configuration[@name='Debug']")
for source_entry in config.findall("sourceEntries/entry"):
directory = source_entry.get("name")
if directory != "Core":
# Add non-virtual folders
if directory not in virtual_dirs:
cubemx_directories.add(directory)
if cubemx_directories:
print(
"%s: Using the following source directories: 'Core, %s'"
% (log_name, ", ".join(cubemx_directories))
)
else:
raise SCons.Errors.BuildError(
errstr="%s Error: Cannot read source directories from project file "
".cproject in directory '%s'" % (log_name, project_dir)
)
for cubemx_dir in cubemx_directories:
if not path.exists(cubemx_dir):
# we ignore non-existing directories
continue
# For every source file we add its path to either src_dirs or include_dirs
for dirpath, dirnames, filenames in walk(cubemx_dir, followlinks=True):
for file in filenames:
if file.endswith((".c", ".cpp", ".s", ".S")):
src_dirs.add("+<%s/>" % dirpath)
if file.endswith(".s"):
# silently convert it to upper case :)
fn = path.abspath(path.join(dirpath, file))
rename(fn, fn + "S")
rename(fn + "S", fn[:-1] + "S")
#################################################
# now read the include dirs from .cproject
#################################################
tool_chain = config.find("./folderInfo/toolChain")
ws_replacement = re.escape(project_dir) + "/\\1"
for include_entry in tool_chain.findall(
".//option[@superClass='com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.includepaths']/listOptionValue"
):
inc_dir = include_entry.get("value")
# add project-related paths e.g., Middleware-directores
inc_dir = re.sub(
'"\$\{workspace_loc:/\$\{ProjName\}(.*)}"', ws_replacement, inc_dir
)
# Fix references to Core
inc_dir = inc_dir.replace("../", "", 1)
inc_dir = "-I" + inc_dir
include_dirs.append(inc_dir)
for inc_dir in third_party_includes:
inc_dir = "-I" + inc_dir
include_dirs.append(inc_dir)
if not include_dirs:
raise SCons.Errors.BuildError(
errstr="%s Error: Cannot read include directories from project file "
".cproject in directory '%s'" % (log_name, project_dir)
)
#################################################
# We now try to extract the uC-specific build
# flags and add the ones we know are needed.
# We differentiate between flags for both
# compiler and linke (build_flags) and flags
# for the compiler only (cc_only_flags)
#################################################
build_flags = [
# we always choose thumb mode with STM32
"-mthumb",
]
# additional flags for the compiler only
cc_only_flags = [
# These are set by platformio, see
# PIO_FRAMEWORK_ARDUINO_STANDARD_LIB
# additional ones can be added using build flags
]
# additional flags for the linker only
ld_only_flags = []
# extract the cpu type from the board
board_config = env.BoardConfig()
cpu = board_config._manifest["build"]["cpu"]
m_flags = ["-mcpu=%s" % cpu]
# extract other flags from the .cproject file
option_mapping = {
"floatabi": "float-abi",
}
cc_only_option_mapping = {"runtimelibrary_c"}
for option in tool_chain.findall("option[@valueType='enumerated']"):
superClass = option.get("superClass")
value = option.get("value").replace(superClass + ".value.", "")
m_flag = superClass.replace("com.st.stm32cube.ide.mcu.gnu.managedbuild.option.", "")
# If the eclipse notation of the flag differs we correct that
if m_flag in option_mapping:
m_flag = option_mapping[m_flag]
# Add the flag to the list of flags
if m_flag in cc_only_option_mapping:
print("Removing %s=%s" % (m_flag, value))
else:
m_flags += ["-m%s=%s" % (m_flag, value)]
build_flags += m_flags
print("%s: Adding the following build flags: '%s'" % (log_name, ", ".join(build_flags)))
#################################################
# Get the ld script from .cproject
#################################################
# The GCC linker entries
ld_script_entry = tool_chain.find("tool[@name='MCU GCC Linker']/option")
if ld_script_entry == None:
# The G++ linker entries
ld_script_entry = tool_chain.find("tool[@name='MCU G++ Linker']/option")
if ld_script_entry == None:
# Some versions of STM32CubeIDE use "MCU/MPU GCC Linker"
ld_script_entry = tool_chain.find("tool[@name='MCU/MPU GCC Linker']/option")
ld_script = None
if ld_script_entry != None:
ld_script_entry = ld_script_entry.get("value")
ld_script = re.search("\$\{workspace_loc:/\$\{ProjName\}/(.+)\}", ld_script_entry)
if ld_script_entry != None and ld_script != None:
ld_script = ld_script.group(1)
else:
# How do I send a warning to platformio?
print(
"%s: Warning, ld script not found in directory in project configuration"
% log_name
)
print("%s: Using the ld script provided by platformio" % log_name)
#################################################
# Now we create the needed entries
# in the environment
#################################################
# Build the src_filter
if "SRC_FILTER" not in env:
env["SRC_FILTER"] = []
# we explicitly add the Core files at the beginning
env["SRC_FILTER"] += ["+<Core/Src/>"]
# Now we add the collected source directories to the src_filter
env["SRC_FILTER"] += src_dirs
# Create the list of all needed include directories that we have collected
if "BUILD_FLAGS" not in env:
env["BUILD_FLAGS"] = []
# Add the collected include directories to the build_flags
env["BUILD_FLAGS"] += include_dirs
# Add the necessary build flags to compiler and linker flags.
env["BUILD_FLAGS"] += cc_only_flags
env["BUILD_FLAGS"] += build_flags
env["LINKFLAGS"] += build_flags
env["LINKFLAGS"] += ld_only_flags
# Add the correct ld_script from the project dir
if ld_script != None:
env["LDSCRIPT_PATH"] = ld_script
# Remove the framwork from the environment
if "PIOFRAMEWORK" in env:
del env["PIOFRAMEWORK"]