From 941551cb930a8a802b4805b95cf9292f4ce1facb Mon Sep 17 00:00:00 2001 From: Johnny <22727137+johnnynunez@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:23:05 +0200 Subject: [PATCH] fix: honor Windows codegen target ABI and CMake generator --- src/osqp/codegen/pywrapper/setup.py.jinja | 10 ++-- src/osqp/tests/codegen_platform_test.py | 63 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/osqp/tests/codegen_platform_test.py diff --git a/src/osqp/codegen/pywrapper/setup.py.jinja b/src/osqp/codegen/pywrapper/setup.py.jinja index d025f5aa..e6e065f2 100644 --- a/src/osqp/codegen/pywrapper/setup.py.jinja +++ b/src/osqp/codegen/pywrapper/setup.py.jinja @@ -33,11 +33,13 @@ class CmdCMakeBuild(build_ext): build_args += [f'--config={cfg}'] if system() == "Windows": - cmake_args += ['-G', 'Visual Studio 17 2022'] + generator = os.environ.get('CMAKE_GENERATOR', 'Visual Studio 17 2022') + cmake_args += ['-G', generator] cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] - if sys.maxsize > 2 ** 32: - cmake_args += ['-A', 'x64'] - build_args += ['--', '/m'] + if generator.startswith('Visual Studio'): + platform = {'win32': 'Win32', 'win-amd64': 'x64', 'win-arm64': 'ARM64'}[self.plat_name] + cmake_args += ['-A', platform] + build_args += ['--parallel', '2'] else: cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] build_args += ['--', '-j2'] diff --git a/src/osqp/tests/codegen_platform_test.py b/src/osqp/tests/codegen_platform_test.py new file mode 100644 index 00000000..70aab7ab --- /dev/null +++ b/src/osqp/tests/codegen_platform_test.py @@ -0,0 +1,63 @@ +import os +from pathlib import Path +import tempfile +import unittest +from unittest.mock import Mock, patch +from typing import Any + +from setuptools import Distribution + + +class codegen_platform_tests(unittest.TestCase): + def configure(self, platform, generator=None): + template = Path(__file__).parents[1] / 'codegen' / 'pywrapper' / 'setup.py.jinja' + namespace: dict[str, Any] = {'__file__': str(template)} + source = template.read_text().replace('{{extension_name}}', 'test_ext').replace('{{embedded_mode}}', '1') + with patch('setuptools.setup'): + exec(compile(source, str(template), 'exec'), namespace) + command = namespace['CmdCMakeBuild'](Distribution()) + command.plat_name = platform + command.debug = False + extension = namespace['CMakeExtension']('test_ext') + env = dict(os.environ) + env.pop('CMAKE_GENERATOR', None) + if generator is not None: + env['CMAKE_GENERATOR'] = generator + with tempfile.TemporaryDirectory() as directory: + command.build_temp = str(Path(directory) / 'build') + command.build_lib = str(Path(directory) / 'lib') + with patch.dict(os.environ, env, clear=True), patch.dict(namespace, {'system': lambda: 'Windows'}): + with patch.dict(namespace, {'check_call': Mock()}) as mocked: + command.build_extension(extension) + return [call.args[0] for call in mocked['check_call'].call_args_list] + + def test_native_arm64_visual_studio(self): + configure, build = self.configure('win-arm64') + self.assertEqual(configure[configure.index('-A') + 1], 'ARM64') + self.assertNotIn('/m', build) + + def test_emulated_amd64_interpreter(self): + configure, _ = self.configure('win-amd64') + self.assertEqual(configure[configure.index('-A') + 1], 'x64') + + def test_win32_interpreter(self): + configure, _ = self.configure('win32') + self.assertEqual(configure[configure.index('-A') + 1], 'Win32') + + def test_explicit_visual_studio_generator(self): + configure, _ = self.configure('win-arm64', 'Visual Studio 18 2026') + self.assertEqual(configure[configure.index('-G') + 1], 'Visual Studio 18 2026') + self.assertEqual(configure[configure.index('-A') + 1], 'ARM64') + + def test_ninja_does_not_receive_visual_studio_options(self): + for generator in ('Ninja', 'Ninja Multi-Config'): + with self.subTest(generator=generator): + configure, build = self.configure('win-arm64', generator) + self.assertEqual(configure[configure.index('-G') + 1], generator) + self.assertNotIn('-A', configure) + self.assertNotIn('/m', build) + self.assertIn('--parallel', build) + + +if __name__ == '__main__': + unittest.main()