Development Environment

The development environment uses VS Code as the editor. While this documentation only describes the use of VS Code, the project may be developed with any editor.

To maintain toolchains and dependencies, Docker is used.

Python is used as the scripting language, e.g. to interact with Docker.

Installation

To work on this project on a local machine, the following tools need to be installed. The version of the tools is uncritical, as all the critical tools and dependencies are in the Docker containers.

For Debugging there might be some more tools necessary. (see: Debugging)

VS Code

All necessary files can be created and edited with VS Code.

_images/vs_code_files.png

configuration

devcontainer definitions are found in the .devcontainer/ directory
devcontainer.json
{
    "name": "doc",
    "dockerComposeFile": "${localWorkspaceFolder}/doc/docker-compose.yml",
    "service": "main",
    "workspaceFolder": "/home/${localEnv:USER}/project/doc",
    "customizations": {
        "vscode": {
            "settings": {
                "terminal.integrated.shell.linux": "/bin/bash"
            },
            "extensions": [
                "ms-azuretools.vscode-containers",
                "ms-python.python",
                "ybaumes.highlight-trailing-white-spaces"
            ]
        }
    },
    "remoteUser": "root"
}
devcontainer.json
{
    "name": "software",
    "dockerComposeFile": "${localWorkspaceFolder}/software/docker-compose.yml",
    "service": "main",
    "workspaceFolder": "/home/${localEnv:USER}/project/software",
    "customizations": {
        "vscode": {
            "settings": {
                "terminal.integrated.shell.linux": "/bin/bash",
                "rust-analyzer.check.allTargets": false,
                "rust-analyzer.inlayHints.typeHints.enable": false,
                "rust-analyzer.inlayHints.parameterHints.enable": false,
                "editor.semanticTokenColorCustomizations": {
                    "enabled": true,
                    "rules": {
                        "*.mutable": {
                            "underline": false
                        }
                    }
                }
            },
            "extensions": [
                "rust-lang.rust-analyzer",
                "fill-labs.dependi",
                "tamasfe.even-better-toml",
                "ms-azuretools.vscode-containers",
                "ybaumes.highlight-trailing-white-spaces",
                "probe-rs.probe-rs-debugger",
                "vadimcn.vscode-lldb",
                "ms-python.python"
            ]
        }
    }
}

Devcontainers are Docker containers that VS Code can open into. It guarantiees a consistent development environment while using the benefits of VS Code. See for more information on devcontainers.

.gitignore(s)

There might be multiple .gitignore files. They are placed with reasonable granularity. See File Structure where they are.

reStructuredText

The documentation is created with Sphinx from reStructuredText. It may contain diagrams, which are created with draw.io and stuff from other sources. There may also be PlantUML diagrams directly embedded into code documentation or other documentation files.

draw.io files

Diagrams especially for the documentation are created with draw.io.

workflows

There shall be as much automation as reasonable possible. GitHub Actions with workflows are used for automation.

build scripts

For simple project handling (test, build, download to target, …) Python scripts are used. As the same scripts are used by the GitHub Actions workflows, the build process is consistent locally and remotely. The scripts are powerfull and almost everything can be done with them. See see: Scripts how they work.

Dockerfile(s)

There are multiple Dockerfiles and docker-compose.yml files. They are placed with reasonable granularity. See File Structure where they are. Everything generating output like compilers, flash-tools, unit-test-frameworks, documentation-build-chain, … are in the Docker containers. Docker Compose files control the configuration of the containers (e.g. ports, volumes, …). This makes the development environment consistent, reproducible and documented.

code

Rust has been chosen as the programming language, as it is an upcoming language with a lot of potential and a good community. It might not be the easiest solution for the task, but that is not a criterium here. See File Structure for the code structure.

Local Toolchain

_images/toolchain_local.png

Remote Toolchain

_images/toolchain_remote.png

Git

Is used for version control.

github.com

GitHub is used as the repository host.

Python

Python is used to execute the scripts.

Docker

Together with Docker Compose, defines the containers.

Sphinx

Is used to generate the documentation.

Rust tools

The Rust tools are used to build and test the code and to deploy to the target.

actions

GitHub Actions are used to automate the build and test process.

Debugging

This chapter explains how to setup the debugging with SEGGER J-Link EDU Mini and rs-probe.

@startuml
[Devcontainer with \n probe-rs] <-> [VS Code]
[VS Code] <-> [Development Workstation]
[Development Workstation] <-(0->  [SEGGER J-Link EDU Mini] : "  USB"
[SEGGER J-Link EDU Mini] <-(0-> [RPi Pico W] : "  SWD"
@enduml

probe-rs

probe-rs is installed in the container and the plugin is used in the devcontainer. So, no installation by hand.

Dockerfile with installation of probe-rs
$/software/Dockerfile
FROM rust:1.87.0

RUN apt-get update && apt-get install -y \
    # tig is great for viewing git history
    tig \
    # less is needed e.g. for 'git diff' to make the output scrollable
    less

ARG USER
ARG UID
RUN useradd -m -s /bin/bash -u ${UID:-2222} $USER
USER ${USER}

RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/download/v0.25.0/probe-rs-tools-installer.sh | sh

# run this as sudo on the host machine to make st-link accessible
# curl -o /etc/udev/rules.d/69-probe-rs.rules https://probe.rs/files/69-probe-rs.rules

RUN cargo install \
    cargo-tarpaulin@0.32.8 \
    cargo-modules@0.24.3

RUN rustup target add thumbv7em-none-eabihf

WORKDIR /home/$USER/dependencies_fetch_project/dummy/l6360
RUN cargo init
COPY ./l6360/Cargo.toml .
RUN cargo fetch

WORKDIR /home/$USER/dependencies_fetch_project/dummy/iol
RUN cargo init
COPY ./iol/Cargo.toml .
RUN cargo fetch

WORKDIR /home/$USER/dependencies_fetch_project/dummy/examples/std
RUN cargo init
COPY ./examples/std/Cargo.toml .
RUN cargo fetch

WORKDIR /home/$USER/dependencies_fetch_project/dummy/examples/stm32f446re
RUN cargo init
COPY ./examples/stm32f446re/Cargo.toml .
RUN cargo fetch

# - For example when used as devcontainer, the UID is set to a default value (see above).
#   I wasn't able to pass the UID of the local user to the container in this case.
#   So when using the devcontainer, the local user is then used and can't access the fetched dependencies.
#   To solve this, the fetched dependencies are made readable and writable by anyone.
RUN chmod -R a+rw /usr/local/cargo/registry
devcontainer with extension probe-rs.probe-rs-debugger
$/.devcontainer/software/devontainer.json
{
    "name": "software",
    "dockerComposeFile": "${localWorkspaceFolder}/software/docker-compose.yml",
    "service": "main",
    "workspaceFolder": "/home/${localEnv:USER}/project/software",
    "customizations": {
        "vscode": {
            "settings": {
                "terminal.integrated.shell.linux": "/bin/bash",
                "rust-analyzer.check.allTargets": false,
                "rust-analyzer.inlayHints.typeHints.enable": false,
                "rust-analyzer.inlayHints.parameterHints.enable": false,
                "editor.semanticTokenColorCustomizations": {
                    "enabled": true,
                    "rules": {
                        "*.mutable": {
                            "underline": false
                        }
                    }
                }
            },
            "extensions": [
                "rust-lang.rust-analyzer",
                "fill-labs.dependi",
                "tamasfe.even-better-toml",
                "ms-azuretools.vscode-containers",
                "ybaumes.highlight-trailing-white-spaces",
                "probe-rs.probe-rs-debugger",
                "vadimcn.vscode-lldb",
                "ms-python.python"
            ]
        }
    }
}

Scripts

The scripts help to simplify development workflows like creating documentation, running tests, flashing firmware and more. They are very powerfull and are in most cases the way to interact with the development environment if you want to execute any task.

There is a main script run.py in the project root folder. All the scripts in subfolders can be run from this script. The scripts in the subfolders are also named run.py. So any run.py belongs to the script system described here. Any run.py can be run from the folder it is in. Additionally the main script in the project root folder can run all of them.

The scripts are written in Python as this is widely available, spripts are easy to read and simplifies the parsing of command line arguments. Bash scripts for example have been found to be less readable and less flexible.

The github workflows use the exact same scripts, what leads to even more consistency.

Whenever you feel lost there is help in every script.

./run.py --help

run.py (project root)

This is the main script in the root folder. Everything can be done from here. It acts as a wrapper for the other scripts and forwards the parameters to them.

For example to run unit-tests, you can use the following command.

./run.py --software --test

This forwards the --test parameter to the software script. The local script can be run in the same way but without the --software parameter.

See the help message for more information.

help message

To get this help message run in the project root folder:

./run.py --help_all

usage: run.py [-h] (--software ... | --doc ... | --ha)

Execute common tasks (building, testing, ...)

options:
  -h, --help        show this help message and exit
  --software ...    Pass the remaining arguments to software/run.py.
  --doc ...         Pass the remaining arguments to doc/run.py.
  --ha, --help_all  Show help for this and all direct subscripts.


*** below are the help messages of the subscripts ***


*** software/run.py ***
usage: Execute feature tests [-h] [-b] [-t] [-d] [-v] [-k] [-p]

options:
  -h, --help            show this help message and exit
  -b, --build           Build the software.
  -t, --test            Test the software.
  -d, --doc             Document the software.
  -v, --verbose         Verbose output.
  -k, --keep_open       Enter the command line of the container.
  -p, --pseudo_tty_off  Disable colorfull output.


*** doc/run.py ***
usage: Execute unit-tests [-h] [-b] [-a] [-v] [-k] [-p]

options:
  -h, --help            show this help message and exit
  -b, --build           Build documentation.
  -a, --autobuild       Start sphinx-autobuild.
  -v, --verbose         Verbose output.
  -k, --keep_open       Enter the command line of the container.
  -p, --pseudo_tty_off  Disable colorfull output.