Repository: conda/conda-build

Issue:
Cross compiling with virtual packages
### Checklist

- [X] I added a descriptive title
- [X] I searched open reports and couldn't find a duplicate

### What happened?

Building on a x86 Linux machine for x86 windows fails.

```
$ cat meta.yaml 
package:
  name: foo
  version: 0.0.1

build:
  number: 0
  script:
    - echo 'hello'
    - ls $PREFIX
    # this is the unix layout.
    - test ! -d $PREFIX/x86_64-w64-mingw32
    - test -d $PREFIX/Library

requirements:
  build:
  host:
    - m2w64-sysroot_win-64
  run:
```
```
$ cat conda_build_config.yaml 
target_platform:
  - win-64
```

Running
```
conda build . -c conda-forge/label/m2w64-experimental -c conda-forge
```
fails the test, because it uses the unix noarch package instead of the windows noarch package.

cc @mbargull @jaimergp 

### Conda Info

```shell
active environment : base
    active env location : /home/isuru/miniforge3
            shell level : 1
       user config file : /home/isuru/.condarc
 populated config files : /home/isuru/miniforge3/.condarc
                          /home/isuru/.condarc
          conda version : 24.5.0
    conda-build version : 24.5.0
         python version : 3.10.13.final.0
                 solver : libmamba (default)
       virtual packages : __archspec=1=zen3
                          __conda=24.5.0=0
                          __glibc=2.35=0
                          __linux=6.5.0=0
                          __unix=0=0
       base environment : /home/isuru/miniforge3  (writable)
      conda av data dir : /home/isuru/miniforge3/etc/conda
  conda av metadata url : None
           channel URLs : https://conda.anaconda.org/conda-forge/linux-64
                          https://conda.anaconda.org/conda-forge/noarch
          package cache : /home/isuru/miniforge3/pkgs
                          /home/isuru/.conda/pkgs
       envs directories : /home/isuru/miniforge3/envs
                          /home/isuru/.conda/envs
               platform : linux-64
             user-agent : conda/24.5.0 requests/2.31.0 CPython/3.10.13 Linux/6.5.0-28-generic ubuntu/22.04.4 glibc/2.35 solver/libmamba conda-libmamba-solver/24.1.0 libmambapy/1.5.7
                UID:GID : 1000:1000
             netrc file : /home/isuru/.netrc
           offline mode : False
```


### Conda Config

```shell
==> /home/isuru/miniforge3/.condarc <==
channels:
  - conda-forge
solver: libmamba

==> /home/isuru/.condarc <==
channels:
  - conda-forge
report_errors: False
solver: libmamba
conda_build:
  pkg_format: 2
```


### Conda list

```shell
$ conda list --show-channel-urls | grep "^conda"
conda                     24.5.0          py310hff52083_0    conda-forge
conda-build               24.5.0          py310hff52083_1    conda-forge
conda-content-trust       0.2.0              pyhd8ed1ab_0    conda-forge
conda-forge-metadata      0.5.3                    pypi_0    pypi
conda-forge-pinning       2023.11.07.18.09.01      hd8ed1ab_0    conda-forge
conda-index               0.4.0              pyhd8ed1ab_0    conda-forge
conda-libmamba-solver     24.1.0             pyhd8ed1ab_0    conda-forge
conda-lock                2.4.2              pyhd8ed1ab_0    conda-forge
conda-oci-mirror          0.1.0                    pypi_0    pypi
conda-pack                0.7.1              pyhd8ed1ab_0    conda-forge
conda-package-handling    2.2.0              pyh38be061_0    conda-forge
conda-package-streaming   0.9.0              pyhd8ed1ab_0    conda-forge
conda-smithy              3.35.0             pyhd8ed1ab_0    conda-forge
conda-souschef            2.2.3              pyhd8ed1ab_0    conda-forge
conda-suggest             0.1.1              pyh9f0ad1d_0    conda-forge
conda-suggest-conda-forge 2021.8.24            ha770c72_0    conda-forge
conda-tree                1.1.0              pyhd8ed1ab_2    conda-forge
```


### Additional Context

_No response_


Discussion:
I took a quick look:
Fixing this should be fairly simple with just
```diff
     with env_vars(
         {
             "CONDA_ALLOW_NON_CHANNEL_URLS": "true",
             "CONDA_SOLVER_IGNORE_TIMESTAMPS": "false",
+            "CONDA_SUBDIR": subdir,
         },
         callback=reset_context,
     ):
```
at https://github.com/conda/conda-build/blob/24.5.0/conda_build/environ.py#L1263-L1269 and passing `subdir` on to `install_actions` from `get_install_actions`/`get_package_records`, i.e., adding a simple test would be most of the work.
(Yeah, that's an unexpectedly obvious oversight on our part, whoops. Should also be fixed when gh-5154 gets tackled.)

As for when/how to include this:
1. We could include this in a `24.5.x` patch release ("fixes cross-platform building with virtual packages"),
2. or include this only in `24.7.x` ("adding support for proper cross-platform building").

In principle, I'd be in favor of option 1, but am also not sure if this could be an "unexpected environment resolving change for a patch release" for some.

Resolving patch:
diff --git a/conda_build/environ.py b/conda_build/environ.py
--- a/conda_build/environ.py
+++ b/conda_build/environ.py
@@ -888,7 +888,8 @@ def get_install_actions(
         with utils.LoggingContext(conda_log_level):
             with capture():
                 try:
-                    precs = _install_actions(prefix, index, specs)["LINK"]
+                    _actions = _install_actions(prefix, index, specs, subdir=subdir)
+                    precs = _actions["LINK"]
                 except (NoPackagesFoundError, UnsatisfiableError) as exc:
                     raise DependencyNeedsBuildingError(exc, subdir=subdir)
                 except (
@@ -1256,14 +1257,19 @@ def install_actions(
     prefix: str | os.PathLike | Path,
     index,
     specs: Iterable[str | MatchSpec],
+    subdir: str | None = None,
 ) -> InstallActionsType:
     # This is copied over from https://github.com/conda/conda/blob/23.11.0/conda/plan.py#L471
     # but reduced to only the functionality actually used within conda-build.
+    subdir_kwargs = {}
+    if subdir not in (None, "", "noarch"):
+        subdir_kwargs["CONDA_SUBDIR"] = subdir
 
     with env_vars(
         {
             "CONDA_ALLOW_NON_CHANNEL_URLS": "true",
             "CONDA_SOLVER_IGNORE_TIMESTAMPS": "false",
+            **subdir_kwargs,
         },
         callback=reset_context,
     ):


Test patch:
diff --git a/tests/test-recipes/metadata/_cross_unix_windows_mingw/conda_build_config.yaml b/tests/test-recipes/metadata/_cross_unix_windows_mingw/conda_build_config.yaml
new file mode 100644
--- /dev/null
+++ b/tests/test-recipes/metadata/_cross_unix_windows_mingw/conda_build_config.yaml
@@ -0,0 +1,2 @@
+target_platform:
+  - win-64
\ No newline at end of file
diff --git a/tests/test-recipes/metadata/_cross_unix_windows_mingw/meta.yaml b/tests/test-recipes/metadata/_cross_unix_windows_mingw/meta.yaml
new file mode 100644
--- /dev/null
+++ b/tests/test-recipes/metadata/_cross_unix_windows_mingw/meta.yaml
@@ -0,0 +1,18 @@
+package:
+  name: foo
+  version: 0.0.1
+
+build:
+  number: 0
+  script:
+    - echo 'hello'
+    - ls $PREFIX
+    # this is the unix layout.
+    - test ! -d $PREFIX/x86_64-w64-mingw32
+    - test -d $PREFIX/Library
+
+requirements:
+  build:
+  host:
+    - m2w64-sysroot_win-64
+  run:
diff --git a/tests/test_api_build.py b/tests/test_api_build.py
--- a/tests/test_api_build.py
+++ b/tests/test_api_build.py
@@ -1971,6 +1971,15 @@ def test_add_pip_as_python_dependency_from_condarc_file(
             api.build(testing_metadata)
 
 
+@pytest.mark.skipif(on_win, reason="Tests cross-compilation targeting Windows")
+def test_cross_unix_windows_mingw(testing_config):
+    recipe = os.path.join(metadata_dir, "_cross_unix_windows_mingw")
+    testing_config.channel_urls = [
+        "conda-forge",
+    ]
+    api.build(recipe, config=testing_config)
+
+
 @pytest.mark.parametrize(
     "recipe", sorted(Path(metadata_dir, "_build_script_errors").glob("*"))
 )