Skip to content

Add FreeBSD cross build support#150

Merged
leynos merged 11 commits intomainfrom
codex/add-freebsd-cross-compilation-support
Oct 2, 2025
Merged

Add FreeBSD cross build support#150
leynos merged 11 commits intomainfrom
codex/add-freebsd-cross-compilation-support

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Sep 30, 2025

Summary

  • require container-backed cross builds when targeting FreeBSD from non-FreeBSD hosts and document the behaviour
  • add coverage to ensure FreeBSD targets emit a clear error when cross is unavailable
  • enable the x86_64-unknown-freebsd matrix entry in the rust-toy-app workflow

Testing

  • make test

https://chatgpt.com/codex/tasks/task_e_68db910147848322894bed2eac8fbe0f

Summary by Sourcery

Support FreeBSD cross-compilation by enforcing the use of a container-backed cross tool on non-FreeBSD hosts, including detection and clear error messaging, backed by tests, documentation updates, and CI configuration

New Features:

  • Require containerized cross builds for FreeBSD targets on non-FreeBSD hosts

Enhancements:

  • Add runtime detection to emit explicit errors when container-backed cross is unavailable for FreeBSD

CI:

  • Enable x86_64-unknown-freebsd target in the GitHub Actions build matrix

Documentation:

  • Update README to document container requirement for FreeBSD cross builds

Tests:

  • Add test to ensure error is raised when cross container is missing for FreeBSD targets

Chores:

  • Update CHANGELOG with FreeBSD cross-build requirement

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Sep 30, 2025

Reviewer's Guide

Introduce support for requiring containerized cross builds when targeting FreeBSD from non-FreeBSD hosts by adding detection logic and error handling, extending test coverage, updating CI matrix, and documenting the new behavior.

Sequence diagram for FreeBSD cross build error handling

sequenceDiagram
    participant "main()"
    participant "_requires_cross_container()"
    participant "decision"
    participant "typer"
    "main()"->>"_requires_cross_container()": Check if cross container is required
    "_requires_cross_container()"-->>"main()": Return True/False
    alt Cross container required and not available
        "main()"->>"decision": Check cross_path, use_cross_local_backend, has_container
        "main()"->>"typer": Emit error message
        "main()"->>"typer": Exit with error
    end
Loading

File-Level Changes

Change Details Files
Add test for missing containerized cross when targeting FreeBSD
  • Add test_errors_when_cross_required_but_unavailable covering error path
  • Stub rustup and which calls to simulate missing cross and container
  • Assert that the CLI emits a clear error and exits
.github/actions/rust-build-release/tests/test_target_install.py
Implement FreeBSD cross-container requirement detection and error exit
  • Introduce _requires_cross_container to detect FreeBSD targets on non-FreeBSD hosts
  • Insert pre-build check in main() to emit error and exit if no cross/container
  • Compose detailed error message listing missing cross or container runtime
.github/actions/rust-build-release/src/main.py
Document FreeBSD cross requirement in README and CHANGELOG
  • Describe container-based cross requirement in README.md
  • Add CHANGELOG entry about requiring containerized cross for FreeBSD
.github/actions/rust-build-release/README.md
.github/actions/rust-build-release/CHANGELOG.md
Enable x86_64-unknown-freebsd target in GitHub Actions workflow
  • Uncomment and activate the FreeBSD entry in the workflow matrix
  • Update rust-toy-app.yml to include x86_64-unknown-freebsd on ubuntu-latest
.github/workflows/rust-toy-app.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Summary by CodeRabbit

  • New Features

    • Automatic detection and use of Docker/Podman for FreeBSD cross-builds on non-FreeBSD hosts, with environment auto-configured and restored.
    • Clear, early errors when a required container runtime is unavailable or cannot start.
  • Documentation

    • Updated guidance on FreeBSD cross-build requirements and behaviour with container runtimes.
  • Tests

    • Extensive coverage for container detection, error paths, fallbacks, Windows probing, and linker configuration.
  • Chores

    • CI now builds a FreeBSD target on Ubuntu runners.
    • Added FreeBSD cross-builder configuration.
    • Changelog updated.

Walkthrough

Enforce container-backed cross builds for FreeBSD targets on non-FreeBSD hosts; detect Docker/Podman and export CROSS_CONTAINER_ENGINE during container-backed runs; fail fast when prerequisites are missing; add docs, tests, CI matrix entry and Cross.toml configuration.

Changes

Cohort / File(s) Summary
Docs: FreeBSD cross-build requirement
.github/actions/rust-build-release/CHANGELOG.md, .github/actions/rust-build-release/README.md
Add changelog entries and README documentation stating FreeBSD targets on non-FreeBSD hosts require containerised cross; document fail-fast behaviour and automatic export of CROSS_CONTAINER_ENGINE when a container runtime is detected.
Action logic: enforce cross/container for FreeBSD
.github/actions/rust-build-release/src/main.py
Extend _CrossDecision with container_engine and requires_cross_container; add host_target parameter to decision logic; detect Docker/Podman; mark BSD-like targets as requiring container-backed cross; set and restore CROSS_CONTAINER_ENGINE; add container-start error handling and environment cleanup.
Tests: container/cross behaviours
.github/actions/rust-build-release/tests/test_target_install.py
Add extensive tests covering required-cross error paths; successful builds with cross+container; container-engine export and preservation; fallbacks to cargo; Docker/Podman probe and timeout handling; Windows probing and linker configuration.
CI workflow: enable FreeBSD target on Ubuntu
.github/workflows/rust-toy-app.yml
Activate matrix entry to build x86_64-unknown-freebsd on ubuntu-latest.
Cross config: FreeBSD builder image
rust-toy-app/Cross.toml
Add [target.x86_64-unknown-freebsd] with ghcr.io/cross-rs/x86_64-unknown-freebsd:edge as the builder image.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CI as CI Workflow
  participant Action as rust-build-release/main.py
  participant Host as Host Env
  participant Cross as cross Tool
  participant CR as Container Runtime

  CI->>Action: Run build for target (e.g. x86_64-unknown-freebsd)
  Action->>Action: Compute host_target and decide cross usage
  alt FreeBSD target on non-FreeBSD host
    Action->>Host: Check `cross` installed
    Host-->>Action: cross present? (yes/no)
    Action->>Host: Probe container runtimes (docker/podman)
    Host-->>Action: runtime present? (docker/podman/none)
    alt Missing cross or runtime
      Action-->>CI: Exit with error (requires cross + container runtime)
    else Prerequisites present
      Action->>Action: Export `CROSS_CONTAINER_ENGINE`
      Action->>Cross: Invoke cross (container-backed)
      Cross->>CR: Launch container build
      CR-->>Cross: Build completes
      Cross-->>Action: Return status
      Action-->>CI: Success
    end
  else No container requirement
    Action->>Host: Use cargo/cross as usual
    Action-->>CI: Proceed with standard build
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Rust sails to FreeBSD’s shore,
Cross in a crate knocks at the door.
"Bring Docker, bring Podman, or halt the run!"
CI probes, the engines hum.
Tests cheer, docs sing on.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly summarises the addition of FreeBSD cross build support, which is the primary focus of this pull request.
Description Check ✅ Passed The description outlines the enforcement of container-backed cross builds for FreeBSD targets, documentation and test updates, and CI configuration changes, which directly reflect the changes in this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/add-freebsd-cross-compilation-support

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce419a6 and e39e90d.

📒 Files selected for processing (1)
  • .github/actions/rust-build-release/src/main.py (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/rust-build-release/src/main.py (2)
.github/actions/rust-build-release/src/runtime.py (1)
  • runtime_available (205-264)
.github/actions/rust-build-release/src/cross_manager.py (1)
  • ensure_cross (164-249)
🔍 Remote MCP Deepwiki

Below is the key context for PR #150 review:

  1. Error‐message formatting in main.py (still using leading semicolon)

    # around line ~432 in .github/actions/rust-build-release/src/main.py
    details = []
    if not decision.cross_installed:
        details.append("cross is not installed")
    if decision.requires_cross_container and not decision.container_engine:
        details.append("no container runtime detected")
    detail_suffix = f"; {', '.join(details)}" if details else ""
    print(f"error: cannot build target {target}{detail_suffix}", file=sys.stderr)
    sys.exit(1)

    • The prefix "; " remains, so the output is e.g.
    “error: cannot build target x86_64-unknown-freebsd; cross is not installed, no container runtime detected”
    Requested change: use a leading comma or remove the semicolon so that the suffix begins with ", " instead of "; ". (extracted from repository history)

  2. Positive‐path test added

    def test_builds_freebsd_target_with_cross_and_container(...):
        # Mocks cross installed and docker available
        result = run_build(target="x86_64-unknown-freebsd", ...)
        assert result.success
        assert "CROSS_CONTAINER_ENGINE" in result.env

    Confirms the intended behavior when both cross and Docker/Podman are present.

    [::ai_summary::]

  3. Decision dataclass updated

    @dataclass
    class _CrossDecision:
        cross_installed: bool
        requires_cross: bool
        requires_cross_container: bool  # newly added
        container_engine: Optional[str]  # newly added
        ...

    Indicates refactoring completed as requested.

    [::ai_summary::]

Summary:

  • Follow‐up 2 (refactor) and 3 (positive‐path test) are resolved.
  • Follow‐up 1 (error‐message formatting prefix) remains outstanding: change "; {…}" to ", {…}".
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: python-tests-windows
  • GitHub Check: python-tests (ubuntu-latest)
  • GitHub Check: python-tests (macos-13)
  • GitHub Check: build-release (windows-latest, aarch64-pc-windows-gnullvm)
  • GitHub Check: build-release (windows-latest, x86_64-pc-windows-gnullvm)
  • GitHub Check: Sourcery review
🔇 Additional comments (10)
.github/actions/rust-build-release/src/main.py (10)

21-21: LGTM! Constant extraction follows DRY principles.

The BSD_TARGET_SUFFIXES tuple and the additional imports are consistent with the existing pattern and improve maintainability.

Also applies to: 38-42


84-85: LGTM! Refactor into _CrossDecision is complete.

The addition of container_engine and requires_cross_container fields aligns with the requested refactoring and improves the decision structure.


293-299: LGTM! BSD container requirement logic is correct.

The suffix-based detection correctly identifies BSD targets that require container-backed cross-compilation when the host is not also BSD. Normalisation ensures case-insensitive matching.


306-310: LGTM! Container engine detection is correct.

The logic correctly prioritises Docker over Podman and assigns the detected engine to container_engine.


359-360: LGTM! Return statement is consistent with the updated fields.

The return statement correctly includes the new container_engine and requires_cross_container fields.


410-412: LGTM! Host target is correctly passed to the decision logic.

The host_target variable is now passed to _decide_cross_usage, enabling the BSD container requirement logic to compare target and host.


415-440: LGTM! Error handling is correct and follow-up #2 is resolved.

The error handling logic correctly rejects builds when:

  1. CROSS_NO_DOCKER=1 is set for targets requiring containers (lines 416-424).
  2. Cross or container runtime is missing for targets requiring containers (lines 426-440).

Line 432 now uses a leading comma (", ") instead of a semicolon, which resolves follow-up #2 from the PR objectives. The error messages are clear and actionable.


454-460: LGTM! Environment variable export is correct.

The logic correctly exports CROSS_CONTAINER_ENGINE only when using cross with a container and tracks whether to restore the variable in the finally block.


476-486: LGTM! Enhanced error message for container failures.

The enhanced error message correctly provides specific context when cross fails to start a container for targets requiring containers (e.g., FreeBSD on Linux hosts).


503-505: LGTM! Environment variable restoration is correct.

The finally block correctly restores the environment variable to prevent pollution after the build.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Ruff (0.13.2)
.github/actions/rust-build-release/src/main.py

�[1;31mruff failed�[0m
�[1mCause:�[0m Failed to load configuration /ruff.toml
�[1mCause:�[0m Failed to parse /ruff.toml
�[1mCause:�[0m TOML parse error at line 26, column 3
|
26 | "RSE100", # Use of assert detected
| ^^^^^^^^
Unknown rule selector: RSE100


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • Refactor the new _requires_cross_container logic into the existing decision flow (e.g. CrossDecision) to avoid duplicating normalization and make cross support rules more centralized.
  • Simplify the error message detail suffix by listing missing prerequisites in a single, concise phrase (using commas instead of a semicolon list) to improve readability.
  • Add a positive-path test for FreeBSD targets to verify that builds succeed when both cross and a container runtime are available.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Refactor the new _requires_cross_container logic into the existing decision flow (e.g. CrossDecision) to avoid duplicating normalization and make cross support rules more centralized.
- Simplify the error message detail suffix by listing missing prerequisites in a single, concise phrase (using commas instead of a semicolon list) to improve readability.
- Add a positive-path test for FreeBSD targets to verify that builds succeed when both cross and a container runtime are available.

## Individual Comments

### Comment 1
<location> `.github/actions/rust-build-release/src/main.py:408-413` </location>
<code_context>
         toolchain_name, installed_names, rustup_exec, target_to_build
     )

+    if _requires_cross_container(target_to_build, DEFAULT_HOST_TARGET) and not decision.use_cross:
+        details: list[str] = []
+        if decision.cross_path is None:
+            details.append("cross is not installed")
+        if not decision.use_cross_local_backend and not decision.has_container:
+            details.append("no container runtime detected")
+        detail_suffix = f" ({'; '.join(details)})" if details else ""
+        typer.echo(
+            "::error:: target "
+            f"'{target_to_build}' requires cross with a container runtime on this host"
+            f"{detail_suffix}",
+            err=True,
+        )
+        raise typer.Exit(1)
+
     if not target_installed and (
</code_context>

<issue_to_address>
**suggestion:** Error message could clarify which host and target are involved.

Displaying both host and target triples in the error will help users quickly identify configuration issues, particularly in automated environments.

```suggestion
        typer.echo(
            "::error:: target "
            f"'{target_to_build}' requires cross with a container runtime "
            f"on host '{DEFAULT_HOST_TARGET}' (target: '{target_to_build}'){detail_suffix}",
            err=True,
        )
```
</issue_to_address>

### Comment 2
<location> `.github/actions/rust-build-release/src/main.py:142` </location>
<code_context>
     return toolchain_name


+def _requires_cross_container(target: str, host_target: str) -> bool:
+    """Return True when *target* needs cross with containers on this host."""
+
</code_context>

<issue_to_address>
**issue (review_instructions):** Add behavioural and unit tests for the new _requires_cross_container function.

The new function _requires_cross_container introduces logic for determining when a containerized cross build is required. You must add both behavioural and unit tests to verify its correctness under various target/host combinations.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 3
<location> `.github/actions/rust-build-release/src/main.py:401` </location>
<code_context>
         toolchain_name, installed_names, rustup_exec, target_to_build
     )

+    if _requires_cross_container(target_to_build, DEFAULT_HOST_TARGET) and not decision.use_cross:
+        details: list[str] = []
+        if decision.cross_path is None:
</code_context>

<issue_to_address>
**issue (review_instructions):** Add behavioural and unit tests for the new error handling logic for FreeBSD cross builds.

The new error handling and early exit for FreeBSD cross builds on non-FreeBSD hosts is a behavioural change. You must add tests that demonstrate this behaviour, including cases where the error is triggered and where it is not.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 4
<location> `.github/actions/rust-build-release/README.md:5` </location>
<code_context>

 Build Rust application release artefacts using the repository's `setup-rust` action, `uv`, and `cross`.

+FreeBSD targets (for example `x86_64-unknown-freebsd`) require `cross` with a
+container runtime when built on non-FreeBSD hosts. The action enforces this so
+that builds fail fast if Docker or Podman are unavailable.
</code_context>

<issue_to_address>
**suggestion (review_instructions):** This paragraph exceeds the 80 column wrapping limit for Markdown text.

Please wrap this paragraph so that no line exceeds 80 columns, as per the documentation style guide.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Paragraphs and bullets must be wrapped to 80 columns

</details>
</issue_to_address>

### Comment 5
<location> `.github/actions/rust-build-release/README.md:6` </location>
<code_context>
 Build Rust application release artefacts using the repository's `setup-rust` action, `uv`, and `cross`.

+FreeBSD targets (for example `x86_64-unknown-freebsd`) require `cross` with a
+container runtime when built on non-FreeBSD hosts. The action enforces this so
+that builds fail fast if Docker or Podman are unavailable.
+
</code_context>

<issue_to_address>
**suggestion (review_instructions):** This line is part of a paragraph that is not wrapped to 80 columns.

Ensure all lines in paragraphs and bullet points are wrapped to 80 columns for readability.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Paragraphs and bullets must be wrapped to 80 columns

</details>
</issue_to_address>

### Comment 6
<location> `.github/actions/rust-build-release/README.md:7` </location>
<code_context>

+FreeBSD targets (for example `x86_64-unknown-freebsd`) require `cross` with a
+container runtime when built on non-FreeBSD hosts. The action enforces this so
+that builds fail fast if Docker or Podman are unavailable.
+
 > [!NOTE]
</code_context>

<issue_to_address>
**suggestion (review_instructions):** This line continues the paragraph and should be wrapped to 80 columns.

Please wrap this paragraph so that each line does not exceed 80 columns.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Paragraphs and bullets must be wrapped to 80 columns

</details>
</issue_to_address>

### Comment 7
<location> `.github/actions/rust-build-release/src/main.py:146` </location>
<code_context>
def _requires_cross_container(target: str, host_target: str) -> bool:
    """Return True when *target* needs cross with containers on this host."""

    normalized = target.strip().lower()
    host_normalized = host_target.strip().lower()
    if normalized.endswith("unknown-freebsd"):
        return not host_normalized.endswith("unknown-freebsd")
    return False

</code_context>

<issue_to_address>
**suggestion (code-quality):** Move assignments closer to their usage ([`move-assign`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/move-assign/))

```suggestion
        host_normalized = host_target.strip().lower()
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/actions/rust-build-release/src/main.py Outdated
Comment thread .github/actions/rust-build-release/src/main.py Outdated
coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
.github/actions/rust-build-release/tests/test_target_install.py (1)

114-146: LGTM! Test validates FreeBSD cross-requirement error path.

The test correctly exercises the fail-fast logic when x86_64-unknown-freebsd is requested without cross+container support. The assertion confirms the expected error is raised.

Optionally strengthen the assertion to validate the detailed diagnostic message structure introduced at lines 407-421 of main.py:

 err = capsys.readouterr().err
 assert "requires cross" in err
+assert "cross" in err or "container runtime" in err
+assert "missing:" in err

This would catch regressions in the diagnostic detail formatting.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bac6f5d and b35f464.

📒 Files selected for processing (2)
  • .github/actions/rust-build-release/src/main.py (7 hunks)
  • .github/actions/rust-build-release/tests/test_target_install.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
.github/actions/rust-build-release/src/main.py (2)
.github/actions/rust-build-release/src/runtime.py (1)
  • runtime_available (205-264)
.github/actions/rust-build-release/src/cross_manager.py (1)
  • ensure_cross (164-249)
.github/actions/rust-build-release/tests/test_target_install.py (3)
.github/actions/rust-build-release/tests/conftest.py (5)
  • module_harness (169-178)
  • patch_shutil_which (134-136)
  • patch_platform (138-140)
  • patch_attr (142-144)
  • patch_run_cmd (123-132)
conftest.py (8)
  • cmd_mox (149-153)
  • CmdMox (58-77)
  • _register_rustup_toolchain_stub (115-121)
  • replay (71-73)
  • verify (75-77)
  • _register_cross_version_stub (96-112)
  • _register_docker_info_stub (124-131)
  • _register_podman_info_stub (134-141)
.github/actions/rust-build-release/src/main.py (1)
  • main (382-474)
🔍 Remote MCP

Additional Context for FreeBSD Container-Backed Cross Builds

  • The cross tool requires a Linux host with binfmt_misc support for container-based cross builds. Without binfmt_misc (a kernel facility for interpreting non-native binaries), containerized targets will fail to launch properly in QEMU contexts.

  • By default, cross probes for Docker first, then Podman, when invoking containerized builds. If neither runtime is found, cross will error fast.

  • You can override which container engine cross uses by setting the CROSS_CONTAINER_ENGINE environment variable. For example, CROSS_CONTAINER_ENGINE=podman forces cross to exclusively use Podman.

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: python-tests (macos-13)
  • GitHub Check: python-tests-windows
  • GitHub Check: python-tests (ubuntu-latest)
  • GitHub Check: build-release (windows-latest, x86_64-pc-windows-gnullvm)
  • GitHub Check: Sourcery review
🔇 Additional comments (14)
.github/actions/rust-build-release/src/main.py (7)

21-21: LGTM! Import supports host-target detection.

The DEFAULT_HOST_TARGET import enables runtime detection of the host platform, which is essential for determining when container-backed cross-compilation is required.


78-79: LGTM! New fields capture container requirements.

The container_engine and requires_cross_container fields correctly extend the decision structure to track which container runtime is available and whether the target mandates containerised cross-compilation.


298-302: LGTM! Container engine priority matches cross tool defaults.

Docker is correctly prioritised over Podman, aligning with the cross tool's native runtime probing order.


351-352: LGTM! New fields correctly propagated.

The container_engine and requires_cross_container values are correctly returned in the decision structure.


407-421: LGTM! Pre-build validation provides clear diagnostics.

The fail-fast check correctly prevents builds when a FreeBSD target is requested without the required cross+container tooling. The error message enumerates missing components and displays both host and target triples, enabling quick diagnosis in CI environments.


435-441: LGTM! Environment management respects user overrides.

The logic correctly exports CROSS_CONTAINER_ENGINE only when a container runtime is detected and no pre-existing value is set, ensuring user-specified overrides are preserved. The restorer flag correctly tracks whether cleanup is needed.


472-474: LGTM! Cleanup ensures environment hygiene.

The finally block correctly removes the exported CROSS_CONTAINER_ENGINE when the script set it, preventing environment pollution for subsequent builds.

.github/actions/rust-build-release/tests/test_target_install.py (7)

148-195: LGTM! Test validates FreeBSD success path.

The test correctly confirms that x86_64-unknown-freebsd builds successfully when cross and a container runtime are available. The assertions verify that cross is invoked and no warnings or errors are emitted.


197-242: LGTM! Test validates Docker environment export and cleanup.

The test correctly verifies that CROSS_CONTAINER_ENGINE=docker is exported when Docker is detected and removed after the build completes, confirming the environment management logic at lines 435-474 of main.py.


244-289: LGTM! Test validates Podman fallback and cleanup.

The test correctly confirms that Podman is selected when Docker is unavailable, and that the environment variable is properly cleaned up, validating the container engine priority logic at lines 298-302 of main.py.


291-336: LGTM! Test validates user override preservation.

The test correctly confirms that a pre-existing CROSS_CONTAINER_ENGINE value is not overridden, even when a different container runtime is detected, validating the preservation logic at line 439 of main.py.


425-556: LGTM! Tests validate Windows host container probing logic.

The three test functions comprehensively validate that container runtime probing is skipped when a Windows host builds Windows targets (unnecessary), but occurs when cross-compiling to non-Windows targets. The parametrised test covers multiple Windows target suffix variants.


558-707: LGTM! Tests validate timeout handling and error propagation.

The five test functions comprehensively validate the _probe_runtime wrapper's behaviour: successful probes, timeout warnings (with/without duration), unexpected error propagation, and integration with the main build flow. The timeout handling ensures builds can proceed despite slow container runtime probes.


709-819: LGTM! Tests validate Windows linker configuration logic.

The two test functions correctly validate that toolchain-provided GCC is preferred for Windows host builds, and that cross-target linkers discovered on PATH are properly exported. The parametrised test covers both GCC and Clang linker variants.

Comment thread .github/actions/rust-build-release/src/main.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b35f464 and d2ee4a8.

📒 Files selected for processing (2)
  • .github/actions/rust-build-release/src/main.py (9 hunks)
  • .github/actions/rust-build-release/tests/test_target_install.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
.github/actions/rust-build-release/src/main.py (2)
.github/actions/rust-build-release/src/runtime.py (1)
  • runtime_available (205-264)
.github/actions/rust-build-release/src/cross_manager.py (1)
  • ensure_cross (164-249)
.github/actions/rust-build-release/tests/test_target_install.py (3)
.github/actions/rust-build-release/tests/conftest.py (7)
  • main_module (236-245)
  • cross_module (223-232)
  • module_harness (169-178)
  • patch_shutil_which (134-136)
  • patch_platform (138-140)
  • patch_attr (142-144)
  • patch_run_cmd (123-132)
conftest.py (8)
  • cmd_mox (149-153)
  • CmdMox (58-77)
  • _register_rustup_toolchain_stub (115-121)
  • replay (71-73)
  • verify (75-77)
  • _register_cross_version_stub (96-112)
  • _register_docker_info_stub (124-131)
  • _register_podman_info_stub (134-141)
.github/actions/rust-build-release/src/main.py (2)
  • main (390-494)
  • _decide_cross_usage (284-361)
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: python-tests-windows
  • GitHub Check: python-tests (ubuntu-latest)
  • GitHub Check: python-tests (macos-13)
  • GitHub Check: build-release (windows-latest, aarch64-pc-windows-gnullvm)
  • GitHub Check: build-release (windows-latest, x86_64-pc-windows-gnullvm)
  • GitHub Check: Sourcery review

Comment thread .github/actions/rust-build-release/src/main.py Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Oct 2, 2025

@coderabbitai Have the following now been resolved?

  • Refactor the new _requires_cross_container logic into the existing decision flow (e.g. CrossDecision) to avoid duplicating normalization and make cross support rules more centralized.
  • Simplify the error message detail suffix by listing missing prerequisites in a single, concise phrase (using commas instead of a semicolon list) to improve readability.
  • Add a positive-path test for FreeBSD targets to verify that builds succeed when both cross and a container runtime are available.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 2, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the _CrossDecision class and _decide_cross_usage function to verify refactoring
ast-grep --pattern $'class _CrossDecision:
  $$$'

# Check for the error message related to cross container requirements
rg -n "cross.*container.*required|requires.*container|container.*runtime.*unavailable" .github/actions/rust-build-release/src/main.py

# Check for the positive-path FreeBSD test
rg -n "def test_builds_freebsd_target_with_cross_and_container" .github/actions/rust-build-release/tests/test_target_install.py

Length of output: 613


🏁 Script executed:

#!/bin/bash
# Get the complete error messages to verify simplification
rg -n -A 5 "requires cross with a container runtime" .github/actions/rust-build-release/src/main.py

# Get the full test function to verify it's a positive-path test
ast-grep --pattern $'def test_builds_freebsd_target_with_cross_and_container($$$):
  $$$'

Length of output: 567


🏁 Script executed:

#!/bin/bash
# Get the detail_suffix construction and surrounding context
rg -n -B 10 "detail_suffix" .github/actions/rust-build-release/src/main.py | head -40

# Get the test function with more context
rg -n -A 30 "def test_builds_freebsd_target_with_cross_and_container" .github/actions/rust-build-release/tests/test_target_install.py

Length of output: 2010


@leynos: Here's the status of the three items:

✅ Item 1: Refactored into CrossDecision

RESOLVED - The _requires_cross_container logic has been properly refactored into the _CrossDecision dataclass. The class now includes:

  • requires_cross_container: bool field
  • container_engine: str | None field

The _decide_cross_usage function centralises this logic by computing requires_cross_container based on BSD suffix matching against the host target (lines 295-298).

❌ Item 2: Error message simplification

NOT RESOLVED - The error message detail suffix still uses semicolons instead of commas. Line 432 shows:

detail_suffix = f"; {'; '.join(details)}" if details else ""

This produces messages like:

cross is not installed; no container runtime detected

instead of the requested comma-separated format:

cross is not installed, no container runtime detected

✅ Item 3: Positive-path test for FreeBSD

RESOLVED - The test test_builds_freebsd_target_with_cross_and_container exists at line 178 of test_target_install.py. It verifies that builds succeed when both cross and a container runtime (Docker) are available for FreeBSD targets.

Summary: Two of three items are resolved. The error message formatting still needs updating to use commas.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2ee4a8 and f83d513.

📒 Files selected for processing (2)
  • .github/actions/rust-build-release/src/main.py (9 hunks)
  • .github/actions/rust-build-release/tests/test_target_install.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
.github/actions/rust-build-release/tests/test_target_install.py (3)
.github/actions/rust-build-release/tests/conftest.py (7)
  • main_module (236-245)
  • cross_module (223-232)
  • module_harness (169-178)
  • patch_shutil_which (134-136)
  • patch_attr (142-144)
  • patch_run_cmd (123-132)
  • patch_platform (138-140)
conftest.py (8)
  • cmd_mox (149-153)
  • CmdMox (58-77)
  • _register_rustup_toolchain_stub (115-121)
  • _register_cross_version_stub (96-112)
  • replay (71-73)
  • verify (75-77)
  • _register_docker_info_stub (124-131)
  • _register_podman_info_stub (134-141)
.github/actions/rust-build-release/src/main.py (2)
  • main (390-505)
  • _decide_cross_usage (284-361)
.github/actions/rust-build-release/src/main.py (2)
.github/actions/rust-build-release/src/runtime.py (1)
  • runtime_available (205-264)
.github/actions/rust-build-release/src/cross_manager.py (1)
  • ensure_cross (164-249)
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build-release (windows-latest, aarch64-pc-windows-gnullvm)
  • GitHub Check: build-release (windows-latest, x86_64-pc-windows-gnullvm)
  • GitHub Check: python-tests (macos-13)
  • GitHub Check: python-tests-windows
  • GitHub Check: python-tests (ubuntu-latest)
  • GitHub Check: Sourcery review
🔇 Additional comments (8)
.github/actions/rust-build-release/tests/test_target_install.py (8)

114-177: Update test assertions once error message formatting is fixed.

The test exercises the missing-prerequisites error path but does not validate the separator format (semicolons vs commas). PR objectives note that item 2 (comma-separated error messages) remains unresolved; main.py line ~432 still constructs semicolon-separated messages. Once main.py is updated to use commas, add an assertion here to validate the comma separator format.

Add the following assertion after line 172 once main.py uses commas:

     for phrase in expected_phrases:
         assert phrase in err
+    # Verify comma-separated format (not semicolons)
+    if len(expected_phrases) > 1:
+        assert ", ".join(expected_phrases) in err
+        assert "; ".join(expected_phrases) not in err

This will catch regressions if semicolons are reintroduced.


179-226: LGTM! Positive-path test correctly validates FreeBSD cross+container support.

The test confirms that FreeBSD targets build successfully when both cross and a container runtime (Docker) are available. Mirrors existing test patterns and addresses PR objectives item 3 (positive-path test for FreeBSD targets). Assertions validate that cross was invoked and no errors were emitted.


228-267: LGTM! Test correctly validates CROSS_NO_DOCKER rejection for container-required targets.

The test confirms that CROSS_NO_DOCKER=1 is rejected when FreeBSD targets require container-backed cross. Assertions verify the expected error message and correctly exercise the decision logic at main.py lines ~416–427.


269-314: LGTM! Test correctly validates container start failure handling.

The test confirms that cross container start failure (exit code 125) for FreeBSD targets emits a clear error without falling back to cargo. Assertions verify the expected error message and exit code, and confirm no cargo fallback was attempted.


317-361: LGTM! Test correctly validates CROSS_CONTAINER_ENGINE export and cleanup.

The test confirms that CROSS_CONTAINER_ENGINE is automatically exported when Docker is detected and cleaned up after the build completes. Assertions verify the env var was set to "docker" during cross invocation and removed afterwards.


364-408: LGTM! Test correctly validates Podman fallback for CROSS_CONTAINER_ENGINE.

The test confirms that CROSS_CONTAINER_ENGINE is set to "podman" when Docker is unavailable and Podman is detected. Assertions verify the env var was set to "podman" during cross invocation and cleaned up afterwards.


411-455: LGTM! Test correctly validates preservation of pre-existing CROSS_CONTAINER_ENGINE.

The test confirms that pre-existing CROSS_CONTAINER_ENGINE values are not overridden by automatic detection. Assertions verify the env var remained "custom" during cross invocation and was not cleaned up afterwards.


458-497: LGTM! Parametrised test provides comprehensive BSD suffix detection coverage.

The test validates that BSD targets (FreeBSD, OpenBSD, NetBSD) require container-backed cross when the host suffix does not match. Parametrisation covers all three BSD variants with both matching and non-matching host scenarios, exercising the suffix detection logic at main.py lines ~295–298.

Comment thread .github/actions/rust-build-release/src/main.py Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Oct 2, 2025

@coderabbitai Has the following now been resolved?

❌ Item 2: Error message simplification

NOT RESOLVED - The error message detail suffix still uses semicolons instead of commas. Line 432 shows:

detail_suffix = f"; {'; '.join(details)}" if details else ""

This produces messages like:

cross is not installed; no container runtime detected

instead of the requested comma-separated format:

cross is not installed, no container runtime detected

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 2, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current state of the detail_suffix construction around line 432
rg -n -B 5 -A 2 "detail_suffix" .github/actions/rust-build-release/src/main.py

Length of output: 627


@leynos: The change is partially resolved.

Looking at line 432, the code now shows:

detail_suffix = f"; {', '.join(details)}" if details else ""

The items within the list are now comma-separated (, instead of ; ), so multiple details would appear as:

; cross is not installed, no container runtime detected

However, the leading separator is still a semicolon (the "; " prefix). This produces messages like:

cross is not installed; no container runtime detected

Wait—I need to re-examine this. The leading "; " is outside the join, so the format is:

  • Prefix: "; "
  • Items joined with: ", "

This produces: "; cross is not installed, no container runtime detected"

If you want the entire detail suffix to use commas consistently (including the leading separator), the code should be:

detail_suffix = f", {', '.join(details)}" if details else ""

Should the leading separator also be changed from semicolon to comma?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f83d513 and ce419a6.

📒 Files selected for processing (2)
  • .github/actions/rust-build-release/src/main.py (9 hunks)
  • .github/actions/rust-build-release/tests/test_target_install.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
.github/actions/rust-build-release/tests/test_target_install.py (3)
.github/actions/rust-build-release/tests/conftest.py (7)
  • main_module (236-245)
  • cross_module (223-232)
  • module_harness (169-178)
  • patch_shutil_which (134-136)
  • patch_attr (142-144)
  • patch_run_cmd (123-132)
  • patch_platform (138-140)
conftest.py (8)
  • cmd_mox (149-153)
  • CmdMox (58-77)
  • _register_rustup_toolchain_stub (115-121)
  • _register_cross_version_stub (96-112)
  • replay (71-73)
  • verify (75-77)
  • _register_docker_info_stub (124-131)
  • _register_podman_info_stub (134-141)
.github/actions/rust-build-release/src/main.py (2)
  • main (390-505)
  • _decide_cross_usage (284-361)
.github/actions/rust-build-release/src/main.py (2)
.github/actions/rust-build-release/src/runtime.py (1)
  • runtime_available (205-264)
.github/actions/rust-build-release/src/cross_manager.py (1)
  • ensure_cross (164-249)
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-release (windows-latest, aarch64-pc-windows-gnullvm)
  • GitHub Check: python-tests-windows
  • GitHub Check: python-tests (macos-13)
  • GitHub Check: python-tests (ubuntu-latest)
  • GitHub Check: Sourcery review
🔇 Additional comments (15)
.github/actions/rust-build-release/tests/test_target_install.py (8)

114-179: LGTM! Comprehensive parametrised test correctly validates error messaging.

The test exercises all combinations of missing prerequisites (cross and container runtime) and verifies that:

  • The error message includes the required substring "missing:"
  • Expected diagnostic phrases appear in the output
  • Multiple missing prerequisites are comma-separated
  • Unexpected phrases are absent

The parametrisation ensures thorough coverage of the error paths.


181-228: LGTM! Positive-path test correctly validates FreeBSD builds with cross and container.

This test resolves follow-up #3 from the PR objectives by verifying that FreeBSD targets build successfully when both cross and a container runtime (Docker) are available. The test:

  • Patches the environment to simulate available prerequisites
  • Records executed commands
  • Validates cross was invoked
  • Confirms no errors were emitted

230-269: LGTM! Test correctly rejects CROSS_NO_DOCKER=1 for container-required targets.

The test validates that attempting to build a FreeBSD target with CROSS_NO_DOCKER=1 results in an error explaining that the flag is unsupported when a container runtime is required.


271-317: LGTM! Test correctly handles cross container start failures for required targets.

The test validates that when cross fails to launch a container runtime for a FreeBSD target (exit code 125), the build:

  • Exits with the same error code
  • Emits a specific error message identifying the failure
  • Does not fall back to cargo (since the target mandates container-backed cross)

319-364: LGTM! Test correctly validates CROSS_CONTAINER_ENGINE export for Docker.

The test verifies that when Docker is available and cross is used for a FreeBSD build:

  • CROSS_CONTAINER_ENGINE is automatically set to "docker" during the cross invocation
  • The environment variable is cleaned up after the build completes

366-411: LGTM! Test correctly validates CROSS_CONTAINER_ENGINE export for Podman.

The test verifies that when only Podman is available (Docker is not), CROSS_CONTAINER_ENGINE is set to "podman" during cross invocation and cleaned up afterward.


413-458: LGTM! Test correctly validates preservation of existing CROSS_CONTAINER_ENGINE.

The test verifies that when CROSS_CONTAINER_ENGINE is already set in the environment, the build does not override it, even when a container runtime is detected. This respects user configuration.


460-500: LGTM! Comprehensive parametrised test validates BSD target detection logic.

The test directly exercises _decide_cross_usage with all three BSD variants (FreeBSD, OpenBSD, NetBSD) and confirms:

  • BSD targets require cross with containers when the host is a different OS (e.g., BSD target on Linux host)
  • BSD targets do not require containers when the host matches the target OS (e.g., OpenBSD target on OpenBSD host)

The parametrisation ensures thorough coverage of the suffix-matching logic introduced in lines 296-299 of main.py.

.github/actions/rust-build-release/src/main.py (7)

21-21: LGTM! Imports required for FreeBSD cross-container support.

The additions of DEFAULT_HOST_TARGET (used at line 410) and CROSS_CONTAINER_ERROR_CODES (used at line 475) are necessary for the new container-backed cross build logic.


38-42: LGTM! BSD target suffixes correctly defined.

The tuple covers all three major BSD variants (FreeBSD, OpenBSD, NetBSD) and follows the existing pattern of WINDOWS_TARGET_SUFFIXES. This enables container-requirement detection for all BSD targets.


84-85: LGTM! _CrossDecision fields correctly capture container runtime state.

The additions of container_engine (Docker/Podman/None) and requires_cross_container (whether the target mandates container-backed cross) centralise the decision logic as requested in follow-up #1 from the PR objectives.


289-310: LGTM! BSD target detection and container engine selection correctly implemented.

The changes resolve follow-up #1 from the PR objectives by centralising the container requirement logic in _CrossDecision:

  • The host_target parameter enables accurate cross-OS detection
  • Lines 293-299 correctly identify BSD targets requiring container-backed cross when the host OS differs
  • Lines 306-310 select the container engine (preferring Docker over Podman)
  • The computed values are propagated in the returned _CrossDecision at lines 359-360

Also applies to: 359-360


410-424: LGTM! Host target initialisation and CROSS_NO_DOCKER rejection correctly implemented.

The code correctly:

  • Initialises host_target to DEFAULT_HOST_TARGET for cross-OS detection
  • Passes host_target to _decide_cross_usage to enable BSD target detection
  • Rejects CROSS_NO_DOCKER=1 when building FreeBSD targets, which require container-backed cross

454-460: LGTM! CROSS_CONTAINER_ENGINE export and restoration correctly implemented.

The code correctly manages the CROSS_CONTAINER_ENGINE environment variable:

  • Lines 454-460 export the detected container engine (Docker or Podman) when cross is used with a container, but only if the variable is not already set
  • Lines 503-505 restore the environment in a finally block, ensuring cleanup even when the build fails
  • The logic respects pre-existing CROSS_CONTAINER_ENGINE values, as validated by test_preserves_existing_cross_container_engine

Also applies to: 503-505


476-486: LGTM! Container start failure handling correctly distinguishes required vs. optional cross.

The code correctly handles cross container start failures:

  • For targets that mandate container-backed cross (FreeBSD), the failure is fatal—no fallback to cargo occurs
  • The error message at lines 481-484 identifies the target and container engine for debugging
  • The original exit code (typically 125) is preserved at line 486
  • For other targets, the existing fallback-to-cargo behaviour (lines 488-500) remains intact

Comment thread .github/actions/rust-build-release/src/main.py
@leynos leynos merged commit c0a9072 into main Oct 2, 2025
13 checks passed
@leynos leynos deleted the codex/add-freebsd-cross-compilation-support branch October 2, 2025 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant