A tool to clean up sysroots for Linux embedded devices to save storage space.
Find a file
2025-09-08 17:12:57 +02:00
LICENSES Initial skeleton 2025-01-24 22:00:18 +01:00
src fix(dso): allow specifying extra lib folders on the cmdline 2025-06-02 19:04:14 +02:00
.editorconfig Initial skeleton 2025-01-24 22:00:18 +01:00
.gitignore feat(cleaners): add main async runner 2025-01-25 02:32:21 +01:00
.pre-commit-config.yaml chore(.pre-commit-config.yaml): add cargo deny to pre-commit, bump deps 2025-09-08 17:02:03 +02:00
build.rs Initial skeleton 2025-01-24 22:00:18 +01:00
Cargo.lock chore(Cargo.toml): update deps 2025-09-08 17:10:42 +02:00
Cargo.lock.license Initial skeleton 2025-01-24 22:00:18 +01:00
Cargo.toml chore(Cargo.toml): add missing description 2025-09-08 17:12:57 +02:00
deny.toml chore(deny.toml): add linter config for cargo 2025-09-08 17:02:03 +02:00
README.md chore(README.md): write proper README documentation 2025-09-08 17:02:03 +02:00
rust-toolchain.toml chore(deny.toml): add linter config for cargo 2025-09-08 17:02:03 +02:00

[//]: # SPDX-FileCopyrightText: Matteo Settenvini matteo.settenvini@montecristosoftware.eu

[//]: # SPDX-License-Identifier: CC-BY-SA-4.0

🧹 Sysroot Cleaner

A tool to clean up sysroots for Linux embedded devices in order to save storage space.

Here by sysroot we mean the final target system filesystem, rather than the staging folder potentially containing intermediate cross-compilation byproducts.

What does it do?

Sysroot cleaner is a simple tool used to remove unnecessary files from a target folder which is holding the filesystem of an ELF-based OS (such as Linux). This can for instance be either a cross-compiled device target tree, or a folder being prepared for a local chroot jail. It recurses across all subfolders part of the same filesystem and looks for files that can be safely removed to reduce space usage.

The full list of found files is passed to a few modules (aka "cleaners") that can decide whether to keep or remove a specific file. These are:

  • dso: maps all ELF files and their library dependencies to a directed acyclic graph. For each library, remove it transitively if unreachanble from any executable binary. Note: Libraries that are dynamically opened at runtime need to be manually allow-listed. If there is interest, we might support .note.dlopen as it gains more widespread adoption.
  • allow-/block-list: given a file of gitignore patterns, either mark the file for keeping (if in the allowlist) or for removal (if in the blocklist).

Commandline Options

Usage: sysroot-cleaner [option…] <sysroot>, where <sysroot> is mandatory, and the path to the root of the sysroot to clean up.

Options can be:

  • -n, --dry-run: Simulate operations without carrying them out.
  • --split-to <dir>: Instead of simply removing files, move them to the given location, preserving their relative folder structure.
  • --allowlist <file>: An allowlist of files to keep, in .gitignore format. Can be passed multiple times. Note: this will take precedence over all other removal decisions.
  • --blocklist <file>: A blocklist of files to remove, in .gitignore format. Can be passed multiple times.
  • --output-dotfile <file>: An optional path to save the file graph of the DSO cleaner in GraphViz format. Useful for debugging.
  • --ld-path <dir>: An additional path to consider when resolving libraries, relative to the sysroot root. Its behavior is similar of the one of the LD_LIBRARY_PATH environment variable when specified to the dynamic linker.

The log level can be controlled via the LOG_LEVEL environment variable, and can be one of: error, warn, info, debug, trace, or off (run completely silent).

Example Usage

Assume that you have built a filesystem image, for instance through a tool like buildroot.

You could add a simple shell script to invoke sysroot-cleaner:

#!/bin/bash

# file: post_build.sh

set -e -o pipefail

readonly SCRIPT_DIR=$(realpath "$(dirname $0)")
readonly TARGET_DIR=$1

if [ ! -d "${TARGET_DIR}" ]; then
    echo "Expecting the rootfs folder as first argument"
    exit 1
fi

# Base lists
allow_lists=("${SCRIPT_DIR}/base.allowlist")
block_lists=("${SCRIPT_DIR}/base.blocklist")

LOG_LEVEL=info sysroot-cleaner \
    $(printf -- '--allowlist %s ' "${allow_lists[@]}") \
    $(printf -- '--blocklist %s ' "${block_lists[@]}") \
    "${TARGET_DIR}"

Then, you can set BR2_ROOTFS_POST_BUILD_SCRIPT to invoke post_build.sh.

Changelog

v1.0.0

  • Initial stable release.