Skip to content

Fixed build_ext assumption that cross-compilation on Windows always uses MSVCCompiler#399

Open
Avasam wants to merge 2 commits into
pypa:mainfrom
Avasam:patch-1
Open

Fixed build_ext assumption that cross-compilation on Windows always uses MSVCCompiler#399
Avasam wants to merge 2 commits into
pypa:mainfrom
Avasam:patch-1

Conversation

@Avasam

@Avasam Avasam commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Fixes the following error if providing --plat-name when using msys2-mingw on Windows:

Traceback (most recent call last):
  File "D:/a/_temp/msys64/mingw64/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 389, in <module>
    main()
  File "D:/a/_temp/msys64/mingw64/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 373, in main
    json_out["return_val"] = hook(**hook_input["kwargs"])
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:/a/_temp/msys64/mingw64/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 280, in build_wheel
    return _build_backend().build_wheel(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/build_meta.py", line 438, in build_wheel
    return _build(['bdist_wheel'])
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/build_meta.py", line 429, in _build
    return self._build_with_temp_dir(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/build_meta.py", line 410, in _build_with_temp_dir
    self.run_setup()
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/build_meta.py", line 317, in run_setup
    exec(code, locals())
  File "<string>", line 1963, in <module>
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/__init__.py", line 117, in setup
    return distutils.core.setup(**attrs)  # type: ignore[return-value]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/core.py", line 186, in setup
    return run_commands(dist)
           ^^^^^^^^^^^^^^^^^^
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/core.py", line 202, in run_commands
    dist.run_commands()
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1000, in run_commands
    self.run_command(cmd)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/dist.py", line 1107, in run_command
    super().run_command(command)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
    cmd_obj.run()
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py", line 370, in run
    self.run_command("build")
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/cmd.py", line 341, in run_command
    self.distribution.run_command(command)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/dist.py", line 1107, in run_command
    super().run_command(command)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
    cmd_obj.run()
  File "<string>", line 354, in run
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/command/build.py", line 135, in run
    self.run_command(cmd_name)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/cmd.py", line 341, in run_command
    self.distribution.run_command(command)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/dist.py", line 1107, in run_command
    super().run_command(command)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
    cmd_obj.run()
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/command/build_ext.py", line 97, in run
    _build_ext.run(self)
  File "D:/a/_temp/msys64/tmp/build-env-g6pjoax9/lib/python3.12/site-packages/setuptools/_distutils/command/build_ext.py", line 336, in run
    self.compiler.initialize(self.plat_name)
    ^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'MinGW32Compiler' object has no attribute 'initialize'

This could've been caught with static type checking, but attr-defined is currently disabled in mypy

Comment thread distutils/command/build_ext.py Outdated
# late initialization of compiler even if they shouldn't...)
self.plat_name != get_platform()
# Initialization is a MSVCCompiler specific concept
and hasattr(self.compiler, "initialize")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would've done

Suggested change
and hasattr(self.compiler, "initialize")
and isinstance(MSVCCompiler, self.compiler)

But who knows if someone downstream added a custom non-MSVCCompiler with .initialize that relies on being called.

Or it could be considered so internal that it's fine to do the more "proper" check that also helps static checkers.

@jaraco jaraco Jul 5, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we shouldn't instead just have the base Compiler provide a degenerate initialize() method, allowing any Compiler to potentially implement it.

But what's really annoying is this other factor "people may rely on late initialization of compiler even if they shouldn't". That really deserves a better explanation. Looks like it was made 18 years ago (Avasam@f39783d#diff-08754ed5a9d7fff430bd6aa04a88d70fd4ef8972aecab7b07373c86271a7c07c) or maybe even earlier, as that's a bundle that seems to trace back to issue 2513 aka python/cpython#46765.

@Avasam Avasam Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not fully convinced I'd add a dummy def initialize to base Compiler. Unless there's a demand for it. As it would also subtly affect downstream hasattr(self.compiler, "initialize") checks.
I think it's fine for the concept to be MSVC specific.

Sidenote: Funny how this comes full circle, mhammond being the author of that issue you linked, and I'm opening this PR for MinGW support in his pywin32 package

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we don't need to deal with the 'cross-compiling' concern here. Let's implement the degenerate method to eliminate the one conditional.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As it would also subtly affect downstream hasattr(self.compiler, "initialize") checks

I checked and Setuptools doesn't rely on this interface. And if there are checks further downstream, they shouldn't be relying on the presence of class attributes for such behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fine by me ! 👍

…or non-MSVC compilers

build_ext.run() unconditionally called compiler.initialize() when
cross-compiling on Windows, assuming an MSVCCompiler. With any other
compiler (e.g. MinGW/msys2) this raised AttributeError.

Rather than guard the call with hasattr(), give the base Compiler a no-op
initialize() that non-MSVC compilers inherit; MSVCCompiler continues to
override it. This also makes the method visible to static type checkers.
The redundant os.name == 'nt' guard is dropped since initialize() is now
safe on every compiler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants