Skip to main content

Multiple Targets

Track memory across different boards, build types, or feature variants. Each target maintains its own independent history in the MemBrowse Portal.

Examples of targets:

  • Different boards: stm32f4, nrf52840, esp32
  • Build types: debug, release
  • Feature variants: with-bluetooth, minimal

Targets Configuration

Create .github/membrowse-targets.json to define your build targets:

{
"targets": [
{
"name": "stm32f4",
"elf": "build/stm32f4/firmware.elf",
"ld": "boards/stm32f4/linker.ld",
"build_script": "make TARGET=stm32f4"
},
{
"name": "nrf52840",
"elf": "build/nrf52840/firmware.elf",
"ld": "boards/nrf52840/linker.ld boards/nrf52840/memory.ld",
"build_script": "make TARGET=nrf52840"
}
]
}

Target Properties

PropertyRequiredDescription
nameYesUnique identifier for this target
elfYesPath to the ELF file after building
ldNoPath to linker script(s), space-separated if multiple. If omitted, MemBrowse uses default Code/Data regions based on ELF section flags.
build_scriptYesCommand to build this target
defNoLinker script variable definitions (e.g., "__flash_size__=4096K __ram_size__=256K")

Multiple Linker Scripts

If your project uses multiple linker scripts, list them space-separated:

{
"name": "my-target",
"elf": "build/firmware.elf",
"ld": "memory.ld sections.ld board.ld"
}

MemBrowse will parse all scripts to understand your complete memory layout.

Linker Variable Definitions

Use the def property to pass variable definitions to the linker script parser. This is useful when your linker scripts use variables for memory sizes:

{
"name": "stm32f4",
"elf": "build/firmware.elf",
"ld": "linker.ld",
"build_script": "make all",
"def": "__flash_size__=512K __ram_size__=128K"
}

In the workflow, pass this to the action:

      - name: Analyze memory
uses: membrowse/membrowse-action@v1
with:
elf: ${{ matrix.target.elf }}
ld: ${{ matrix.target.ld }}
target_name: ${{ matrix.target.name }}
api_key: ${{ secrets.MEMBROWSE_API_KEY }}
linker_vars: ${{ matrix.target.def }}

Full Workflow with Matrix Builds

With multiple targets, each target runs as a separate matrix job. Unlike the single-target setup where the comment step runs inline, here the reports from each matrix job need to be collected into one place first. Each analyze job uploads its report as an artifact:

      - name: Upload report artifact
if: ${{ steps.analyze.outcome == 'success' }}
uses: actions/upload-artifact@v4
with:
name: membrowse-report-${{ matrix.target.name }}
path: ${{ steps.analyze.outputs.report_path }}

Then a separate comment job downloads all the artifacts and aggregates them into a single PR comment:

  comment:
needs: analyze
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v5

- name: Download report artifacts
uses: actions/download-artifact@v4
with:
pattern: membrowse-report-*
path: reports
merge-multiple: true

- name: Post PR comment
uses: membrowse/membrowse-action/comment-action@v1
with:
json_files: "reports/*.json"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Complete workflow

Here's the full workflow. Create .github/workflows/membrowse.yml:

name: MemBrowse Memory Report
on:
pull_request:
push:
branches:
- main

permissions:
contents: read
pull-requests: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
setup:
runs-on: ubuntu-latest
outputs:
targets: ${{ steps.read-targets.outputs.targets }}
steps:
- uses: actions/checkout@v5

- name: Read targets configuration
id: read-targets
run: echo "targets=$(jq -c '.targets' .github/membrowse-targets.json)" >> $GITHUB_OUTPUT

analyze:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
target: ${{ fromJson(needs.setup.outputs.targets) }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

# Add your build tool setup here (e.g., arm-none-eabi-gcc)

- name: Build ${{ matrix.target.name }}
run: ${{ matrix.target.build_script }}

- name: Analyze memory
id: analyze
continue-on-error: true
uses: membrowse/membrowse-action@v1
with:
elf: ${{ matrix.target.elf }}
ld: ${{ matrix.target.ld }}
target_name: ${{ matrix.target.name }}
api_key: ${{ secrets.MEMBROWSE_API_KEY }}
linker_vars: ${{ matrix.target.def }}

- name: Upload report artifact
if: ${{ steps.analyze.outcome == 'success' }}
uses: actions/upload-artifact@v4
with:
name: membrowse-report-${{ matrix.target.name }}
path: ${{ steps.analyze.outputs.report_path }}

comment:
needs: analyze
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v5

- name: Download report artifacts
uses: actions/download-artifact@v4
with:
pattern: membrowse-report-*
path: reports
merge-multiple: true

- name: Post PR comment
uses: membrowse/membrowse-action/comment-action@v1
with:
json_files: "reports/*.json"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

For multiple targets, all results appear in a single PR comment.

Action Reference

Inputs (membrowse/membrowse-action@v1)

InputRequiredDescription
elfNoPath to ELF file. Not required when identical=true.
ldNoPath to linker script(s), space-separated. If omitted, MemBrowse uses default Code/Data regions based on ELF section flags.
target_nameYesName for this build target
api_keyNoMemBrowse API key. Not required for tokenless uploads in open-source projects.
api_urlNoMemBrowse API base URL (default: https://api.membrowse.com)
linker_varsNoLinker script variable definitions, space-separated (e.g., __flash_size__=4096K __ram_size__=256K)
dont_fail_on_alertsNoDon't exit with failure on budget alerts (default: false)
verboseNoLogging level: WARNING, INFO, or DEBUG (default: WARNING)
pr_author_nameNoPR author name (auto-detected from GitHub event if not provided)
pr_author_emailNoPR author email (auto-detected from GitHub event if not provided)
identicalNoMetadata-only upload, skip ELF analysis (default: false)

Outputs

OutputDescription
report_pathFile path to the generated JSON report file

Inputs (membrowse/membrowse-action/comment-action@v1)

The comment-action supports two modes: file mode (reads local JSON report files) and summary mode (fetches per-target summaries from the MemBrowse API). Use file mode for standard workflows, and summary mode when report artifacts aren't available (e.g., cross-workflow comments).

InputRequiredDescription
json_filesNoSpace-separated list of JSON report file paths or glob pattern (e.g., reports/*.json). Used in file mode.
api_keyNoMemBrowse API key. When provided with commit, enables summary mode — fetches data from the API instead of reading JSON files.
commitNoCommit SHA to fetch summary for. Requires api_key.
pr_numberNoPR number to post comment on. Auto-detected from GitHub event context or commit SHA if not provided.
api_urlNoMemBrowse API base URL (default: https://api.membrowse.com)
comment_templateNoPath to a custom Jinja2 template file for comment formatting

Either json_files or both api_key + commit must be provided.