general: refactor build script; readme misc.

This commit is contained in:
2026-09-13 19:56:49 +02:00
parent 0cd1499fa3
commit de6bcec887
6 changed files with 320 additions and 111 deletions
+1
View File
@@ -1,2 +1,3 @@
/twrp-build/
/recovery.img
/recovery-stock.img
+107 -11
View File
@@ -1,19 +1,115 @@
# builders\_rubenslte
This repository contains build scripts that automate the building of
recoveries and ROMs for the Galaxy Tab Active SM-T365.
Build scripts for TWRP recoveries (and, eventually, ROMs) for the **Samsung
Galaxy Tab Active SM-T365** (codename `rubenslte`, Qualcomm MSM8926).
# Build
This repo only orchestrates the build. The actual device configuration lives in
two other repos:
## Recovery
| Repo | Contents |
|------|----------|
| `rubenslte/twrp_device_samsung_rubenslte` | TWRP device tree (BoardConfig.mk, mkbootimg.mk, dtbtool, recovery.fstab) |
| `rubenslte/android_kernel_samsung_rubenslte` | Samsung msm8226 stock kernel (Linux 3.4), built from source |
| this repo | `build_twrp.sh` (driver), `shell.nix` (nix-shell env), `patches/` (fixes), docs |
## Quick start
Requirements: a NixOS system (or Nix with `nix-shell`) with network access and
`nix` in your user's `allowed-users` / `trusted-users`.
```
./build_twrp.sh
./build_twrp.sh # full build -> recovery.img
./build_twrp.sh --no-sync # rebuild without re-syncing/re-patching sources
./build_twrp.sh clean # delete twrp-build/ and recovery.img
./build_twrp.sh env # interactive shell inside the build env
```
This builds the recovery inside the nix-shell FHS environment (shell.nix);
the script re-executes itself there, so it works from the host even though
`nix-shell --run` is broken by the `exec android-env` shellHook.
The first run downloads the omni `twrp-5.1` manifest source (~2-3 GB with the
kernel and toolchain) and can take over an hour; later `--no-sync` rebuilds are
much faster. The final artifact is `recovery.img` at the repo root.
## How the build works
1. **Environment** — everything runs inside the nix-shell FHS environment
(`shell.nix`). `nix-shell --run` is unreliable here because the shellHook
does `exec android-env`, so `build_twrp.sh` re-enters the environment by
piping its own body into `nix-shell` (see `reenter_build_env`).
2. **Sources** — `repo init/sync` fetches the omni `twrp-5.1` manifest; the
device tree and kernel are shallow clones from gitea (and refreshed on each
full build).
3. **Patches** — `patches/*.patch` fix the old 5.1 codebase for modern host
tools (see `patches/README.md`). Applied idempotently.
4. **Toolchain** — arm-eabi-4.8 (gcc 4.8, the same one stock builds used) is
downloaded from Android's googlesource and put on `PATH`.
5. **Kernel** — built from source via the omni kernel build rules
(base defconfig + rubenslte variant + selinux defconfigs). The variant
defconfig only enables `CONFIG_MACH_RUBENSLTE_OPEN` (see caveats).
6. **dt.img** — `dtbToolLineage` packs the compiled `.dtb` files; the bootloader
selects the right one for the hardware.
7. **Recovery ramdisk** — the stock minigzip ramdisk overflows the 10,485,248
byte partition, so `mkbootimg.mk` replaces the core ramdisk rules: drops
tzdata, keeps only `en.xml`/`pl.xml` languages, and compresses with LZMA
(`CONFIG_RD_LZMA=y` in the rubenslte kernel).
Image layout of the shipped `recovery.img`:
```
offset size content
0 2,048 boot image header (base 0x0, pagesize 2048)
2,048 7,223,736 zImage (kernel)
7,225,744 1,608 padding
7,227,352 2,882,224 ramdisk, LZMA-compressed (gzip would overflow)
10,109,576 1,360 padding
10,110,936 296,960 dt.img (QCDT: two EUR dtbs, r02+r03)
10,407,896 56 SEANDROIDENFORCE trailer
```
Artifact facts:
- `recovery.img` — 10,407,952 bytes (fits the 10,485,248 byte partition)
- sha256 `add52ab4d7ccbedd24ad08a89fe075c3cf918646bf6e77c6d610bcabd86a6b5b`
- TWRP **3.7.0_9-0**, Linux kernel 3.4.x from Samsung source, built for
Android 5.1.1 (omni twrp-5.1)
## Flashing
Enter Download mode: **Volume Down + Home + Power**, then confirm with
**Volume Up**.
- Odin 3.x: flash `recovery.img` in the **AP** slot (leave BL/CP/CSC empty).
- Heimdall: `heimdall flash --RECOVERY recovery.img --no-reboot`
Boot into TWRP with **Volume Up + Home + Power**. Note: the bootloader sets the
KNOX warranty bit on flashing — irreversible on this device.
`recovery-stock.img` (in this directory, git-ignored) is the stock recovery
image dumped from the device; flash it back to restore the factory recovery.
## Device notes
- **dt.img is EUR-only.** The kernel is built with `CONFIG_MACH_RUBENSLTE_OPEN`,
so dt.img carries only the two EUR DTBs (r02/r03). SM-T365Y (AUS) / SM-T365M
/ KOR units need the corresponding `CONFIG_MACH_RUBENSLTE_*` enabled and a
rebuild, otherwise the device may not boot from this kernel.
- **`/data` is ext4.** The kernel has no F2FS driver, so do not format `Data`
as F2FS in TWRP. The `TARGET_USERIMAGES_USE_F2FS` flag is deliberately unset.
- **`adb shell` needs `/system` mounted.** `adb` itself works in recovery, but
the device-side shell is `/system/bin/sh`; until you check **Mount → System**
in TWRP you'll see `CANNOT LINK EXECUTABLE DEPENDENCIES: library "libc.so"
not found`. The in-TWRP terminal (Advanced → Terminal) always works.
## Troubleshooting
- `make: *** [.../mkbootimg.mk:24: /dt.img] Błąd 255` — the custom bootimg
rules are included before the kernel tasks define their paths; the recursive
variables in BoardConfig.mk are what keep `/dt.img` from collapsing to an
absolute base path. Don't "simplify" them to `:=`.
- Image won't fit the partition — the ramdisk must stay LZMA and trimmed;
re-check `mkbootimg.mk` if you add files under `twres/` or `system/`.
- `nix-shell --run ...` produces no output — known limitation of the
`exec android-env` shellHook; use `./build_twrp.sh` or `./build_twrp.sh env`.
- Parallel `nix-shell` invocations can deadlock on the nix store — run builds
sequentially.
# Device information
@@ -38,10 +134,10 @@ the script re-executes itself there, so it works from the host even though
| Front camera | 1.2 Mpx |
| Similar devices | milletlte, matisselte, motorola_thea |
# Links to device trees, manifests, etc.
# Links
- https://github.com/matteo0026/android_device_samsung_rubenslte
- https://git.cfpi-fpsi.eu/rubenslte/twrp_device_samsung_rubenslte
- https://git.cfpi-fpsi.eu/rubenslte/android_kernel_samsung_rubenslte
- https://github.com/matteo0026/android_device_samsung_rubenslte
- https://github.com/matteo0026/twrp_device_samsung_milletlte
- https://github.com/matteo0026/twrp_device_samsung_matisselte
- https://github.com/matteo0026/twrp_device_samsung_matisselte
+143 -51
View File
@@ -1,62 +1,154 @@
#!/usr/bin/env bash
# Build a TWRP recovery image for Samsung Galaxy Tab Active (SM-T365, rubenslte).
# Must run inside the nix-shell FHS environment (shell.nix): if invoked from the
# host, it re-executes itself inside that environment via its stdin pipe.
# Build a TWRP recovery image for the Samsung Galaxy Tab Active SM-T365
# (codename rubenslte, msm8226).
#
# Usage:
# ./build_twrp.sh build recovery.img (default)
# ./build_twrp.sh --no-sync skip repo sync / tree updates (fast iteration)
# ./build_twrp.sh clean remove twrp-build/ and recovery.img
# ./build_twrp.sh env drop into the interactive nix-shell build env
#
# The build runs inside the nix-shell FHS environment defined in shell.nix.
# `nix-shell --run` cannot be relied on here, so when invoked from the host the
# script re-executes itself *inside* the env via a stdin pipe
# (RUBENSLTE_BUILD_IN_ENV prevents infinite recursion).
set -euo pipefail
SCRIPT="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
if [ -z "${RUBENSLTE_BUILD_IN_ENV:-}" ]; then
echo "[*] Entering nix-shell FHS build environment ..."
cd "$SCRIPT"
printf '\nexport RUBENSLTE_BUILD_IN_ENV=1\nbash "%s/build_twrp.sh"\n' "$SCRIPT" \
| nix-shell shell.nix
exit $?
fi
export LC_ALL=C LANG=C LC_CTYPE=C
SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT"
mkdir -p twrp-build
cd twrp-build
if [ ! -d .repo ]; then
repo init --depth 1 -u https://github.com/minimal-manifest-twrp/platform_manifest_twrp_omni.git -b twrp-5.1
fi
RECOVERY_PARTITION_SIZE=10485248
repo sync -c -j$(nproc)
MANIFEST_URL="https://github.com/minimal-manifest-twrp/platform_manifest_twrp_omni.git"
MANIFEST_BRANCH="twrp-5.1"
DEVICE_TREE_URL="https://git.cfpi-fpsi.eu/rubenslte/twrp_device_samsung_rubenslte"
KERNEL_URL="https://git.cfpi-fpsi.eu/rubenslte/android_kernel_samsung_rubenslte"
TOOLCHAIN_URL="https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/+archive/refs/tags/android-5.1.1_r38.tar.gz"
TOOLCHAIN_DIR="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8"
# Device tree (gitea mirror of this repo)
if [ ! -d device/samsung/rubenslte ]; then
git clone --depth 1 https://git.cfpi-fpsi.eu/rubenslte/twrp_device_samsung_rubenslte device/samsung/rubenslte
fi
usage() {
sed -n '2,9p' "$0" | sed 's/^# \{0,1\}//'
}
# Kernel built from source (gitea mirror of Samsung msm8226 stock tree)
if [ ! -d kernel/samsung/rubenslte ]; then
git clone --depth 1 https://git.cfpi-fpsi.eu/rubenslte/android_kernel_samsung_rubenslte kernel/samsung/rubenslte
fi
reenter_build_env() {
if [ -z "${RUBENSLTE_BUILD_IN_ENV:-}" ]; then
echo "[*] Re-entering build inside the nix-shell FHS environment ..."
printf 'export RUBENSLTE_BUILD_IN_ENV=1\nbash "%s"\n' "$SCRIPT/build_twrp.sh" \
| nix-shell shell.nix
exit $?
fi
}
for p in "$SCRIPT"/patches/*.patch; do
patch -p1 -s < "$p" 2>/dev/null || true
clone_or_update() {
local name="$1" url="$2" dest="$3"
if [ ! -d "$dest/.git" ]; then
echo "[*] Cloning $name ..."
git clone -q --depth 1 "$url" "$dest"
else
echo "[*] Updating $name ..."
local branch
branch="$(git -C "$dest" rev-parse --abbrev-ref HEAD)"
git -C "$dest" fetch -q --depth 1 origin "$branch"
git -C "$dest" reset -q --hard "origin/$branch"
fi
}
apply_patches() {
local p log
for p in "$SCRIPT"/patches/*.patch; do
log="$(mktemp)"
if patch -p1 -s < "$p" >"$log" 2>&1; then
echo "[*] Applied patch: $(basename "$p")"
elif grep -q -E 'Reversed|already applied' "$log"; then
echo "[*] Patch already applied: $(basename "$p")"
else
echo "[!] WARNING: patch failed: $(basename "$p")"
cat "$log"
fi
rm -f "$log"
done
}
fetch_toolchain() {
if [ -x "$TOOLCHAIN_DIR/bin/arm-eabi-gcc" ]; then
return
fi
echo "[*] Downloading arm-eabi-4.8 prebuilt toolchain ..."
mkdir -p "$TOOLCHAIN_DIR"
curl -L --fail "$TOOLCHAIN_URL" | tar -xz -C "$TOOLCHAIN_DIR"
}
verify_image() {
local img="$1"
[ -f "$img" ] || { echo "ERROR: $img was not produced" >&2; exit 1; }
local size
size="$(stat -c%s "$img")"
echo "[*] $img: $size bytes (partition budget: $RECOVERY_PARTITION_SIZE)"
if [ "$size" -gt "$RECOVERY_PARTITION_SIZE" ]; then
echo "ERROR: image exceeds the recovery partition" >&2
exit 1
fi
sha256sum "$img"
echo "Done: $img"
}
do_build() {
export LC_ALL=C LANG=C LC_CTYPE=C
mkdir -p twrp-build
cd twrp-build
if [ ! -d .repo ]; then
repo init --depth 1 -u "$MANIFEST_URL" -b "$MANIFEST_BRANCH"
fi
if [ -z "${NO_SYNC:-}" ]; then
repo sync -c -j"$(nproc)"
clone_or_update "device tree" "$DEVICE_TREE_URL" device/samsung/rubenslte
clone_or_update "kernel" "$KERNEL_URL" kernel/samsung/rubenslte
apply_patches
fi
fetch_toolchain
export PATH="$PWD/$TOOLCHAIN_DIR/bin:$PATH"
export TOP="$PWD"
set +u
source build/envsetup.sh
lunch omni_rubenslte-userdebug
set -u
mka recoveryimage
cp out/target/product/rubenslte/recovery.img "$SCRIPT/"
verify_image "$SCRIPT/recovery.img"
}
do_clean() {
rm -rf twrp-build recovery.img
}
do_env() {
exec nix-shell shell.nix
}
cmd="${1:-build}"
case "$cmd" in
clean) shift 2>/dev/null || true ;;
env) shift 2>/dev/null || true ;;
build) shift 2>/dev/null || true ;;
--*) cmd=build ;;
*) usage >&2; exit 2 ;;
esac
NO_SYNC=0
for a in "$@"; do
case "$a" in
--no-sync) NO_SYNC=1 ;;
*) usage >&2; exit 2 ;;
esac
done
# arm-eabi-4.8 cross toolchain (AOSP prebuilt, plain gcc host binary)
ARM_EABI_DIR="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8"
if [ ! -x "$ARM_EABI_DIR/bin/arm-eabi-gcc" ]; then
echo "[*] Downloading arm-eabi-4.8 prebuilt toolchain ..."
mkdir -p "$ARM_EABI_DIR"
curl -L --fail \
"https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/+archive/refs/tags/android-5.1.1_r38.tar.gz" \
| tar -xz -C "$ARM_EABI_DIR"
fi
export PATH="$PWD/$ARM_EABI_DIR/bin:$PATH"
export TOP="$PWD"
set +u
source build/envsetup.sh
lunch omni_rubenslte-userdebug
set -u
mka recoveryimage
cp out/target/product/rubenslte/recovery.img "$SCRIPT/"
echo "Done: $SCRIPT/recovery.img"
case "$cmd" in
build) reenter_build_env; do_build ;;
clean) do_clean ;;
env) do_env ;;
esac
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
rm -rf twrp-build recovery.img
+20
View File
@@ -0,0 +1,20 @@
# Patches
These patches make the omni `twrp-5.1` sources (2015-era) build on a modern
host toolchain. They are applied automatically by `build_twrp.sh` and are
idempotent (already-applied patches are detected and skipped).
| Patch | File(s) | Problem fixed |
|-------|---------|---------------|
| `0001-host-cflags-suppress-stringop-truncation.patch` | `build/core/combo/HOST_linux-x86*.mk` | `-Werror=stringop-truncation` errors with host GCC >= 8 |
| `0002-build-core-fix-ar-absolute-paths.patch` | `build/core/definitions.mk` | host `ar`/absolute path handling in whole-archive extraction breaks with newer binutils; extract to a temp dir instead |
| `0003-kernel-add-missing-ARCH_MSM-config-and-defconfig-fixes.patch` | `kernel/.../arch/arm/mach-msm/Kconfig`, `msm8926-sec_rubenslte_defconfig` | adds the missing `ARCH_MSM` Kconfig entry and enables `CONFIG_OF`/`ARCH_MSM` in the variant defconfig (fresh kernel tree lacks them) |
| `0004-recovery-add-std-gnu++11-for-nullptr-support.patch` | `bootable/recovery/Android.mk` | adds `-std=gnu++11` for `nullptr` under the recovery Clang build |
| `0005-build-core-combo-remove-icf-safe-for-BFD-ld.patch` | `build/core/combo/TARGET_linux-arm.mk` | `-Wl,--icf=safe` is gold-only; commented out for the BFD linker supplied by the toolchain download |
| `0006-kernel-timeconst-pl-fix-deprecated-defined-array.patch` | `kernel/.../kernel/timeconst.pl` | `defined(@array)` removed in Perl 5.22+; use `!@val` |
| `0007-scripts-Makefile-modpost-treat-section-mismatches-as-non-fatal.patch` | `kernel/.../scripts/Makefile.modpost` | drops the `-E` flag so section mismatches are warnings, not errors (newer host binutils) |
| `0008-build-core-tasks-kernel-fix-KERNEL_CONFIG_OVERRIDE-ordering-and-passthrough.patch` | `build/core/tasks/kernel.mk` | applies the additional config before the override, drops the clobbering `oldconfig` after override, and passes the override on the kernel build command line |
The kernel patches (`0003`, `0006`, `0007`) target `kernel/samsung/rubenslte`
as checked out by `build_twrp.sh`; the others target the omni `twrp-5.1`
manifest sources — they only apply in the repo workspace, not on a host.
+49 -43
View File
@@ -1,57 +1,63 @@
# Build environment for the SM-T365 TWRP build (omni twrp-5.1, GCC 4.8-era).
#
# nixpkgs 22.11 is pinned because this old toolchain only links against legacy
# host libraries (ncurses5, 32-bit libstdc++, old glibc layout). buildFHSUserEnv
# synthesises those paths so AOSP prebuilt host binaries run unpatched.
let
pkgs = import (fetchTarball {
# An older nixpkgs version is required for old GCC and C++
# libraries.
url = "https://github.com/NixOS/nixpkgs/archive/nixos-22.11.tar.gz";
}) {};
fhs = pkgs.buildFHSUserEnv {
name = "android-env";
targetPkgs = pkgs: with pkgs; [
git
gitRepo
gnupg
python2
curl
procps
openssl
gnumake
nettools
android-tools
jdk8
schedtool
util-linux
m4
gperf
perl
libxml2
zip
unzip
bison
flex
lzop
xz
python3
];
multiPkgs = pkgs: with pkgs; [
zlib
ncurses5
pkgsi686Linux.gcc9
pkgsi686Linux.libstdcxx5
];
runScript = "bash";
profile = "export ANDROID_JAVA_HOME=${pkgs.jdk8.home}";
};
fhs = pkgs.buildFHSUserEnv {
name = "android-env";
# Tools called directly on PATH by the build and by the user.
targetPkgs = pkgs: with pkgs; [
git # the device/kernel clones (and `repo` plumbing)
gitRepo # AOSP `repo` multi-repo tool
gnumake # `mka` -> make for the omni build
python2 # omni-5.1 era build scripts expect python2
python3 # modern `repo` runs under python3
curl # toolchain tarball download in build_twrp.sh
openssl # used by a few AOSP host tools
procps # `ps`/`pidof` referenced by build scripts
util-linux # uuidgen and friends used during image building
android-tools # adb / fastboot for flashing and testing
jdk8 # 5.1 needs java <=8 (signapk, dx)
schedtool # legacy tool invoked by some omni scripts
m4 gperf bison flex # classic AOSP host toolchain
perl # kernel/scripting helper (timeconst.pl, etc.)
libxml2 # xml libs used by host tools
zip unzip # packaging
lzop # legacy lzo support
xz # LZMA ramdisk compression (mkbootimg.mk)
gnupg # gpg for repo manifest verification (optional)
nettools # minor networking helpers used by host scripts
];
# Multilib: the 5.1 ia32 host tools need 32-bit libstdc++ and zlib;
# ncurses5 is required by older host builds.
multiPkgs = pkgs: with pkgs; [
zlib
ncurses5
pkgsi686Linux.gcc9
pkgsi686Linux.libstdcxx5
];
runScript = "bash";
profile = "export ANDROID_JAVA_HOME=${pkgs.jdk8.home}";
};
in pkgs.stdenv.mkDerivation {
name = "android-env-shell";
nativeBuildInputs = [ fhs ];
shellHook = ''
# `nix-shell --run "cmd"` is unreliable here: the unconditional
# `exec android-env` would swallow the command, so forward it explicitly.
# build_twrp.sh still uses a stdin pipe because it is the tested path.
if [ -n "$BASH_EXECUTION_STRING" ]; then
exec android-env -c "$BASH_EXECUTION_STRING"
fi
exec android-env
'';
}
}