Skip to content

Commit

Permalink
Tools: test_build_options.py: implement detection of feature missing
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbarker committed Aug 19, 2024
1 parent 7ee5fb9 commit e642e88
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 30 deletions.
213 changes: 209 additions & 4 deletions Tools/autotest/test_build_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import optparse
import os
import pathlib
import re
import sys

from pysim import util
Expand Down Expand Up @@ -53,6 +54,7 @@ def __init__(self,
board="CubeOrange", # DevEBoxH7v2 also works
extra_hwdef=None,
emit_disable_all_defines=None,
resume=False,
):
self.extra_hwdef = extra_hwdef
self.sizes_nothing_disabled = None
Expand All @@ -67,6 +69,7 @@ def __init__(self,
self.build_targets = self.all_targets()
self._board = board
self.emit_disable_all_defines = emit_disable_all_defines
self.resume = resume
self.results = {}

self.enable_in_turn_results = {}
Expand Down Expand Up @@ -187,6 +190,9 @@ def test_disable_feature(self, feature, options):

self.test_compile_with_defines(defines)

self.assert_feature_not_in_code(defines, feature)

def assert_feature_not_in_code(self, defines, feature):
# if the feature is truly disabled then extract_features.py
# should say so:
for target in self.build_targets:
Expand All @@ -198,8 +204,7 @@ def test_disable_feature(self, feature, options):
# or all vehicles:
feature_define_whitelist = set([
'AP_RANGEFINDER_ENABLED', # only at vehicle level ATM
'BEACON_ENABLED', # Rover doesn't obey this (should also be AP_BEACON_ENABLED)
'WINCH_ENABLED', # Copter doesn't use this; should use AP_WINCH_ENABLED
'HAL_PERIPH_SUPPORT_LONG_CAN_PRINTF', # no symbol
])
if define in compiled_in_feature_defines:
error = f"feature gated by {define} still compiled into ({target}); extract_features.py bug?"
Expand All @@ -220,6 +225,177 @@ def test_enable_feature(self, feature, options):

self.test_compile_with_defines(defines)

self.assert_feature_in_code(defines, feature)

def define_is_whitelisted_for_feature_in_code(self, target, define):
'''returns true if we can not expect the define to be extracted from
the binary'''
# the following defines are known not to work on some
# or all vehicles:
feature_define_whitelist = set([
'AP_RANGEFINDER_ENABLED', # only at vehicle level ATM
'HAL_PERIPH_SUPPORT_LONG_CAN_PRINTF', # no symbol
'AP_DRONECAN_VOLZ_FEEDBACK_ENABLED', # broken, no subscriber
# Baro drivers either come in because you have
# external-probing enabled or you have them specified in
# your hwdef. If you're not probing and its not in your
# hwdef then the code will be elided as unreachable
'AP_BARO_ICM20789_ENABLED',
'AP_BARO_ICP101XX_ENABLED',
'AP_BARO_ICP201XX_ENABLED',
'AP_BARO_BMP085_ENABLED',
'AP_BARO_BMP280_ENABLED',
'AP_BARO_BMP388_ENABLED',
'AP_BARO_BMP581_ENABLED',
'AP_BARO_DPS280_ENABLED',
'AP_BARO_FBM320_ENABLED',
'AP_BARO_KELLERLD_ENABLED',
'AP_BARO_LPS2XH_ENABLED',
'AP_BARO_MS56XX_ENABLED',
'AP_BARO_SPL06_ENABLED',
'AP_CAMERA_SEND_FOV_STATUS_ENABLED', # elided unless AP_CAMERA_SEND_FOV_STATUS_ENABLED
'AP_COMPASS_LSM9DS1_ENABLED', # must be in hwdef, not probed
'AP_COMPASS_MAG3110_ENABLED', # must be in hwdef, not probed
'AP_COMPASS_MMC5XX3_ENABLED', # must be in hwdef, not probed
'AP_MAVLINK_AUTOPILOT_VERSION_REQUEST_ENABLED', # completely elided
'AP_MAVLINK_MSG_HIL_GPS_ENABLED', # no symbol available
'AP_MAVLINK_MSG_RELAY_STATUS_ENABLED', # no symbol available
'AP_MAVLINK_MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES_ENABLED', # no symbol available
'HAL_MSP_SENSORS_ENABLED', # no symbol available
'AP_OSD_LINK_STATS_EXTENSIONS_ENABLED', # FIXME: need a new define/feature
'HAL_OSD_SIDEBAR_ENABLE', # FIXME: need a new define/feature
'HAL_PLUSCODE_ENABLE', # FIXME: need a new define/feature
'AP_SERIALMANAGER_REGISTER_ENABLED', # completely elided without a caller
'AP_OPTICALFLOW_ONBOARD_ENABLED', # only instantiated on Linux
'HAL_WITH_FRSKY_TELEM_BIDIRECTIONAL', # entirely elided if no user
'AP_PLANE_BLACKBOX_LOGGING', # entirely elided if no user
])
if target.lower() != "copter":
feature_define_whitelist.add('MODE_ZIGZAG_ENABLED')
feature_define_whitelist.add('MODE_SYSTEMID_ENABLED')
feature_define_whitelist.add('MODE_SPORT_ENABLED')
feature_define_whitelist.add('MODE_FOLLOW_ENABLED')
feature_define_whitelist.add('MODE_TURTLE_ENABLED')
feature_define_whitelist.add('MODE_GUIDED_NOGPS_ENABLED')
feature_define_whitelist.add('MODE_FLOWHOLD_ENABLED')
feature_define_whitelist.add('MODE_FLIP_ENABLED')
feature_define_whitelist.add('MODE_BRAKE_ENABLED')
feature_define_whitelist.add('AP_TEMPCALIBRATION_ENABLED')
feature_define_whitelist.add('AC_PAYLOAD_PLACE_ENABLED')
feature_define_whitelist.add('AP_AVOIDANCE_ENABLED')
feature_define_whitelist.add('AP_WINCH_ENABLED')
feature_define_whitelist.add('AP_WINCH_DAIWA_ENABLED')
feature_define_whitelist.add('AP_WINCH_PWM_ENABLED')
feature_define_whitelist.add(r'AP_MOTORS_FRAME_.*_ENABLED')

if target.lower() != "plane":
# only on Plane:
feature_define_whitelist.add('AP_ICENGINE_ENABLED')
feature_define_whitelist.add('AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED')
feature_define_whitelist.add('AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED')
feature_define_whitelist.add('AP_ADVANCEDFAILSAFE_ENABLED')
feature_define_whitelist.add('AP_TUNING_ENABLED')
feature_define_whitelist.add('HAL_LANDING_DEEPSTALL_ENABLED')
feature_define_whitelist.add('HAL_SOARING_ENABLED')
feature_define_whitelist.add('AP_PLANE_BLACKBOX_LOGGING')
feature_define_whitelist.add('QAUTOTUNE_ENABLED')
feature_define_whitelist.add('AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED')
feature_define_whitelist.add('HAL_QUADPLANE_ENABLED')
feature_define_whitelist.add('AP_BATTERY_WATT_MAX_ENABLED')

if target.lower() not in ["plane", "copter"]:
feature_define_whitelist.add('HAL_ADSB_ENABLED')
feature_define_whitelist.add('AP_LANDINGGEAR_ENABLED')
# only Plane and Copter instantiate Parachute
feature_define_whitelist.add('HAL_PARACHUTE_ENABLED')
# only Plane and Copter have AP_Motors:

if target.lower() not in ["rover", "copter"]:
# only Plane and Copter instantiate Beacon
feature_define_whitelist.add('AP_BEACON_ENABLED')

if target.lower() != "rover":
# only on Rover:
feature_define_whitelist.add('HAL_TORQEEDO_ENABLED')
feature_define_whitelist.add('AP_ROVER_ADVANCED_FAILSAFE_ENABLED')
if target.lower() != "sub":
# only on Sub:
feature_define_whitelist.add('AP_BARO_KELLERLD_ENABLED')
if target.lower() not in frozenset(["rover", "sub"]):
# only Rover and Sub get nmea airspeed
feature_define_whitelist.add('AP_AIRSPEED_NMEA_ENABLED')
if target.lower() not in frozenset(["copter", "rover"]):
feature_define_whitelist.add('HAL_SPRAYER_ENABLED')
feature_define_whitelist.add('HAL_PROXIMITY_ENABLED')
feature_define_whitelist.add('AP_PROXIMITY_.*_ENABLED')
feature_define_whitelist.add('AP_OAPATHPLANNER_ENABLED')

if target.lower() in ["blimp", "antennatracker"]:
# no airspeed on blimp/tracker
feature_define_whitelist.add(r'AP_AIRSPEED_.*_ENABLED')
feature_define_whitelist.add(r'HAL_MOUNT_ENABLED')
feature_define_whitelist.add(r'AP_MOUNT_.*_ENABLED')
feature_define_whitelist.add(r'HAL_MOUNT_.*_ENABLED')
feature_define_whitelist.add(r'HAL_SOLO_GIMBAL_ENABLED')
feature_define_whitelist.add(r'AP_OPTICALFLOW_ENABLED')
feature_define_whitelist.add(r'AP_OPTICALFLOW_.*_ENABLED')
feature_define_whitelist.add(r'HAL_MSP_OPTICALFLOW_ENABLED')
# missing calls to fence.check():
feature_define_whitelist.add(r'AP_FENCE_ENABLED')
# RPM not instantiated on Blimp or Rover:
feature_define_whitelist.add(r'AP_RPM_ENABLED')
feature_define_whitelist.add(r'AP_RPM_.*_ENABLED')
# rangefinder init is not called:
feature_define_whitelist.add(r'HAL_MSP_RANGEFINDER_ENABLED')
# these guys don't instantiate anything which uses sd-card storage:
feature_define_whitelist.add(r'AP_SDCARD_STORAGE_ENABLED')
feature_define_whitelist.add(r'AP_RANGEFINDER_ENABLED')
feature_define_whitelist.add(r'AP_RANGEFINDER_.*_ENABLED')

if target.lower() in ["blimp", "antennatracker", "sub"]:
# no OSD on Sub/blimp/tracker
feature_define_whitelist.add(r'OSD_ENABLED')
feature_define_whitelist.add(r'OSD_PARAM_ENABLED')
# AP_OSD is not instantiated, , so no MSP backend:
feature_define_whitelist.add(r'HAL_WITH_MSP_DISPLAYPORT')
# camera instantiated in specific vehicles:
feature_define_whitelist.add(r'AP_CAMERA_ENABLED')
feature_define_whitelist.add(r'AP_CAMERA_.*_ENABLED')
# button update is not called in these vehicles
feature_define_whitelist.add(r'HAL_BUTTON_ENABLED')
# precland not instantiated on these vehicles
feature_define_whitelist.add(r'AC_PRECLAND_ENABLED')
feature_define_whitelist.add(r'AC_PRECLAND_.*_ENABLED')
# RSSI is not initialised - probably should be for some
feature_define_whitelist.add(r'AP_RSSI_ENABLED')

if target.lower() in ["antennatracker", "sub"]:
# missing the init call to the relay library:
feature_define_whitelist.add(r'AP_RELAY_ENABLED')
feature_define_whitelist.add(r'AP_RC_CHANNEL_AUX_FUNCTION_STRINGS_ENABLED')

for some_re in feature_define_whitelist:
if re.match(some_re, define):
return True

def assert_feature_in_code(self, defines, feature):
# if the feature is truly disabled then extract_features.py
# should say so:
for target in self.build_targets:
path = self.target_to_elf_path(target)
extracter = extract_features.ExtractFeatures(path)
(compiled_in_feature_defines, not_compiled_in_feature_defines) = extracter.extract()
for define in defines:
if not defines[define]:
continue
if define in compiled_in_feature_defines:
continue
error = f"feature gated by {define} not compiled into ({target}); extract_features.py bug?"
if self.define_is_whitelisted_for_feature_in_code(target, define):
print("warn: " + error)
continue
raise ValueError(error)

def board(self):
'''returns board to build for'''
return self._board
Expand Down Expand Up @@ -314,9 +490,15 @@ def disable_in_turn_check_sizes(self, feature, sizes_nothing_disabled):
f.write(self.csv_for_results(self.results))

def run_disable_in_turn(self):
progress_file = pathlib.Path("/tmp/run-disable-in-turn-progress")
resume_number = self.resume_number_from_progress_Path(progress_file)
options = self.get_build_options_from_ardupilot_tree()
count = 1
for feature in sorted(options, key=lambda x : x.define):
if resume_number is not None:
if count < resume_number:
count += 1
continue
if self.match_glob is not None:
if not fnmatch.fnmatch(feature.define, self.match_glob):
continue
Expand Down Expand Up @@ -351,16 +533,35 @@ def enable_in_turn_check_sizes(self, feature, sizes_everything_disabled):
with open("/tmp/enable-in-turn.csv", "w") as f:
f.write(self.csv_for_results(self.enable_in_turn_results))

def resume_number_from_progress_Path(self, progress_file):
if not self.resume:
return None
try:
content = progress_file.read_text().rstrip()
m = re.match(r"(\d+)/\d+ \w+", content)
if m is None:
raise ValueError(f"{progress_file} not matched")
return int(m.group(1))
except FileNotFoundError:
pass
return None

def run_enable_in_turn(self):
progress_file = pathlib.Path("/tmp/run-enable-in-turn-progress")
resume_number = self.resume_number_from_progress_Path(progress_file)
options = self.get_build_options_from_ardupilot_tree()
count = 1
for feature in options:
if resume_number is not None:
if count < resume_number:
count += 1
continue
if self.match_glob is not None:
if not fnmatch.fnmatch(feature.define, self.match_glob):
continue
self.progress("Enabling feature %s(%s) (%u/%u)" %
(feature.label, feature.define, count, len(options)))
with open("/tmp/run-enable-in-turn-progress", "w") as f:
with open(progress_file, "w") as f:
f.write(f"{count}/{len(options)} {feature.define}\n")
self.test_enable_feature(feature, options)
count += 1
Expand Down Expand Up @@ -482,11 +683,14 @@ def run(self):
help="file containing extra hwdef information")
parser.add_option("--board",
type='string',
default="DevEBoxH7v2",
default="CubeOrange", # DevEBoxH7v2 also works
help='board to build for')
parser.add_option("--emit-disable-all-defines",
action='store_true',
help='emit defines used for disabling all features then exit')
parser.add_option("--resume",
action='store_true',
help='resume from previous progress file')

opts, args = parser.parse_args()

Expand All @@ -501,6 +705,7 @@ def run(self):
board=opts.board,
extra_hwdef=opts.extra_hwdef,
emit_disable_all_defines=opts.emit_disable_all_defines,
resume=opts.resume,
)

tbo.run()
Loading

0 comments on commit e642e88

Please sign in to comment.