diff --git a/.gitignore b/.gitignore index bd1112407..4203b2564 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ doc/html/ doc/latex/ .vscode/settings.json +__pycache__ +/lib/utilities/vcs_identifier_gen.h diff --git a/README.md b/README.md index 4b3d7dd45..3e0e675a1 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,11 @@ Alternatively the software may be executed in a simulation environment. This project uses [PlatformIO](https://platformio.org/) as build system and package manager. PlatformIO may be used via a GUI (PlatformIO IDE) or command line interface (PlatformIO Core). The project configuration ([`platformio.ini`](platformio.ini)) is part of this repository. +System requirements: + +- [PlatformIO Core](https://docs.platformio.org/en/stable/core/index.html#piocore) +- optional: [git](https://git-scm.com/) client is used to generate version identifier if project files are in a git clone + In order to use the software (some call it "firmware"), the following steps are required: 1. Build (the default configuration of) the project. diff --git a/generate_vcs_identifier.py b/generate_vcs_identifier.py new file mode 100644 index 000000000..f6cb18d80 --- /dev/null +++ b/generate_vcs_identifier.py @@ -0,0 +1,25 @@ +""" +generate_vcs_identifier.py + +This script generates a source file containing a version control system (VCS) +identifier string. It retrieves the VCS identifier from the 'vcs_utils' module +and writes it to a specified source file. + +This script should be loaded by PlatformIO during the project build and is not +designed to be run independently. +""" + +from vcs_utils import get_vcs_id, write_vcs_id_to_file +import os.path +Import("env") + +# Get the source directory path from PlatformIO configuration +source_dir = env.subst("$PROJECT_DIR") + +# Path to the output file within +output_file = os.path.join(source_dir, 'lib', 'utilities', 'vcs_identifier_gen.h') + +vcs_id = get_vcs_id() + +if vcs_id: + write_vcs_id_to_file(vcs_id, output_file) diff --git a/lib/utilities/version.hpp b/lib/utilities/version.hpp new file mode 100644 index 000000000..a690328bf --- /dev/null +++ b/lib/utilities/version.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include +#if __has_include("vcs_identifier_gen.h") +#include "vcs_identifier_gen.h" +constexpr std::optional vcsId = VCS_ID; +#else +constexpr std::optional vcsId = std::nullopt; +#endif diff --git a/platformio.ini b/platformio.ini index 8e7713d93..e8cf6456c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,8 @@ build_flags = -DLV_CONF_PATH="${PROJECT_DIR}/lib/3rd_party_adapters/LVGL/lv_conf.h" ; lvgl: use this config file -DBAUD_RATE=${this.monitor_speed} monitor_speed = 115200 +extra_scripts = + pre:generate_vcs_identifier.py [env:native] platform = native diff --git a/src/main.cpp b/src/main.cpp index 8f0f98480..7e5f7f48f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,14 +13,15 @@ #include #include #include +#include void setup() { serial_port::initialize(); serial_port::cout << "\x1b[20h"; // Tell the terminal to use CR/LF for newlines instead of just CR. - static constexpr const auto programIdentificationString = __FILE__ " compiled at " __DATE__ " " __TIME__; - serial_port::cout << std::endl - << " begin program '" << programIdentificationString << std::endl; + serial_port::cout + << std::endl + << " begin program version '" << vcsId.value_or("unknown") << "'" << std::endl; serial_port::setCallbackForLineReception([](const serial_port::String &commandLine) { ProtocolHandler::execute(commandLine.c_str()); }); diff --git a/vcs_utils.py b/vcs_utils.py new file mode 100644 index 000000000..b8d8f8669 --- /dev/null +++ b/vcs_utils.py @@ -0,0 +1,65 @@ +""" +vcs_utils.py + +This module provides utility functions to retrieve the version control system +(VCS) identifier using Git and to write it to a file. + +The functions handle cases where Git is not installed or the current directory +is not a Git repository, and handle file write failures gracefully. +""" + +import subprocess +import sys + +def get_vcs_id(): + """ + Attempts to retrieve the VCS identifier using the 'git describe' command. + + Returns: + str: A string containing the VCS identifier if successful, or an empty + string if Git is not available or the directory is not a Git repository. + + This function handles the following exceptions: + - subprocess.CalledProcessError: Raised if the 'git' command fails, e.g., + if the current directory is not a Git repository. + - FileNotFoundError: Raised if 'git' is not installed or not found in the + system's PATH. + + All warnings are printed to stderr. + """ + try: + vcs_output = subprocess.run( + ["git", "describe", "--always", "--dirty", "--all", "--long"], + stdout=subprocess.PIPE, text=True, check=True + ) + vcs_string = vcs_output.stdout.strip() + return vcs_string + except (subprocess.CalledProcessError, FileNotFoundError) as e: + # Print the warning to stderr + print("Warning: Unable to retrieve VCS description. Error:", str(e), file=sys.stderr) + return "" + + +def write_vcs_id_to_file(vcs_id, file_path): + """ + Defines a macro with the given VCS identifier as a string literal to a file. + + Args: + vcs_id (str): The VCS identifier string to write to the file. + file_path (str): The path to the file to write to. + + If the file exists, it will be overwritten. If writing to the file fails, + a warning will be printed to stderr, but the script will not terminate + with an error. + """ + try: + # Prepare the C-style string definition + c_content = f'#define VCS_ID "{vcs_id}"\n' + + # Write the content to the file (overwriting if it exists) + with open(file_path, 'w') as file: + file.write(c_content) + + except IOError as e: + # If writing to the file fails, print the warning to stderr + print(f"Warning: Unable to write VCS ID to file '{file_path}'. Error: {str(e)}", file=sys.stderr)