Build Runner & Parsing Services
Relevant source files
The following files were used as context for generating this wiki page:
The Build Runner & Parsing Services form the core engine responsible for executing Gradle tasks, capturing real-time output, and transforming raw logs into structured diagnostic data. This system manages the lifecycle of build processes, persists build history, and provides the necessary intelligence to identify compiler errors, warnings, and build artifacts like APKs.
This module bridges the gap between the underlying shell-based Gradle execution and the high-level reactive UI. It ensures that builds are monitored for timeouts, can be cancelled safely, and that their results are archived for future inspection or AI-driven analysis via the MCP Server.
Sources: [src-tauri/src/services/build_runner.rs], [src-tauri/src/commands/build.rs]
Architecture & Data Flow
The architecture follows a delegation pattern where Tauri commands validate user input before handing off execution to a specialized BuildState service. The system uses a multi-threaded approach: the Rust backend manages a process_manager to handle child processes, while the frontend maintains a reactive build.store.ts to reflect the current state to the user.
Build Execution Flow
The following diagram illustrates the sequence of events when a user initiates a build task.
The system utilizes a Channel<BuildLine> to stream output asynchronously from Rust to TypeScript, ensuring the UI remains responsive during long-running builds.
Sources: [src-tauri/src/commands/build.rs:55-154], [src/stores/build.store.ts:107-124]
Build State Management
The BuildState struct is the central authority for build information. It tracks the currently running process ID (PID), the current BuildStatus, and maintains a bounded history of past builds.
Data Structures
| Structure | Description | Storage |
|---|---|---|
BuildStateInner | Tracks current_build PID, status, and history deque. | Memory (Arc/Mutex) |
BuildRecord | A snapshot of a completed build (ID, task, status, errors, timestamp). | JSON (Disk) |
BuildLog | A ring-buffer of raw output lines, capped at MAX_BUILD_LOG (5,000). | Memory |
Sources: [src-tauri/src/services/build_runner.rs:60-120]
Retention & Persistence
Build results are persisted to ~/.keynobi/build-history.json. Additionally, raw logs are stored as JSON Lines (.jsonl) files in a dedicated build-logs directory. The system implements a rotation policy based on age, orphan status (not in history), and a total folder size cap.
Sources: [src-tauri/src/services/build_runner.rs:24-58], [src-tauri/src/services/build_runner.rs:136-213]
Output Parsing & Diagnostics
The build_parser (integrated within build_runner) uses regex and string patterns to interpret Gradle's plain console output. It specifically identifies:
- Task Boundaries: Detects
> Task :app:compileDebugKotlinand task outcomes (e.g.,UP-TO-DATE,FAILED). - Compiler Issues: Parses Kotlin and Java errors/warnings, extracting file paths, line numbers, and column offsets.
- Build Summary: Detects
BUILD SUCCESSFULorBUILD FAILEDand extracts duration strings (e.g.,1m 23s).
Parsing Logic Example
The parser recognizes various diagnostic formats, including Kotlin file URIs and standard Java compiler errors:
// Matches: e: file:///Users/dev/app/src/Main.kt:42:13: Unresolved reference
// Matches: src/main/java/com/example/Foo.java:23: error: cannot find symbol
Sources: [src-tauri/src/services/build_runner.rs:400-450], [src-tauri/tests/build_integration.rs:30-80]
Build Inspection
Before running a build, the build_inspector can statically analyze build.gradle or build.gradle.kts files. This allows the system to discover project metadata without executing a full Gradle sync.
| Field | Extraction Method |
|---|---|
| SDK Levels | Extracts compileSdk, minSdk, and targetSdk. |
| Application ID | Extracts the package name, handling applicationIdSuffix. |
| Build Variants | Parses buildTypes and productFlavors blocks. |
Sources: [src-tauri/src/services/build_inspector.rs:37-70], [src-tauri/src/commands/file_system.rs:200-230]
MCP Integration
The Build Runner is exposed to AI agents via the Model Context Protocol (MCP). This allows agents to trigger builds, read diagnostic errors, and fetch raw logs for troubleshooting.
Available Build Tools in MCP
| Tool Name | Purpose | Parameters |
|---|---|---|
run_gradle_task | Executes a specific Gradle task with a timeout. | task, variant |
get_build_errors | Returns structured JSON errors from the last build. | None |
get_build_log | Fetches raw lines from the ring buffer. | lines (limit) |
find_apk_path | Resolves the location of a generated APK for a variant. | variant |
Sources: [src-tauri/src/services/mcp_server.rs:218-350]
Artifact Resolution
Once a build succeeds, the build_runner searches for the resulting APK. It follows a prioritized search strategy:
- Variant Match: Signed APK in a directory matching the variant name (e.g.,
outputs/apk/debug/). - Unsigned Fallback: Unsigned APK in a variant-specific directory.
- Global Search: Any usable APK under the
outputs/apktree, excluding-unaligned.apkfiles.
Sources: [src-tauri/src/services/build_runner.rs:232-300]
Conclusion
The Build Runner & Parsing Services provide a robust, asynchronous framework for Android development. By combining low-level process management with sophisticated log parsing and static file inspection, Keynobi offers a highly integrated environment that serves both human developers and AI agents through its structured data models and MCP interfaces.