deb packaging: build-deb.sh + packaging/deb, icons from ico/

- scripts/build-deb.sh: binary built in ubuntu:22.04 (glibc 2.35 baseline),
  Depends via dpkg-shlibdeps, DEB_REVISION (0.1.28-1), changelog.Debian.gz,
  postinst/postrm cache refresh, REPO=1 local apt index, self-checks
  (version/upgrade path, stale binary, desktop-file-validate)
- packaging/deb/: control.in, rcalc.desktop, appdata metainfo, changelog, hooks
- ico/: new app icon in 32/48/64/96/256/512, used for the hicolor slots
- convert.rs: fix clippy::approx_constant error in roundtrip test (PI constant)
- README: install/update/release instructions
This commit is contained in:
2026-09-17 19:48:05 +03:00
parent 93e193f7fa
commit 3c14906e40
17 changed files with 657 additions and 3 deletions
+5
View File
@@ -1,8 +1,13 @@
/target
/target-appimage
/target-deb
/.cache
/dist/*.AppImage
/dist/rcalc.AppDir/
/dist/deb-root/
/dist/*.deb
/dist/Packages
/dist/Packages.gz
/dist/.glibc-build.txt
.rcalc_last_bump
/target-windows
+71 -2
View File
@@ -12,6 +12,70 @@ cargo run --release
./dist/rcalc
```
## Install as a Debian package
```bash
./scripts/build-deb.sh # → dist/rcalc_<version>-<rev>_<arch>.deb
sudo apt install ./dist/rcalc_*_amd64.deb
rcalc # or start it from the application menu
```
The binary is compiled in an Ubuntu 22.04 container (Docker) by default, so the
package keeps a glibc 2.35 baseline and installs on Ubuntu 22.04 / 24.04 / 26.04.
`Depends:` are generated from the ELF with `dpkg-shlibdeps`.
Menu/taskbar icons come from `ico/` — every PNG there is placed into the
hicolor slot matching its real pixel size (file names do not matter), and the
remaining smaller sizes are downsampled from the biggest one. Without `ico/`
the icons are resized from `assets/icon.png`. The icon inside the window
(`ui/app.slint`) and the Windows `rcalc.exe` icon still use `assets/`.
Useful switches:
| Variable | Effect |
| --- | --- |
| `DEB_REVISION=` | packaging revision, `1` by default → `Version: 0.1.28-1` |
| `REPO=1` | also write `dist/Packages(.gz)` so `dist/` works as a local apt repo |
| `SKIP_DOCKER=1` | build on the host (needs this host's glibc on the target) |
| `PREBUILT=1` | package the existing `dist/rcalc`, no compile |
| `NO_STRIP=1` | keep symbols (bigger package) |
| `KEEP_STAGE=1` | leave `dist/deb-root/` for inspection |
| `DOCKER_IMAGE=` | other build image, e.g. `debian:12` for an older baseline |
| `DEB_MAINTAINER=` | `"Name <mail>"` in `Maintainer:` (default: git user) |
### Version and updates
Package version = `<version from Cargo.toml>-<DEB_REVISION>`, e.g. `0.1.28-1`.
* changed app code → bump `version` in `Cargo.toml` (it is also the window title);
* changed only packaging (icon, `.desktop`, `Depends`, rebuilt binary) → `DEB_REVISION=2`.
`dpkg` compares these in order (`0.1.28-1 < 0.1.28-2 < 0.1.29-1`, and
`0.1.9 < 0.1.10`), so every new build is an upgrade, never a downgrade. The
script prints the resulting upgrade path and warns if the version being built is
not newer than the installed one — an `apt upgrade` of such a package does nothing.
Release notes live in `packaging/deb/changelog` and are installed as
`/usr/share/doc/rcalc/changelog.Debian.gz`.
Update in place (config in `~/.config/rcalc/config.json` is a user file, it survives):
```bash
sudo apt install ./dist/rcalc_0.1.28-2_amd64.deb
```
Or keep `dist/` as a local repository and update with plain `apt` (build with
`REPO=1`, then point apt at the folder):
```bash
echo 'deb [trusted=yes] file:/path/to/RCalc/dist ./' | sudo tee /etc/apt/sources.list.d/rcalc.list
sudo apt update && sudo apt install rcalc # later: sudo apt upgrade
```
> If `scripts/install-desktop.sh` was ever run, `~/.local/bin/rcalc` (a symlink to
> `dist/rcalc`) shadows `/usr/bin/rcalc` — remove it after switching to the package,
> otherwise `rcalc` in the terminal still starts the old binary. The script warns
> about this during the build.
### Icon (Linux / Wayland)
On **Wayland** the window `icon` property is ignored — the desktop uses
@@ -27,8 +91,9 @@ clicking a random binary path, so KDE/GNOME can match `app_id=rcalc` to
## Features
- Modes: **Standard**, **Programmer**, **Convert**
- Standard: `+ − × ÷`, `%`, `±`, `1/x`, `x²`, `√x`, memory
- Modes: **Standard**, **Engineering**, **Programmer**, **Convert**, **CRC**, **Settings**
- Standard: `+ − × ÷`, `%`, `±`, `1/x`, `x²`, `√x`, memory, formula panel
- Engineering: trig functions, log/pow, DEG/RAD, SI prefixes
- Programmer: simultaneous **HEX / DEC / OCT / BIN**, sizes **QWORD / DWORD / DINT / WORD / INT / BYTE**,
bitwise ops, clickable bits
(WORD/DWORD — unsigned DEC; INT/DINT — signed)
@@ -46,3 +111,7 @@ clicking a random binary path, so KDE/GNOME can match `app_id=rcalc` to
- `src/main.rs` — UI wiring
- `ui/app.slint` — Slint interface
- `dist/rcalc` — release binary
- `scripts/build-deb.sh` + `packaging/deb/` — .deb package (control, desktop
file, AppStream metainfo, maintainer scripts)
- `scripts/build-appimage.sh` — portable AppImage
- `scripts/install-desktop.sh` — per-user launcher + icon (no root needed)
Vendored
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+12
View File
@@ -0,0 +1,12 @@
rcalc (0.1.28-1) unstable; urgency=medium
* Upstream 0.1.28 (version from Cargo.toml), first Debian packaging.
* Binary is built in an Ubuntu 22.04 container (glibc 2.35 baseline), so the
package installs on Ubuntu 22.04 / 24.04 / 26.04.
* Ships /usr/bin/rcalc, /usr/share/applications/rcalc.desktop, hicolor icons
taken from ico/, AppStream metainfo and postinst/postrm cache refresh hooks.
* Depends are generated from the ELF with dpkg-shlibdeps.
* Build fix: clippy::approx_constant error in the convert round-trip test
(3.1415927 -> std::f32::consts::PI).
-- alexMB5300 <prom-ing@yandex.ru> Thu, 17 Sep 2026 19:12:31 +0300
+23
View File
@@ -0,0 +1,23 @@
Package: rcalc
Version: @VERSION@
Architecture: @ARCHITECTURE@
Maintainer: @MAINTAINER@
Section: math
Priority: optional
Installed-Size: @INSTALLED_SIZE@
Depends: @DEPENDS@
Recommends: libegl1, libgl1, libx11-6, libwayland-client0
Description: desktop calculator with programmer, engineering and convert tools
Calculator inspired by the Windows Calculator (standard mode), written in
Rust with the Slint UI toolkit. Modes: Standard, Engineering, Programmer,
Convert, CRC and Settings.
.
Programmer mode shows HEX / DEC / OCT / BIN at once, with QWORD / DWORD /
DINT / WORD / INT / BYTE word sizes, bitwise operators and clickable bits.
.
Convert mode maps a float32 to two 16-bit PLC words in every byte order
(AB CD / CD AB / BA DC / DC BA), approximates electronic gear ratios as
num/den and converts Char / Hex / Dec ASCII codes.
.
CRC mode computes Modbus RTU CRC-16 and Modbus ASCII LRC from a string of
hex bytes, e.g. "01 03 00 00 00 0A".
+31
View File
@@ -0,0 +1,31 @@
#!/bin/sh
# Maintainer script of the generated rcalc .deb (see scripts/build-deb.sh).
set -e
case "$1" in
configure)
;;
abort-upgrade|abort-remove|abort-deconfigure)
exit 0
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# Refresh the XDG caches. Never fail the installation because of them: the
# tools may be absent (minimal systems, chroot, dpkg -x in a container).
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database -q /usr/share/applications || true
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor || true
fi
if command -v appstreamcli >/dev/null 2>&1; then
appstreamcli refresh --force >/dev/null 2>&1 || true
fi
exit 0
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
# Maintainer script of the generated rcalc .deb (see scripts/build-deb.sh).
set -e
case "$1" in
remove|upgrade|disappear|failed-upgrade|abort-install|abort-upgrade|purge)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# Rebuild the caches after the files were (or are about to be) removed.
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database -q /usr/share/applications || true
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor || true
fi
if command -v appstreamcli >/dev/null 2>&1; then
appstreamcli refresh --force >/dev/null 2>&1 || true
fi
exit 0
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- @RELEASE_DATE@ = UTC date of the build; @VERSION@ comes from Cargo.toml. -->
<component type="desktop-application">
<id>rcalc.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<!-- LICENSE file is missing in the repository: NOASSERTION until it is added. -->
<project_license>NOASSERTION</project_license>
<name>Rcalc</name>
<summary>Calculator with programmer, engineering, convert and CRC modes</summary>
<description>
<p>
Desktop calculator inspired by the Windows Calculator. Standard mode uses
exact rational arithmetic, so 0.1 + 0.2 really gives 0.3.
</p>
<ul>
<li>Standard: + − × ÷, %, ±, 1/x, x², √x, memory, formula panel</li>
<li>Engineering: trigonometry, log/pow, DEG/RAD, SI prefixes</li>
<li>Programmer: simultaneous HEX / DEC / OCT / BIN, QWORD … BYTE word
sizes, bitwise ops, clickable bits</li>
<li>Convert: float32 to two 16-bit words (AB CD / CD AB / BA DC / DC BA),
gear ratio num/den, ASCII codes</li>
<li>CRC: Modbus RTU CRC-16 and Modbus ASCII LRC from hex bytes</li>
<li>Full keyboard input, three themes, three button sizes</li>
</ul>
</description>
<categories>
<category>Utility</category>
<category>Calculator</category>
<category>Science</category>
</categories>
<releases>
<release version="@VERSION@" date="@RELEASE_DATE@"/>
</releases>
<content_rating type="oars-1.1"/>
</component>
+13
View File
@@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Name=Rcalc
GenericName=Calculator
Comment=Calculator with Programmer, Engineering, Convert and CRC tools
Exec=rcalc
Icon=rcalc
Terminal=false
Categories=Utility;Calculator;
Keywords=calculator;programmer;hex;bitwise;modbus;crc;lrc;
StartupNotify=true
# Wayland app_id / X11 WM_CLASS — must match set_xdg_app_id("rcalc") in src/main.rs
StartupWMClass=rcalc
+439
View File
@@ -0,0 +1,439 @@
#!/usr/bin/env bash
# Build a Debian package (.deb) for rcalc.
#
# By default the release binary is compiled inside Ubuntu 22.04 (glibc 2.35) —
# the same baseline and the same cargo cache (target-appimage) as
# scripts/build-appimage.sh — so the package installs on Ubuntu 22.04 / 24.04
# and not only on this rolling host distro.
#
# Package layout (FHS):
# /usr/bin/rcalc stripped release binary
# /usr/share/applications/rcalc.desktop launcher (Exec=rcalc)
# /usr/share/icons/hicolor/*x*/apps/rcalc.png icons from ico/ (or assets/)
# /usr/share/metainfo/rcalc.appdata.xml AppStream metadata
# /usr/share/doc/rcalc/README.md
#
# Env switches:
# DEB_REVISION packaging revision appended to the Cargo.toml version
# (0.1.28 + 1 → "Version: 0.1.28-1"); bump it for updates that
# change only the package, not the app
# REPO=1 also generate dist/Packages(.gz) → dist/ usable as an apt repo
# SKIP_DOCKER=1 build on the host instead of Docker (fast, but the deb will
# then require this host's glibc — see DEPENDS below)
# PREBUILT=1 do not compile, package the existing dist/rcalc as-is
# NO_STRIP=1 keep symbols in the binary (much bigger package)
# KEEP_STAGE=1 leave dist/deb-root/ around for inspection
# DOCKER_IMAGE build container image (default: ubuntu:22.04)
# DEB_MAINTAINER "Name <mail>" (default: git user.name/user.email)
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
PKG="rcalc"
OUT_DIR="${OUT_DIR:-${ROOT}/dist}"
META_DIR="${ROOT}/packaging/deb"
STAGE="${OUT_DIR}/deb-root"
ARCH="$(dpkg --print-architecture 2>/dev/null || uname -m)"
VERSION="$(grep -m1 '^version' "${ROOT}/Cargo.toml" | sed 's/.*"\(.*\)".*/\1/')"
# Debian version = upstream (Cargo.toml) + packaging revision: "0.1.28-1".
# The revision lets you ship an update (fixed desktop file, new icon, rebuilt
# binary) WITHOUT touching Cargo.toml; dpkg compares 0.1.28-1 < 0.1.28-2 <
# 0.1.29-1 correctly. DEB_REVISION is the knob for that.
DEB_REVISION="${DEB_REVISION:-1}"
DEB_VERSION="${VERSION}-${DEB_REVISION}"
DEB_FILE="${OUT_DIR}/${PKG}_${DEB_VERSION}_${ARCH}.deb"
DOCKER_IMAGE="${DOCKER_IMAGE:-ubuntu:22.04}"
SKIP_DOCKER="${SKIP_DOCKER:-0}"
PREBUILT="${PREBUILT:-0}"
NO_STRIP="${NO_STRIP:-0}"
KEEP_STAGE="${KEEP_STAGE:-0}"
# Nested $( ) with quotes inside ${:- } is parsed unreliably by bash — resolve first.
GIT_NAME="$(git -C "${ROOT}" config user.name 2>/dev/null || true)"
GIT_MAIL="$(git -C "${ROOT}" config user.email 2>/dev/null || true)"
MAINTAINER="${DEB_MAINTAINER:-${GIT_NAME:-${PKG}-packaging} <${GIT_MAIL:-localhost}>}"
need_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "error: required command not found: $1" >&2
exit 1
}
}
# ---------------------------------------------------------------- binary ----
build_binary_docker() {
need_cmd docker
echo "Building release binary in ${DOCKER_IMAGE} (glibc 2.35 baseline)…"
mkdir -p "${HOME}/.cargo/registry" "${HOME}/.cargo/git"
docker run --rm \
-v "${ROOT}:/src" \
-v "${HOME}/.cargo/registry:/root/.cargo/registry" \
-v "${HOME}/.cargo/git:/root/.cargo/git" \
-w /src \
"${DOCKER_IMAGE}" \
bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl build-essential pkg-config libfontconfig1-dev ca-certificates
curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
# shellcheck disable=SC1091
source "$HOME/.cargo/env"
export CARGO_TARGET_DIR=/src/target-appimage
cargo build --release
install -D -m755 /src/target-appimage/release/rcalc /src/dist/rcalc
ldd --version | head -1 > /src/dist/.glibc-build.txt
# Make artifacts writable by the host user
chown -R "$(id -u):$(id -g)" /src/dist/rcalc /src/dist/.glibc-build.txt /src/target-appimage || true
'
}
build_binary_host() {
echo "Building release binary on the host ($(ldd --version | head -1))…"
(cd "${ROOT}" && cargo build --release)
install -D -m755 "${ROOT}/target/release/${PKG}" "${OUT_DIR}/${PKG}"
ldd --version | head -1 > "${OUT_DIR}/.glibc-build.txt" || true
}
ensure_binary() {
mkdir -p "${OUT_DIR}"
if [[ "${PREBUILT}" == "1" ]]; then
echo "PREBUILT=1: packaging ${OUT_DIR}/${PKG} as-is"
elif [[ "${SKIP_DOCKER}" == "1" ]]; then
build_binary_host
else
build_binary_docker
fi
if [[ ! -x "${OUT_DIR}/${PKG}" ]]; then
echo "error: ${OUT_DIR}/${PKG} missing — run without PREBUILT=1" >&2
exit 1
fi
}
# ---------------------------------------------------------------- staging ---
stage_tree() {
rm -rf "${STAGE}"
mkdir -p \
"${STAGE}/DEBIAN" \
"${STAGE}/usr/bin" \
"${STAGE}/usr/share/applications" \
"${STAGE}/usr/share/metainfo" \
"${STAGE}/usr/share/doc/${PKG}"
install -m755 "${OUT_DIR}/${PKG}" "${STAGE}/usr/bin/${PKG}"
if [[ "${NO_STRIP}" != "1" ]] && command -v strip >/dev/null 2>&1; then
strip --strip-unneeded "${STAGE}/usr/bin/${PKG}"
fi
install -m644 "${META_DIR}/${PKG}.desktop" \
"${STAGE}/usr/share/applications/${PKG}.desktop"
sed -e "s|@VERSION@|${VERSION}|" \
-e "s|@RELEASE_DATE@|$(date -u +%Y-%m-%d)|" \
"${META_DIR}/${PKG}.appdata.xml.in" \
> "${STAGE}/usr/share/metainfo/${PKG}.appdata.xml"
gzip -9 -c "${ROOT}/README.md" > "${STAGE}/usr/share/doc/${PKG}/README.md.gz"
echo "Binary: $(file -b "${STAGE}/usr/bin/${PKG}" | cut -d, -f1-3)"
}
# Icons for the desktop entry: /usr/share/icons/hicolor/<N>x<N>/apps/rcalc.png
#
# Source of truth, in order:
# 1. ico/*.png — ready-made sizes dropped in by hand. The REAL pixel size of
# each file decides which hicolor slot it fills (file names are ignored),
# and ico/ wins for the whole family: mixing ico/ artwork with assets/
# artwork would show a different picture depending on the icon size.
# 2. assets/icon.png resized to the standard sizes (needs ImageMagick).
# 3. the three PNGs bundled in assets/, as-is.
stage_icons() {
local dst="${STAGE}/usr/share/icons/hicolor"
local prepared=0 master="" master_size=0 size
local magick=""
for tool in magick convert; do
if command -v "${tool}" >/dev/null 2>&1; then
magick="${tool}"
break
fi
done
if [[ -d "${ROOT}/ico" ]] && [[ -n "${magick}" ]]; then
for f in "${ROOT}"/ico/*.png; do
[[ -f "${f}" ]] || continue
local w h
read -r w h <<<"$(identify -format '%w %h' "${f}" 2>/dev/null || echo '0 0')" || true
[[ "${w}" == "${h}" ]] && [[ "${w}" -gt 0 ]] || continue
if (( w > master_size )); then
master="${f}"
master_size="${w}"
fi
local slot="${dst}/${w}x${w}/apps/${PKG}.png"
if [[ ! -e "${slot}" ]]; then
mkdir -p "${dst}/${w}x${w}/apps"
install -m644 "${f}" "${slot}"
prepared=$((prepared + 1))
fi
done
fi
if (( prepared > 0 )); then
# Fill the remaining standard sizes from the biggest ico/ file — never
# upscale, so a 512px source does not produce a blurry 1024px icon.
if [[ -n "${magick}" ]]; then
for size in 16 24 32 48 64 96 128 256 512; do
[[ -e "${dst}/${size}x${size}/apps/${PKG}.png" ]] && continue
(( size <= master_size )) || continue
mkdir -p "${dst}/${size}x${size}/apps"
"${magick}" "${master}" -resize "${size}x${size}" \
"${dst}/${size}x${size}/apps/${PKG}.png"
done
fi
echo "Icons: ${prepared} ready-made from ico/ (master ${master_size}px)"
return 0
fi
if [[ -n "${magick}" ]]; then
for size in 16 24 32 48 64 128 256 512 1024; do
mkdir -p "${dst}/${size}x${size}/apps"
"${magick}" "${ROOT}/assets/icon.png" -resize "${size}x${size}" \
"${dst}/${size}x${size}/apps/${PKG}.png"
done
echo "Icons: resized from assets/icon.png (1024px master)"
else
install -D -m644 "${ROOT}/assets/icon-64.png" "${dst}/64x64/apps/${PKG}.png"
install -D -m644 "${ROOT}/assets/icon-256.png" "${dst}/256x256/apps/${PKG}.png"
install -D -m644 "${ROOT}/assets/icon.png" "${dst}/1024x1024/apps/${PKG}.png"
echo "Icons: assets/*.png as-is (ImageMagick not found)"
fi
}
# ------------------------------------------------------------ dependencies --
# Real library dependencies, read from the ELF itself. GL / X11 / Wayland
# libraries are dlopen()ed by winit at runtime, so they never show up in
# NEEDED — they are listed in Recommends in control.in instead.
compute_depends() {
local depends=""
if [[ "${USE_SHLIBDEPS:-1}" == "1" ]] && command -v dpkg-shlibdeps >/dev/null 2>&1; then
local tmp
tmp="$(mktemp -d)"
mkdir -p "${tmp}/debian"
# dpkg-shlibdeps insists on reading a debian/control next to it.
cat > "${tmp}/debian/control" <<EOF
Source: ${PKG}
Package: ${PKG}
Architecture: ${ARCH}
Depends: \${shlibs:Depends}
Description: temporary, for dpkg-shlibdeps
EOF
: > "${tmp}/debian/${PKG}.substvars"
(cd "${tmp}" && dpkg-shlibdeps -T"debian/${PKG}.substvars" "${STAGE}/usr/bin/${PKG}") \
>/dev/null 2>&1 || true
depends="$(sed -n 's/^shlibs:Depends=//p' "${tmp}/debian/${PKG}.substvars")"
rm -rf "${tmp}"
fi
if [[ -z "${depends}" ]]; then
echo "warning: dpkg-shlibdeps gave nothing, using a hand-written Depends list" >&2
depends='libc6, libfontconfig1, libgcc-s1'
fi
printf '%s' "${depends}"
}
# ---------------------------------------------------------------- control ---
stage_control() {
local installed_size depends
installed_size="$(du -s -k --exclude="${STAGE}/DEBIAN" "${STAGE}" | awk '{print $1}')"
depends="$(compute_depends)"
sed -e "s|@VERSION@|${DEB_VERSION}|" \
-e "s|@ARCHITECTURE@|${ARCH}|" \
-e "s|@MAINTAINER@|${MAINTAINER}|" \
-e "s|@INSTALLED_SIZE@|${installed_size}|" \
-e "s|@DEPENDS@|${depends}|" \
"${META_DIR}/control.in" > "${STAGE}/DEBIAN/control"
# Maintainer scripts must be executables owned by root in the archive.
install -m755 "${META_DIR}/postinst" "${STAGE}/DEBIAN/postinst"
install -m755 "${META_DIR}/postrm" "${STAGE}/DEBIAN/postrm"
(cd "${STAGE}" && find . -path ./DEBIAN -prune -o -type f -print \
| sed 's|^\./||' | sort | xargs -r md5sum) > "${STAGE}/DEBIAN/md5sums"
echo "Version: ${DEB_VERSION} (upstream ${VERSION} + revision ${DEB_REVISION})"
echo "Architecture: ${ARCH}"
echo "Maintainer: ${MAINTAINER}"
echo "Installed-Size: ${installed_size} KB"
echo "Depends: ${depends}"
}
# ------------------------------------------------------- version / update ---
# What "apt upgrade" needs: a version that is strictly greater than the one
# already installed, and a package name that does not change. Both are checked
# here so a release is never silently unpromotable.
check_version() {
case "${VERSION}" in
[0-9]*) ;;
*) echo "error: upstream version '${VERSION}' must start with a digit (dpkg rule)" >&2; exit 1 ;;
esac
if [[ ! "${VERSION}" =~ ^[0-9A-Za-z.+-~:]+$ || ! "${DEB_REVISION}" =~ ^[0-9A-Za-z.+-~]+$ ]]; then
echo "error: bad version '${DEB_VERSION}' — allowed chars are letters, digits and . + - ~ :" >&2
exit 1
fi
local installed
installed="$(dpkg-query -W -f='${Version}' "${PKG}" 2>/dev/null || true)"
if [[ -n "${installed}" ]]; then
if dpkg --compare-versions "${installed}" ge "${DEB_VERSION}"; then
echo "warning: ${PKG} ${installed} is ALREADY installed and is not older than ${DEB_VERSION}" >&2
echo " → 'apt upgrade' will do nothing. Bump Cargo.toml or DEB_REVISION." >&2
else
echo "Upgrade path: ${installed} → ${DEB_VERSION} (apt upgrade will pick this up)"
fi
else
echo "Upgrade path: fresh install of ${DEB_VERSION}"
fi
# scripts/install-desktop.sh links ~/.local/bin/rcalc → dist/rcalc, and
# ~/.local/bin comes before /usr/bin in PATH: the packaged binary would be
# shadowed by an old copy even after a successful upgrade.
if [[ -e "${HOME}/.local/bin/${PKG}" ]]; then
echo "warning: ${HOME}/.local/bin/${PKG} exists and shadows /usr/bin/${PKG}" >&2
echo " (installed by scripts/install-desktop.sh — remove it after switching to the .deb)" >&2
fi
}
# /usr/share/doc/rcalc/changelog.Debian.gz — Debian policy expects a changelog,
# and it is the human-readable history of what every version changed.
# packaging/deb/changelog (kept in git) wins; otherwise one entry is generated
# from the current commit so the package is never changelog-less.
stage_changelog() {
local doc="${STAGE}/usr/share/doc/${PKG}"
mkdir -p "${doc}"
local plain="${doc}/changelog.Debian" # gzip → changelog.Debian.gz (Debian-side history)
if [[ -f "${META_DIR}/changelog" ]]; then
cp "${META_DIR}/changelog" "${plain}"
echo "Changelog: packaging/deb/changelog"
else
local githash gitsubject
githash="$(git -C "${ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)"
gitsubject="$(git -C "${ROOT}" log -1 --pretty=%s 2>/dev/null || echo packaging)"
{
printf '%s (%s) unstable; urgency=medium\n\n' "${PKG}" "${DEB_VERSION}"
printf ' * Upstream %s, packaged as %s.\n' "${VERSION}" "${DEB_VERSION}"
if [[ -f "${OUT_DIR}/.glibc-build.txt" ]]; then
printf ' * Built against %s\n' "$(cat "${OUT_DIR}/.glibc-build.txt")"
fi
printf ' * git %s: %s\n\n' "${githash}" "${gitsubject}"
printf ' -- %s %s\n' "${MAINTAINER}" "$(date -R)"
} > "${plain}"
echo "Changelog: generated (add packaging/deb/changelog to keep real history)"
fi
gzip -9n "${plain}" # -n: no timestamp → reproducible .gz
rm -f "${plain}"
}
# REPO=1 → turn dist/ into a flat apt repository, so updates can be installed
# with plain `apt update && apt upgrade` instead of pointing at a .deb file.
make_repo() {
[[ "${REPO:-0}" == "1" ]] || return 0
need_cmd dpkg-scanpackages
(
cd "${OUT_DIR}"
# One pass: Packages is the index, Packages.gz the compressed copy apt reads.
# Keep both files: apt reads Packages.gz, humans diff/read Packages.
dpkg-scanpackages --multiversion . /dev/null > Packages 2>/dev/null
gzip -9n -c Packages > Packages.gz
)
echo "Repo index: ${OUT_DIR}/Packages.gz ($(grep -c '^Package:' "${OUT_DIR}/Packages") entries)"
echo " apt one-liner: echo 'deb [trusted=yes] file:${OUT_DIR} ./' \\"
echo " | sudo tee /etc/apt/sources.list.d/${PKG}.list"
echo " then: sudo apt update && sudo apt install ${PKG}"
}
# ------------------------------------------------------------------ build ---
build_package() {
need_cmd dpkg-deb
rm -f "${DEB_FILE}"
# The stage tree inherits the build user's umask (often 002 → group-writable).
# Debian expects 755 dirs / 644 data files, plus the exec bits on the binary
# and the maintainer scripts.
find "${STAGE}" -type d -exec chmod 0755 {} +
find "${STAGE}" -type f -exec chmod 0644 {} +
chmod 0755 "${STAGE}/usr/bin/${PKG}" \
"${STAGE}/DEBIAN/postinst" "${STAGE}/DEBIAN/postrm"
# --root-owner-group: the stage tree is owned by the build user, the archive
# must not be (dpkg rejects foreign uids unless forced).
dpkg-deb --root-owner-group --build "${STAGE}" "${DEB_FILE}"
}
verify_package() {
local ok=1
# Fields straight from the archive (dpkg-deb --info output is localised).
dpkg-deb --field "${DEB_FILE}"
if command -v desktop-file-validate >/dev/null 2>&1; then
if desktop-file-validate "${STAGE}/usr/share/applications/${PKG}.desktop"; then
echo "desktop-file-validate: OK"
else
echo "warning: the desktop file did not validate" >&2
ok=0
fi
fi
# dist/rcalc can be stale: build.rs embeds APP_VERSION from Cargo.toml only at
# compile time, and the AppImage packaging had exactly that bug (old binary in
# a new image). Anything newer than the packaged binary means "rebuild it".
local stale
stale="$(find "${ROOT}/src" "${ROOT}/ui" "${ROOT}/build.rs" "${ROOT}/Cargo.toml" \
-newer "${OUT_DIR}/${PKG}" -print -quit 2>/dev/null || true)"
if [[ -n "${stale}" ]]; then
echo "warning: ${stale} is newer than the packaged binary" >&2
echo " (PREBUILT=1 / SKIP_DOCKER=1 reused an old dist/${PKG} — rebuild to be safe)" >&2
ok=0
fi
echo "Files in package: $(dpkg-deb -c "${DEB_FILE}" | grep -c '^-')"
if [[ "${KEEP_STAGE}" != "1" ]]; then
rm -rf "${STAGE}"
fi
echo
ls -l "${DEB_FILE}"
[[ "${ok}" == "1" ]] || echo "(check the warnings above)"
}
check_version
ensure_binary
stage_tree
stage_icons
stage_changelog
stage_control
build_package
make_repo
verify_package
echo
echo "OK: ${DEB_FILE}"
echo "Version: ${DEB_VERSION} (upstream ${VERSION} + revision ${DEB_REVISION})"
echo "Install: sudo apt install '${DEB_FILE}'"
echo "Next release: bump version in Cargo.toml (app changes) — or just"
echo " DEB_REVISION=2 ./scripts/build-deb.sh (packaging/icon only),"
echo " then install the new .deb: apt replaces the old one in place."
echo "Run: rcalc (or start it from the application menu)"
echo "Remove: sudo apt remove ${PKG}"
+1 -1
View File
@@ -858,7 +858,7 @@ mod tests {
#[test]
fn roundtrip_all_endians() {
let samples = [0.0f32, -1.0, 3.1415927, 12345.678, f32::MIN_POSITIVE];
let samples = [0.0f32, -1.0, std::f32::consts::PI, 12345.678, f32::MIN_POSITIVE];
for e in Endian::all() {
for &f in &samples {
let (w0, w1) = float_to_words(f, e);