From de6bcec8872d7315d3218935b08eb3b9c603cd17 Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Sat, 5 Sep 2026 16:44:55 +0200 Subject: [PATCH] general: refactor build script; readme misc. --- .gitignore | 1 + README.md | 118 +++++++++++++++++++++++++--- build_twrp.sh | 194 ++++++++++++++++++++++++++++++++++------------ clean.sh | 6 -- patches/README.md | 20 +++++ shell.nix | 92 ++++++++++++---------- 6 files changed, 320 insertions(+), 111 deletions(-) delete mode 100755 clean.sh create mode 100644 patches/README.md diff --git a/.gitignore b/.gitignore index a6124fe..20ab098 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /twrp-build/ /recovery.img +/recovery-stock.img diff --git a/README.md b/README.md index ab87854..643c53d 100644 --- a/README.md +++ b/README.md @@ -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 \ No newline at end of file diff --git a/build_twrp.sh b/build_twrp.sh index 31de96a..b342d90 100755 --- a/build_twrp.sh +++ b/build_twrp.sh @@ -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" \ No newline at end of file +case "$cmd" in + build) reenter_build_env; do_build ;; + clean) do_clean ;; + env) do_env ;; +esac \ No newline at end of file diff --git a/clean.sh b/clean.sh deleted file mode 100755 index 2d9b3b6..0000000 --- a/clean.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cd "$(realpath "$(dirname "${BASH_SOURCE[0]}")")" - -rm -rf twrp-build recovery.img diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 0000000..a6e2314 --- /dev/null +++ b/patches/README.md @@ -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. \ No newline at end of file diff --git a/shell.nix b/shell.nix index 1174d22..c08e33a 100644 --- a/shell.nix +++ b/shell.nix @@ -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 ''; -} +} \ No newline at end of file