29 KiB
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:
-
Python application/backend
- GUI
- project state
- configuration loading
- environment generation
- profile management
- terminal integration
- resource monitoring
- host management
- updater handling
-
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
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:
gui.py
The application initializes GTK, creates the ROMBuilder window, and constructs the application's tabs.
The current interface contains:
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:
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:
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:
DEVICE_CODENAME="milanf"
DEVICE_SOC="sm6375"
ROM_DISPLAY_NAME="LineageOS"
becomes a Python dictionary:
{
"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:
builder/device_manager.py
The application scans:
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:
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:
configs/devices/<device_name>.conf
For example:
configs/devices/example.conf
A practical device configuration should define the device identity and the variables required by its scripts.
Example:
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:
DEVICE_CODENAME="milanf"
DEVICE_DISPLAY_CODENAME
Human-readable short name used in messages.
DEVICE_DISPLAY_CODENAME="Milanf"
DEVICE_DISPLAY_NAME
Name shown in the GUI device selector.
DEVICE_DISPLAY_NAME="Moto G Stylus 5G 2022"
DEVICE_SYNC_SCRIPT
Name of the device-specific synchronization script.
DEVICE_SYNC_SCRIPT="milanf-sync.sh"
The main scripts/sync.sh script uses this value to locate:
scripts/devices_sync/<DEVICE_SYNC_SCRIPT>
Device-specific variables
Any additional variables can be added to the device configuration.
For example:
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:
scripts/devices_sync/
A device configuration selects its script with:
DEVICE_SYNC_SCRIPT="example-sync.sh"
The main synchronization flow effectively becomes:
scripts/sync.sh
|
+--> load device configuration
|
+--> A-Team setup
|
+--> determine DEVICE_SYNC_SCRIPT
|
+--> cd "$ROM_PATH"
|
+--> run scripts/devices_sync/<device-script>
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:
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:
ROM_BRANCH
ROM_NAME
from the directory structure.
For a path such as:
/home/pizzag/Android/Roms/Lineage/23.2
the project interprets:
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
configs/rom.conf
ROM-local override
<ROM_ROOT>/rom.conf
The loader checks the selected ROM root first.
If:
<ROM_ROOT>/rom.conf
exists, it is loaded.
Otherwise:
configs/rom.conf
is loaded.
This allows a ROM tree to carry its own configuration.
Example
Builder:
A-Team-Android_ROM_Builder/
└── configs/
└── rom.conf
ROM:
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:
rom.conf
in lowercase.
On Linux:
rom.conf
ROM.conf
Rom.conf
are different filenames.
The build-command dropdown may display a human-readable entry such as:
Default --> Rom.conf
but the actual loader filename is:
rom.conf
12. ROM Configuration Variables
The ROM configuration is not restricted to a fixed set of variables.
Example:
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:
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:
device_vars
rom_vars
setup_vars
upload_vars
The project then resolves references such as:
$DEVICE_CODENAME
${DEVICE_CODENAME}
Nested variable references are supported through repeated resolution passes.
This allows a ROM configuration to contain:
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:
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:
configs/rom_build_commands.conf
The current file can contain entries such as:
Default --> Rom.conf
Lineage
Derpfest
Lines beginning with # and blank lines are ignored.
Entries containing:
-->
are treated as display-only entries by the current loader.
Normal entries are converted into a lowercase command value.
The selected command becomes:
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:
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:
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:
- Copies the current process environment.
- Adds
project.as_environment(). - Verifies that the script exists.
- Executes it with Bash.
- 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:
scripts/sync.sh
It:
- Determines the builder root.
- Locates the device configuration.
- Checks for the A-Team setup marker.
- Installs/runs the A-Team addon setup when necessary.
- Determines the selected device synchronization script.
- Changes to the selected ROM source.
- Runs the device-specific synchronization script.
The device setup marker is stored under the ROM tree:
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:
scripts/gapps_sync.sh
The script selects the MindTheGapps branch from ANDROID_VERSION.
The current mapping includes:
Android 16 -> baklava
Android 17 -> cinnamonbun
The destination is:
$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:
16
17
The selected value becomes:
ANDROID_VERSION
Build Variant
Current options include:
Userdebug
Eng
User
The internal project values are lowercase equivalents.
GApps Variant
Current options include:
Vanilla
Gapps
Micro-G
Foss
Clean Variant
Current options include:
Full
Lite
None
Installer Variant
Current options include:
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:
USE_CCACHE
Custom APN
The Custom APN checkbox controls:
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:
profiles/
A profile can preserve:
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:
builder/hosts_manager.py
Configuration:
configs/hosts.conf
The format is INI-style.
Example:
[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:
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:
/bin/bash
This is intentionally independent from the script runner.
It gives the developer a direct shell for:
repo
git
lunch
m
brunch
adb
fastboot
grep
sed
python
bash
and other normal Android development operations.
25. Resource Monitor
The Resource Monitor launches:
btop
when available.
If btop is unavailable, the script checks for:
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:
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:
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:
button.set_size_request(140, -1)
prefer:
button.set_size_request(
BUILD_ROM_BUTTON_WIDTH,
-1
)
This keeps layout tuning centralized.
28. Updater Architecture
Updater functionality is split between:
builder/updater.py
builder/updater_overlay.py
scripts/updater.sh
The shell updater emits machine-readable progress messages.
For example:
::PROGRESS::1::5
::STATUS::Downloading Update ...
The current updater stages are:
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:
- Create or sync the ROM source tree.
- Select the ROM source directory in the GUI.
- Add or select the appropriate ROM configuration.
- Define required variables in
rom.conf. - Add a build command entry if desired.
- Ensure
scripts/build.shunderstands the resulting environment. - Add any ROM-specific sync/build scripts where needed.
If the ROM should carry its own settings, place:
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:
configs/devices/<codename>.conf
Step 2 — Device identity
Define:
DEVICE_CODENAME
DEVICE_DISPLAY_CODENAME
DEVICE_DISPLAY_NAME
DEVICE_MANUFACTURER
DEVICE_MODEL_NUMBER
Step 3 — Device sync script
Create:
scripts/devices_sync/<codename>-sync.sh
Step 4 — Connect the two
Set:
DEVICE_SYNC_SCRIPT="<codename>-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:
configs/rom_build_commands.conf
Add a simple entry:
Lineage
or:
Derpfest
The current GUI turns normal entries into a lowercase command value.
If a display-only/configuration entry is desired:
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:
configs/devices/<device>.conf
ROM-specific
Put it in:
configs/rom.conf
or preferably the ROM-local:
<ROM_ROOT>/rom.conf
when the value belongs only to that ROM.
A-Team-wide setup
Put it in:
configs/a_team_setup.conf
Upload-specific
Put it in:
configs/upload_services.conf
Then consume it from 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:
- Is the file present?
- Is the filename correct?
- Is the filename case correct?
- Does the line contain
=? - Is the variable name spelled correctly?
- Is the selected device correct?
- Does the selected ROM contain its own
rom.conf? - If it does, is that local file overriding
configs/rom.conf? - Does the variable reference another variable that exists?
- 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:
cd /path/to/A-Team-Android_ROM_Builder
bash scripts/sync.sh
or:
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
python3 -m py_compile gui.py
Startup
python3 gui.py
UI construction
Verify that all tabs initialize:
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:
- Add a constant near the other UI constants.
- Create the widget.
- Apply the constant.
- Connect the signal.
- Implement the callback.
- Keep actual work in the backend/action/script layer.
Example:
MY_BUTTON_WIDTH = 140
Then:
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:
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:
Device:
Moto G Stylus 5G 2022
ROM:
Lineage
Branch:
23.2
Android:
16
Build Variant:
Userdebug
Jobs:
32
The project state may become conceptually:
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:
DEVICE_CODENAME=milanf
DEVICE_SOC=sm6375
DEVICE_MANUFACTURER=Motorola
The ROM configuration adds values such as:
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:
configs/devices/milanf.conf
over:
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:
configs/devices/
and make sure the file ends in:
.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:
<ROM_ROOT>/rom.conf
exists.
If it does, it overrides:
configs/rom.conf
Build command is missing
Check:
configs/rom_build_commands.conf
and restart the application.
Device sync script is missing
Check:
DEVICE_SYNC_SCRIPT
and verify:
scripts/devices_sync/<script>
exists.
Remote host does not appear
The host loader only exposes the Local host or SSH host entries with a non-empty:
server_address
Resource monitor does not start
Install:
btop
or:
bpytop
GApps sync fails
Check:
- Android version
- Git
- Git LFS
- network access
- destination ROM path
41. Extending the Architecture
The project is suitable for adding additional managers.
A future feature should generally follow this structure:
GUI
|
+--> Backend module
|
+--> Project/config data
|
+--> Action
|
+--> Script
For example, a future kernel configuration feature could be:
gui.py
|
builder/kernel_manager.py
|
scripts/kernel_setup.sh
rather than placing the entire kernel setup implementation inside gui.py.
42. Current Configuration Files
The current project contains:
configs/
├── a_team_setup.conf
├── devices/
│ ├── avatrn.conf
│ ├── fogo.conf
│ ├── fogos-1.conf
│ ├── fogos-2.conf
│ ├── genevn.conf
│ ├── milanf.conf
│ ├── mona.conf
│ ├── rtwo-1.conf
│ ├── rtwo-2.conf
│ └── rtwo-3.conf
├── hosts.conf
├── rom.conf
├── rom_build_commands.conf
└── upload_services.conf
This demonstrates the intended extensibility of the configuration system.
43. Current Script Categories
The script layer contains functionality for:
Build
Clean
Device Sync
A-Team Setup
GApps Sync
Updater
Resource Monitoring
Uploading
Device-specific synchronization scripts live separately under:
scripts/devices_sync/
This allows the common synchronization flow to remain stable while individual devices can have different setup requirements.
44. Updater Progress Protocol
Updater status is communicated using dedicated lines.
Status:
::STATUS::Some Status Text
Progress:
::PROGRESS::<current>::<total>
For example:
::PROGRESS::3::5
::STATUS::Backing Up Current Installation ...
The GUI recognizes these messages and updates the updater overlay.
If future updater stages are added, keep this protocol stable.
45. Safe Extension Pattern for Scripts
When adding a script:
- Put it under
scripts/. - Make it executable where appropriate.
- Validate required environment variables.
- Fail with a non-zero exit code when an operation fails.
- Print useful progress information.
- Avoid assuming a specific ROM unless the script is intentionally ROM-specific.
- Use
$ROM_PATHrather than hard-coded source paths. - Use device configuration variables instead of duplicating device-specific values.
46. Summary
A-Team Android ROM Builder is best understood as a project orchestration layer for Android ROM development.
The most important concepts are:
Device config
+
ROM config
+
GUI project selections
|
v
Project environment
|
v
Bash scripts
|
v
Android ROM source tree
The configuration-driven design means developers can add devices, ROMs, build commands, variables, scripts, and hosts without continuously modifying the core GUI.
For new development, preserve that separation.
The GUI should remain the control surface.
The builder/ package should manage application state and actions.
The configs/ directory should describe the environment.
The scripts/ directory should perform the actual ROM-development work.
The ROM-local rom.conf mechanism should be used whenever a ROM needs to carry its own build configuration independently of the application-wide default.