# A-Team Android ROM Builder — Technical Documentation ## 1. Purpose A-Team Android ROM Builder is a Python/GTK 3 application that provides a graphical project-management layer around Android ROM development. The application is deliberately split into two major areas: 1. **Python application/backend** - GUI - project state - configuration loading - environment generation - profile management - terminal integration - resource monitoring - host management - updater handling 2. **Bash/script layer** - ROM synchronization - device setup - GApps synchronization - ROM building - cleaning - uploading - device-specific patches and setup This separation is one of the most important architectural characteristics of the project. The GUI should generally manage **what** project is being operated on, while scripts determine **how** the Android source tree is synchronized, modified, built, cleaned, or uploaded. --- ## 2. High-Level Architecture ```text gui.py | +-------------+-------------+ | | | v v v Project Configs UI Managers | | | +-------------+-------------+ | v Project.as_environment() | v Script / Action Layer | +------------+------------+ | | | v v v sync.sh build.sh clean.sh | v device sync scripts | v Android ROM tree ``` The GUI owns project selections. The `Project` object converts those selections and configuration files into an environment that scripts consume. --- # 3. Main Application Entry Point The primary entry point is: ```text gui.py ``` The application initializes GTK, creates the `ROMBuilder` window, and constructs the application's tabs. The current interface contains: ```text 1- Build Setup 2- Sync & Setup Device Source 3- Build Terminal Resource Monitor Tools ``` The exact tab construction is contained in `ROMBuilder`. --- # 4. Python Backend The `builder/` directory contains the application backend. Current major modules include: ```text builder/ ├── actions.py ├── button_actions.py ├── config_loader.py ├── device_manager.py ├── hosts_manager.py ├── icon_manager.py ├── launcher_shortcut.py ├── profile_manager.py ├── project.py ├── project_loader.py ├── project_summary.py ├── resource_monitor.py ├── script_runner.py ├── splash.py ├── splash_manager.py ├── terminal.py ├── updater.py ├── updater_overlay.py └── version.py ``` The backend is intentionally separated from GTK-specific code wherever practical. --- # 5. Configuration Loader `builder/config_loader.py` contains the basic configuration parser. It reads simple: ```text KEY=VALUE ``` files. Supported behavior includes: - Blank lines are ignored - Lines beginning with `#` are comments - Lines without `=` are ignored - Values may be surrounded by single or double quotes - `~` is expanded to the user's home directory Example: ```text DEVICE_CODENAME="milanf" DEVICE_SOC="sm6375" ROM_DISPLAY_NAME="LineageOS" ``` becomes a Python dictionary: ```python { "DEVICE_CODENAME": "milanf", "DEVICE_SOC": "sm6375", "ROM_DISPLAY_NAME": "LineageOS" } ``` This deliberately simple format makes configuration files easy to edit from a terminal or text editor. --- # 6. Device Discovery Device discovery is handled by: ```text builder/device_manager.py ``` The application scans: ```text configs/devices/ ``` for `.conf` files. A device does not need to be hard-coded into the GUI. This means adding a properly formatted configuration file is the primary mechanism for adding a device. The device manager reads: ```text DEVICE_DISPLAY_NAME ``` for the user-facing device name. If it is not present, the configuration filename is used as a fallback display name. The device list is sorted by display name. --- # 7. Adding a New Device To add a new device, create: ```text configs/devices/.conf ``` For example: ```text configs/devices/example.conf ``` A practical device configuration should define the device identity and the variables required by its scripts. Example: ```text DEVICE_CODENAME="example" DEVICE_DISPLAY_CODENAME="Example" DEVICE_DISPLAY_NAME="Example Android Device" DEVICE_KERNEL="5.4" DEVICE_SOC="smXXXX" DEVICE_MANUFACTURER="Motorola" DEVICE_MODEL_NUMBER="XTXXXX" DEVICE_SYNC_SCRIPT="example-sync.sh" ``` ### Important variables #### `DEVICE_CODENAME` The Android/device-tree codename used by scripts. Example: ```text DEVICE_CODENAME="milanf" ``` #### `DEVICE_DISPLAY_CODENAME` Human-readable short name used in messages. ```text DEVICE_DISPLAY_CODENAME="Milanf" ``` #### `DEVICE_DISPLAY_NAME` Name shown in the GUI device selector. ```text DEVICE_DISPLAY_NAME="Moto G Stylus 5G 2022" ``` #### `DEVICE_SYNC_SCRIPT` Name of the device-specific synchronization script. ```text DEVICE_SYNC_SCRIPT="milanf-sync.sh" ``` The main `scripts/sync.sh` script uses this value to locate: ```text scripts/devices_sync/ ``` ### Device-specific variables Any additional variables can be added to the device configuration. For example: ```text DEVICE_KERNEL="5.4" DEVICE_SOC="sm6375" DEVICE_MANUFACTURER="Motorola" DEVICE_MODEL_NUMBER="XT2215" ``` Those values become part of the project environment and can therefore be consumed by Bash scripts. --- # 8. Device Sync Scripts Device-specific sync scripts live under: ```text scripts/devices_sync/ ``` A device configuration selects its script with: ```text DEVICE_SYNC_SCRIPT="example-sync.sh" ``` The main synchronization flow effectively becomes: ```text scripts/sync.sh | +--> load device configuration | +--> A-Team setup | +--> determine DEVICE_SYNC_SCRIPT | +--> cd "$ROM_PATH" | +--> run scripts/devices_sync/ ``` This allows each device to have completely different synchronization or patching requirements without turning the main GUI into a collection of device-specific conditionals. --- # 9. Project Object `builder/project.py` contains the `Project` dataclass. Important project state includes: ```text profile_name host_name device_name rom_name rom_branch android_version rom_path build_variant build_command gapps_variant clean_variant installer_variant build_jobs use_ccache custom_apn device_vars rom_vars rom_config_path setup_vars upload_vars ``` This object is the central representation of the currently selected project. --- # 10. ROM Path Interpretation When a ROM source path is selected, `Project.set_rom_path()` derives: ```text ROM_BRANCH ROM_NAME ``` from the directory structure. For a path such as: ```text /home/pizzag/Android/Roms/Lineage/23.2 ``` the project interprets: ```text ROM_NAME = Lineage ROM_BRANCH = 23.2 ROM_PATH = /home/pizzag/Android/Roms/Lineage/23.2 ``` This is important because the scripts receive these values automatically. --- # 11. ROM Configuration System There are two supported locations for the ROM configuration. ### Application-level default ```text configs/rom.conf ``` ### ROM-local override ```text /rom.conf ``` The loader checks the selected ROM root first. If: ```text /rom.conf ``` exists, it is loaded. Otherwise: ```text configs/rom.conf ``` is loaded. This allows a ROM tree to carry its own configuration. ### Example Builder: ```text A-Team-Android_ROM_Builder/ └── configs/ └── rom.conf ``` ROM: ```text Lineage/ └── 23.2/ ├── build/ ├── device/ ├── vendor/ └── rom.conf ``` The second file overrides the builder-level ROM configuration for that project. ### Case sensitivity The loader currently checks for: ```text rom.conf ``` in lowercase. On Linux: ```text rom.conf ROM.conf Rom.conf ``` are different filenames. The build-command dropdown may display a human-readable entry such as: ```text Default --> Rom.conf ``` but the actual loader filename is: ```text rom.conf ``` --- # 12. ROM Configuration Variables The ROM configuration is not restricted to a fixed set of variables. Example: ```text M="make" CCACHE_SIZE="300" DEFAULT_ROM_BUILD_COMMAND="brunch $DEVICE_CODENAME" ROM_BUILD_NAME=lineage ROM_TARGET_RELEASE="bp4a" ROM_FINAL_LOCATION="~/Desktop/Release_Test" ROM_BUILD_ZIP_NAME="*-UNOFFICIAL-$DEVICE_CODENAME.zip" 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" ``` Additional variables can be added when scripts require them. --- # 13. Variable Resolution `Project.as_environment()` combines several sources of variables. The environment begins with GUI/project values. Examples: ```text 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 ``` Then configuration dictionaries are merged: ```text device_vars rom_vars setup_vars upload_vars ``` The project then resolves references such as: ```text $DEVICE_CODENAME ${DEVICE_CODENAME} ``` Nested variable references are supported through repeated resolution passes. This allows a ROM configuration to contain: ```text DEFAULT_ROM_BUILD_COMMAND="brunch $DEVICE_CODENAME" ``` without requiring Python code to know what the device codename is. --- # 14. Environment Precedence The environment is constructed approximately in this order: ```text Base project variables | v Device variables | v ROM variables | v A-Team setup variables | v Upload variables | v Variable reference resolution ``` Therefore, configuration values should be named carefully to avoid unintentionally overriding a project variable. --- # 15. Build Commands The Build tab reads: ```text configs/rom_build_commands.conf ``` The current file can contain entries such as: ```text Default --> Rom.conf Lineage Derpfest ``` Lines beginning with `#` and blank lines are ignored. Entries containing: ```text --> ``` are treated as display-only entries by the current loader. Normal entries are converted into a lowercase command value. The selected command becomes: ```text BUILD_COMMAND ``` in the project environment. This makes it possible to expand the GUI's build command selector without modifying the Build tab itself. --- # 16. Build Execution The Build button calls the action layer. The action layer creates a temporary environment shell file containing the current project variables. Conceptually: ```bash export DEVICE_NAME='milanf' export ROM_NAME='Lineage' export ROM_BRANCH='23.2' export ROM_PATH='/home/pizzag/...' export ANDROID_VERSION='16' ... ``` The requested script is then executed. The build script is: ```text scripts/build.sh ``` The GUI therefore does not need to contain the ROM's actual build command logic. --- # 17. Script Execution `builder/script_runner.py` provides generic script execution. It: 1. Copies the current process environment. 2. Adds `project.as_environment()`. 3. Verifies that the script exists. 4. Executes it with Bash. 5. Returns the exit status. The action layer provides another mechanism that is useful when the GUI needs a shell command containing the generated environment. This architecture allows scripts to remain independently testable. For example, a script can generally be debugged directly from a terminal after exporting the required variables. --- # 18. Sync Workflow The main synchronization entry point is: ```text scripts/sync.sh ``` It: 1. Determines the builder root. 2. Locates the device configuration. 3. Checks for the A-Team setup marker. 4. Installs/runs the A-Team addon setup when necessary. 5. Determines the selected device synchronization script. 6. Changes to the selected ROM source. 7. Runs the device-specific synchronization script. The device setup marker is stored under the ROM tree: ```text device/A-Team/A-Team-Setup.check ``` When the marker exists, the main A-Team setup is skipped. This avoids repeatedly performing setup operations on the same ROM tree. --- # 19. MindTheGapps Synchronization The GApps script is: ```text scripts/gapps_sync.sh ``` The script selects the MindTheGapps branch from `ANDROID_VERSION`. The current mapping includes: ```text Android 16 -> baklava Android 17 -> cinnamonbun ``` The destination is: ```text $ROM_PATH/vendor/gapps ``` The existing GApps source is removed before cloning the selected branch. Git LFS assets are then pulled. --- # 20. Build Options The GUI currently exposes several build-related selections. ### Android Version Current GUI options include: ```text 16 17 ``` The selected value becomes: ```text ANDROID_VERSION ``` ### Build Variant Current options include: ```text Userdebug Eng User ``` The internal project values are lowercase equivalents. ### GApps Variant Current options include: ```text Vanilla Gapps Micro-G Foss ``` ### Clean Variant Current options include: ```text Full Lite None ``` ### Installer Variant Current options include: ```text Custom A-Team ROM Installer Default ROM Installer ``` ### Build Jobs The Build Jobs control determines the parallel job count passed into the project. ### Ccache The Ccache checkbox controls: ```text USE_CCACHE ``` ### Custom APN The Custom APN checkbox controls: ```text APN_VARIANT ``` The current project environment represents enabled/disabled boolean selections using the values expected by the existing script layer. --- # 21. Project Profiles Project profiles are JSON files stored in: ```text profiles/ ``` A profile can preserve: ```text profile_name host_name device_name rom_path android_version build_variant build_command gapps_variant clean_variant installer_variant build_jobs use_ccache custom_apn ``` Profiles are useful for repeatedly building the same device/ROM combination. A profile does not replace the device or ROM configuration files. Instead, it stores the user's selected project state and the configuration system is still loaded when the project is used. --- # 22. Build Hosts Hosts are managed by: ```text builder/hosts_manager.py ``` Configuration: ```text configs/hosts.conf ``` The format is INI-style. Example: ```ini [Local] type=local [BuildServer] type=ssh server_address=192.168.1.100 port=22 user=pizzag remote_rom_source_path=/home/pizzag/Android ssh_options= ``` The host manager builds SSH arguments using: ```text server_address user port ssh_options ``` SSH options can contain an identity file and other supported SSH arguments. --- # 23. Remote Build Considerations Remote builds require more than simply having SSH available. The remote machine needs: - Android build dependencies - Git - Repo where required - Git LFS where required - ROM source at the expected location - Device/vendor/kernel source as required by the ROM - Any external tools required by the scripts The GUI is the management layer. It does not automatically install every Android build dependency on a remote machine. --- # 24. Terminal The Terminal tab creates a VTE terminal and starts: ```text /bin/bash ``` This is intentionally independent from the script runner. It gives the developer a direct shell for: ```text repo git lunch m brunch adb fastboot grep sed python bash ``` and other normal Android development operations. --- # 25. Resource Monitor The Resource Monitor launches: ```text btop ``` when available. If `btop` is unavailable, the script checks for: ```text bpytop ``` This is useful during large builds because Android compilation can heavily stress CPU, memory, storage, and other system resources. --- # 26. Tools Tab The Tools tab currently handles application-side utility functions. ## Splash Screen The splash selector loads available splash assets and stores the selected splash. ## Desktop Launcher Icon The icon selector loads available launcher icon choices. The selected icon is used when creating the application desktop launcher. ## Desktop Launcher The launcher button invokes the launcher creation backend. ## Clean ROM The clean button invokes the ROM cleaning action. The clean operation itself remains script/backend driven. --- # 27. UI Sizing Variables UI dimensions are deliberately exposed as constants near the top of `gui.py`. Current button width controls include: ```text SYNC_SETUP_BUTTON_WIDTH SYNC_GAPPS_BUTTON_WIDTH BUILD_ROM_BUTTON_WIDTH LAUNCHER_BUTTON_WIDTH CLEAN_BUTTON_WIDTH ``` This means button widths can be adjusted without hunting through individual GTK layout blocks. Other UI spacing controls include: ```text BUILD_SETUP_TOP_SPACING PROJECT_PROFILES_TOP_SPACING SYNC_SETUP_TOP_SPACING ``` These provide independent control over vertical spacing in the corresponding UI sections. This pattern should be followed when adding future UI dimensions. Instead of hard-coding: ```python button.set_size_request(140, -1) ``` prefer: ```python button.set_size_request( BUILD_ROM_BUTTON_WIDTH, -1 ) ``` This keeps layout tuning centralized. --- # 28. Updater Architecture Updater functionality is split between: ```text builder/updater.py builder/updater_overlay.py scripts/updater.sh ``` The shell updater emits machine-readable progress messages. For example: ```text ::PROGRESS::1::5 ::STATUS::Downloading Update ... ``` The current updater stages are: ```text 1/5 Downloading Update 2/5 Extracting Update 3/5 Backing Up Current Installation 4/5 Installing Update 5/5 Update Installed Successfully ``` The GTK overlay consumes these messages and advances its progress bar. This is preferable to guessing progress from arbitrary terminal output. --- # 29. Adding a New ROM A ROM does not normally require a new Python module. The typical process is: 1. Create or sync the ROM source tree. 2. Select the ROM source directory in the GUI. 3. Add or select the appropriate ROM configuration. 4. Define required variables in `rom.conf`. 5. Add a build command entry if desired. 6. Ensure `scripts/build.sh` understands the resulting environment. 7. Add any ROM-specific sync/build scripts where needed. If the ROM should carry its own settings, place: ```text rom.conf ``` at the ROM root. This is especially useful when multiple ROM trees have different: - build commands - manifest information - branch naming - vendor names - output locations - release naming - version values --- # 30. Recommended Device Addition Workflow When adding a device, use this order: ### Step 1 — Device config Create: ```text configs/devices/.conf ``` ### Step 2 — Device identity Define: ```text DEVICE_CODENAME DEVICE_DISPLAY_CODENAME DEVICE_DISPLAY_NAME DEVICE_MANUFACTURER DEVICE_MODEL_NUMBER ``` ### Step 3 — Device sync script Create: ```text scripts/devices_sync/-sync.sh ``` ### Step 4 — Connect the two Set: ```text DEVICE_SYNC_SCRIPT="-sync.sh" ``` ### Step 5 — Add device-specific variables Add only the values actually required by the device's scripts. ### Step 6 — Test the sync script directly Before debugging the GUI, test the shell script with the required environment. ### Step 7 — Launch the GUI The device should automatically appear in the device selector because discovery scans `configs/devices/*.conf`. --- # 31. Adding a New Build Command Edit: ```text configs/rom_build_commands.conf ``` Add a simple entry: ```text Lineage ``` or: ```text Derpfest ``` The current GUI turns normal entries into a lowercase command value. If a display-only/configuration entry is desired: ```text Default --> Rom.conf ``` can be used. After changing this file, restart the application so the Build tab reloads the list. --- # 32. Adding a New Build Variable If a script needs another value, first determine where the value belongs. ### Device-specific Put it in: ```text configs/devices/.conf ``` ### ROM-specific Put it in: ```text configs/rom.conf ``` or preferably the ROM-local: ```text /rom.conf ``` when the value belongs only to that ROM. ### A-Team-wide setup Put it in: ```text configs/a_team_setup.conf ``` ### Upload-specific Put it in: ```text configs/upload_services.conf ``` Then consume it from Bash: ```bash echo "$MY_VARIABLE" ``` Avoid modifying Python simply to introduce a variable that is naturally a configuration value. --- # 33. Debugging Configuration If a configuration value appears to be missing, check in this order: 1. Is the file present? 2. Is the filename correct? 3. Is the filename case correct? 4. Does the line contain `=`? 5. Is the variable name spelled correctly? 6. Is the selected device correct? 7. Does the selected ROM contain its own `rom.conf`? 8. If it does, is that local file overriding `configs/rom.conf`? 9. Does the variable reference another variable that exists? 10. Does the script receive the environment being expected? The `Project.debug_dump()` helper can be useful when inspecting project state. --- # 34. Debugging the Script Layer Because the project is script-driven, a useful debugging technique is to run the underlying script manually. For example: ```bash cd /path/to/A-Team-Android_ROM_Builder bash scripts/sync.sh ``` or: ```bash bash scripts/build.sh ``` with the required environment exported. This isolates Android build problems from GTK problems. If the script fails directly, changing the GUI usually will not fix the underlying ROM build problem. --- # 35. Debugging the GUI When modifying `gui.py`, test in layers. ### Syntax ```bash python3 -m py_compile gui.py ``` ### Startup ```bash python3 gui.py ``` ### UI construction Verify that all tabs initialize: ```text Build Setup Sync & Setup Device Source Build Terminal Resource Monitor Tools ``` ### Action testing Exercise the changed button or dropdown rather than stopping after startup. This is particularly important for GTK code because a valid Python file can still fail during widget construction. --- # 36. Adding a New GUI Control The preferred pattern is: 1. Add a constant near the other UI constants. 2. Create the widget. 3. Apply the constant. 4. Connect the signal. 5. Implement the callback. 6. Keep actual work in the backend/action/script layer. Example: ```python MY_BUTTON_WIDTH = 140 ``` Then: ```python self.my_button = Gtk.Button( label="My Action" ) set_button_width( self.my_button, MY_BUTTON_WIDTH ) self.my_button.connect( "clicked", self.my_action_clicked ) ``` This is preferable to embedding a large shell workflow directly inside a GTK callback. --- # 37. Why Scripts Should Stay Separate Android ROM build operations frequently become device- and ROM-specific. Keeping the logic in: ```text scripts/ ``` allows: - shell testing without GTK - easier debugging - device-specific scripts - ROM-specific behavior - reuse outside the GUI - simpler Python code - easier migration between machines The GUI should primarily select and prepare the environment. --- # 38. Environment Flow Example Suppose the user selects: ```text Device: Moto G Stylus 5G 2022 ROM: Lineage Branch: 23.2 Android: 16 Build Variant: Userdebug Jobs: 32 ``` The project state may become conceptually: ```text DEVICE_NAME=milanf ROM_NAME=Lineage ROM_BRANCH=23.2 ANDROID_VERSION=16 BUILD_VARIANT=userdebug BUILD_JOBS=32 ``` The device configuration then adds values such as: ```text DEVICE_CODENAME=milanf DEVICE_SOC=sm6375 DEVICE_MANUFACTURER=Motorola ``` The ROM configuration adds values such as: ```text ROM_VENDOR_NAME=lineage ROM_BRANCH_PREFIX=lineage- ROM_MAJOR_VERSION=23 ROM_MINOR_VERSION=2 ``` The final environment is what the scripts consume. --- # 39. Design Principles for Future Development When extending the project, prefer these rules: ### Keep GUI and build logic separate Do not move large Bash workflows into GTK callbacks. ### Prefer configuration over hard-coding If a value changes between devices or ROMs, it probably belongs in a config file. ### Prefer device configs over device conditionals Prefer: ```text configs/devices/milanf.conf ``` over: ```python if device == "milanf": ... ``` ### Prefer ROM-local `rom.conf` for ROM-specific behavior This keeps a ROM's settings with its source tree. ### Keep reusable UI dimensions centralized Add constants rather than hard-coding sizes in multiple places. ### Test the actual UI Python syntax validation is not sufficient for GTK changes. ### Test scripts independently A failing Android build script should be debugged as a script before changing GUI code. --- # 40. Common Failure Areas ## Device does not appear Check: ```text configs/devices/ ``` and make sure the file ends in: ```text .conf ``` Also verify that it is not named one of the configuration files intentionally ignored by the device scanner. ## ROM settings seem wrong Check whether: ```text /rom.conf ``` exists. If it does, it overrides: ```text configs/rom.conf ``` ## Build command is missing Check: ```text configs/rom_build_commands.conf ``` and restart the application. ## Device sync script is missing Check: ```text DEVICE_SYNC_SCRIPT ``` and verify: ```text scripts/devices_sync/