Skip to content

[FR] When sorting sources, move Message Compiler (.mc) files first #4986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
Avasam opened this issue May 6, 2025 · 3 comments · May be fixed by pypa/distutils#370
Open
1 task done

[FR] When sorting sources, move Message Compiler (.mc) files first #4986

Avasam opened this issue May 6, 2025 · 3 comments · May be fixed by pypa/distutils#370
Labels
enhancement Needs Triage Issues that need to be evaluated for severity and status.

Comments

@Avasam
Copy link
Contributor

Avasam commented May 6, 2025

What's the problem this feature will solve?

This is re-opening python/cpython#86175 for setuptools.

If I'm not mistaken, python/cpython#12341 is responsible for sorting sources in distutils to enable reproducible builds.

But since then, pywin32 has had to manually re-sort the sources by doing this:

from distutils import ccompiler
from distutils._msvccompiler import MSVCCompiler

def my_new_compiler(**kw):
    if "compiler" in kw and kw["compiler"] in (None, "msvc"):
        return my_compiler()
    return orig_new_compiler(**kw)

# No way to cleanly wedge our compiler sub-class in.
orig_new_compiler = ccompiler.new_compiler
ccompiler.new_compiler = my_new_compiler

# This import has to be delayed to AFTER the compiler hack
from setuptools.command.build_ext import build_ext  # noqa: E402

class my_compiler(MSVCCompiler):
    # Work around bpo-36302/bpo-42009 - it sorts sources but this breaks
    # support for building .mc files etc :(
    def compile(self, sources, **kwargs) -> list[str]:
        # re-sort the list of source files but ensure all .mc files come first.
        def key_reverse_mc(a):
            b, e = os.path.splitext(a)
            e = "" if e == ".mc" else e
            return (e, b)

        sources = sorted(sources, key=key_reverse_mc)
        return MSVCCompiler.compile(self, sources, **kwargs)

    # [...]

If pywin32 didn't have to do this "compiler patching", I'm pretty sure I could remove this hack and 2 deprecated distutils imports

Describe the solution you'd like

I'd like setuptools (distutils) to sort sources in such a way that Message Compiler (.mc) files come first.

If there's an already existing better way to handle .mc files. Please let me know!

Alternative Solutions

(see the current workaround posted above)

Additional context

Code of Conduct

  • I agree to follow the PSF Code of Conduct
@Avasam Avasam added enhancement Needs Triage Issues that need to be evaluated for severity and status. labels May 6, 2025
@Avasam
Copy link
Contributor Author

Avasam commented May 6, 2025

I know this would have to be done in https://github.com/pypa/distutils

Do you think build_ext.build_extension should update its sorting?

# sort to make the resulting .so file build reproducible
sources = sorted(sources)

Or should that happen in compilers.C.msvc.Compiler.compile if compiling .mc files is a concern specific to MSVC?

compile_info = self._setup_compile(
output_dir, macros, include_dirs, sources, depends, extra_postargs
)

Or even compilers.C.base.Compiler._setup_compile to return object in a compilable order.

# Get the list of expected output (object) files
objects = self.object_filenames(sources, strip_dir=False, output_dir=outdir)

(It doesn't feel like compilers.C.base.Compiler.object_filenames would be the right place)

@abravalheri
Copy link
Contributor

Thanks @Avasam, I am not very familiar with Message Compiler so I have a question:

(a) Is it always the case that .mc files need to come first in Windows, or
(b) is it a concern particular to a project and the way that project is designed/organised?

If (a), then I wouldn't see a problem in possibly modifying build_ext.build_extension or compilers/C/msvc.py.
If (b), then the best approach would be allowing projects to explicitly opt out of automatic sorting1.

That said, it is up to the pypa/distutils maintainers to decide and those are just personal opinions.

Footnotes

  1. Vaguely related we have an example of setuptools allowing opt-out of version normalisation: https://github.com/pypa/setuptools/blob/v80.3.1/setuptools/dist.py#L367

@Avasam
Copy link
Contributor Author

Avasam commented May 7, 2025

I think either solution would work for pywin32. But my preference of course lies in the build backend being "smarter" and working out of the box in more scenarios where its concerned.

My current understanding and experience (which is limited to "pywin32 uses it") is that Message Compiler (.mc) files produces Header (.h) and Resource Compiler Script (.rc) files. So if those aren't compiled first, then the headers are missing, and compiling other files referencing those headers fails. (that was the symptoms of #4968).

You'll find that for every .mc file in pywin32, there's no .h file checked-in, because they're generated on build.

Windows' official doc on the Message Compiler: https://learn.microsoft.com/en-us/windows/win32/wes/message-compiler--mc-exe-

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Needs Triage Issues that need to be evaluated for severity and status.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants