A-Team-Android_ROM_Builder/README-TECHNICAL.md
2026-08-14 21:48:48 -05:00

24 KiB
Raw Permalink Blame History

A-Team Android ROM Builder — Technical Guide

1. Architecture at a Glance

A-Team Android ROM Builder is a Python/GTK application wrapped around a script-driven Android ROM workflow.

The central design is:

GTK interface
    |
    v
Project state
    |
    +--> device configuration
    |
    +--> ROM configuration
    |
    +--> host configuration
    |
    +--> profile configuration
    |
    v
Environment generation
    |
    v
Bash scripts
    |
    +--> sync
    +--> device setup
    +--> GApps
    +--> build
    +--> clean
    +--> updater

The GUI should be treated as the control plane. The scripts remain the execution plane.

That distinction is important when extending the application: a new ROM-specific operation normally belongs in the script/configuration layer rather than becoming a large block of shell logic inside a GTK callback.


2. Source Tree

The important top-level areas are:

builder/
    Python backend and application helpers

configs/
    Device, ROM, host, setup, build-command, and upload configuration

profiles/
    Saved project profiles

scripts/
    Build and synchronization operations

gui.py
    GTK application and tab construction

The project also contains application assets and supporting files used by the GUI.


3. gui.py

gui.py is the main GTK application.

It is responsible for:

  • Creating the main window
  • Creating the application tabs
  • Creating controls
  • Reading user selections
  • Building the current project state
  • Starting backend actions
  • Starting scripts
  • Displaying terminal output
  • Displaying updater state
  • Handling application-side UI tools

The application uses GTK 3 and VTE for the embedded terminal.

UI constants

UI dimensions are intentionally centralized.

Current spacing controls include:

BUILD_COMMANDS_TOP_BOTTOM_SPACING
BUILD_SETUP_TOP_SPACING
PROJECT_PROFILES_TOP_SPACING
SYNC_SETUP_TOP_SPACING
SPLASH_TOP_BOTTOM_SPACING

Current button-width controls include:

SYNC_SETUP_BUTTON_WIDTH
SYNC_GAPPS_BUTTON_WIDTH
BUILD_ROM_BUTTON_WIDTH
LAUNCHER_BUTTON_WIDTH
CLEAN_BUTTON_WIDTH

Keep these constants centralized when modifying layout. Do not scatter repeated pixel values through widget construction unless there is a specific reason.


4. Configuration Parsing

The configuration system uses simple key/value files.

Example:

DEVICE_CODENAME="milanf"
DEVICE_SOC="sm6375"
DEVICE_MANUFACTURER="Motorola"

Conceptually this becomes:

{
    "DEVICE_CODENAME": "milanf",
    "DEVICE_SOC": "sm6375",
    "DEVICE_MANUFACTURER": "Motorola"
}

Configuration parsing supports the projects simple KEY=VALUE format and ignores comments and blank lines.

This intentionally avoids making device and ROM configuration dependent on Python code.


5. Device Discovery

Device discovery is configuration-driven.

The builder searches:

configs/devices/

for device configuration files.

A new device therefore normally does not require a Python edit.

Minimum device integration

Create:

configs/devices/example.conf

Then provide the values required by the device scripts.

For example:

DEVICE_CODENAME="example"
DEVICE_DISPLAY_CODENAME="Example"
DEVICE_DISPLAY_NAME="Example Android Device"
DEVICE_MANUFACTURER="Motorola"
DEVICE_MODEL_NUMBER="XT0000"
DEVICE_SYNC_SCRIPT="example-sync.sh"

The GUI can then discover the device from the configuration directory.


6. Device Sync Scripts

Device-specific scripts live under:

scripts/devices_sync/

A device configuration points to its script:

DEVICE_SYNC_SCRIPT="example-sync.sh"

The common synchronization workflow can then load the selected device configuration and execute the selected device-specific script.

  1. Create the device .conf.
  2. Add the device identity variables.
  3. Add only device-specific variables that scripts actually require.
  4. Create the device sync script.
  5. Set DEVICE_SYNC_SCRIPT.
  6. Test the script independently.
  7. Start the GUI and confirm the device appears.
  8. Run Sync & Setup Device Source.

Testing the shell script independently is useful because it separates Android source/setup failures from GTK failures.


7. ROM Configuration

ROM configuration can come from either:

configs/rom.conf

or:

<ROM_ROOT>/rom.conf

The ROM-local configuration is the preferred location when a ROM needs its own settings.

Why two locations exist

The builder-wide file provides defaults.

The ROM-local file allows a ROM source tree to carry its own configuration.

For example:

configs/rom.conf

could provide generic defaults while:

/home/pizzag/Android/Roms/Lineage/23.2/rom.conf

contains values specific to that ROM tree.

Case sensitivity

On Linux:

rom.conf
Rom.conf
ROM.conf

are three different names.

The loader's expected filename is:

rom.conf

8. ROM Configuration Variables

ROM configuration can contain build metadata and script variables.

Examples:

ROM_BUILD_NAME="lineage"
ROM_TARGET_RELEASE="bp4a"
DEFAULT_ROM_BUILD_COMMAND="brunch $DEVICE_CODENAME"
ROM_DISPLAY_NAME="LineageOS"
ROM_VENDOR_NAME="lineage"
ROM_MANIFEST="https://github.com/LineageOS/android.git"
ROM_BRANCH_PREFIX="lineage-"
ROM_MAJOR_VERSION="23"
ROM_MINOR_VERSION="2"

The important principle is that the ROM configuration should contain values that describe the ROM rather than values that uniquely identify a single physical device.

Device-specific values belong in the device configuration.


9. Project State

The project state is the bridge between GUI selections and scripts.

Typical values include:

DEVICE_NAME
ROM_NAME
ROM_BRANCH
ROM_PATH
ANDROID_VERSION
BUILD_VARIANT
BUILD_COMMAND
GAPPS_VARIANT
CLEAN_VARIANT
INSTALLER_VARIANT
BUILD_JOBS
USE_CCACHE
APN_VARIANT

Additional variables are merged from configuration sources.

The result is converted into an environment that can be consumed by Bash.


10. Environment Construction

The project environment combines GUI selections with configuration values.

A simplified model is:

GUI/project values
        |
        v
device variables
        |
        v
ROM variables
        |
        v
A-Team setup variables
        |
        v
upload variables
        |
        v
variable expansion

This is what allows a configuration value such as:

DEFAULT_ROM_BUILD_COMMAND="brunch $DEVICE_CODENAME"

to use the currently selected device without duplicating the device name in the ROM configuration.

When adding a new variable, decide whether it belongs to:

  • project state
  • device configuration
  • ROM configuration
  • A-Team setup
  • upload configuration

before adding Python-specific handling.


11. ROM Path Interpretation

The selected ROM directory is used to derive ROM identity.

For example:

/home/pizzag/Android/Roms/Lineage/23.2

can represent:

ROM_NAME   = Lineage
ROM_BRANCH = 23.2
ROM_PATH   = /home/pizzag/Android/Roms/Lineage/23.2

The project then exports these values to the scripts.

This is why the expected ROM directory structure matters to the GUI.


12. Build Command System

The Build tab reads:

configs/rom_build_commands.conf

Example:

Default --> rom.conf
Lineage
Derpfest

Normal entries become selectable build commands.

The command implementation is deliberately separate from the GUI:

scripts/build_options/rom_build_commands.sh

This allows the command selector to remain simple while the actual command-specific environment is handled in Bash.


13. rom_build_commands()

The build-command implementation is a Bash function:

rom_build_commands() {
    ...
}

It currently handles three paths.

Lineage

if [[ $BUILD_COMMAND == "lineage" ]]; then
   LUNCH_COMMAND="${ROM_BUILD_NAME}_${DEVICE_NAME}-${ROM_TARGET_RELEASE}-${BUILD_VARIANT}"
   ROM_BUILD_COMMAND="brunch $DEVICE_CODENAME"

Derpfest

elif [[ $BUILD_COMMAND == "derpfest" ]]; then
   LUNCH_COMMAND="${ROM_BUILD_NAME}_${DEVICE_NAME}-${ROM_TARGET_RELEASE}-${BUILD_VARIANT}"
   ROM_BUILD_COMMAND="m derp"

Default

else
   LUNCH_COMMAND="${ROM_BUILD_NAME}_${DEVICE_NAME}-${ROM_TARGET_RELEASE}-${BUILD_VARIANT}"
   ROM_BUILD_COMMAND="$DEFAULT_ROM_BUILD_COMMAND"

Each branch exports:

LUNCH_COMMAND
ROM_BUILD_COMMAND

Why the function is sourced

build.sh uses:

source "$BUILD_OPTIONS_DIR/rom_build_commands.sh"
rom_build_commands

It is important that the file is sourced rather than executed with a separate bash process.

If the function ran in a child shell, exported variables would not remain available to the parent build script.


14. Build Execution

The build entry point is:

scripts/build.sh

The script receives the project environment and performs the build preparation.

The command-selection flow is:

GUI selection
     |
     v
BUILD_COMMAND
     |
     v
rom_build_commands()
     |
     +--> LUNCH_COMMAND
     |
     +--> ROM_BUILD_COMMAND
     |
     v
build execution

The current implementation uses the resulting command strings for the ROM build.

When a command is stored as a string containing shell syntax or multiple arguments, the build script must execute it in a way consistent with how the command is defined.


15. Build Options

The GUI exposes project-level build options.

Android version

The selected Android version is exported as:

ANDROID_VERSION

Build variant

The project supports the Android build variants exposed by the current GUI.

The selected value becomes:

BUILD_VARIANT

GApps

The selected GApps mode becomes:

GAPPS_VARIANT

Clean

The clean selection becomes:

CLEAN_VARIANT

Installer

The installer selection becomes:

INSTALLER_VARIANT

Build jobs

The selected job count becomes:

BUILD_JOBS

Ccache

The checkbox controls:

USE_CCACHE

Custom APN

The Custom APN option controls the APN-related project value consumed by the build scripts.


16. Sync Workflow

The common synchronization entry point is:

scripts/sync.sh

Its role is to coordinate common setup and then dispatch to the selected device workflow.

The architecture is:

sync.sh
   |
   +--> load configuration
   |
   +--> establish A-Team setup
   |
   +--> select device script
   |
   +--> execute device sync

Device-specific behavior remains in:

scripts/devices_sync/

This prevents the common sync script from becoming a large device-specific conditional tree.


17. A-Team Setup Marker

The project uses a setup marker under the ROM tree to identify whether the A-Team setup process has already been performed.

The marker is:

device/A-Team/A-Team-Setup.check

The purpose is to prevent the common setup work from being repeated unnecessarily.

If the setup requirements change, the marker/versioning logic should be reviewed so existing ROM trees can be migrated safely.


18. MindTheGapps Synchronization

GApps synchronization is handled by:

scripts/gapps_sync.sh

The script uses the Android version to determine the appropriate MindTheGapps source.

The source is synchronized into the ROM tree at the location expected by the current build system.

This operation is separate from the core ROM build so it can be repeated without rebuilding the entire ROM.


19. Project Profiles

Profiles are stored under:

profiles/

A profile represents a reusable set of project selections.

Examples of profile-level information include:

device
ROM path
Android version
build variant
build command
GApps variant
clean variant
installer variant
build jobs
ccache
APN selection
host

Profiles should not be used as a replacement for device or ROM configuration files.

A profile says:

"Build this project this way."

A device configuration says:

"This is how this device is defined."

A ROM configuration says:

"This is how this ROM is defined."

Keeping those responsibilities separate makes the project easier to maintain.


20. Build Hosts

Host configuration is stored in:

configs/hosts.conf

The builder supports local and SSH-based hosts.

A remote host needs:

  • SSH access
  • Android build dependencies
  • ROM source
  • required device/vendor/kernel source
  • Git and other source-management tools
  • any ROM-specific build dependencies

The host configuration tells the application how to reach the machine; it does not replace the machine's Android build environment.


21. Embedded Terminal

The terminal uses VTE and provides a real interactive shell inside the application.

The terminal is intentionally not treated as a static output window.

It supports shell input and now provides desktop clipboard integration for terminal text.

Copy

Selected terminal text can be copied to the system clipboard.

The terminal copy action should not replace normal:

Ctrl+C

terminal behavior, because Ctrl+C is commonly used to interrupt a running process.

Paste

Clipboard contents can be inserted into the terminal.

Design consideration

Terminal keyboard shortcuts must be implemented carefully because shortcuts such as Ctrl+C have established Unix terminal semantics.


22. Resource Monitor

The Resource Monitor provides live system information through:

btop

with:

bpytop

as a fallback where supported by the current implementation.

It is particularly useful during Android builds because compilation can consume large amounts of:

  • CPU
  • RAM
  • storage I/O
  • swap
  • filesystem capacity

23. Tools

The Tools area contains application-side utilities.

Splash selection

The splash manager handles selection of available splash assets.

Launcher icons

The icon manager handles launcher icon selection.

Desktop launcher

The launcher utility creates a desktop entry for the builder.

Clean ROM

The clean operation invokes the appropriate backend/script workflow rather than embedding a large cleanup implementation in the GUI.


24. Updater Architecture

The updater has three distinct responsibilities:

Python updater logic
        |
        +--> update discovery
        +--> download/extraction
        |
GTK updater UI
        |
        +--> progress bar
        +--> status text
        +--> update dialog
        |
scripts/updater.sh
        |
        +--> actual staged update operations

This separation matters because the shell script reports what is happening, while the GUI decides how those reports are presented.


25. Updater Progress Protocol

The shell updater communicates progress using:

::PROGRESS::<current>::<total>

For example:

::PROGRESS::3::5

represents:

3 / 5

or:

60%

Status messages use:

::STATUS::<message>

Example:

::STATUS::Backing Up Current Installation ...

The GUI listens for these markers and updates the updater interface.

Adding an updater stage

If another stage is required:

  1. Increase TOTAL_STEPS.
  2. Add a ::PROGRESS::N::TOTAL message.
  3. Add the corresponding ::STATUS::... message.
  4. Keep the numbering sequential.
  5. Test both the script and GUI interpretation.

Do not change the marker syntax unless the Python parser is changed at the same time.


26. Updater Status Appearance

Updater status messages are intentionally displayed in:

cyan
bold

The status text is separate from the progress calculation.

Therefore:

status appearance

can be changed without changing:

progress percentage

and vice versa.

The progress calculation is based on:

current / total

while the status label is simply the human-readable description associated with that stage.


27. Update JSON and Changelog

The update JSON is loaded as a complete object.

The update popup uses the available version information and the optional:

"changelog": "..."

field.

For example:

{
    "version": 102,
    "display": "1.02",
    "file": "A-Team-Android_ROM_Builder-v1.02.7z",
    "changelog": "Improved terminal clipboard support."
}

The popup can present:

Installed Version : 1.01
Available Version : 1.02

Changelog:
Improved terminal clipboard support.

If the field is absent, the application uses its fallback text rather than failing the update dialog.


28. Adding a Device — Complete Example

Suppose a new device is called:

example

Device configuration

Create:

configs/devices/example.conf

Example:

DEVICE_CODENAME="example"
DEVICE_DISPLAY_CODENAME="Example"
DEVICE_DISPLAY_NAME="Example Phone"
DEVICE_MANUFACTURER="Motorola"
DEVICE_MODEL_NUMBER="XT0000"
DEVICE_SYNC_SCRIPT="example-sync.sh"

Device sync script

Create:

scripts/devices_sync/example-sync.sh

Make it executable:

chmod +x scripts/devices_sync/example-sync.sh

Test it

Run it manually with the required environment before involving the GUI.

Test discovery

Start the GUI and verify that:

Example Phone

appears in the device selector.

Test sync

Select the device and run:

Sync & Setup Device Source

This isolates the device integration into configuration plus one device-specific script.


29. Adding a ROM

A new ROM normally needs:

  1. A valid ROM source tree.
  2. A recognizable ROM path.
  3. A ROM configuration.
  4. A compatible build command.
  5. Any required synchronization logic.

If the ROM has unique settings, put them in:

<ROM_ROOT>/rom.conf

rather than modifying the global:

configs/rom.conf

If it needs a unique build command, add the command to:

configs/rom_build_commands.conf

and implement the command behavior in:

scripts/build_options/rom_build_commands.sh

30. Adding a Build Command

There are two parts.

Part 1 — GUI selection

Add the command name to:

configs/rom_build_commands.conf

For example:

Aosp

Part 2 — command behavior

Add a branch to:

rom_build_commands()

For example:

elif [[ $BUILD_COMMAND == "aosp" ]]; then
   LUNCH_COMMAND="${ROM_BUILD_NAME}_${DEVICE_NAME}-${ROM_TARGET_RELEASE}-${BUILD_VARIANT}"
   ROM_BUILD_COMMAND="m"
   export LUNCH_COMMAND
   export ROM_BUILD_COMMAND

Then run:

bash -n scripts/build_options/rom_build_commands.sh

before testing the GUI.


31. Adding a Configuration Variable

Before adding a new variable, identify its scope.

Device scope

configs/devices/<device>.conf

Use for:

SOC
codename
model
device-specific paths
device-specific flags

ROM scope

rom.conf

Use for:

ROM name
ROM build metadata
ROM default command
ROM manifest
ROM release metadata

Application-wide setup

configs/a_team_setup.conf

Use for values shared across projects.

Upload services

configs/upload_services.conf

Use for upload-service configuration.

Keeping variable scope correct prevents device-specific details from leaking into ROM-wide configuration.


32. Debugging Strategy

When something fails, isolate the layer.

GUI problem

Check:

gui.py
builder/*.py

Configuration problem

Check:

configs/
profiles/

Sync problem

Run:

scripts/sync.sh

directly.

Device problem

Run the device-specific script directly.

Build problem

Run:

scripts/build.sh

directly with the required environment.

Updater problem

Run:

scripts/updater.sh

directly and inspect:

::PROGRESS::
::STATUS::

output.

This approach avoids changing GUI code to solve a shell or Android-source problem.


33. Bash Validation

Before committing a shell change:

bash -n scripts/build.sh
bash -n scripts/sync.sh
bash -n scripts/gapps_sync.sh
bash -n scripts/updater.sh

For the command-selection function:

bash -n scripts/build_options/rom_build_commands.sh

Syntax validation does not prove that an Android build will succeed, but it catches malformed shell syntax before a full build.


34. Python Validation

For Python changes:

python3 -m py_compile gui.py

and for backend modules:

python3 -m py_compile builder/*.py

For GTK changes, syntax validation is not enough.

The affected UI path should also be exercised in an environment containing:

gi
Gtk 3
VTE

because GTK errors commonly occur during widget creation or signal execution rather than Python parsing.


35. Preserving Executable Permissions

The application contains executable shell scripts.

Important files include:

scripts/*.sh
scripts/build_options/*.sh
scripts/devices_sync/*.sh

The updater is one example where executable permissions matter because the Python updater launches:

scripts/updater.sh

directly.

When packaging a release, preserve executable mode, normally:

0755

for shell scripts and the executable application entry point where applicable.

A ZIP process that strips Unix permissions can cause:

PermissionError: [Errno 13] Permission denied

even when the script contents themselves are correct.


36. UI Extension Pattern

For a new button:

  1. Add a width constant if its width should be configurable.
  2. Create the GTK button.
  3. Apply the shared sizing/alignment helper.
  4. Connect its callback.
  5. Put the actual operation in an appropriate backend or script.

For example:

MY_BUTTON_WIDTH = 140

then:

set_button_width(
    my_button,
    MY_BUTTON_WIDTH
)

This keeps layout changes localized.


37. UI Layout Maintenance

When adjusting spacing:

  • Change the corresponding named constant.
  • Avoid replacing unrelated margins.
  • Keep the existing alignment model.
  • Do not duplicate spacing constants for the same visual relationship.
  • Verify the neighboring controls after a change.

The current UI intentionally exposes independent spacing values for sections that need independent tuning.


For a new feature:

Step 1 — Identify the layer

Decide whether the feature belongs in:

GUI
backend
config
script

Step 2 — Find the existing path

Reuse the existing action/config/script architecture where possible.

Step 3 — Make the smallest change

Avoid changing unrelated layout or configuration code.

Step 4 — Validate syntax

Run Python or Bash syntax checks.

Step 5 — Exercise the affected operation

Do not stop at syntax validation.

Step 6 — Verify packaging

Ensure scripts retain executable permissions.

Step 7 — Update documentation

Update README.md for user-visible functionality and README-TECHNICAL.md for implementation details.


39. Documentation Boundary

README.md should answer:

What does the application do, how do I run it, and how do I configure it?

README-TECHNICAL.md should answer:

How does it work, where is the code, how do the components communicate, and how do I extend or debug it?

Avoid duplicating every implementation detail in the normal README.

The technical document should be detailed where implementation knowledge matters, but should remain organized around the application's actual architecture and workflows.


40. Current Extension Points

The cleanest existing extension points are:

configs/devices/
    Add devices

configs/rom.conf
    Add ROM defaults

<ROM_ROOT>/rom.conf
    Add ROM-local configuration

configs/rom_build_commands.conf
    Add build command choices

scripts/devices_sync/
    Add device-specific sync logic

scripts/build_options/
    Add build-option behavior

scripts/
    Add reusable workflow operations

builder/
    Add reusable Python-side functionality

gui.py
    Add/modify user interface

When possible, use these extension points instead of introducing special-case logic into unrelated components.