From 8d2ea14f0694af81f7e0b04232e910e504d8cfc4 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 29 May 2023 20:40:56 +0400 Subject: [PATCH] [FL-3330] fbt: added hooks for build & dist environments; added FW_ORIGIN_* macro for apps & SDK (#2705) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: added hooks for build & dist environments * Moved env hooks to an optional file * Fixed var name * Added fw origin to device info * Bumped device info version * fbt: added FIRMWARE_ORIGIN option. Different implementation for FW_ORIGIN_* C macro. * api: bumped versions * fbt: added fbt_options_local.py * gitignore: cleanup Co-authored-by: あく --- .gitignore | 22 +++---- documentation/fbt.md | 2 + fbt_options.py | 7 ++ firmware.scons | 11 ++++ firmware/targets/f18/api_symbols.csv | 4 +- firmware/targets/f7/api_symbols.csv | 4 +- firmware/targets/f7/furi_hal/furi_hal_info.c | 20 +++++- furi/core/core_defines.h | 5 ++ lib/toolbox/version.c | 15 ++++- lib/toolbox/version.h | 11 ++++ scripts/debug/flipperversion.py | 16 ++++- scripts/fbt_tools/fbt_envhooks.py | 67 ++++++++++++++++++++ scripts/fbt_tools/fbt_version.py | 4 +- scripts/ufbt/SConstruct | 5 ++ scripts/version.py | 23 +++++++ site_scons/commandline.scons | 7 ++ 16 files changed, 206 insertions(+), 17 deletions(-) create mode 100644 scripts/fbt_tools/fbt_envhooks.py diff --git a/.gitignore b/.gitignore index bf17a94e2..d60dcec3f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,27 +30,25 @@ bindings/ .mxproject Brewfile.lock.json -# Visual Studio Code -/.vscode/ - # Kate .kateproject .kateconfig -# legendary cmake's -build -CMakeLists.txt - -# bundle output -dist - # kde .directory # SCons .sconsign.dblite + + +# Visual Studio Code +/.vscode + +# bundle output +/dist + # SCons build dir -build/ +/build # Toolchain /toolchain @@ -64,3 +62,5 @@ PVS-Studio.log *.PVS-Studio.* .gdbinit + +/fbt_options_local.py \ No newline at end of file diff --git a/documentation/fbt.md b/documentation/fbt.md index d9eb8f4aa..c19780ef5 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -114,6 +114,8 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio Default configuration variables are set in the configuration file: `fbt_options.py`. Values set in the command line have higher precedence over the configuration file. +You can also create a file called `fbt_options_local.py` that will be evaluated when loading default options file, enabling persisent overriding of default options without modifying default configuration. + You can find out available options with `./fbt -h`. ### Firmware application set diff --git a/fbt_options.py b/fbt_options.py index d05b882a0..b6fcc70f2 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -1,7 +1,9 @@ +from pathlib import Path import posixpath # For more details on these options, run 'fbt -h' +FIRMWARE_ORIGIN = "Official" # Default hardware target TARGET_HW = 7 @@ -75,3 +77,8 @@ FIRMWARE_APPS = { } FIRMWARE_APP_SET = "default" + +custom_options_fn = "fbt_options_local.py" + +if Path(custom_options_fn).exists(): + exec(compile(Path(custom_options_fn).read_text(), custom_options_fn, "exec")) diff --git a/firmware.scons b/firmware.scons index c46996899..a91b7cdfc 100644 --- a/firmware.scons +++ b/firmware.scons @@ -18,6 +18,7 @@ env = ENV.Clone( "fbt_apps", "pvsstudio", "fbt_hwtarget", + "fbt_envhooks", ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], @@ -72,6 +73,8 @@ env = ENV.Clone( _APP_ICONS=None, ) +env.PreConfigureFwEnvionment() + if env["IS_BASE_FIRMWARE"]: env.Append( FIRMWARE_BUILD_CFG="firmware", @@ -100,6 +103,13 @@ lib_targets = env.BuildModules( ], ) +# Configure firmware origin definitions +env.Append( + CPPDEFINES=[ + env.subst("FW_ORIGIN_${FIRMWARE_ORIGIN}"), + ] +) + # Now, env is fully set up with everything to build apps fwenv = env.Clone(FW_ARTIFACTS=[]) @@ -271,5 +281,6 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts) +env.PostConfigureFwEnvionment() Return("fwenv") diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 92c8697aa..7f0dcebd5 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.0,, +Version,+,28.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2003,6 +2003,8 @@ Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" Function,+,version_get,const Version*, Function,+,version_get_builddate,const char*,const Version* Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_firmware_origin,const char*,const Version* +Function,+,version_get_git_origin,const char*,const Version* Function,+,version_get_gitbranch,const char*,const Version* Function,+,version_get_gitbranchnum,const char*,const Version* Function,+,version_get_githash,const char*,const Version* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4b48949d9..1259f0fce 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.0,, +Version,+,28.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2945,6 +2945,8 @@ Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" Function,+,version_get,const Version*, Function,+,version_get_builddate,const char*,const Version* Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_firmware_origin,const char*,const Version* +Function,+,version_get_git_origin,const char*,const Version* Function,+,version_get_gitbranch,const char*,const Version* Function,+,version_get_gitbranchnum,const char*,const Version* Function,+,version_get_githash,const char*,const Version* diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 4c034ff35..47672c97a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -26,7 +26,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { property_value_out(&property_context, NULL, 2, "format", "minor", "1"); } else { property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); - property_value_out(&property_context, NULL, 3, "device", "info", "minor", "1"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "2"); } // Model name @@ -173,6 +173,24 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { &property_context, "%d", 3, "firmware", "api", "major", api_version_major); property_value_out( &property_context, "%d", 3, "firmware", "api", "minor", api_version_minor); + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "origin", + "fork", + version_get_firmware_origin(firmware_version)); + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "origin", + "git", + version_get_git_origin(firmware_version)); } if(furi_hal_bt_is_alive()) { diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 830bb191c..4309c20c5 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -78,6 +78,11 @@ extern "C" { #define TOSTRING(x) STRINGIFY(x) #endif +#ifndef CONCATENATE +#define CONCATENATE(a, b) CONCATENATE_(a, b) +#define CONCATENATE_(a, b) a##b +#endif + #ifndef REVERSE_BYTES_U32 #define REVERSE_BYTES_U32(x) \ ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ diff --git a/lib/toolbox/version.c b/lib/toolbox/version.c index 6ba68e364..876695f07 100644 --- a/lib/toolbox/version.c +++ b/lib/toolbox/version.c @@ -5,7 +5,7 @@ #define VERSION_MAGIC (0xBE40u) #define VERSION_MAJOR (0x1u) -#define VERSION_MINOR (0x0u) +#define VERSION_MINOR (0x1u) struct Version { // Header @@ -20,6 +20,9 @@ struct Version { // Payload bits and pieces const uint8_t target; const bool build_is_dirty; + // v 1.1 + const char* firmware_origin; + const char* git_origin; }; /* version of current running firmware (bootloader/flipper) */ @@ -37,6 +40,8 @@ static const Version version = { , .target = TARGET, .build_is_dirty = BUILD_DIRTY, + .firmware_origin = FIRMWARE_ORIGIN, + .git_origin = GIT_ORIGIN, }; const Version* version_get(void) { @@ -71,3 +76,11 @@ uint8_t version_get_target(const Version* v) { bool version_get_dirty_flag(const Version* v) { return v ? v->build_is_dirty : version.build_is_dirty; } + +const char* version_get_firmware_origin(const Version* v) { + return v ? v->firmware_origin : version.firmware_origin; +} + +const char* version_get_git_origin(const Version* v) { + return v ? v->git_origin : version.git_origin; +} diff --git a/lib/toolbox/version.h b/lib/toolbox/version.h index 652ff3fea..0c04e5c75 100644 --- a/lib/toolbox/version.h +++ b/lib/toolbox/version.h @@ -82,6 +82,17 @@ uint8_t version_get_target(const Version* v); */ bool version_get_dirty_flag(const Version* v); +/** + * Get firmware origin. "Official" for mainline firmware, fork name for forks. + * Set by FIRMWARE_ORIGIN fbt argument. +*/ +const char* version_get_firmware_origin(const Version* v); + +/** + * Get git repo origin +*/ +const char* version_get_git_origin(const Version* v); + #ifdef __cplusplus } #endif diff --git a/scripts/debug/flipperversion.py b/scripts/debug/flipperversion.py index 4ac3bd200..56915c0f2 100644 --- a/scripts/debug/flipperversion.py +++ b/scripts/debug/flipperversion.py @@ -23,6 +23,10 @@ class VersionData: version: str target: int build_is_dirty: bool + # Since version 1.1 + firmware_origin: str = "" + git_origin: str = "" + # More fields may be added in the future extra: Optional[Dict[str, str]] = field(default_factory=dict) @@ -52,7 +56,7 @@ class VersionLoader: # Struct version 1.0 extra_data = int(self.version_ptr[5].cast(self._uint_type)) - return VersionData( + version_data = VersionData( git_hash=self.version_ptr[1].cast(self._cstr_type).string(), git_branch=self.version_ptr[2].cast(self._cstr_type).string(), build_date=self.version_ptr[3].cast(self._cstr_type).string(), @@ -60,6 +64,12 @@ class VersionLoader: target=extra_data & 0xF, build_is_dirty=bool((extra_data >> 8) & 0xF), ) + if minor >= 1: + version_data.firmware_origin = ( + self.version_ptr[6].cast(self._cstr_type).string() + ) + version_data.git_origin = self.version_ptr[7].cast(self._cstr_type).string() + return version_data def load_unversioned(self): """Parse an early version of the version struct.""" @@ -104,6 +114,10 @@ class FlipperFwVersion(gdb.Command): print(f"\tGit commit: {v.version.git_hash}") print(f"\tDirty: {v.version.build_is_dirty}") print(f"\tHW Target: {v.version.target}") + if v.version.firmware_origin: + print(f"\tOrigin: {v.version.firmware_origin}") + if v.version.git_origin: + print(f"\tGit origin: {v.version.git_origin}") FlipperFwVersion() diff --git a/scripts/fbt_tools/fbt_envhooks.py b/scripts/fbt_tools/fbt_envhooks.py new file mode 100644 index 000000000..0538e173c --- /dev/null +++ b/scripts/fbt_tools/fbt_envhooks.py @@ -0,0 +1,67 @@ +""" + +To introduce changes to firmware build environment that are specific to your fork: + + create a file "scripts/fbt/fbt_hooks.py" + +With it, you can define functions that will be called at specific points of +firmware build configuration, with environment as an argument. + +For example, you can define a function `PreConfigureFwEnvionment(env)` that +defines that will be a part of SDK build, so applications can +use them for conditional compilation. + +Here is a list of all available hooks: + + PreConfigureFwEnvionment(env): + This function is called on firmware environment (incl. updater) + before any major configuration is done. + + PostConfigureFwEnvionment(env): + This function is called on firmware environment (incl. updater) + after all configuration is done. + + PreConfigureUfbtEnvionment(env): + This function is called on ufbt environment at the beginning of + its configuration, before dist environment is created. + + PostConfigureUfbtEnvionment(env): + This function is called on ufbt dist_env environment after all + configuration and target creation is done. +""" + + +class DefaultFbtHooks: + pass + + +try: + from fbt import fbt_hooks +except ImportError: + fbt_hooks = DefaultFbtHooks() + + +def generate(env): + stub_hook = lambda env: None + control_hooks = [ + "PreConfigureFwEnvionment", + "PostConfigureFwEnvionment", + "PreConfigureUfbtEnvionment", + "PostConfigureUfbtEnvionment", + ] + + if ( + isinstance(fbt_hooks, DefaultFbtHooks) + and env.subst("${FIRMWARE_ORIGIN}") != "Official" + ): + # If fbt_hooks.py is not present, but we are not building official firmware, + # create "scripts/fbt/fbt_hooks.py" to implement changes to firmware build environment. + pass + + for hook_name in control_hooks: + hook_fn = getattr(fbt_hooks, hook_name, stub_hook) + env.AddMethod(hook_fn, hook_name) + + +def exists(): + return True diff --git a/scripts/fbt_tools/fbt_version.py b/scripts/fbt_tools/fbt_version.py index 8469e181a..aead13b29 100644 --- a/scripts/fbt_tools/fbt_version.py +++ b/scripts/fbt_tools/fbt_version.py @@ -19,7 +19,9 @@ def generate(env): BUILDERS={ "VersionBuilder": Builder( action=Action( - '${PYTHON3} "${VERSION_SCRIPT}" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', + '${PYTHON3} "${VERSION_SCRIPT}" generate ' + "-t ${TARGET_HW} -fw-origin ${FIRMWARE_ORIGIN} " + '-o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', "${VERSIONCOMSTR}", ), emitter=version_emitter, diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 4dd1fb5b9..d72de380c 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -98,6 +98,7 @@ env = core_env.Clone( "fbt_apps", "fbt_extapps", "fbt_assets", + "fbt_envhooks", ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), ], FBT_FAP_DEBUG_ELF_ROOT=ufbt_build_dir, @@ -117,6 +118,8 @@ env = core_env.Clone( wrap_tempfile(env, "LINKCOM") wrap_tempfile(env, "ARCOM") +env.PreConfigureUfbtEnvionment() + # print(env.Dump()) # Dist env @@ -474,3 +477,5 @@ dist_env.PhonyTarget( "env", "@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", ) + +dist_env.PostConfigureUfbtEnvionment() diff --git a/scripts/version.py b/scripts/version.py index 3d68b2e98..f00c1f531 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -45,8 +45,23 @@ class GitVersion: "GIT_BRANCH": branch, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, + "GIT_ORIGIN": ",".join(self._get_git_origins()), } + def _get_git_origins(self): + try: + remotes = self._exec_git("remote -v") + except subprocess.CalledProcessError: + return set() + origins = set() + for line in remotes.split("\n"): + if not line: + continue + _, destination = line.split("\t") + url, _ = destination.split(" ") + origins.add(url) + return origins + def _exec_git(self, args): cmd = ["git"] cmd.extend(args.split(" ")) @@ -74,6 +89,13 @@ class Main(App): help="hardware target", required=True, ) + self.parser_generate.add_argument( + "-fw-origin", + dest="firmware_origin", + type=str, + help="firmware origin", + required=True, + ) self.parser_generate.add_argument("--dir", dest="sourcedir", required=True) self.parser_generate.set_defaults(func=self.generate) @@ -89,6 +111,7 @@ class Main(App): { "BUILD_DATE": build_date.strftime("%d-%m-%Y"), "TARGET": self.args.target, + "FIRMWARE_ORIGIN": self.args.firmware_origin, } ) diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 2e9486627..e31927c59 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -236,6 +236,13 @@ vars.AddVariables( help="Don't open browser after generating error repots", default=False, ), + ( + "FIRMWARE_ORIGIN", + "Firmware origin. 'Official' if follows upstream's API structure, otherwise fork name. " + " This will also create a C define FW_ORIGIN_ so that " + " app can check what version it is being built for.", + "Official", + ), ) Return("vars")