Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,17 +380,23 @@ BSP Registry
#### `build` — Build a BSP image

```bash
bsp build <bsp_name> [--clean] [--checkout] [--target TARGET] [--task TASK]
bsp build <bsp_name> [--deploy] [--deploy-provider PROVIDER] [--deploy-container CONTAINER] [--deploy-prefix PREFIX]
bsp build <bsp_name> [--test [--wait] [--lava-server URL] [--lava-token TOKEN] [--artifact-url URL]]
bsp build --device <device> --release <release> [--feature FEATURE...] [--target TARGET] [--task TASK] [--checkout] [--test ...]
# Build by preset name
bsp build <bsp_name> [--clean] [--checkout] [--path PATH] [--target TARGET] [--task TASK] [--deploy] [--deploy-provider PROVIDER] [--deploy-container CONTAINER] [--deploy-prefix PREFIX]
bsp build <bsp_name> [--clean] [--checkout] [--test [--wait] [--lava-server URL] [--lava-token TOKEN] [--artifact-url URL]]

# Build by components
bsp build --device DEVICE --release RELEASE [--feature FEATURE ...] [--clean] [--checkout] [--path PATH] [--target TARGET] [--task TASK]
bsp build --device <device> --release <release> [--feature FEATURE...] [--checkout] [--test ...]
```

| Option | Description |
|--------|-------------|
| `--device DEVICE`, `-d DEVICE` | Device slug (use with `--release` for component-based build) |
| `--release RELEASE` | Release slug (use with `--device` for component-based build) |
| `--feature FEATURE`, `-f FEATURE` | Feature slug to enable (can be specified multiple times) |
| `--clean` | Clean build directory before building |
| `--checkout` | Validate configuration and checkout repos without building |
| `--path PATH` | Override the output build directory path defined in the registry |
| `--target TARGET` | Bitbake build target (image or recipe) to pass to KAS, overriding any targets defined in the registry preset |
| `--task TASK` | Bitbake task to run (e.g. `compile`, `configure`) to pass to KAS |
| `--deploy` | Deploy artifacts to cloud storage after a successful build |
Expand All @@ -409,12 +415,24 @@ bsp build --device <device> --release <release> [--feature FEATURE...] [--target
**Examples:**

```bash
# Full build
# Full build using a preset
bsp build poky-qemuarm64-scarthgap

# Build using device and release components
bsp build --device qemuarm64 --release scarthgap

# Build with additional features
bsp build --device qemuarm64 --release scarthgap --feature ota --feature secure-boot

# Checkout/validate only (fast, no build)
bsp build poky-qemuarm64-scarthgap --checkout

# Override the output build directory
bsp build poky-qemuarm64-scarthgap --path /mnt/fast-ssd/build

# Component-based build with custom output path
bsp build --device qemuarm64 --release scarthgap --path /mnt/fast-ssd/build

# Build a specific Bitbake image (overrides registry-configured targets)
bsp build poky-qemuarm64-scarthgap --target core-image-minimal

Expand Down
12 changes: 12 additions & 0 deletions bsp/bsp_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,7 @@ def _build_resolved(
deploy_after_build: bool = False,
preset: Optional[BspPreset] = None,
deploy_overrides: Optional[Dict] = None,
build_path_override: Optional[str] = None,
target: Optional[str] = None,
task: Optional[str] = None,
) -> None:
Expand All @@ -994,12 +995,17 @@ def _build_resolved(
preset: Optional BSP preset whose ``deploy`` block is applied on
top of the global deploy config before CLI overrides.
deploy_overrides: CLI-level overrides for the deploy configuration
build_path_override: If provided, overrides the build output path from the registry
target: Optional Bitbake build target to override registry targets
task: Optional Bitbake task to run (e.g. compile, configure)
"""
action = "Checking out" if checkout_only else "Building"
logging.info(f"{action} {label or resolved.device.slug}")

if build_path_override is not None:
logging.info(f"Overriding build path: {build_path_override}")
resolved.build_path = build_path_override

# Build Docker image if needed (skip in checkout mode)
if not checkout_only and resolved.container:
container = resolved.container
Expand Down Expand Up @@ -1049,6 +1055,7 @@ def build_bsp(
checkout_only: bool = False,
deploy_after_build: bool = False,
deploy_overrides: Optional[Dict] = None,
build_path_override: Optional[str] = None,
target: Optional[str] = None,
task: Optional[str] = None,
) -> None:
Expand All @@ -1060,6 +1067,7 @@ def build_bsp(
checkout_only: If True, only checkout and validate without building
deploy_after_build: If True, deploy artifacts after a successful build
deploy_overrides: CLI-level overrides for the deploy configuration
build_path_override: If provided, overrides the build output path from the registry
target: Optional Bitbake build target to override registry targets
task: Optional Bitbake task to run (e.g. compile, configure)

Expand All @@ -1075,6 +1083,7 @@ def build_bsp(
deploy_after_build=deploy_after_build,
preset=preset,
deploy_overrides=deploy_overrides,
build_path_override=build_path_override,
target=target,
task=task,
)
Expand All @@ -1087,6 +1096,7 @@ def build_by_components(
checkout_only: bool = False,
deploy_after_build: bool = False,
deploy_overrides: Optional[Dict] = None,
build_path_override: Optional[str] = None,
target: Optional[str] = None,
task: Optional[str] = None,
) -> None:
Expand All @@ -1100,6 +1110,7 @@ def build_by_components(
checkout_only: If True, only checkout and validate without building
deploy_after_build: If True, deploy artifacts after a successful build
deploy_overrides: CLI-level overrides for the deploy configuration
build_path_override: If provided, overrides the build output path from the registry
target: Optional Bitbake build target to override registry targets
task: Optional Bitbake task to run (e.g. compile, configure)

Expand All @@ -1118,6 +1129,7 @@ def build_by_components(
label=f"{device_slug}/{release_slug}",
deploy_after_build=deploy_after_build,
deploy_overrides=deploy_overrides,
build_path_override=build_path_override,
target=target,
task=task,
)
Expand Down
10 changes: 10 additions & 0 deletions bsp/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ def main() -> int:
action="store_true",
help="Checkout and validate build configuration without building (fast)"
)
build_parser.add_argument(
"--path",
type=str,
dest="build_path",
metavar="PATH",
help="Override output build directory path"
)
build_parser.add_argument(
"--deploy",
action="store_true",
Expand Down Expand Up @@ -690,6 +697,7 @@ def _check_exclusive(bsp_name, device, release, parser):
release = getattr(args, "release", None)
features = getattr(args, "features", None) or []
bsp_name = getattr(args, "bsp_name", None)
build_path = getattr(args, "build_path", None)
deploy_after_build = getattr(args, "deploy_after_build", False)
deploy_overrides = _collect_deploy_overrides(args)
run_test = getattr(args, "run_test", False)
Expand All @@ -708,6 +716,7 @@ def _check_exclusive(bsp_name, device, release, parser):
checkout_only=checkout_only,
deploy_after_build=deploy_after_build,
deploy_overrides=deploy_overrides,
build_path_override=build_path,
target=target,
task=task,
)
Expand All @@ -727,6 +736,7 @@ def _check_exclusive(bsp_name, device, release, parser):
checkout_only=checkout_only,
deploy_after_build=deploy_after_build,
deploy_overrides=deploy_overrides,
build_path_override=build_path,
target=target,
task=task,
)
Expand Down
40 changes: 40 additions & 0 deletions tests/test_bsp_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,46 @@ def test_build_bsp_preset_calls_kas(self, registry_with_features_file):
manager.build_bsp("imx8-scarthgap-ota")
mock_build.assert_called_once()

def test_build_bsp_path_override(self, registry_with_features_file, tmp_dir):
"""build_bsp() with build_path_override sets resolved.build_path before building."""
custom_path = str(tmp_dir / "custom-output")
manager = BspManager(config_path=str(registry_with_features_file))
manager.initialize()
captured_paths = []

def capture_build_dir(build_path):
captured_paths.append(build_path)

with patch("bsp.bsp_manager.build_docker"), \
patch("bsp.kas_manager.KasManager.build_project"), \
patch("bsp.kas_manager.KasManager.dump_config", return_value=None), \
patch("bsp.kas_manager.KasManager.validate_kas_files", return_value=True), \
patch("bsp.kas_manager.KasManager.check_kas_available", return_value=True), \
patch.object(manager, "prepare_build_directory", side_effect=capture_build_dir):
manager.build_bsp("imx8-scarthgap-ota", build_path_override=custom_path)

assert captured_paths == [custom_path]

def test_build_by_components_path_override(self, registry_with_features_file, tmp_dir):
"""build_by_components() with build_path_override sets resolved.build_path before building."""
custom_path = str(tmp_dir / "custom-components-output")
manager = BspManager(config_path=str(registry_with_features_file))
manager.initialize()
captured_paths = []

def capture_build_dir(build_path):
captured_paths.append(build_path)

with patch("bsp.bsp_manager.build_docker"), \
patch("bsp.kas_manager.KasManager.build_project"), \
patch("bsp.kas_manager.KasManager.dump_config", return_value=None), \
patch("bsp.kas_manager.KasManager.validate_kas_files", return_value=True), \
patch("bsp.kas_manager.KasManager.check_kas_available", return_value=True), \
patch.object(manager, "prepare_build_directory", side_effect=capture_build_dir):
manager.build_by_components("imx8-board", "scarthgap", build_path_override=custom_path)

assert captured_paths == [custom_path]

def test_build_bsp_passes_target_to_kas(self, registry_with_features_file):
manager = BspManager(config_path=str(registry_with_features_file))
manager.initialize()
Expand Down
33 changes: 33 additions & 0 deletions tests/test_cli_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,39 @@ def test_main_list_releases_shows_vendor_overrides(self, registry_with_vendor_ov
assert "override" in captured.out
assert "imx-6.6.53" in captured.out

def test_main_build_with_path_override(self, registry_file, tmp_dir):
"""--path argument is forwarded to build_bsp() as build_path_override."""
custom_path = str(tmp_dir / "my-custom-build")
with patch("sys.argv", [
"bsp", "--registry", str(registry_file),
"build", "--checkout", "--path", custom_path, "test-bsp"
]):
with patch.object(BspManager, "build_bsp") as mock_build_bsp:
mock_build_bsp.return_value = None
exit_code = bsp.main()
assert exit_code == 0
mock_build_bsp.assert_called_once_with(
"test-bsp", checkout_only=True, deploy_after_build=False, deploy_overrides={},
build_path_override=custom_path, target=None, task=None
)

def test_main_build_by_components_with_path_override(self, registry_file, tmp_dir):
"""--path argument is forwarded to build_by_components() as build_path_override."""
custom_path = str(tmp_dir / "components-build")
with patch("sys.argv", [
"bsp", "--registry", str(registry_file),
"build", "--checkout", "--device", "test-device", "--release", "test-release",
"--path", custom_path
]):
with patch.object(BspManager, "build_by_components") as mock_build:
mock_build.return_value = None
exit_code = bsp.main()
assert exit_code == 0
mock_build.assert_called_once_with(
"test-device", "test-release", [], checkout_only=True, deploy_after_build=False,
deploy_overrides={}, build_path_override=custom_path, target=None, task=None
)


class TestBuildCommand:
def test_build_target_passed_to_build_bsp(self, registry_file):
Expand Down
Loading