version: 2.1 commands: setup-toolchain: parameters: toolchain: type: string steps: - run: name: Set toolchain url and key command: | toolchain_url=$(jq -r '."<< parameters.toolchain >>"' .github/actions/setup_toolchain/toolchain.json) # only cache if not a github link if [[ $toolchain_url != "https://github.com"* ]]; then echo "<< parameters.toolchain >>-$toolchain_url" > toolchain_key fi echo "export toolchain_url=$toolchain_url" >> $BASH_ENV - restore_cache: name: Restore Toolchain Cache key: deps-{{ checksum "toolchain_key" }} paths: - ~/cache/<< parameters.toolchain >> - run: name: Install Toolchain command: | # download if folder does not exist (not cached) if [ ! -d ~/cache/<< parameters.toolchain >> ]; then mkdir -p ~/cache/<< parameters.toolchain >> if [[ << parameters.toolchain >> == rx-gcc ]]; then wget --progress=dot:giga $toolchain_url -O toolchain.run chmod +x toolchain.run ./toolchain.run -p ~/cache/<< parameters.toolchain >>/gnurx -y elif [[ << parameters.toolchain >> == ft9xx-gcc ]]; then wget --progress=dot:giga $toolchain_url -O ~/cache/<< parameters.toolchain >>/ft9xxtoolchain.deb elif [[ << parameters.toolchain >> == arm-iar ]]; then wget --progress=dot:giga https://netstorage.iar.com/FileStore/STANDARD/001/003/926/iar-lmsc-tools_1.8_amd64.deb -O ~/cache/<< parameters.toolchain >>/iar-lmsc-tools.deb wget --progress=dot:giga $toolchain_url -O ~/cache/<< parameters.toolchain >>/toolchain.deb else wget --progress=dot:giga $toolchain_url -O toolchain.tar.gz tar -C ~/cache/<< parameters.toolchain >> -xaf toolchain.tar.gz fi fi # Add toolchain to PATH if [[ << parameters.toolchain >> == arm-iar ]]; then # Install IAR since we only cache deb file sudo dpkg -i ~/cache/<< parameters.toolchain >>/iar-lmsc-tools.deb sudo dpkg --ignore-depends=libusb-1.0-0 -i ~/cache/<< parameters.toolchain >>/toolchain.deb echo "export PATH=$PATH:/opt/iar/cxarm/arm/bin" >> $BASH_ENV elif [[ << parameters.toolchain >> == ft9xx-gcc ]]; then sudo apt install -y ~/cache/<< parameters.toolchain >>/ft9xxtoolchain.deb echo "export PATH=$PATH:/opt/ft32/bin" >> $BASH_ENV else echo "export PATH=$PATH:`echo ~/cache/<< parameters.toolchain >>/*/bin`" >> $BASH_ENV fi - save_cache: name: Save Toolchain Cache key: deps-{{ checksum "toolchain_key" }} paths: - ~/cache/<< parameters.toolchain >> build: parameters: build-system: type: string toolchain: type: string family: type: string build-args: type: string default: "" steps: - checkout - run: name: Get Dependencies command: | python tools/get_deps.py << parameters.family >> # Install ninja if cmake build system if [ << parameters.build-system >> == "cmake" ]; then NINJA_URL=https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip wget $NINJA_URL -O ninja-linux.zip unzip ninja-linux.zip -d ~/bin fi # rx-gcc is 32-bit binary if [[ << parameters.toolchain >> == rx-gcc ]]; then sudo dpkg --add-architecture i386 sudo apt update sudo apt install libc6:i386 libstdc++6:i386 zlib1g:i386 fi # Install Pico SDK if [ << parameters.family >> == "rp2040" ]; then git clone --depth 1 https://github.com/raspberrypi/pico-sdk.git ~/pico-sdk echo "export PICO_SDK_PATH=~/pico-sdk" >> $BASH_ENV fi - when: condition: not: equal: [esp-idf, << parameters.toolchain >>] steps: - setup-toolchain: toolchain: << parameters.toolchain >> - run: name: Build no_output_timeout: 20m command: | if [ << parameters.toolchain >> == esp-idf ]; then docker run --rm -v $PWD:/project -w /project espressif/idf:v5.5.3 python tools/build.py << parameters.build-args >> --target all << parameters.family >> else # Toolchain option default is gcc if [ << parameters.toolchain >> == arm-clang ]; then TOOLCHAIN_OPTION="--toolchain clang" elif [ << parameters.toolchain >> == arm-iar ]; then TOOLCHAIN_OPTION="--toolchain iar" iccarm --version elif [ << parameters.toolchain >> == arm-gcc ]; then TOOLCHAIN_OPTION="--toolchain gcc" fi # circleci docker return $nproc as 36 core, limit parallel to 4 (resource-class = large) # Required for IAR, also prevent crashed/killed by docker BUILD_PY_ARGS="-s << parameters.build-system >> $TOOLCHAIN_OPTION -j 4 << parameters.build-args >> --target all" if [ << parameters.build-system >> == "cmake" ]; then BUILD_PY_ARGS="$BUILD_PY_ARGS --target tinyusb_metrics" fi python tools/build.py $BUILD_PY_ARGS << parameters.family >> fi # Only collect and persist metrics for cmake builds (excluding esp-idf and --one-random) - when: condition: and: - equal: [ cmake, << parameters.build-system >> ] - not: equal: [ esp-idf, << parameters.toolchain >> ] - not: equal: [ arm-iar, << parameters.toolchain >> ] steps: - run: name: Collect Metrics command: | # Create unique directory per toolchain to avoid workspace conflicts METRICS_DIR="/tmp/metrics/<< parameters.toolchain >>" mkdir -p "${METRICS_DIR}" # Copy all metrics.json files for f in cmake-build/cmake-build-*/metrics.json; do if [ -f "$f" ]; then BOARD_DIR=$(dirname "$f" | xargs basename) cp "$f" "${METRICS_DIR}/${BOARD_DIR}.json" fi done - persist_to_workspace: root: /tmp paths: - metrics/<< parameters.toolchain >> jobs: # Build using docker build: parameters: resource_class: type: string default: large build-system: type: string toolchain: type: string family: type: string build-args: type: string default: "" docker: - image: cimg/base:current working_directory: ~/project/tinyusb resource_class: << parameters.resource_class >> steps: - build: build-system: << parameters.build-system >> toolchain: << parameters.toolchain >> family: << parameters.family >> build-args: << parameters.build-args >> # Build using VM build-vm: parameters: resource_class: type: string default: large build-system: type: string toolchain: type: string family: type: string build-args: type: string default: "" machine: image: ubuntu-2404:current working_directory: ~/project/tinyusb resource_class: << parameters.resource_class >> steps: - build: build-system: << parameters.build-system >> toolchain: << parameters.toolchain >> family: << parameters.family >> build-args: << parameters.build-args >> # Aggregate code metrics from all builds code-metrics: docker: - image: cimg/python:3.12 resource_class: large steps: - checkout - attach_workspace: at: /tmp - run: name: Aggregate Code Metrics command: | python tools/get_deps.py # Combine all metrics files from all toolchain subdirectories ls -R /tmp/metrics if ls /tmp/metrics/*/*.json 1> /dev/null 2>&1; then python tools/metrics.py combine -j -m -f tinyusb/src /tmp/metrics/*/*.json else echo "No metrics files found" exit 1 fi - store_artifacts: path: metrics.json destination: metrics.json # Compare with base master metrics on PR branches - when: condition: not: equal: [ master, << pipeline.git.branch >> ] steps: - run: name: Download Base Branch Metrics command: | # Download metrics.json artifact from the latest successful build on master branch mkdir -p base-metrics # Use CircleCI API to get the latest artifact curl -s -L "https://dl.circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/latest/artifacts?branch=master&filter=successful" \ -H "Circle-Token: ${CIRCLE_TOKEN:-}" | \ jq -r '.items[] | select(.path == "metrics.json") | .url' | \ head -1 | xargs -I {} curl -s -L -o base-metrics/metrics.json {} || true - run: name: Compare with Base Branch command: | if [ -f base-metrics/metrics.json ]; then python tools/metrics.py compare -f tinyusb/src base-metrics/metrics.json metrics.json cat metrics_compare.md else echo "No base metrics found, skipping comparison" cp metrics.md metrics_compare.md fi - store_artifacts: path: metrics_compare.md destination: metrics_compare.md workflows: build: jobs: # The jobs below are populated dynamically by config.yml set-matrix job # Example entries that will be generated: # - build: # matrix: # alias: build-cmake-arm-gcc # parameters: # toolchain: [ 'arm-gcc' ] # build-system: [ 'cmake' ] # family: [ 'nrf' ] # resource_class: ['large'] # - code-metrics: # requires: # - build-cmake-arm-gcc