From 9dfa09a07eb8be0eb3124db06f69efe1987c1ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A1roly=20Kiripolszky?= Date: Thu, 21 Mar 2024 18:29:22 +0100 Subject: [PATCH] Extend build info with defined flags (#13333) * Extend build info with defined flags * Fix CI * Formatting * Update license header * Build options (WIP) * Review fixes * Add MSP build info generator * Review fixes * Add input hash * Fix for PascalCase * Add comment about MSP version --- DEFAULT_LICENCE.md => DEFAULT_LICENSE.md | 0 mk/source.mk | 1 + src/main/msp/msp.c | 3 + src/main/msp/msp_build_info.c | 176 +++++++++++++++++++++++ src/main/msp/msp_build_info.h | 82 +++++++++++ src/utils/make-build-info.py | 170 ++++++++++++++++++++++ 6 files changed, 432 insertions(+) rename DEFAULT_LICENCE.md => DEFAULT_LICENSE.md (100%) create mode 100644 src/main/msp/msp_build_info.c create mode 100644 src/main/msp/msp_build_info.h create mode 100755 src/utils/make-build-info.py diff --git a/DEFAULT_LICENCE.md b/DEFAULT_LICENSE.md similarity index 100% rename from DEFAULT_LICENCE.md rename to DEFAULT_LICENSE.md diff --git a/mk/source.mk b/mk/source.mk index b4e9b07aabe..6ab52f66e3b 100644 --- a/mk/source.mk +++ b/mk/source.mk @@ -64,6 +64,7 @@ COMMON_SRC = \ io/usb_msc.c \ msp/msp.c \ msp/msp_box.c \ + msp/msp_build_info.c \ msp/msp_serial.c \ scheduler/scheduler.c \ sensors/adcinternal.c \ diff --git a/src/main/msp/msp.c b/src/main/msp/msp.c index 5c8f944611c..70df7a07dea 100644 --- a/src/main/msp/msp.c +++ b/src/main/msp/msp.c @@ -107,6 +107,7 @@ #include "io/vtx_msp.h" #include "msp/msp_box.h" +#include "msp/msp_build_info.h" #include "msp/msp_protocol.h" #include "msp/msp_protocol_v2_betaflight.h" #include "msp/msp_protocol_v2_common.h" @@ -749,6 +750,8 @@ static bool mspCommonProcessOutCommand(int16_t cmdMSP, sbuf_t *dst, mspPostProce sbufWriteData(dst, buildDate, BUILD_DATE_LENGTH); sbufWriteData(dst, buildTime, BUILD_TIME_LENGTH); sbufWriteData(dst, shortGitRevision, GIT_SHORT_REVISION_LENGTH); + // Added in API version 1.47 + sbufWriteBuildInfoFlags(dst); break; case MSP_ANALOG: diff --git a/src/main/msp/msp_build_info.c b/src/main/msp/msp_build_info.c new file mode 100644 index 00000000000..4fb0b421124 --- /dev/null +++ b/src/main/msp/msp_build_info.c @@ -0,0 +1,176 @@ +/* + * This file is part of Betaflight. + * + * Betaflight is free software. You can redistribute this software + * and/or modify this software under the terms of the GNU General + * Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later + * version. + * + * Betaflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this software. + * + * If not, see . + */ + +/* + * WARNING: This is an auto-generated file, please do not edit directly! + * + * Generator : `src/utils/make-build-info.py` + * Source : https://build.betaflight.com/api/options/4.5.0-zulu + * Input hash : 2b66ff01549c4d858593dbf96c6aae12 + */ + +#include + +#include "platform.h" + +#include "common/streambuf.h" + +#include "msp/msp_build_info.h" + +void sbufWriteBuildInfoFlags(sbuf_t *dst) +{ + static const uint16_t options[] = { +#ifdef USE_SERIALRX_CRSF + BUILD_OPTION_SERIALRX_CRSF, +#endif +#ifdef USE_SERIALRX_FPORT + BUILD_OPTION_SERIALRX_FPORT, +#endif +#ifdef USE_SERIALRX_GHST + BUILD_OPTION_SERIALRX_GHST, +#endif +#ifdef USE_SERIALRX_IBUS + BUILD_OPTION_SERIALRX_IBUS, +#endif +#ifdef USE_SERIALRX_JETIEXBUS + BUILD_OPTION_SERIALRX_JETIEXBUS, +#endif +#ifdef USE_RX_PPM + BUILD_OPTION_RX_PPM, +#endif +#ifdef USE_SERIALRX_SBUS + BUILD_OPTION_SERIALRX_SBUS, +#endif +#ifdef USE_SERIALRX_SPEKTRUM + BUILD_OPTION_SERIALRX_SPEKTRUM, +#endif +#ifdef USE_SERIALRX_SRXL2 + BUILD_OPTION_SERIALRX_SRXL2, +#endif +#ifdef USE_SERIALRX_SUMD + BUILD_OPTION_SERIALRX_SUMD, +#endif +#ifdef USE_SERIALRX_SUMH + BUILD_OPTION_SERIALRX_SUMH, +#endif +#ifdef USE_SERIALRX_XBUS + BUILD_OPTION_SERIALRX_XBUS, +#endif +#ifdef USE_TELEMETRY_FRSKY_HUB + BUILD_OPTION_TELEMETRY_FRSKY_HUB, +#endif +#ifdef USE_TELEMETRY_HOTT + BUILD_OPTION_TELEMETRY_HOTT, +#endif +#ifdef USE_TELEMETRY_IBUS_EXTENDED + BUILD_OPTION_TELEMETRY_IBUS_EXTENDED, +#endif +#ifdef USE_TELEMETRY_LTM + BUILD_OPTION_TELEMETRY_LTM, +#endif +#ifdef USE_TELEMETRY_MAVLINK + BUILD_OPTION_TELEMETRY_MAVLINK, +#endif +#ifdef USE_TELEMETRY_SMARTPORT + BUILD_OPTION_TELEMETRY_SMARTPORT, +#endif +#ifdef USE_TELEMETRY_SRXL + BUILD_OPTION_TELEMETRY_SRXL, +#endif +#ifdef USE_ACRO_TRAINER + BUILD_OPTION_ACRO_TRAINER, +#endif +#ifdef USE_AKK_SMARTAUDIO + BUILD_OPTION_AKK_SMARTAUDIO, +#endif +#ifdef USE_BATTERY_CONTINUE + BUILD_OPTION_BATTERY_CONTINUE, +#endif +#ifdef USE_CAMERA_CONTROL + BUILD_OPTION_CAMERA_CONTROL, +#endif +#ifdef USE_DASHBOARD + BUILD_OPTION_DASHBOARD, +#endif +#ifdef USE_EMFAT_TOOLS + BUILD_OPTION_EMFAT_TOOLS, +#endif +#ifdef USE_ESCSERIAL_SIMONK + BUILD_OPTION_ESCSERIAL_SIMONK, +#endif +#ifdef USE_FRSKYOSD + BUILD_OPTION_FRSKYOSD, +#endif +#ifdef USE_GPS + BUILD_OPTION_GPS, +#endif +#ifdef USE_LED_STRIP + BUILD_OPTION_LED_STRIP, +#endif +#ifdef USE_LED_STRIP_64 + BUILD_OPTION_LED_STRIP_64, +#endif +#ifdef USE_MAG + BUILD_OPTION_MAG, +#endif +#ifdef USE_OSD_SD + BUILD_OPTION_OSD_SD, +#endif +#ifdef USE_OSD_HD + BUILD_OPTION_OSD_HD, +#endif +#ifdef USE_PINIO + BUILD_OPTION_PINIO, +#endif +#ifdef USE_RACE_PRO + BUILD_OPTION_RACE_PRO, +#endif +#ifdef USE_SERVOS + BUILD_OPTION_SERVOS, +#endif +#ifdef USE_VTX + BUILD_OPTION_VTX, +#endif +#ifdef USE_BRUSHED + BUILD_OPTION_BRUSHED, +#endif +#ifdef USE_DSHOT + BUILD_OPTION_DSHOT, +#endif +#ifdef USE_MULTISHOT + BUILD_OPTION_MULTISHOT, +#endif +#ifdef USE_ONESHOT + BUILD_OPTION_ONESHOT, +#endif +#ifdef USE_PROSHOT + BUILD_OPTION_PROSHOT, +#endif +#ifdef USE_PWM_OUTPUT + BUILD_OPTION_PWM_OUTPUT, +#endif + }; + + for (unsigned i = 0; i < ARRAYLEN(options); i++) + { + sbufWriteU16(dst, options[i]); + } +} diff --git a/src/main/msp/msp_build_info.h b/src/main/msp/msp_build_info.h new file mode 100644 index 00000000000..0690b7b4c24 --- /dev/null +++ b/src/main/msp/msp_build_info.h @@ -0,0 +1,82 @@ +/* + * This file is part of Betaflight. + * + * Betaflight is free software. You can redistribute this software + * and/or modify this software under the terms of the GNU General + * Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later + * version. + * + * Betaflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this software. + * + * If not, see . + */ + +/* + * WARNING: This is an auto-generated file, please do not edit directly! + * + * Generator : `src/utils/make-build-info.py` + * Source : https://build.betaflight.com/api/options/4.5.0-zulu + * Input hash : 2b66ff01549c4d858593dbf96c6aae12 + */ + +#pragma once + +#include "common/streambuf.h" + +// Radio Protocols +#define BUILD_OPTION_SERIALRX_CRSF 4097 +#define BUILD_OPTION_SERIALRX_FPORT 4098 +#define BUILD_OPTION_SERIALRX_GHST 4099 +#define BUILD_OPTION_SERIALRX_IBUS 4100 +#define BUILD_OPTION_SERIALRX_JETIEXBUS 4101 +#define BUILD_OPTION_RX_PPM 4102 +#define BUILD_OPTION_SERIALRX_SBUS 4103 +#define BUILD_OPTION_SERIALRX_SPEKTRUM 4104 +#define BUILD_OPTION_SERIALRX_SRXL2 4105 +#define BUILD_OPTION_SERIALRX_SUMD 4106 +#define BUILD_OPTION_SERIALRX_SUMH 4107 +#define BUILD_OPTION_SERIALRX_XBUS 4108 +// Telemetry Protocols +#define BUILD_OPTION_TELEMETRY_FRSKY_HUB 12301 +#define BUILD_OPTION_TELEMETRY_HOTT 12302 +#define BUILD_OPTION_TELEMETRY_IBUS_EXTENDED 12303 +#define BUILD_OPTION_TELEMETRY_LTM 12304 +#define BUILD_OPTION_TELEMETRY_MAVLINK 12305 +#define BUILD_OPTION_TELEMETRY_SMARTPORT 12306 +#define BUILD_OPTION_TELEMETRY_SRXL 12307 +// General Options +#define BUILD_OPTION_ACRO_TRAINER 16404 +#define BUILD_OPTION_AKK_SMARTAUDIO 16405 +#define BUILD_OPTION_BATTERY_CONTINUE 16406 +#define BUILD_OPTION_CAMERA_CONTROL 16407 +#define BUILD_OPTION_DASHBOARD 16408 +#define BUILD_OPTION_EMFAT_TOOLS 16409 +#define BUILD_OPTION_ESCSERIAL_SIMONK 16410 +#define BUILD_OPTION_FRSKYOSD 16411 +#define BUILD_OPTION_GPS 16412 +#define BUILD_OPTION_LED_STRIP 16413 +#define BUILD_OPTION_LED_STRIP_64 16414 +#define BUILD_OPTION_MAG 16415 +#define BUILD_OPTION_OSD_SD 16416 +#define BUILD_OPTION_OSD_HD 16417 +#define BUILD_OPTION_PINIO 16418 +#define BUILD_OPTION_RACE_PRO 16419 +#define BUILD_OPTION_SERVOS 16420 +#define BUILD_OPTION_VTX 16421 +// Motor Protocols +#define BUILD_OPTION_BRUSHED 8230 +#define BUILD_OPTION_DSHOT 8231 +#define BUILD_OPTION_MULTISHOT 8232 +#define BUILD_OPTION_ONESHOT 8233 +#define BUILD_OPTION_PROSHOT 8234 +#define BUILD_OPTION_PWM_OUTPUT 8235 + +void sbufWriteBuildInfoFlags(sbuf_t *dst); diff --git a/src/utils/make-build-info.py b/src/utils/make-build-info.py new file mode 100755 index 00000000000..fcb468b1249 --- /dev/null +++ b/src/utils/make-build-info.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter +from hashlib import md5 +import json +import logging +import os + +import requests + + +HEADER_FILE_TEMPLATE = """{license_header} + +{generated_warning} + +#pragma once + +#include "common/streambuf.h" + +{defines} + +void sbufWriteBuildInfoFlags(sbuf_t *dst); +""" + +SOURCE_FILE_TEMPLATE = """{license_header} + +{generated_warning} + +#include + +#include "platform.h" + +#include "common/streambuf.h" + +#include "msp/msp_build_info.h" + +void sbufWriteBuildInfoFlags(sbuf_t *dst) +{ + static const uint16_t options[] = { +{build_options} + }; + + for (unsigned i = 0; i < ARRAYLEN(options); i++) + { + sbufWriteU16(dst, options[i]); + } +} +""" + +WARNING_COMMENT_TEMPLATE = """ +/* + * WARNING: This is an auto-generated file, please do not edit directly! + * + * Generator : `src/utils/make-build-info.py` + * Source : {source} + * Input hash : {input_hash} + */ +""" + + +def __find_project_root() -> str: + utils_dir = os.path.abspath(os.path.dirname(__file__)) + src_dir = os.path.dirname(utils_dir) + root_dir = os.path.dirname(src_dir) + return os.path.realpath(root_dir) + + +def camel_case_to_title(s: str) -> str: + if not s: + return "Unspecified" + else: + spaceless = s.replace(" ", "") + return "".join([(c if not c.isupper() else f" {c}") for c in spaceless]) \ + .lstrip() \ + .title() + + +def fetch_build_options(endpoint_url: str) -> tuple: + logging.info(f"Fetching JSON: {endpoint_url}") + data = requests.get(endpoint_url, timeout=2).json() + input_hash = md5(json.dumps(data, sort_keys=True).encode()).hexdigest() + logging.info(f"Input hash: {input_hash}") + + defines = [] + options = [] + groups = list(data.keys()) + for group_index, option_list in enumerate(data.values()): + for option in option_list: + define = option.get("value") + if define: + defines.append(define) + number = option.get("key") + name = define.replace("USE_", "BUILD_OPTION_") + options.append((name, number, camel_case_to_title(groups[group_index]))) + logging.info(f"Number of defines: {len(defines)}") + return defines, options, input_hash + + +def get_warning_comment(source: str, input_hash: str) -> str: + return WARNING_COMMENT_TEMPLATE \ + .strip() \ + .replace("{source}", source) \ + .replace("{input_hash}", input_hash) \ + + +def main(root_path: str, target_path: str, endpoint_url: str): + logging.info(f"Project root: {root_path}") + + license_file_path = os.path.join(root_path, "DEFAULT_LICENSE.md") + msp_build_info_c_path = os.path.join(target_path, "msp_build_info.c") + msp_build_info_h_path = os.path.join(target_path, "msp_build_info.h") + + with open(license_file_path) as f: + license_header = f.read().rstrip() + + gates, options, input_hash = fetch_build_options(endpoint_url) + + generated_warning = get_warning_comment(endpoint_url, input_hash) + + with open(msp_build_info_h_path, "w+") as f: + max_len = max(map(lambda x: len(x[0]), options)) + 4 + lines = [] + last_group = None + for option_name, option_value, group in options: + if group != last_group: + lines.append(f"// {group}") + last_group = group + lines.append(f"#define {option_name:<{max_len}}{option_value}") + data = HEADER_FILE_TEMPLATE \ + .replace("{license_header}", license_header) \ + .replace("{generated_warning}", generated_warning) \ + .replace("{defines}", "\n".join(lines)) + f.write(data) + + logging.info(f"Written header file: {msp_build_info_h_path}") + + with open(msp_build_info_c_path, "w+") as f: + lines = [] + indent = " " * 8 + for i, define in enumerate(gates): + option_name, _, _ = options[i] + lines.append(f"#ifdef {define}") + lines.append(f"{indent}{option_name},") + lines.append("#endif") + data = SOURCE_FILE_TEMPLATE \ + .replace("{license_header}", license_header) \ + .replace("{generated_warning}", generated_warning) \ + .replace("{build_options}", "\n".join(lines)) + f.write(data) + + logging.info(f"Written source file: {msp_build_info_c_path}") + + +if __name__ == "__main__": + PROJECT_ROOT_DIR = __find_project_root() + DEFAULT_TARGET_DIR = os.path.join(PROJECT_ROOT_DIR, "src", "main", "msp") + + parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) + parser.add_argument("endpoint_url", help="URL to build options API endpoint") + parser.add_argument("-d", "--target-dir", default=DEFAULT_TARGET_DIR, help="Path to output directory") + parser.add_argument("-v", "--verbose", action="store_true") + + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO if args.verbose else logging.ERROR) + + main( + root_path=PROJECT_ROOT_DIR, + target_path=args.target_dir, + endpoint_url=args.endpoint_url, + )