diff --git a/distutils/compilers/C/msvc.py b/distutils/compilers/C/msvc.py index 6db062a9..a4ec8e61 100644 --- a/distutils/compilers/C/msvc.py +++ b/distutils/compilers/C/msvc.py @@ -18,7 +18,8 @@ import subprocess import unittest.mock as mock import warnings -from collections.abc import Iterable +from collections.abc import Iterable, Sequence +from pathlib import Path with contextlib.suppress(ImportError): import winreg @@ -371,7 +372,7 @@ def out_extensions(self) -> dict[str, str]: def compile( # noqa: C901 self, - sources, + sources: Sequence[str | os.PathLike[str]], output_dir=None, macros=None, include_dirs=None, @@ -379,9 +380,16 @@ def compile( # noqa: C901 extra_preargs=None, extra_postargs=None, depends=None, - ): + ) -> list[str]: if not self.initialized: self.initialize() + + # Move .mc files to the start of the list, otherwise keep the same order + # See pypa/setuptools#4986 + sources = sorted( + sources, key=lambda source: Path(source).suffix not in self._mc_extensions + ) + compile_info = self._setup_compile( output_dir, macros, include_dirs, sources, depends, extra_postargs ) diff --git a/distutils/compilers/C/tests/test_msvc.py b/distutils/compilers/C/tests/test_msvc.py index eca83199..31a317a4 100644 --- a/distutils/compilers/C/tests/test_msvc.py +++ b/distutils/compilers/C/tests/test_msvc.py @@ -6,6 +6,7 @@ from distutils.errors import DistutilsPlatformError from distutils.tests import support from distutils.util import get_platform +from pathlib import Path import pytest @@ -53,6 +54,23 @@ def _get_vcvars_spec(host_platform, platform): monkeypatch.setattr(msvc, '_get_vcvars_spec', _get_vcvars_spec) compiler.initialize(plat_name) + @pytest.mark.skipif( + not sysconfig.get_platform().startswith("win"), + reason="Only run test for non-mingw Windows platforms", + ) + def test_sources_compilation_order(self, tmp_path: Path) -> None: + # We expect the the mc files to be compiled first, but otherwise keep the same order + test_sources = [tmp_path / file for file in ("b.cpp", "y.mc", "a.c", "z.mc")] + expected_objects = [ + str(tmp_path / obj) for obj in ("y.res", "z.res", "b.obj", "a.obj") + ] + for source in test_sources: + source.write_text("") + + compiler = msvc.Compiler() + objects = compiler.compile(test_sources) + assert objects == expected_objects + @needs_winreg def test_get_vc_env_unicode(self): test_var = 'ṰḖṤṪ┅ṼẨṜ'