# Copyright (C) 2026 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.
import subprocess import re import dataclasses from typing import Any, Callable, Optional from pathlib import Path from .env import BuildContext from .constants import SOONG_UI_BASH from interface.errors import ToolError
@dataclasses.dataclass(frozen=True) class BuildFailure: """Represents a specific failure in the build."""
message: str
target: Optional[str] = None
command: Optional[str] = None
outputs: Optional[str] = None
@dataclasses.dataclass(frozen=True) class BuildResult: """Represents the outcome of a build."""
success: bool
exit_code: int
failure_details: Optional[list[BuildFailure]] = None
def strip_ansi_codes(text: str) -> str: """Removes ANSI escape sequences from a string."""
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') return ansi_escape.sub('', text)
def parse_build_log(raw_log: str) -> list[BuildFailure]: """
Parses the Android error.log content into a structured list of failures.
Returns a list of BuildFailure objects. """ # Remove ANSI codes to ensure clean regex matching
clean_log = strip_ansi_codes(raw_log)
# Regex to extract structured Soong errors # Format is defined in build/soong/ui/status/log.go
soong_pattern = re.compile(
r"FAILED:\s+(?P<target>.*)\n"
r"(?:Outputs:\s+(?P<outputs>.*)\n)?"
r"(?:Error:\s+(?P<error_summary>.*)\n)?"
r"(?:Command:\s+(?P<command>.*)\n)?"
r"Output:\n(?P<output>[\s\S]*?)(?=\n\nFAILED:|$)"
)
matches = list(soong_pattern.finditer(clean_log))
if matches: # Scenario 1: Structured Log Found
structured_failures = [] for m in matches:
data = m.groupdict()
if process.stdout: for line in iter(process.stdout.readline, ''): if progress_callback:
match = progress_re.match(line) if match:
current = float(match.group(2))
total = float(match.group(3))
progress_callback(current, total)
if process.stderr: for line in iter(process.stderr.readline, ''): pass
def build_targets(ctx: BuildContext, targets: list[str], clean: bool = False, enforce_no_reanalysis: bool = False, progress_callback: Optional[Callable[[float, Optional[float]], None]] = None) -> BuildResult: """
Executes an Android build for the given targets.
Use this to compile code, generate artifacts, or run phony targets like 'nothing'or'module-info'.
Returns a BuildResult object with success status and failure details if applicable.
Args:
ctx: Evaluated build configuration.
targets: List of build targets (e.g., 'SystemUI', 'nothing'). These can be module names or Ninja targets.
clean: IfTrue, runs 'installclean' before building.
enforce_no_reanalysis: IfTrue, blocks re-running analysis and throws an error if reanalysis is required.
progress_callback: Optional callback for progress reporting. """ # Step 1: Installclean if requested if clean:
exit_code = _execute_build_command(ctx, ["installclean"], enforce_no_reanalysis=False, progress_callback=None) if exit_code != 0: return _create_failed_result(ctx, exit_code)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.