diff --git a/.github/membrowse-targets.json b/.github/membrowse-targets.json index dae01eac5b5..1e60c22bced 100644 --- a/.github/membrowse-targets.json +++ b/.github/membrowse-targets.json @@ -6,7 +6,12 @@ "elf": "bsp/stm32/stm32f407-rt-spark/rt-thread.elf", "ld": "bsp/stm32/stm32f407-rt-spark/board/linker_scripts/link.lds", "map_file": "bsp/stm32/stm32f407-rt-spark/rt-thread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/stm32/stm32f407-rt-spark", + "bsp/stm32/libraries", + "bsp/stm32/tools" + ] }, { "target_name": "stm32l475-atk-pandora-llvm", @@ -16,7 +21,12 @@ "elf": "bsp/stm32/stm32l475-atk-pandora/rt-thread.elf", "ld": "bsp/stm32/stm32l475-atk-pandora/board/linker_scripts/link.lds", "map_file": "bsp/stm32/stm32l475-atk-pandora/rt-thread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/stm32/stm32l475-atk-pandora", + "bsp/stm32/libraries", + "bsp/stm32/tools" + ] }, { "target_name": "raspberry-pico-rp2040", @@ -25,7 +35,12 @@ "elf": "bsp/raspberry-pico/RP2040/rtthread-pico.elf", "ld": "bsp/raspberry-pico/RP2040/link.ld", "map_file": "bsp/raspberry-pico/RP2040/rt-thread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/raspberry-pico/RP2040", + "bsp/raspberry-pico/libraries", + "bsp/raspberry-pico/tools" + ] }, { "target_name": "at32f415-start", @@ -34,7 +49,12 @@ "elf": "bsp/at32/at32f415-start/rtthread.elf", "ld": "bsp/at32/at32f415-start/board/linker_scripts/link.lds", "map_file": "bsp/at32/at32f415-start/rt-thread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/at32/at32f415-start", + "bsp/at32/libraries", + "bsp/at32/tools" + ] }, { "target_name": "gd32105r-start", @@ -43,7 +63,12 @@ "elf": "bsp/gd32/arm/gd32105r-start/rtthread.elf", "ld": "bsp/gd32/arm/gd32105r-start/board/linker_scripts/link.ld", "map_file": "bsp/gd32/arm/gd32105r-start/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/gd32/arm/gd32105r-start", + "bsp/gd32/arm/libraries", + "bsp/gd32/arm/tools" + ] }, { "target_name": "hc32f334", @@ -52,7 +77,13 @@ "elf": "bsp/hc32/ev_hc32f334_lqfp64/rtthread.elf", "ld": "bsp/hc32/ev_hc32f334_lqfp64/board/linker_scripts/link.ld", "map_file": "bsp/hc32/ev_hc32f334_lqfp64/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/hc32/ev_hc32f334_lqfp64", + "bsp/hc32/libraries", + "bsp/hc32/platform", + "bsp/hc32/tools" + ] }, { "target_name": "nxp-lpc1114", @@ -61,7 +92,10 @@ "elf": "bsp/nxp/lpc/lpc1114/rtthread-lpc1114.elf", "ld": "bsp/nxp/lpc/lpc1114/link.lds", "map_file": "bsp/nxp/lpc/lpc1114/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/nxp/lpc/lpc1114" + ] }, { "target_name": "nordic-nrf51822", @@ -70,7 +104,12 @@ "elf": "bsp/nrf5x/nrf51822/rt-thread.elf", "ld": "bsp/nrf5x/nrf51822/board/linker_scripts/link.lds", "map_file": "bsp/nrf5x/nrf51822/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/nrf5x/nrf51822", + "bsp/nrf5x/libraries", + "bsp/nrf5x/tools" + ] }, { "target_name": "nuvoton-m487", @@ -79,7 +118,12 @@ "elf": "bsp/nuvoton/numaker-iot-m487/rtthread.elf", "ld": "bsp/nuvoton/numaker-iot-m487/linking_scripts/m480_link.ld", "map_file": "bsp/nuvoton/numaker-iot-m487/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/nuvoton/numaker-iot-m487", + "bsp/nuvoton/libraries", + "bsp/nuvoton/tools" + ] }, { "target_name": "infineon-psoc6", @@ -88,7 +132,12 @@ "elf": "bsp/Infineon/psoc6-cy8ckit-062s4/rt-thread.elf", "ld": "bsp/Infineon/psoc6-cy8ckit-062s4/board/linker_scripts/link.ld", "map_file": "bsp/Infineon/psoc6-cy8ckit-062s4/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/Infineon/psoc6-cy8ckit-062s4", + "bsp/Infineon/libraries", + "bsp/Infineon/tools" + ] }, { "target_name": "renesas-ra2l1", @@ -97,7 +146,12 @@ "elf": "bsp/renesas/ra2l1-cpk/rtthread.elf", "ld": "bsp/renesas/ra2l1-cpk/script/fsp.ld", "map_file": "bsp/renesas/ra2l1-cpk/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/renesas/ra2l1-cpk", + "bsp/renesas/libraries", + "bsp/renesas/tools" + ] }, { "target_name": "qemu-virt64-aarch64", @@ -106,7 +160,10 @@ "elf": "bsp/qemu-virt64-aarch64/rtthread.elf", "ld": "libcpu/aarch64/link.lds", "map_file": "bsp/qemu-virt64-aarch64/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/qemu-virt64-aarch64" + ] }, { "target_name": "wch-ch32v208w-r0", @@ -115,7 +172,12 @@ "elf": "bsp/wch/risc-v/ch32v208w-r0/rtthread.elf", "ld": "bsp/wch/risc-v/ch32v208w-r0/board/linker_scripts/link.lds", "map_file": "bsp/wch/risc-v/ch32v208w-r0/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/wch/risc-v/ch32v208w-r0", + "bsp/wch/risc-v/Libraries", + "bsp/wch/risc-v/tools" + ] }, { "target_name": "hpmicro-hpm5301evklite", @@ -124,7 +186,12 @@ "elf": "bsp/hpmicro/hpm5301evklite/rtthread.elf", "ld": "bsp/hpmicro/hpm5301evklite/board/linker_scripts/gcc/flash_rtt.ld", "map_file": "", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/hpmicro/hpm5301evklite", + "bsp/hpmicro/libraries", + "bsp/hpmicro/tools" + ] }, { "target_name": "xuantie-e901plus", @@ -133,7 +200,12 @@ "elf": "bsp/xuantie/smartl/e901plus/rtthread.elf", "ld": "bsp/xuantie/libraries/xuantie_libraries/chip_riscv_dummy/gcc_flash_smartl_lite.ld", "map_file": "bsp/xuantie/smartl/e901plus/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/xuantie/smartl/e901plus", + "bsp/xuantie/libraries", + "bsp/xuantie/tools" + ] }, { "target_name": "esp32-c3", @@ -142,7 +214,12 @@ "elf": "bsp/ESP/ESP32_C3/rtthread.elf", "ld": "bsp/ESP/ESP32_C3/idf_port/ld/memory.ld bsp/ESP/ESP32_C3/idf_port/ld/sections.ld", "map_file": "bsp/ESP/ESP32_C3/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/ESP/ESP32_C3", + "bsp/ESP/libraries", + "bsp/ESP/tools" + ] }, { "target_name": "k230", @@ -151,7 +228,10 @@ "elf": "bsp/k230/rtthread.elf", "ld": "bsp/k230/link.lds.generated", "map_file": "bsp/k230/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/k230" + ] }, { "target_name": "loongson-ls1cdev", @@ -160,7 +240,10 @@ "elf": "bsp/loongson/ls1cdev/rtthread.elf", "ld": "bsp/loongson/ls1cdev/ls1c_ram.lds", "map_file": "bsp/loongson/ls1cdev/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/loongson/ls1cdev" + ] }, { "target_name": "x86", @@ -169,7 +252,10 @@ "elf": "bsp/x86/rtthread.elf", "ld": "bsp/x86/x86_ram.lds", "map_file": "bsp/x86/rtthread.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/x86" + ] }, { "target_name": "simulator", @@ -178,6 +264,9 @@ "elf": "bsp/simulator/rtthread", "ld": "bsp/simulator/gcc_elf64.ld", "map_file": "bsp/simulator/rtthread-linux.map", - "linker_vars": "" + "linker_vars": "", + "watch_paths": [ + "bsp/simulator" + ] } ] diff --git a/.github/workflows/membrowse-report.yml b/.github/workflows/membrowse-report.yml index 98ea34ea862..79c88db8413 100644 --- a/.github/workflows/membrowse-report.yml +++ b/.github/workflows/membrowse-report.yml @@ -2,9 +2,35 @@ name: MemBrowse Memory Report on: pull_request: + paths: + - '.github/membrowse-targets.json' + - '.github/workflows/membrowse-report.yml' + - 'Kconfig' + - 'bsp/**' + - 'components/**' + - 'include/**' + - 'libcpu/**' + - 'src/**' + - 'tools/**' + - '!**/README*' + - '!**/readme*' + - '!bsp/**/docs/**' push: branches: - master + paths: + - '.github/membrowse-targets.json' + - '.github/workflows/membrowse-report.yml' + - 'Kconfig' + - 'bsp/**' + - 'components/**' + - 'include/**' + - 'libcpu/**' + - 'src/**' + - 'tools/**' + - '!**/README*' + - '!**/readme*' + - '!bsp/**/docs/**' concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }} @@ -15,16 +41,31 @@ jobs: runs-on: ubuntu-22.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} + target_count: ${{ steps.set-matrix.outputs.target_count }} steps: - name: Checkout repository uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Collect changed files + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + git diff --name-only origin/${{ github.base_ref }}...HEAD > changed_files.txt + elif [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then + git diff --name-only ${{ github.event.before }} ${{ github.sha }} > changed_files.txt + else + git diff --name-only origin/${{ github.event.repository.default_branch }}...HEAD > changed_files.txt + fi + cat changed_files.txt - name: Load target matrix id: set-matrix - run: echo "matrix=$(jq -c '.' .github/membrowse-targets.json)" >> $GITHUB_OUTPUT + run: python3 tools/ci/membrowse_filter_targets.py --targets .github/membrowse-targets.json --changed-files changed_files.txt analyze: needs: load-targets + if: ${{ needs.load-targets.outputs.target_count != '0' }} runs-on: ubuntu-22.04 strategy: fail-fast: false diff --git a/tools/ci/membrowse_filter_targets.py b/tools/ci/membrowse_filter_targets.py new file mode 100644 index 00000000000..626adade8e3 --- /dev/null +++ b/tools/ci/membrowse_filter_targets.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 +# + +import argparse +import json +import os + + +GLOBAL_FILES = { + ".github/membrowse-targets.json", + ".github/workflows/membrowse-report.yml", + "Kconfig", +} + +GLOBAL_PREFIXES = ( + "components/", + "include/", + "libcpu/", + "src/", + "tools/", +) + +def normalize_path(path): + return path.replace("\\", "/").strip().lstrip("./") + + +def is_ignored_change(path): + name = path.rsplit("/", 1)[-1] + return name.lower().startswith("readme") or (path.startswith("bsp/") and "/docs/" in path) + + +def is_under(path, prefix): + path = normalize_path(path) + prefix = normalize_path(prefix).rstrip("/") + return path == prefix or path.startswith(prefix + "/") + + +def read_changed_files(path): + with open(path, "r", encoding="utf-8") as file: + return [ + changed_file + for changed_file in (normalize_path(line) for line in file if line.strip()) + if not is_ignored_change(changed_file) + ] + + +def is_global_change(path): + return path in GLOBAL_FILES or any(path.startswith(prefix) for prefix in GLOBAL_PREFIXES) + + +def target_related_prefixes(target): + return [ + normalize_path(prefix) + for prefix in target.get("watch_paths", [target["bsp_path"]]) + ] + + +def select_targets(targets, changed_files): + if not changed_files: + return [] + + if any(is_global_change(path) for path in changed_files): + return targets + + selected = [] + for target in targets: + related_prefixes = target_related_prefixes(target) + if any( + is_under(changed_file, related_prefix) + for changed_file in changed_files + for related_prefix in related_prefixes + ): + selected.append(target) + + return selected + + +def write_github_output(matrix, output_path): + if not output_path: + return + + with open(output_path, "a", encoding="utf-8") as output: + output.write("matrix={}\n".format(json.dumps(matrix, separators=(",", ":")))) + output.write("target_count={}\n".format(len(matrix))) + + +def main(): + parser = argparse.ArgumentParser(description="Filter MemBrowse targets by changed files.") + parser.add_argument("--targets", required=True, help="Path to membrowse-targets.json.") + parser.add_argument("--changed-files", required=True, help="Path to a newline-delimited changed file list.") + args = parser.parse_args() + + with open(args.targets, "r", encoding="utf-8") as file: + targets = json.load(file) + + changed_files = read_changed_files(args.changed_files) + selected_targets = select_targets(targets, changed_files) + + print("Changed files:") + for changed_file in changed_files: + print(" {}".format(changed_file)) + + print("Selected MemBrowse targets:") + for target in selected_targets: + print(" {}".format(target["target_name"])) + + print("Selected {}/{} MemBrowse targets".format(len(selected_targets), len(targets))) + write_github_output(selected_targets, os.getenv("GITHUB_OUTPUT")) + + +if __name__ == "__main__": + main()