fastfetch: add new package#29564
Conversation
Fastfetch is a neofetch-like tool for fetching system information and displaying it in a visually appealing way. Signed-off-by: Albrecht Lohofener <albrecht@albrechtloh.de>
|
I just realized that there is already an open PR for this tool here: #27949 |
|
I'm not sure I understand the utility of these tools on mostly-embedded hardware. |
|
I am on this one with @GeorgeSapkin. Similar PR for fancy tool to vizualize system details is this one: #29292 (review) I understand that three users are interested in adding neofetch/fastfetch/afetch, but all of these are just "super cool" gadgets that display system information. Do we really need to have all three of these programs here in the repository? We are not in a position where we can prioritize quantity over quality. I would be more interested in knowing why we should add this package. We can get this system information via the CLI, it's displayed in LuCI, and either way, if a user wants to install this package, they have to do it through LuCI or the CLI. And since there is no LuCI interface for any of these packages, we are left only with the CLI. In general, for all pull requests that add these kinds of "super cool" gadgets, I would like to know how much space they take up on the router. Let's keep in mind that adding a single package here means the OpenWrt buildbots will compile it for all platforms. This not only increases build time but also consumes resources for building the package—not just within OpenWrt, but for downstream distributions as well. I definitely don't want to discourage you from adding packages or contributing to OpenWrt, but we need to look at the bigger picture and consider whether this package is truly useful. Keep in mind that OpenWrt supports devices with as little as 16 MB flash and 128 MB RAM, and this is Rust. |
|
@BKPepe @GeorgeSapkin food for thought: I've built this for x86_64, the APK size is about 500kb, the installed binary size on device is ~ 1.3Mb. Honestly, for the output it provides, this is a shell script Claude generated which does the same and takes ~6kb: #!/bin/sh
# Lightweight fastfetch-style system info for OpenWrt.
if [ -t 1 ] && [ "${TERM:-dumb}" != dumb ]; then
C_BLUE=$(printf '\033[1;34m')
C_GREEN=$(printf '\033[32m')
C_YELLOW=$(printf '\033[93m')
C_RED=$(printf '\033[91m')
C_RST=$(printf '\033[0m')
else
C_BLUE= C_GREEN= C_YELLOW= C_RED= C_RST=
fi
pct_color() {
if [ "$1" -gt 80 ]; then printf '%s' "$C_RED"
elif [ "$1" -gt 50 ]; then printf '%s' "$C_YELLOW"
else printf '%s' "$C_GREEN"
fi
}
logo() {
cat <<'EOF'
_______
| |.-----.-----.-----.
| - || _ | -__| |
|_______|| __|_____|__|__|
|__|
________ __
| | | |.----.| |_
| | | || _|| _|
|________||__| |____|
EOF
}
human_kib() {
awk -v k="$1" 'BEGIN{
if (k >= 1048576) printf "%.2f GiB", k/1048576
else printf "%.2f MiB", k/1024
}'
}
get_user_host() { printf '%s@%s' "$(id -un)" "$(uname -n)"; }
get_os() {
if [ -r /etc/os-release ]; then
. /etc/os-release
printf '%s %s %s' "${NAME:-Linux}" "${VERSION:-}" "$(uname -m)"
else
printf '%s %s' "$(uname -s)" "$(uname -m)"
fi
}
get_host() {
model= rev=
if [ -r /tmp/sysinfo/model ]; then
model=$(cat /tmp/sysinfo/model)
elif [ -r /sys/devices/virtual/dmi/id/product_name ]; then
model=$(cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null)
rev=$(cat /sys/devices/virtual/dmi/id/product_version 2>/dev/null)
else
model=$(uname -n)
fi
case "$rev" in ''|'Not Specified'|'To be filled by O.E.M.') rev= ;; esac
if [ -n "$rev" ]; then printf '%s (%s)' "$model" "$rev"
else printf '%s' "$model"; fi
}
get_kernel() { printf '%s %s' "$(uname -s)" "$(uname -r)"; }
get_uptime() {
up=$(awk '{print int($1)}' /proc/uptime)
d=$((up/86400)); h=$(((up%86400)/3600)); m=$(((up%3600)/60))
out=
[ $d -gt 0 ] && { s=; [ $d -ne 1 ] && s=s; out="$d day$s"; }
[ $h -gt 0 ] && { s=; [ $h -ne 1 ] && s=s; out="${out:+$out, }$h hour$s"; }
[ $m -gt 0 ] && { s=; [ $m -ne 1 ] && s=s; out="${out:+$out, }$m min$s"; }
printf '%s' "${out:-less than a minute}"
}
get_packages() {
if command -v apk >/dev/null 2>&1; then
printf '%d (apk)' "$(apk list --installed 2>/dev/null | wc -l)"
elif command -v opkg >/dev/null 2>&1; then
printf '%d (opkg)' "$(opkg list-installed 2>/dev/null | wc -l)"
elif command -v dpkg-query >/dev/null 2>&1; then
printf '%d (dpkg)' "$(dpkg-query -f '.\n' -W 2>/dev/null | wc -l)"
fi
}
get_shell() {
sh=${SHELL##*/}
case "$sh" in
bash) printf 'bash %s' "$(bash -c 'echo "$BASH_VERSION"' 2>/dev/null | cut -d'(' -f1)" ;;
*) printf '%s' "${sh:-unknown}" ;;
esac
}
get_terminal() {
t=$(tty 2>/dev/null)
[ -n "$t" ] && [ "$t" != 'not a tty' ] && printf '%s' "$t" || printf '%s' "${TERM:-unknown}"
}
get_cpu() {
model=$(awk -F: '/^model name/{sub(/^ +/,"",$2); print $2; exit}' /proc/cpuinfo)
[ -z "$model" ] && model=$(awk -F: '/^(Hardware|Model|Processor)/{sub(/^ +/,"",$2); print $2; exit}' /proc/cpuinfo)
[ -z "$model" ] && [ -r /sys/firmware/devicetree/base/model ] && model=$(tr -d '\0' < /sys/firmware/devicetree/base/model)
[ -z "$model" ] && model=$(uname -p 2>/dev/null) && [ "$model" = unknown ] && model=$(uname -m)
cores=$(grep -c '^processor' /proc/cpuinfo)
if [ -r /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]; then
freq=$(awk '{printf "%.2f", $1/1000000}' /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq)
else
freq=$(awk -F: '/cpu MHz/{printf "%.2f", $2/1000; exit}' /proc/cpuinfo)
fi
if [ -n "$freq" ]; then printf '%s (%d) @ %s GHz' "$model" "$cores" "$freq"
else printf '%s (%d)' "$model" "$cores"; fi
}
get_memory() {
awk -v cg="$C_GREEN" -v cy="$C_YELLOW" -v cr="$C_RED" -v ce="$C_RST" '
/^MemTotal:/ {t=$2}
/^MemAvailable:/ {a=$2}
END {
u=t-a; p=(u*100)/t
if (t >= 1048576) th=sprintf("%.2f GiB", t/1048576); else th=sprintf("%.2f MiB", t/1024)
if (u >= 1048576) uh=sprintf("%.2f GiB", u/1048576); else uh=sprintf("%.2f MiB", u/1024)
c = (p > 80 ? cr : (p > 50 ? cy : cg))
printf "%s / %s (%s%d%%%s)", uh, th, c, p, ce
}' /proc/meminfo
}
get_swap() {
total=$(awk '/^SwapTotal:/{print $2}' /proc/meminfo)
if [ -z "$total" ] || [ "$total" -eq 0 ]; then
printf 'Disabled'
return
fi
free=$(awk '/^SwapFree:/{print $2}' /proc/meminfo)
used=$((total-free))
pct=$((used*100/total))
printf '%s / %s (%s%d%%%s)' "$(human_kib "$used")" "$(human_kib "$total")" "$(pct_color "$pct")" "$pct" "$C_RST"
}
get_disks() {
df -P -T 2>/dev/null | awk -v cg="$C_GREEN" -v cy="$C_YELLOW" -v cr="$C_RED" -v ce="$C_RST" '
NR>1 && $2 !~ /^(tmpfs|devtmpfs|squashfs|overlay|proc|sysfs|cgroup|rootfs|none|efivarfs|debugfs|tracefs|fusectl|configfs|pstore|bpf|securityfs|hugetlbfs|mqueue|autofs|binfmt_misc)$/ && $7 ~ /^\// {
u=$4; t=$3
if (u >= 1048576) uh=sprintf("%.2f GiB", u/1048576); else uh=sprintf("%.2f MiB", u/1024)
if (t >= 1048576) th=sprintf("%.2f GiB", t/1048576); else th=sprintf("%.2f MiB", t/1024)
p=$6; gsub(/%/,"",p); p=p+0
c = (p > 80 ? cr : (p > 50 ? cy : cg))
printf "Disk (%s)\t%s / %s (%s%s%s) - %s\n", $7, uh, th, c, $6, ce, $2
}'
}
get_local_ips() {
command -v ip >/dev/null 2>&1 || return
ip -o -4 addr show scope global 2>/dev/null | awk '{printf "Local IP (%s)\t%s\n", $2, $4}'
}
get_locale() { printf '%s' "${LANG:-${LC_ALL:-C}}"; }
build_info() {
uh=$(get_user_host)
user=${uh%@*}; host=${uh#*@}
printf '%s%s%s@%s%s%s\n' "$C_BLUE" "$user" "$C_RST" "$C_BLUE" "$host" "$C_RST"
printf '%s\n' "$uh" | sed 's/./-/g'
printf '%sOS%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_os)"
printf '%sHost%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_host)"
printf '%sKernel%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_kernel)"
printf '%sUptime%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_uptime)"
p=$(get_packages); [ -n "$p" ] && printf '%sPackages%s\t%s\n' "$C_BLUE" "$C_RST" "$p"
printf '%sShell%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_shell)"
printf '%sTerminal%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_terminal)"
printf '%sCPU%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_cpu)"
printf '%sMemory%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_memory)"
printf '%sSwap%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_swap)"
get_disks | awk -v c="$C_BLUE" -v e="$C_RST" -F'\t' '{printf "%s%s%s\t%s\n", c, $1, e, $2}'
get_local_ips | awk -v c="$C_BLUE" -v e="$C_RST" -F'\t' '{printf "%s%s%s\t%s\n", c, $1, e, $2}'
printf '%sLocale%s\t%s\n' "$C_BLUE" "$C_RST" "$(get_locale)"
}
fastfetch() {
LOGO=$(logo)
INFO=$(build_info)
logo_w=$(printf '%s\n' "$LOGO" | awk '{ if (length>m) m=length } END{print m+0}')
pad=4
gap=$(printf '%*s' "$pad" '')
n_logo=$(printf '%s\n' "$LOGO" | wc -l)
n_info=$(printf '%s\n' "$INFO" | wc -l)
n=$n_logo; [ "$n_info" -gt "$n" ] && n=$n_info
i=1
while [ "$i" -le "$n" ]; do
l=$(printf '%s\n' "$LOGO" | sed -n "${i}p")
r=$(printf '%s\n' "$INFO" | sed -n "${i}p")
case "$r" in
*" "*)
label=${r%% *}
value=${r#* }
r="${label}: ${value}"
;;
esac
printf "%s%-${logo_w}s%s%s%s\n" "$C_BLUE" "$l" "$C_RST" "$gap" "$r"
i=$((i+1))
done
}
fastfetch "$@"throw it into /etc/profile.d/ and/or create a Makefile which installs it into /usr/bin/. Even if it's missing any non-x86_64 bits and grows to double the size, it's still peanuts compared to compiled binary. |
|
@BKPepe I'd like to chime in as the author of the pervious fastfetch PR (#27949). I understand the reasoning behind keeping out the software that is impractical to waste the limited resources of a small home router on. But, looking at the broad range of capabilities that OpenWrt offers and the kind of packages already existing in the repo, I think that ship has sailed long ago. For many users, OpenWrt has become another lean server Linux, even if still specialized to a large extent. In that case, why not let in optional software which is found in other distros? As for the extra load on the build system, that is yet another consequence of the project outgrowing its original purpose. Along with the apparent lack of resources to deal with PRs and bug reports, sadly. |
|
I think you missed the core point of my question: why is this package actually useful on a router? If we look at how these tools are handled in other distributions, it tells a clear story:
This clearly shows that the lack of maintainers isn't just an OpenWrt problem—it’s a widespread issue for these specific packages. Regarding your point about PRs and issues: anyone has the opportunity to contribute to OpenWrt and help others, including by doing code reviews. It’s easy to point fingers and complain about the project's shortcomings just because you want to get a specific package merged. But we are all volunteers here. If you want to lend a hand and help us review or maintain things, we’d more than welcome it. TL;DR: As we can see from the great example by @stangri, a simple shell script is a much better and more efficient solution for anyone who just wants a "super cool" gimmick in their CLI. It completely bypasses the need for constant maintenance, compilation, and wasting precious storage space on the router. |
On a simple home router which is only configured via LuCi, not useful at all. On more of a regular server, which OpenWrt is capable of being, fastfetch-like tools can be genuinely helpful despite their reputation of being cosmetic. I visit my OpenWrt box over SSH quite often and it's nice to see stats like uptime and IP addresses on login. Sure, a script can do the same, but neofetch/fastfetch is also highly customizable and available on different platforms. I use it everywhere there is a shell available to get the same output presented in the same familiar way.
I'm sorry my words came across as finger-pointing. I only wanted to contrast the "basic router as the primary target platform" argument with the actual scale and scalability of OpenWrt, which is the reason behind the shortage of both maintainer time and build infrastructure resources. |
It's unfair bro... You used |
root@OpenWrt:~# ls -lh /usr/bin/awk /usr/bin/cut /bin/cat /bin/grep /bin/sh /bin/uname /sbin/ip
lrwxrwxrwx 1 root root 7 May 25 10:41 /bin/cat -> busybox
lrwxrwxrwx 1 root root 7 May 25 10:41 /bin/grep -> busybox
lrwxrwxrwx 1 root root 7 May 25 10:41 /bin/sh -> busybox
lrwxrwxrwx 1 root root 7 May 25 10:41 /bin/uname -> busybox
lrwxrwxrwx 1 root root 14 May 25 10:41 /sbin/ip -> ../bin/busybox
lrwxrwxrwx 1 root root 17 May 25 10:41 /usr/bin/awk -> ../../bin/busybox
lrwxrwxrwx 1 root root 17 May 25 10:41 /usr/bin/cut -> ../../bin/busybox |
|
You are ABSOLUTELY correct! I'm suggesting removing bash from openwrt's packages list as it has MUCH MORE red entries. It MUST cost MUCH trouble to MOST distros! (except rolling distros, I guess) |
So let's count busybox in? |
Brilliant! Let's replace Busybox - the default shell - with this: root@OpenWrt:/tmp# ls -lh /usr/bin/fastfetch /bin/busybox
-rwxr-xr-x 1 root root 396.0K May 25 10:41 /bin/busybox
-rwxr-xr-x 1 root root 1.2M May 26 18:34 /usr/bin/fastfetch |
|
Brilliant! Busybox must not have any dependency (except libc) like fastfetch
Maybe it is time for fastfetch to lose weight by removing these hundreds of brilliant distro logos |
root@OpenWrt:/tmp# ldd `which busybox`
/lib/ld-musl-x86_64.so.1 (0x7febbe5ee000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x7febbe5c8000)
libc.so => /lib/ld-musl-x86_64.so.1 (0x7febbe5ee000)
root@OpenWrt:/tmp# ldd `which fastfetch`
/lib/ld-musl-x86_64.so.1 (0x7f4832621000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x7f48325fb000)
libc.so => /lib/ld-musl-x86_64.so.1 (0x7f4832621000) |
This comment was marked as low quality.
This comment was marked as low quality.
|
Although there's no way I'm installing a 1.3MB binary on even my x86 devices, I can indeed see the utility in this. I too ssh into a dozen+ machines every day, and on almost all of them I've got an Xfetch variant called in their .profile/.bashrc/.cshrc, just so I can get my bearings on login. On my OpenWrt boxes, I usually just hack together something in |
|
Thanks everyone for the feedback and discussion. I’d like to refocus on the main question: what are the actual requirements for getting a package like
Regarding maintenance: based on the discussion so far, there are already at least two people willing to maintain an OpenWrt package for Package size could be a more reasonable concern. However, I tested I also understand the concern about increased build and CI time as more packages are added. That said, this is a general challenge that applies to all new packages, and I am not aware of this being used as a reason to reject otherwise maintained and functional additions in the past. @CarterLi, thanks for joining the discussion as the @GeorgeSapkin, @BKPepe, I also appreciate your perspective, especially given your experience working with OpenWrt packages on a regular basis. |
|
@AlbrechtL In case the repo maintainers allow fastfetch in, I don't mind closing my original PR (#27949) in favor of yours and you becoming the package maintainer. Just a few suggestions about the makefile. I'm pretty sure you don't need these. My makefile included all of the build time dependencies available on OpenWrt. Yours disabled some of them explicitly. I also included the shell completions in the install section, which are only a few dozen extra KB, because I didn't see a reason to try trimming the package that ends up "bloated" anyways. Maybe except for the OS logos as suggested before if that would lead to any meaningful savings. |
|
@betonmischer86 We can also do it the other way around ;-). You put more work in your PR than I did. |
|
@betonmischer86 @AlbrechtL CMake options You can use Note: these options rely on LTO for dead code elimination ( Most In addition, unneeded ASCII logos can now be removed without modifying fastfetch's code. Just run Please test if these options work for you and how much binary size can be reduced. Thanks. Other tips:
|
|
@betonmischer86 Do you want to add @CarterLi recommendations in your PR #27949? |
|
@CarterLi I followed your recommendations and I have a local binary size of 588 kB on a mips32 CPU now (only on my local machine, I didn't update this PR). @BKPepe, @GeorgeSapkin Which binary size would acceptable for you? |
|
@CarterLi Thank you for your directions and the effort to make fastfetch leaner. I revised the build dependencies and cmake options according to what is available on OpenWrt. Do you mind checking if any of the
Isn't it possible for the user to have Nix or Homebrew installed, though? @AlbrechtL To keep the discussion in one place, I uploaded the updated Makefile as a gist for now rather than updating my PR. Also, numbered releases are prioritized over tags and git commits. We should probably wait until the next release of fastfetch is out. https://gist.github.com/betonmischer86/0f3472f8df409e22a265a92fb286c16f |
|
@CarterLi I also find the current OpenWrt logo ugly and not matching the design on the official website and OpenWrt One boards. Is there any chance someone is willing to create a new one? |
| PKG_MAINTAINER:=Albrecht Lohofener <albrecht@albrechtloh.de> | ||
| PKG_LICENSE:=MIT | ||
| PKG_LICENSE_FILES:=LICENSE | ||
| PKG_CPE_ID:=cpe:/a:fastfetch-cli:fastfetch | ||
| PKG_BUILD_PARALLEL:=1 | ||
|
|
||
| CMAKE_INSTALL:=1 |
There was a problem hiding this comment.
| PKG_MAINTAINER:=Albrecht Lohofener <albrecht@albrechtloh.de> | |
| PKG_LICENSE:=MIT | |
| PKG_LICENSE_FILES:=LICENSE | |
| PKG_CPE_ID:=cpe:/a:fastfetch-cli:fastfetch | |
| PKG_BUILD_PARALLEL:=1 | |
| CMAKE_INSTALL:=1 | |
| PKG_MAINTAINER:=Albrecht Lohofener <albrecht@albrechtloh.de> | |
| PKG_LICENSE:=MIT | |
| PKG_LICENSE_FILES:=LICENSE | |
| PKG_CPE_ID:=cpe:/a:fastfetch-cli:fastfetch |
- parallel build is enabled by default for cmake
- there's nothing worthy to be installed to staging_dir
| $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/fastfetch $(1)/usr/bin/ | ||
| endef | ||
|
|
||
| $(eval $(call BuildPackage,fastfetch)) No newline at end of file |
There was a problem hiding this comment.
missing new line at the end of the file
|
People are always paying for those "beautiful" (but useless) things xD |
|
A new version has been cut.
Some suggestions: |
|
@CarterLi thank you. I updated the makefile draft. Not sure how the OpenWrt buildbots handle this, but, when building the whole OpenWrt image, fastfetch will still pick up the libraries already compiled for the target. So, I kept the The binary is now 769K on aarch64. @AlbrechtL @GeorgeSapkin Let us know if the package now meets the criteria for inclusion and I'll update my old PR (#27949). |
|
BTW, an updated OpenWrt logo got merged. |
|
@betonmischer86 Unneeded packages managers should be disabled with You may use @GeorgeSapkin's brilliant cmake . -L 2>/dev/null | awk -F'[:=]' '$1 ~ /^PACKAGES_DISABLE_/ && $1 !~ /_(APK|OPKG)$/ {printf "-D%s=OFF ", $1} END { print "" }'Similar approach works for |
|
I'm closing this PR to let PR #27949 go first. But I'm fine if we are sill using the place here for discussion. |
@CarterLi I tried integrating this line into the package Makefile. For now, just to print out the package manager related cmake options. Problem is, the |
|
Yeah. You need cmake to parse |

Fastfetch is a neofetch-like tool for fetching system information and displaying it in a visually appealing way.
📦 Package Details
Maintainer: @AlbrechtL
(You can find this by checking the history of the package
Makefile.)Description:
Fastfetch is a neofetch-like tool for fetching system information and displaying it in a visually appealing way.
Example
🧪 Run Testing Details
OpenWrt SNAPSHOT, r34251+1-e4b3d5c799
realtek/rtl838x
zyxel_gs1900-8-a1
✅ Formalities
If your PR contains a patch:
git am(e.g., subject line, commit description, etc.)
We must try to upstream patches to reduce maintenance burden.