Buildozer Integration
Overview
PSProject supports Android builds through Buildozer integration. This guide explains how to initialize a project with Buildozer configuration and manage dependencies across both iOS (via psproject) and Android (via Buildozer) platforms using a unified pyproject.toml file.
The toml2spec tool (explained in detail below) enables you to manage all configuration in pyproject.toml and export it to Buildozer's buildozer.spec format when needed.
Getting Started
Initialize Project with Buildozer Support
PSProject can initialize a project with Buildozer configuration included:
Create a new project with Buildozer support with following option added
This creates a project structure with:
- Standard pyproject.toml for project metadata and dependencies
- Buildozer configuration sections under [tool.buildozer]
- Development dependencies including Buildozer
Import existing Buildozer spec
If you have an existing buildozer.spec file, you can import its configuration:
Project Structure
After initialization, your project structure will look like:
MyApp/
├── pyproject.toml # Unified project configuration
├── main.py # Your application entry point
├── project_dist/
│ ├── android/
│ │ └── .buildozer/
│ │ └── buildozer.spec # Generated Buildozer spec
│ └── xcode/ # iOS project (after xcode creation)
Installing toml2spec Tool
The toml2spec tool bridges pyproject.toml and buildozer.spec, allowing you to manage configuration in TOML format and export to Buildozer's spec format.
What is a uv tool?
UV tools are globally available command-line applications installed in an isolated environment. They're accessible from anywhere on your system without activating a virtual environment.
Alternative: Install in project environment
If you prefer to install it within your project's virtual environment:
Verify Installation
Configuration Structure
pyproject.toml with Buildozer Sections
When using --buildozer, your pyproject.toml will include Buildozer configuration under [tool.buildozer]:
[project]
name = "MyApp"
version = "0.1.0"
description = "My cross-platform Kivy application"
readme = "README.md"
authors = [
{ name = "Your Name", email = "you@example.com" }
]
requires-python = ">=3.13"
dependencies = [
"kivy>=2.3.1",
"pillow>=10.0.0",
"requests>=2.31.0",
]
[project.scripts]
myapp = "myapp:main"
[build-system]
requires = ["uv_build>=0.9.2,<0.10.0"]
build-backend = "uv_build"
[dependency-groups]
dev = [
"buildozer>=1.5.0",
"toml2spec>=0.1.0",
]
iphoneos = []
[tool.psproject]
app_name = "MyApp"
backends = []
cythonized = false
extra_index = []
pip_install_app = false
[tool.psproject.ios]
backends = []
extra_index = [
"https://pypi.anaconda.org/beeware/simple",
"https://pypi.anaconda.org/pyswift/simple",
"https://pypi.anaconda.org/kivyschool/simple"
]
[tool.psproject.ios.info_plist]
[tool.psproject.ios.swift_packages]
[tool.psproject.macos]
backends = []
extra_index = []
[tool.psproject.macos.info_plist]
[tool.psproject.macos.swift_packages]
[tool.psproject.swift_packages]
# Buildozer configuration starts here
[tool.buildozer.app]
title = "MyApp"
package_name = "myapp"
package_domain = "org.example"
# Source configuration
source_dir = "../../src/myapp" # Path to your app module (adjust 'myapp' to your actual module name)
source_include_exts = ["py", "png", "jpg", "kv", "atlas"]
source_exclude_dirs = ["tests", "bin", "venv", ".venv", "project_dist"]
# Version info
version = "0.1.0"
version_regex = "__version__ = ['\"](.+?)['\"]"
version_filename = "%(source_dir)s/main.py"
# Requirements for p4a/buildozer-specific needs
# Python dependencies should be in [project.dependencies] instead
requirements = python3,android
# Application settings
orientation = "portrait"
fullscreen = 0
presplash_filename = "%(source_dir)s/data/presplash.png"
icon_filename = "%(source_dir)s/data/icon.png"
[tool.buildozer.android]
# Android API configuration
api = 33
minapi = 21
ndk = 25b
# Build configuration
accept_sdk_license = true
archs = ["arm64-v8a", "armeabi-v7a"]
# Permissions
permissions = [
"INTERNET",
"ACCESS_NETWORK_STATE",
]
[tool.buildozer.android.gradle]
# Gradle dependencies (if needed)
dependencies = []
[tool.buildozer]
# Build output
log_level = 2
warn_on_root = 1
Key Differences from Traditional buildozer.spec
Dependency Management Changes
Traditional buildozer.spec:
With pyproject.toml:
Section Naming Convention
Buildozer spec sections are converted to TOML tables:
| buildozer.spec | pyproject.toml |
|---|---|
[app] |
[tool.buildozer.app] |
[buildozer] |
[tool.buildozer] |
[app:android] |
[tool.buildozer.android] |
[app:android.gradle] |
[tool.buildozer.android.gradle] |
Dependency Management
Python Package Dependencies
All Python packages that can be installed via pip should be listed in [project.dependencies]:
[project]
dependencies = [
"kivy>=2.3.1",
"kivymd>=1.1.1",
"pillow>=10.0.0",
"requests>=2.31.0",
"firebase-admin>=6.0.0",
]
Platform-Specific Requirements
Keep only platform-specific requirements in [tool.buildozer.app]:
[tool.buildozer.app]
# Platform-specific or non-pip requirements
requirements = python3,android,openssl,libffi
Common Platform Requirements
python3- Python interpreterandroid- Android support packagehostpython3- Build-time Pythonopenssl- SSL/TLS supportlibffi- Foreign function interfacesdl2- Simple DirectMedia Layer
These are p4a (python-for-android) recipes, not pip packages.
Source Directory Configuration
The source_dir setting in [tool.buildozer.app] depends on where buildozer runs relative to your source code:
Source directory paths
If your code is in project root:
If your code is in src/app_name/ directory:
Since buildozer runs from project_dist/android/, the path is relative to that location:
- ../../ navigates up to the project root
- src/myapp/ points to your app's source directory
Project structure with src/app_name/
MyApp/
├── src/
│ └── myapp/
│ ├── __init__.py
│ └── main.py
├── pyproject.toml
└── project_dist/
└── android/ # Buildozer runs here
└── .buildozer/
For this structure, use: source_dir = "../../src/myapp"
Development Dependencies
Tools like Buildozer and toml2spec should be development dependencies:
Exporting to buildozer.spec
After modifying your pyproject.toml, export the configuration to buildozer.spec:
Export configuration
What toml2spec Does
- Reads
pyproject.tomlconfiguration - Merges
[project.dependencies]with[tool.buildozer.app]requirements - Converts TOML format to INI format
- Generates a complete
buildozer.specfile
Dependency Merging
Input (pyproject.toml):
[project]
dependencies = ["kivy>=2.3.1", "pillow>=10.0.0"]
[tool.buildozer.app]
requirements = python3,android
Output (buildozer.spec):
Workflow
Typical Development Workflow
-
Initialize project with Buildozer support:
-
Install development tools:
-
Develop your application:
-
Add dependencies to pyproject.toml:
-
Export to buildozer.spec:
-
Build for Android:
-
Build for iOS:
Continuous Integration
For CI/CD pipelines, automate the export step:
- name: Export buildozer configuration
run: |
uv tool install toml2spec
uv run toml2spec pyproject.toml project_dist/android/.buildozer/buildozer.spec
- name: Build Android APK
run: |
cd project_dist/android
buildozer android debug
Complete Example
Full pyproject.toml with Buildozer
[project]
name = "MyKivyApp"
version = "1.0.0"
description = "A cross-platform Kivy application"
readme = "README.md"
authors = [
{ name = "Your Name", email = "you@example.com" }
]
requires-python = ">=3.13"
dependencies = [
"kivy>=2.3.1",
"kivymd>=1.1.1",
"pillow>=10.0.0",
"requests>=2.31.0",
"python-dotenv>=1.0.0",
]
[project.scripts]
mykivyapp = "mykivyapp:main"
[build-system]
requires = ["uv_build>=0.9.2,<0.10.0"]
build-backend = "uv_build"
[dependency-groups]
dev = [
"buildozer>=1.5.0",
"toml2spec>=0.1.0",
"pytest>=7.0.0",
]
iphoneos = []
[tool.psproject]
app_name = "MyKivyApp"
backends = []
cythonized = false
extra_index = []
pip_install_app = false
[tool.psproject.ios]
backends = []
extra_index = [
"https://pypi.anaconda.org/beeware/simple",
"https://pypi.anaconda.org/pyswift/simple",
"https://pypi.anaconda.org/kivyschool/simple"
]
[tool.psproject.ios.info_plist]
[tool.psproject.ios.swift_packages]
[tool.psproject.macos]
backends = []
extra_index = []
[tool.psproject.macos.info_plist]
[tool.psproject.macos.swift_packages]
[tool.psproject.swift_packages]
# Buildozer Configuration
[tool.buildozer.app]
title = "My Kivy App"
package_name = "mykivyapp"
package_domain = "com.example"
source_dir = "../../src/mykivyapp" # Path to your app module (adjust 'mykivyapp' to your actual module name)
source_include_exts = ["py", "png", "jpg", "kv", "atlas", "ttf"]
source_exclude_dirs = ["tests", "bin", ".buildozer", "venv", ".venv", "project_dist"]
source_exclude_patterns = ["*.pyc", "*.pyo", "__pycache__/*"]
version = "1.0.0"
version_regex = "__version__ = ['\"](.+?)['\"]"
version_filename = "%(source_dir)s/main.py"
# Platform-specific requirements only
requirements = python3,android,openssl,libffi
orientation = "portrait"
fullscreen = 0
# Assets
presplash_filename = "%(source_dir)s/assets/presplash.png"
icon_filename = "%(source_dir)s/assets/icon.png"
presplash_color = "#FFFFFF"
[tool.buildozer.android]
# API levels
api = 33
minapi = 21
ndk = 25b
# Build settings
accept_sdk_license = true
archs = ["arm64-v8a", "armeabi-v7a"]
# Permissions
permissions = [
"INTERNET",
"ACCESS_NETWORK_STATE",
"WRITE_EXTERNAL_STORAGE",
"READ_EXTERNAL_STORAGE",
]
# Features
features = ["android.hardware.camera"]
# Metadata
android_entrypoint = "org.kivy.android.PythonActivity"
android_apptheme = "@android:style/Theme.NoTitleBar"
[tool.buildozer.android.gradle]
dependencies = [
"com.google.android.material:material:1.8.0",
]
[tool.buildozer]
log_level = 2
warn_on_root = 1
Build Commands
# Export to buildozer.spec
uv run toml2spec pyproject.toml project_dist/android/.buildozer/buildozer.spec
# Build Android debug APK
cd project_dist/android
buildozer android debug
# Build Android release APK
buildozer android release
# Deploy to connected device
buildozer android deploy run
Migration from Existing buildozer.spec
If you have an existing Buildozer project:
Step 1: Initialize with existing spec
Step 2: Move pip requirements
Identify pip-installable packages in your old spec:
Move pip packages to pyproject.toml:
[project]
dependencies = [
"kivy>=2.3.1",
"pillow>=10.0.0",
"requests>=2.31.0",
"kivymd>=1.1.1",
]
[tool.buildozer.app]
# Keep only platform-specific
requirements = python3,android
Step 3: Verify sections
Check that all sections from your old spec are present in pyproject.toml:
[app]→[tool.buildozer.app][app:android]→[tool.buildozer.android][buildozer]→[tool.buildozer]
Step 4: Export and test
uv run toml2spec pyproject.toml project_dist/android/.buildozer/buildozer.spec
cd project_dist/android
buildozer android debug
Troubleshooting
Common Issues
Requirements not found
Problem: Build fails with "Could not find X recipe"
Solution: Check if X is a pip package. If yes, move it to [project.dependencies]. If it's a p4a recipe, keep it in [tool.buildozer.app] requirements.
Version conflicts
Problem: Buildozer complains about version specifiers
Solution: toml2spec strips version specifiers when exporting to buildozer.spec. Buildozer will use the version installed by pip from [project.dependencies].
Export path doesn't exist
Problem: toml2spec fails because directory doesn't exist
Solution: Create the directory first:
Debugging
Enable verbose logging:
Check exported spec:
Best Practices
Single Source of Truth
Keep all dependency versions in pyproject.toml. Let toml2spec handle the export to buildozer.spec.
Version Control
Add to .gitignore:
Don't commit the generated buildozer.spec - generate it during build from pyproject.toml.
Automated Export
Add a script to automate export:
Test on Both Platforms
Regularly test your app on both iOS and Android to catch platform-specific issues early:
Reference
toml2spec Command Options
# Basic usage
toml2spec <input_toml> <output_spec>
# With custom section mapping
toml2spec --mapping custom_mapping.json pyproject.toml buildozer.spec
# Dry run (don't write file)
toml2spec --dry-run pyproject.toml buildozer.spec
Supported Buildozer Sections
All standard Buildozer sections are supported:
[tool.buildozer.app]- Application settings[tool.buildozer.android]- Android-specific settings[tool.buildozer.android.gradle]- Gradle dependencies[tool.buildozer.ios]- iOS settings (use psproject instead)[tool.buildozer]- Build settings
iOS Configuration
While you can include [tool.buildozer.ios], psproject handles iOS builds independently. Use [tool.psproject.ios] for iOS configuration instead.