ardupilot/Tools/ardupilotwaf/ardupilotwaf.py
Gustavo Jose de Sousa 236a62f4b9 waf: create program_dir directory
There's an upcoming patch that moves all static libraries to
`build/<board>/lib/`. That way, the program directory won't be created
automatically by the build system and that will cause problems for PX4 builds,
since it builds ardupilot programs as static libraries and copies stuff to the
program directory.
2016-05-25 17:47:00 -03:00

391 lines
10 KiB
Python

#!/usr/bin/env python
# encoding: utf-8
from __future__ import print_function
from waflib import Logs, Options, Utils
from waflib.Build import BuildContext
from waflib.Configure import conf
from waflib.TaskGen import before_method, feature
import os.path, os
from collections import OrderedDict
SOURCE_EXTS = [
'*.S',
'*.c',
'*.cpp',
]
UTILITY_SOURCE_EXTS = [ 'utility/' + glob for glob in SOURCE_EXTS ]
COMMON_VEHICLE_DEPENDENT_LIBRARIES = [
'AP_AccelCal',
'AP_ADC',
'AP_AHRS',
'AP_Airspeed',
'AP_Baro',
'AP_BattMonitor',
'AP_BoardConfig',
'AP_Buffer',
'AP_Common',
'AP_Compass',
'AP_Declination',
'AP_GPS',
'AP_HAL',
'AP_HAL_Empty',
'AP_InertialSensor',
'AP_Math',
'AP_Mission',
'AP_NavEKF',
'AP_NavEKF2',
'AP_Notify',
'AP_OpticalFlow',
'AP_Param',
'AP_Rally',
'AP_RangeFinder',
'AP_Scheduler',
'AP_SerialManager',
'AP_Terrain',
'AP_Vehicle',
'DataFlash',
'Filter',
'GCS_MAVLink',
'RC_Channel',
'StorageManager',
'AP_Tuning',
'AP_RPM',
'AP_RSSI',
'AP_Mount',
]
def _get_legacy_defines(sketch_name):
return [
'APM_BUILD_DIRECTORY=APM_BUILD_' + sketch_name,
'SKETCH="' + sketch_name + '"',
'SKETCHNAME="' + sketch_name + '"',
]
IGNORED_AP_LIBRARIES = [
'doc',
'GCS_Console',
]
@conf
def ap_get_all_libraries(bld):
libraries = []
for lib_node in bld.srcnode.ant_glob('libraries/*', dir=True, src=False):
name = lib_node.name
if name in IGNORED_AP_LIBRARIES:
continue
if name.startswith('AP_HAL'):
continue
if name == 'SITL':
continue
libraries.append(name)
libraries.extend(['AP_HAL', 'AP_HAL_Empty'])
return libraries
@conf
def ap_common_vehicle_libraries(bld):
return COMMON_VEHICLE_DEPENDENT_LIBRARIES
_grouped_programs = {}
@conf
def ap_program(bld,
program_groups='bin',
program_dir=None,
use_legacy_defines=True,
program_name=None,
**kw):
if 'target' in kw:
bld.fatal('Do not pass target for program')
if 'defines' not in kw:
kw['defines'] = []
if 'source' not in kw:
kw['source'] = bld.path.ant_glob(SOURCE_EXTS)
if not program_name:
program_name = bld.path.name
if use_legacy_defines:
kw['defines'].extend(_get_legacy_defines(bld.path.name))
kw['cxxflags'] = kw.get('cxxflags', []) + ['-include', 'ap_config.h']
kw['features'] = kw.get('features', []) + bld.env.AP_PROGRAM_FEATURES
program_groups = Utils.to_list(program_groups)
if not program_dir:
program_dir = program_groups[0]
name = os.path.join(program_dir, program_name)
tg_constructor = bld.program
if bld.env.AP_PROGRAM_AS_STLIB:
tg_constructor = bld.stlib
else:
if bld.env.STATIC_LINKING:
kw['features'].append('static_linking')
tg = tg_constructor(
target='#%s' % name,
name=name,
program_name=program_name,
program_dir=program_dir,
**kw
)
for group in program_groups:
_grouped_programs.setdefault(group, []).append(tg)
@conf
def ap_example(bld, **kw):
kw['program_groups'] = 'examples'
ap_program(bld, use_legacy_defines=False, **kw)
# NOTE: Code in libraries/ is compiled multiple times. So ensure each
# compilation is independent by providing different index for each.
# The need for this should disappear when libraries change to be
# independent of vehicle type.
LAST_IDX = 0
def _get_next_idx():
global LAST_IDX
LAST_IDX += 1
return LAST_IDX
def unique_list(items):
'''remove duplicate elements from a list while maintaining ordering'''
return list(OrderedDict.fromkeys(items))
@conf
def ap_stlib(bld, **kw):
if 'name' not in kw:
bld.fatal('Missing name for ap_stlib')
if 'vehicle' not in kw:
bld.fatal('Missing vehicle for ap_stlib')
if 'libraries' not in kw:
bld.fatal('Missing libraries for ap_stlib')
sources = []
libraries = unique_list(kw['libraries'] + bld.env.AP_LIBRARIES)
for lib_name in libraries:
lib_node = bld.srcnode.find_dir('libraries/' + lib_name)
if lib_node is None:
bld.fatal('Could not find library ' + lib_name)
lib_sources = lib_node.ant_glob(SOURCE_EXTS + UTILITY_SOURCE_EXTS)
sources.extend(lib_sources)
kw['cxxflags'] = kw.get('cxxflags', []) + ['-include', 'ap_config.h']
kw['features'] = kw.get('features', []) + bld.env.AP_STLIB_FEATURES
kw['source'] = sources
kw['target'] = kw['name']
kw['defines'] = _get_legacy_defines(kw['vehicle'])
kw['idx'] = _get_next_idx()
bld.stlib(**kw)
_created_program_dirs = set()
@feature('cxxstlib', 'cxxprogram')
@before_method('process_rule')
def ap_create_program_dir(self):
if not hasattr(self, 'program_dir'):
return
if self.program_dir in _created_program_dirs:
return
self.bld.bldnode.make_node(self.program_dir).mkdir()
_created_program_dirs.add(self.program_dir)
@conf
def ap_find_tests(bld, use=[]):
if not bld.env.HAS_GTEST:
return
features = []
if bld.cmd == 'check':
features.append('test')
use = Utils.to_list(use)
use.append('GTEST')
includes = [bld.srcnode.abspath() + '/tests/']
for f in bld.path.ant_glob(incl='*.cpp'):
ap_program(
bld,
features=features,
includes=includes,
source=[f],
use=use,
program_name=f.change_ext('').name,
program_groups='tests',
use_legacy_defines=False,
cxxflags=['-Wno-undef'],
)
_versions = []
@conf
def ap_version_append_str(ctx, k, v):
ctx.env['AP_VERSION_ITEMS'] += [(k, '"{}"'.format(os.environ.get(k, v)))]
@conf
def write_version_header(ctx, tgt):
with open(tgt, 'w') as f:
print('#pragma once\n', file=f)
for k, v in ctx.env['AP_VERSION_ITEMS']:
print('#define {} {}'.format(k, v), file=f)
@conf
def ap_find_benchmarks(bld, use=[]):
if not bld.env.HAS_GBENCHMARK:
return
includes = [bld.srcnode.abspath() + '/benchmarks/']
for f in bld.path.ant_glob(incl='*.cpp'):
ap_program(
bld,
features=['gbenchmark'],
includes=includes,
source=[f],
use=use,
program_name=f.change_ext('').name,
program_groups='benchmarks',
use_legacy_defines=False,
)
def test_summary(bld):
from io import BytesIO
import sys
if not hasattr(bld, 'utest_results'):
Logs.info('check: no test run')
return
fails = []
for filename, exit_code, out, err in bld.utest_results:
Logs.pprint('GREEN' if exit_code == 0 else 'YELLOW',
' %s' % filename,
'returned %d' % exit_code)
if exit_code != 0:
fails.append(filename)
elif not bld.options.check_verbose:
continue
if len(out):
buf = BytesIO(out)
for line in buf:
print(" OUT: %s" % line.decode(), end='', file=sys.stderr)
print()
if len(err):
buf = BytesIO(err)
for line in buf:
print(" ERR: %s" % line.decode(), end='', file=sys.stderr)
print()
if not fails:
Logs.info('check: All %u tests passed!' % len(bld.utest_results))
return
Logs.error('check: %u of %u tests failed' %
(len(fails), len(bld.utest_results)))
for filename in fails:
Logs.error(' %s' % filename)
bld.fatal('check: some tests failed')
_build_commands = {}
def _process_build_command(bld):
if bld.cmd not in _build_commands:
return
params = _build_commands[bld.cmd]
targets = params['targets']
if targets:
if bld.targets:
bld.targets += ',' + targets
else:
bld.targets = targets
program_group_list = Utils.to_list(params['program_group_list'])
bld.options.program_group.extend(program_group_list)
def build_command(name,
targets=None,
program_group_list=[],
doc='build shortcut'):
_build_commands[name] = dict(
targets=targets,
program_group_list=program_group_list,
)
class context_class(BuildContext):
cmd = name
context_class.__doc__ = doc
def _select_programs_from_group(bld):
groups = bld.options.program_group
if not groups:
if bld.targets:
groups = []
else:
groups = ['bin']
if 'all' in groups:
groups = _grouped_programs.keys()
for group in groups:
if group not in _grouped_programs:
bld.fatal('Group %s not found' % group)
tg = _grouped_programs[group][0]
if bld.targets:
bld.targets += ',' + tg.name
else:
bld.targets = tg.name
for tg in _grouped_programs[group][1:]:
bld.targets += ',' + tg.name
def options(opt):
g = opt.ap_groups['build']
g.add_option('--program-group',
action='append',
default=[],
help='Select all programs that go in <PROGRAM_GROUP>/ for the ' +
'build. Example: `waf --program-group examples` builds all ' +
'examples. The special group "all" selects all programs.',
)
g.add_option('--upload',
action='store_true',
help='Upload applicable targets to a connected device. Not all ' +
'platforms may support this. Example: `waf copter --upload` ' +
'means "build arducopter and upload it to my board".',
)
g = opt.ap_groups['check']
g.add_option('--check-verbose',
action='store_true',
help='Output all test programs')
def build(bld):
global LAST_IDX
# FIXME: This is done to prevent same task generators being created with
# different idx when build() is called multiple times (e.g. waf bin tests).
# Ideally, task generators should be created just once.
LAST_IDX = 0
bld.add_pre_fun(_process_build_command)
bld.add_pre_fun(_select_programs_from_group)