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
|
#
# common-dependencies.py
# Convenience script to check dependencies and add libs and sources for Marlin Enabled Features
#
import subprocess
import os
import re
try:
import configparser
except ImportError:
import ConfigParser as configparser
try:
# PIO < 4.4
from platformio.managers.package import PackageManager
except ImportError:
# PIO >= 4.4
from platformio.package.meta import PackageSpec as PackageManager
PIO_VERSION_MIN = (5, 0, 3)
try:
from platformio import VERSION as PIO_VERSION
weights = (1000, 100, 1)
version_min = sum([x[0] * float(re.sub(r'[^0-9]', '.', str(x[1]))) for x in zip(weights, PIO_VERSION_MIN)])
version_cur = sum([x[0] * float(re.sub(r'[^0-9]', '.', str(x[1]))) for x in zip(weights, PIO_VERSION)])
if version_cur < version_min:
print()
print("**************************************************")
print("****** An update to PlatformIO is ******")
print("****** required to build Marlin Firmware. ******")
print("****** ******")
print("****** Minimum version: ", PIO_VERSION_MIN, " ******")
print("****** Current Version: ", PIO_VERSION, " ******")
print("****** ******")
print("****** Update PlatformIO and try again. ******")
print("**************************************************")
print()
exit(1)
except SystemExit:
exit(1)
except:
print("Can't detect PlatformIO Version")
Import("env")
#print(env.Dump())
try:
verbose = int(env.GetProjectOption('custom_verbose'))
except:
verbose = 0
def blab(str):
if verbose:
print(str)
def parse_pkg_uri(spec):
if PackageManager.__name__ == 'PackageSpec':
return PackageManager(spec).name
else:
name, _, _ = PackageManager.parse_pkg_uri(spec)
return name
FEATURE_CONFIG = {}
def add_to_feat_cnf(feature, flines):
try:
feat = FEATURE_CONFIG[feature]
except:
FEATURE_CONFIG[feature] = {}
# Get a reference to the FEATURE_CONFIG under construction
feat = FEATURE_CONFIG[feature]
# Split up passed lines on commas or newlines and iterate
# Add common options to the features config under construction
# For lib_deps replace a previous instance of the same library
atoms = re.sub(r',\\s*', '\n', flines).strip().split('\n')
for line in atoms:
parts = line.split('=')
name = parts.pop(0)
if name in ['build_flags', 'extra_scripts', 'src_filter', 'lib_ignore']:
feat[name] = '='.join(parts)
else:
for dep in line.split(','):
lib_name = re.sub(r'@([~^]|[<>]=?)?[\d.]+', '', dep.strip()).split('=').pop(0)
lib_re = re.compile('(?!^' + lib_name + '\\b)')
feat['lib_deps'] = list(filter(lib_re.match, feat['lib_deps'])) + [dep]
def load_config():
config = configparser.ConfigParser()
config.read("platformio.ini")
items = config.items('features')
for key in items:
feature = key[0].upper()
if not feature in FEATURE_CONFIG:
FEATURE_CONFIG[feature] = { 'lib_deps': [] }
add_to_feat_cnf(feature, key[1])
# Add options matching custom_marlin.MY_OPTION to the pile
all_opts = env.GetProjectOptions()
for n in all_opts:
mat = re.match(r'custom_marlin\.(.+)', n[0])
if mat:
try:
val = env.GetProjectOption(n[0])
except:
val = None
if val:
add_to_feat_cnf(mat.group(1).upper(), val)
def get_all_known_libs():
known_libs = []
for feature in FEATURE_CONFIG:
feat = FEATURE_CONFIG[feature]
if not 'lib_deps' in feat:
continue
for dep in feat['lib_deps']:
name = parse_pkg_uri(dep)
known_libs.append(name)
return known_libs
def get_all_env_libs():
env_libs = []
lib_deps = env.GetProjectOption('lib_deps')
for dep in lib_deps:
name = parse_pkg_uri(dep)
env_libs.append(name)
return env_libs
def set_env_field(field, value):
proj = env.GetProjectConfig()
proj.set("env:" + env['PIOENV'], field, value)
# All unused libs should be ignored so that if a library
# exists in .pio/lib_deps it will not break compilation.
def force_ignore_unused_libs():
env_libs = get_all_env_libs()
known_libs = get_all_known_libs()
diff = (list(set(known_libs) - set(env_libs)))
lib_ignore = env.GetProjectOption('lib_ignore') + diff
blab("Ignore libraries: %s" % lib_ignore)
set_env_field('lib_ignore', lib_ignore)
def apply_features_config():
load_config()
for feature in FEATURE_CONFIG:
if not env.MarlinFeatureIsEnabled(feature):
continue
feat = FEATURE_CONFIG[feature]
if 'lib_deps' in feat and len(feat['lib_deps']):
blab("Adding lib_deps for %s... " % feature)
# feat to add
deps_to_add = {}
for dep in feat['lib_deps']:
name = parse_pkg_uri(dep)
deps_to_add[name] = dep
# Does the env already have the dependency?
deps = env.GetProjectOption('lib_deps')
for dep in deps:
name = parse_pkg_uri(dep)
if name in deps_to_add:
del deps_to_add[name]
# Are there any libraries that should be ignored?
lib_ignore = env.GetProjectOption('lib_ignore')
for dep in deps:
name = parse_pkg_uri(dep)
if name in deps_to_add:
del deps_to_add[name]
# Is there anything left?
if len(deps_to_add) > 0:
# Only add the missing dependencies
set_env_field('lib_deps', deps + list(deps_to_add.values()))
if 'build_flags' in feat:
f = feat['build_flags']
blab("Adding build_flags for %s: %s" % (feature, f))
new_flags = env.GetProjectOption('build_flags') + [ f ]
env.Replace(BUILD_FLAGS=new_flags)
if 'extra_scripts' in feat:
blab("Running extra_scripts for %s... " % feature)
env.SConscript(feat['extra_scripts'], exports="env")
if 'src_filter' in feat:
blab("Adding src_filter for %s... " % feature)
src_filter = ' '.join(env.GetProjectOption('src_filter'))
# first we need to remove the references to the same folder
my_srcs = re.findall(r'[+-](<.*?>)', feat['src_filter'])
cur_srcs = re.findall(r'[+-](<.*?>)', src_filter)
for d in my_srcs:
if d in cur_srcs:
src_filter = re.sub(r'[+-]' + d, '', src_filter)
src_filter = feat['src_filter'] + ' ' + src_filter
set_env_field('src_filter', [src_filter])
env.Replace(SRC_FILTER=src_filter)
if 'lib_ignore' in feat:
blab("Adding lib_ignore for %s... " % feature)
lib_ignore = env.GetProjectOption('lib_ignore') + [feat['lib_ignore']]
set_env_field('lib_ignore', lib_ignore)
#
# Find a compiler, considering the OS
#
ENV_BUILD_PATH = os.path.join(env.Dictionary('PROJECT_BUILD_DIR'), env['PIOENV'])
GCC_PATH_CACHE = os.path.join(ENV_BUILD_PATH, ".gcc_path")
def search_compiler():
try:
filepath = env.GetProjectOption('custom_gcc')
blab("Getting compiler from env")
return filepath
except:
pass
if os.path.exists(GCC_PATH_CACHE):
blab("Getting g++ path from cache")
with open(GCC_PATH_CACHE, 'r') as f:
return f.read()
# Find the current platform compiler by searching the $PATH
# which will be in a platformio toolchain bin folder
path_regex = re.escape(env['PROJECT_PACKAGES_DIR'])
gcc = "g++"
if env['PLATFORM'] == 'win32':
path_separator = ';'
path_regex += r'.*\\bin'
gcc += ".exe"
else:
path_separator = ':'
path_regex += r'/.+/bin'
# Search for the compiler
for pathdir in env['ENV']['PATH'].split(path_separator):
if not re.search(path_regex, pathdir, re.IGNORECASE):
continue
for filepath in os.listdir(pathdir):
if not filepath.endswith(gcc):
continue
# Use entire path to not rely on env PATH
filepath = os.path.sep.join([pathdir, filepath])
# Cache the g++ path to no search always
if os.path.exists(ENV_BUILD_PATH):
blab("Caching g++ for current env")
with open(GCC_PATH_CACHE, 'w+') as f:
f.write(filepath)
return filepath
filepath = env.get('CXX')
blab("Couldn't find a compiler! Fallback to %s" % filepath)
return filepath
#
# Use the compiler to get a list of all enabled features
#
def load_marlin_features():
if 'MARLIN_FEATURES' in env:
return
# Process defines
build_flags = env.get('BUILD_FLAGS')
build_flags = env.ParseFlagsExtended(build_flags)
cxx = search_compiler()
cmd = ['"' + cxx + '"']
# Build flags from board.json
#if 'BOARD' in env:
# cmd += [env.BoardConfig().get("build.extra_flags")]
for s in build_flags['CPPDEFINES']:
if isinstance(s, tuple):
cmd += ['-D' + s[0] + '=' + str(s[1])]
else:
cmd += ['-D' + s]
cmd += ['-D__MARLIN_DEPS__ -w -dM -E -x c++ buildroot/share/PlatformIO/scripts/common-dependencies.h']
cmd = ' '.join(cmd)
blab(cmd)
define_list = subprocess.check_output(cmd, shell=True).splitlines()
marlin_features = {}
for define in define_list:
feature = define[8:].strip().decode().split(' ')
feature, definition = feature[0], ' '.join(feature[1:])
marlin_features[feature] = definition
env['MARLIN_FEATURES'] = marlin_features
#
# Return True if a matching feature is enabled
#
def MarlinFeatureIsEnabled(env, feature):
load_marlin_features()
r = re.compile('^' + feature + '$')
found = list(filter(r.match, env['MARLIN_FEATURES']))
# Defines could still be 'false' or '0', so check
some_on = False
if len(found):
for f in found:
val = env['MARLIN_FEATURES'][f]
if val in [ '', '1', 'true' ]:
some_on = True
elif val in env['MARLIN_FEATURES']:
some_on = env.MarlinFeatureIsEnabled(val)
return some_on
#
# Check for Configfiles in two common incorrect places
#
def check_configfile_locations():
for p in [ env['PROJECT_DIR'], os.path.join(env['PROJECT_DIR'], "config") ]:
for f in [ "Configuration.h", "Configuration_adv.h" ]:
if os.path.isfile(os.path.join(p, f)):
err = 'ERROR: Config files found in directory ' + str(p) + '. Please move them into the Marlin subdirectory.'
raise SystemExit(err)
#
# Add a method for other PIO scripts to query enabled features
#
env.AddMethod(MarlinFeatureIsEnabled)
#
# Add dependencies for enabled Marlin features
#
check_configfile_locations()
apply_features_config()
force_ignore_unused_libs()
|