Skip to content

Cross-compiling from Windows fails: embed_version_info gates on host, not target #735

Description

@MGudgin

Relevant area(s)

Linux, macOS, Windows

Brief description of your issue

mxc_build_common::embed_version_info decides whether to embed Windows
VersionInfo using #[cfg(windows)]. In a build script that cfg describes
the host, not the target — build scripts are compiled for and run on the
host machine. So when cross-compiling from a Windows host to any non-Windows
target, the guard is still true, the Windows resource compiler is invoked for a
non-Windows target, and the build script panics.

The function's own doc comment states the opposite of what the code does:

/// Embed Windows VersionInfo resource metadata into the binary being compiled.
///
/// On non-Windows targets this is a no-op.
pub fn embed_version_info(file_description: &str, original_filename: &str) {
    ...
    #[cfg(windows)]
    embed_version_info_windows(file_description, original_filename);

It is a no-op on non-Windows hosts, not on non-Windows targets.

The correct signal for a build script is the target, exposed by Cargo as the
CARGO_CFG_TARGET_OS environment variable (or CARGO_CFG_TARGET_ENV), not
cfg!(windows).

Scope: this is not specific to one crate. Every build.rs calling
embed_version_info does so unconditionally, so all of them fail to
cross-compile from Windows to a non-Windows target:

  • core/mxc_darwin (the macOS executor, mxc-exec-mac)
  • core/lxc (the Linux executor, lxc-exec)
  • core/wxc, backends/windows_sandbox/{daemon,guest},
    host/plm, host/wxc_host_prep, host/wxc_winhttp_proxy_shim,
    testing/wxc_test_driver, testing/wxc_test_proxy,
    tools/mxc_diagnostic_console (Windows-only, so unaffected in practice)

The practical impact is on the first two: a Windows developer cannot
type-check the macOS or Linux executor binaries locally, even with the
appropriate Rust targets installed. That removes the cheapest available
guard against breaking a #[cfg(target_os = ...)] code path that CI only
compiles on another OS.

Steps to reproduce

From a Windows host, with the cross targets installed
(rustup target add aarch64-apple-darwin x86_64-unknown-linux-gnu):

  1. cd src
  2. cargo check -p mxc_darwin --target aarch64-apple-darwin
  3. cargo check -p lxc --target x86_64-unknown-linux-gnu

Reproduced on main-based commit 8977c188 in a clean worktree, so this is
long-standing and not introduced by any in-flight branch.

Expected behavior

Both commands succeed. embed_version_info embeds the resource only when the
target is Windows and is a no-op otherwise, exactly as its doc comment
claims — so the macOS and Linux executors can be cross-checked from any host.

Actual behavior

Both fail in the build script, with the host's toolchain state deciding which
error surfaces:

cargo check -p mxc_darwin --target aarch64-apple-darwin:

error: failed to run custom build command for `mxc_darwin v0.7.0`

Caused by:
  process didn't exit successfully: `...\build-script-build` (exit code: 101)
  --- stderr
  thread 'main' panicked at core\mxc_build_common\src\lib.rs:54:10:
  failed to embed Windows version info: Custom { kind: Other, error:
  "Can only compile resource file when target_env is \"gnu\" or \"msvc\"" }

cargo check -p lxc --target x86_64-unknown-linux-gnu:

error: failed to run custom build command for `lxc v0.7.0`
  failed to embed Windows version info: Error { kind: NotFound,
  message: "program not found" }

Suggested fix

The two cfgs need to be separated, because they answer different questions:

  • host — is winresource even available? It is declared under
    [target.'cfg(windows)'.dependencies] in mxc_build_common/Cargo.toml, and
    for a build-dependency Cargo resolves that against the host. So
    embed_version_info_windows must stay #[cfg(windows)] or non-Windows hosts
    fail to compile the helper.
  • target — should the resource actually be embedded? That is
    CARGO_CFG_TARGET_OS, and it is the check currently missing.

So keep the existing host gating and add a target check at the call site:

#[cfg(windows)]
{
    // `cfg(windows)` above is the HOST (build scripts run on the host);
    // whether to embed the resource depends on the TARGET.
    if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
        embed_version_info_windows(file_description, original_filename);
    } else {
        let _ = (file_description, original_filename);
    }
}

This fixes every caller at once and keeps the call sites uniform, so no
individual build.rs needs its own guard.

Also worth correcting the doc comment either way, since it currently documents
behaviour the code does not have.

Additional context

Found while cross-checking the macOS code paths of a change that touches
#[cfg(target_os = "macos")] code (the version-windows work). The macOS paths
of mxc_engine, wxc_common and mxc-sdk do cross-check cleanly today —
only crates whose build.rs calls embed_version_info are blocked.

Metadata

Metadata

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions