Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/ardupilotwaf/ap_library.py
9782 views
1
# Copyright (C) 2016 Intel Corporation. All rights reserved.
2
#
3
# This file is free software: you can redistribute it and/or modify it
4
# under the terms of the GNU General Public License as published by the
5
# Free Software Foundation, either version 3 of the License, or
6
# (at your option) any later version.
7
#
8
# This file is distributed in the hope that it will be useful, but
9
# WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
# See the GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License along
14
# with this program. If not, see <http://www.gnu.org/licenses/>.
15
16
# flake8: noqa
17
18
"""
19
Waf tool for Ardupilot libraries. The function bld.ap_library() creates the
20
necessary task generators for creating the objects of a library for a vehicle.
21
That includes the common objects, which are shared among vehicles. That
22
function is used by bld.ap_stlib() and shouldn't need to be called otherwise.
23
24
The environment variable AP_LIBRARIES_OBJECTS_KW is a dictionary of keyword
25
arguments to be passed to bld.objects() when during the creation of the task
26
generators. You can use it to pass extra arguments to that function (although
27
some of them will be rewritten, see the implementation for details).
28
29
This tool also checks if the headers used by the source files don't use
30
vehicle-related headers and fails the build if they do.
31
"""
32
import os
33
import re
34
35
from waflib import Errors, Task, Utils, Logs
36
from waflib.Configure import conf
37
from waflib.TaskGen import after_method, before_method, feature
38
from waflib.Tools import c_preproc
39
40
import ardupilotwaf as ap
41
42
UTILITY_SOURCE_EXTS = ['utility/' + glob for glob in ap.SOURCE_EXTS]
43
44
def _common_tgen_name(library):
45
return 'objs/%s' % library
46
47
def _vehicle_tgen_name(library, vehicle):
48
return 'objs/%s/%s' % (library, vehicle)
49
50
_vehicle_indexes = {}
51
def _vehicle_index(vehicle):
52
""" Used for the objects taskgens idx parameter """
53
if vehicle not in _vehicle_indexes:
54
_vehicle_indexes[vehicle] = len(_vehicle_indexes) + 1
55
return _vehicle_indexes[vehicle]
56
57
# note that AP_NavEKF3_core.h is needed for AP_NavEKF3_feature.h
58
_vehicle_macros = ['APM_BUILD_DIRECTORY', 'AP_BUILD_TARGET_NAME',
59
'APM_BUILD_TYPE', 'APM_BUILD_COPTER_OR_HELI',
60
'AP_NavEKF3_core.h', 'lua_generated_bindings.h',
61
'AP_InertialSensor_rate_config.h']
62
_macros_re = re.compile(r'\b(%s)\b' % '|'.join(_vehicle_macros))
63
64
# some cpp files are not available at the time we run this check so need to be
65
# unilaterally added
66
_vehicle_cpp_need_macros = ['lua_generated_bindings.cpp']
67
_macros_cpp_re = re.compile(r'\b(%s)\b' % '|'.join(_vehicle_cpp_need_macros))
68
69
def _remove_comments(s):
70
return c_preproc.re_cpp.sub(c_preproc.repl, s)
71
72
_depends_on_vehicle_cache = {}
73
def _depends_on_vehicle(bld, source_node):
74
path = source_node.srcpath()
75
76
if not bld.env.BUILDROOT:
77
bld.env.BUILDROOT = bld.bldnode.make_node('').abspath()
78
79
if _macros_cpp_re.search(path) is not None:
80
_depends_on_vehicle_cache[path] = True
81
82
if path not in _depends_on_vehicle_cache:
83
try:
84
s = _remove_comments(source_node.read())
85
_depends_on_vehicle_cache[path] = _macros_re.search(s) is not None
86
except Exception:
87
return False
88
89
return _depends_on_vehicle_cache[path]
90
91
@conf
92
def ap_library(bld, library, vehicle):
93
try:
94
common_tg = bld.get_tgen_by_name(_common_tgen_name(library))
95
except Errors.WafError:
96
common_tg = None
97
98
try:
99
vehicle_tg = bld.get_tgen_by_name(_vehicle_tgen_name(library, vehicle))
100
except Errors.WafError:
101
vehicle_tg = None
102
103
if common_tg and vehicle_tg:
104
return
105
106
if library.find('*') != -1:
107
# allow for wildcard patterns, used for submodules without direct waf support
108
library_dir = bld.srcnode.find_dir('.')
109
wildcard = library
110
else:
111
library_dir = bld.srcnode.find_dir('libraries/%s' % library)
112
wildcard = ap.SOURCE_EXTS + UTILITY_SOURCE_EXTS
113
114
if not library_dir:
115
bld.fatal('ap_library: %s not found' % library)
116
117
src = library_dir.ant_glob(wildcard)
118
119
# allow for dynamically generated sources in a library that inherit the
120
# dependencies and includes
121
if library in bld.env.AP_LIB_EXTRA_SOURCES:
122
for s in bld.env.AP_LIB_EXTRA_SOURCES[library]:
123
src.append(bld.bldnode.find_or_declare(os.path.join('libraries', library, s)))
124
125
if not common_tg:
126
kw = dict(bld.env.AP_LIBRARIES_OBJECTS_KW)
127
kw['features'] = kw.get('features', []) + ['ap_library_object']
128
kw.update(
129
name=_common_tgen_name(library),
130
source=[s for s in src if not _depends_on_vehicle(bld, s)],
131
idx=0,
132
)
133
bld.objects(**kw)
134
135
if not vehicle_tg:
136
source = [s for s in src if _depends_on_vehicle(bld, s)]
137
138
if not source:
139
return
140
141
kw = dict(bld.env.AP_LIBRARIES_OBJECTS_KW)
142
kw['features'] = kw.get('features', []) + ['ap_library_object']
143
kw.update(
144
name=_vehicle_tgen_name(library, vehicle),
145
source=source,
146
defines=ap.get_legacy_defines(vehicle, bld),
147
idx=_vehicle_index(vehicle),
148
)
149
bld.objects(**kw)
150
151
@before_method('process_use')
152
@feature('cxxstlib')
153
def process_ap_libraries(self):
154
self.use = Utils.to_list(getattr(self, 'use', []))
155
libraries = Utils.to_list(getattr(self, 'ap_libraries', []))
156
vehicle = getattr(self, 'ap_vehicle', None)
157
158
for l in libraries:
159
self.use.append(_common_tgen_name(l))
160
if vehicle:
161
self.use.append(_vehicle_tgen_name(l, vehicle))
162
163
@before_method('process_source')
164
@feature('cxxstlib')
165
def dynamic_post(self):
166
if not getattr(self, 'dynamic_source', None):
167
return
168
self.source = Utils.to_list(self.source)
169
self.source.extend(self.bld.bldnode.ant_glob(self.dynamic_source))
170
171
class ap_library_check_headers(Task.Task):
172
color = 'PINK'
173
before = 'cxx c'
174
dispatched_headers = set()
175
whitelist = (
176
'libraries/AP_Vehicle/AP_Vehicle_Type.h',
177
'libraries/AP_Common/AP_FWVersionDefine.h',
178
'libraries/AP_Scripting/lua_generated_bindings.h',
179
'libraries/AP_NavEKF3/AP_NavEKF3_feature.h',
180
'libraries/AP_LandingGear/AP_LandingGear_config.h',
181
'libraries/AP_InertialSensor/AP_InertialSensor_rate_config.h',
182
)
183
whitelist = tuple(os.path.join(*p.split('/')) for p in whitelist)
184
185
def run(self):
186
for n in self.headers:
187
s = _remove_comments(n.read())
188
if _macros_re.search(s):
189
raise Errors.WafError('%s: library header uses vehicle-dependent macros' % n.srcpath())
190
191
def uid(self):
192
try:
193
return self._uid
194
except AttributeError:
195
self._uid = 'check_headers-%s' % self.compiled_task.uid()
196
return self._uid
197
198
def signature(self):
199
bld = self.generator.bld
200
# force scan() to be called
201
bld.imp_sigs[self.uid()] = None
202
return super(ap_library_check_headers, self).signature()
203
204
def scan(self):
205
r = []
206
self.headers = []
207
208
srcnode_path = self.generator.bld.srcnode.abspath()
209
210
# force dependency scan, if necessary
211
self.compiled_task.signature()
212
if not self.compiled_task.uid() in self.generator.bld.node_deps:
213
return r, []
214
for n in self.generator.bld.node_deps[self.compiled_task.uid()]:
215
# using common Node methods doesn't work here
216
p = n.abspath()
217
if not p.startswith(srcnode_path):
218
continue
219
rel_p = os.path.relpath(p, srcnode_path)
220
if rel_p in self.whitelist:
221
continue
222
223
# check if the path ends with something in the white list
224
# this is required for white listing files in 'build/' (for scripting generated bindings)
225
found = False
226
for m in self.whitelist:
227
if rel_p.endswith(m):
228
found = True
229
break
230
231
if found:
232
continue
233
234
r.append(n)
235
if n not in self.dispatched_headers:
236
self.headers.append(n)
237
self.dispatched_headers.add(n)
238
239
return r, []
240
241
def __str__(self):
242
return str(self.compiled_task)
243
244
def keyword(self):
245
return 'Checking included headers'
246
247
def custom_flags_check(tgen):
248
'''
249
check for tasks marked as having custom cpp or c flags
250
a library can do this by setting AP_LIB_EXTRA_CXXFLAGS and AP_LIB_EXTRA_CFLAGS
251
252
For example add this is the configure section of the library, using AP_DDS as an example:
253
254
cfg.env.AP_LIB_EXTRA_CXXFLAGS['AP_DDS'] = ['-DSOME_CXX_FLAG']
255
cfg.env.AP_LIB_EXTRA_CFLAGS['AP_DDS'] = ['-DSOME_C_FLAG']
256
'''
257
if not tgen.name.startswith("objs/"):
258
return
259
libname = tgen.name[5:]
260
if libname in tgen.env.AP_LIB_EXTRA_CXXFLAGS:
261
tgen.env.CXXFLAGS.extend(tgen.env.AP_LIB_EXTRA_CXXFLAGS[libname])
262
if libname in tgen.env.AP_LIB_EXTRA_CFLAGS:
263
tgen.env.CFLAGS.extend(tgen.env.AP_LIB_EXTRA_CFLAGS[libname])
264
265
266
def double_precision_check(tasks):
267
'''check for tasks marked as double precision'''
268
269
for t in tasks:
270
if len(t.inputs) == 1:
271
# get a list of tasks we need to change to be double precision
272
double_tasks = []
273
for library in t.env.DOUBLE_PRECISION_SOURCES.keys():
274
for s in t.env.DOUBLE_PRECISION_SOURCES[library]:
275
double_tasks.append([library, s])
276
277
src = str(t.inputs[0]).split('/')[-2:]
278
double_library = t.env.DOUBLE_PRECISION_LIBRARIES.get(src[0],False)
279
280
if double_library or src in double_tasks:
281
t.env.CXXFLAGS = ap.set_double_precision_flags(t.env.CXXFLAGS)
282
283
284
def gsoap_library_check(bld, tasks):
285
'''check for tasks marked as gSOAP library source'''
286
287
for t in tasks:
288
if len(t.inputs) == 1:
289
gsoap_tasks = []
290
for s in t.env.AP_LIB_EXTRA_SOURCES["AP_ONVIF"]:
291
gsoap_tasks.append(bld.bldnode.find_or_declare(os.path.join('libraries', "AP_ONVIF", s)))
292
293
if t.inputs[0] in gsoap_tasks:
294
t.env.CXXFLAGS += [
295
'-Wno-shadow',
296
]
297
if 'clang++' not in t.env.COMPILER_CXX:
298
t.env.CXXFLAGS += [
299
'-Wno-suggest-override',
300
]
301
302
303
@feature('ap_library_object')
304
@after_method('process_source')
305
def ap_library_register_for_check(self):
306
if not hasattr(self, 'compiled_tasks'):
307
return
308
309
custom_flags_check(self)
310
double_precision_check(self.compiled_tasks)
311
if self.env.ENABLE_ONVIF:
312
gsoap_library_check(self.bld, self.compiled_tasks)
313
314
if not self.env.ENABLE_HEADER_CHECKS:
315
return
316
317
for t in self.compiled_tasks:
318
tsk = self.create_task('ap_library_check_headers')
319
tsk.compiled_task = t
320
321
def write_compilation_database(bld):
322
"""
323
Write the compilation database as JSON
324
"""
325
database_file = bld.bldnode.find_or_declare('compile_commands.json')
326
# don't remove the file at clean
327
328
Logs.info('Build commands will be stored in %s', database_file.path_from(bld.path))
329
try:
330
root = database_file.read_json()
331
except IOError:
332
root = []
333
compile_cmd_db = dict((x['file'], x) for x in root)
334
for task in bld.compilation_database_tasks:
335
try:
336
cmd = task.last_cmd
337
except AttributeError:
338
continue
339
f_node = task.inputs[0]
340
filename = f_node.path_from(task.get_cwd())
341
entry = {
342
"directory": task.get_cwd().abspath(),
343
"arguments": cmd,
344
"file": filename,
345
}
346
compile_cmd_db[filename] = entry
347
root = list(compile_cmd_db.values())
348
database_file.write_json(root)
349
350
def target_list_changed(bld, targets):
351
"""
352
Check if the list of targets has changed recorded in target_list file
353
"""
354
# target_list file is in the root build directory
355
target_list_file = bld.bldnode.find_or_declare('target_list')
356
try:
357
with open(target_list_file.abspath(), 'r') as f:
358
old_targets = f.read().strip().split(',')
359
except IOError:
360
Logs.info('No target_list file found, creating')
361
old_targets = []
362
if old_targets != targets:
363
with open(target_list_file.abspath(), 'w') as f:
364
f.write(','.join(targets))
365
return True
366
return False
367
368
@conf
369
def remove_target_list(cfg):
370
target_list_file = cfg.bldnode.make_node(cfg.options.board + '/target_list')
371
try:
372
Logs.info('Removing target_list file %s', target_list_file.abspath())
373
os.remove(target_list_file.abspath())
374
except OSError:
375
pass
376
377
@feature('cxxprogram', 'cxxstlib')
378
@after_method('propagate_uselib_vars')
379
def dry_run_compilation_database(self):
380
if not hasattr(self, 'bld'):
381
return
382
bld = self.bld
383
bld.compilation_database_tasks = []
384
targets = bld.targets.split(',')
385
use = self.use
386
if isinstance(use, str):
387
use = [use]
388
# if targets have not changed and neither has configuration,
389
# we can skip compilation database generation
390
if not target_list_changed(bld, targets + use):
391
Logs.info('Targets have not changed, skipping compilation database compile_commands.json generation')
392
return
393
Logs.info('Generating compile_commands.json')
394
# we need only to generate last_cmd, so override
395
# exec_command temporarily
396
def exec_command(bld, *k, **kw):
397
return 0
398
399
for g in bld.groups:
400
for tg in g:
401
# we only care to list targets and library objects
402
if not hasattr(tg, 'name'):
403
continue
404
if (tg.name not in targets) and (tg.name not in self.use):
405
continue
406
try:
407
f = tg.post
408
except AttributeError:
409
pass
410
else:
411
f()
412
413
if isinstance(tg, Task.Task):
414
lst = [tg]
415
else:
416
lst = tg.tasks
417
for tsk in lst:
418
if tsk.__class__.__name__ == "swig":
419
tsk.runnable_status()
420
if hasattr(tsk, 'more_tasks'):
421
lst.extend(tsk.more_tasks)
422
# Not all dynamic tasks can be processed, in some cases
423
# one may have to call the method "run()" like this:
424
# elif tsk.__class__.__name__ == 'src2c':
425
# tsk.run()
426
# if hasattr(tsk, 'more_tasks'):
427
# lst.extend(tsk.more_tasks)
428
429
tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
430
if isinstance(tsk, tup):
431
bld.compilation_database_tasks.append(tsk)
432
tsk.nocache = True
433
old_exec = tsk.exec_command
434
tsk.exec_command = exec_command
435
tsk.run()
436
tsk.exec_command = old_exec
437
438
write_compilation_database(bld)
439
440
def configure(cfg):
441
cfg.env.AP_LIBRARIES_OBJECTS_KW = dict()
442
cfg.env.AP_LIB_EXTRA_SOURCES = dict()
443
cfg.env.AP_LIB_EXTRA_CXXFLAGS = dict()
444
cfg.env.AP_LIB_EXTRA_CFLAGS = dict()
445
cfg.env.DOUBLE_PRECISION_SOURCES = dict()
446
cfg.env.DOUBLE_PRECISION_LIBRARIES = dict()
447
448