Issue #7833: Ext. modules built using distutils on Windows no longer get a manifest

This commit is contained in:
Mark Hammond 2011-10-17 11:05:57 +11:00
parent 8d91d454d5
commit 6c58b28f2f
3 changed files with 106 additions and 23 deletions

View File

@ -627,15 +627,7 @@ class MSVCCompiler(CCompiler) :
self.library_filename(dll_name)) self.library_filename(dll_name))
ld_args.append ('/IMPLIB:' + implib_file) ld_args.append ('/IMPLIB:' + implib_file)
# Embedded manifests are recommended - see MSDN article titled self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
# "How to: Embed a Manifest Inside a C/C++ Application"
# (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
# Ask the linker to generate the manifest in the temp dir, so
# we can embed it later.
temp_manifest = os.path.join(
build_temp,
os.path.basename(output_filename) + ".manifest")
ld_args.append('/MANIFESTFILE:' + temp_manifest)
if extra_preargs: if extra_preargs:
ld_args[:0] = extra_preargs ld_args[:0] = extra_preargs
@ -653,21 +645,54 @@ class MSVCCompiler(CCompiler) :
# will still consider the DLL up-to-date, but it will not have a # will still consider the DLL up-to-date, but it will not have a
# manifest. Maybe we should link to a temp file? OTOH, that # manifest. Maybe we should link to a temp file? OTOH, that
# implies a build environment error that shouldn't go undetected. # implies a build environment error that shouldn't go undetected.
if target_desc == CCompiler.EXECUTABLE: mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
mfid = 1 if mfinfo is not None:
else: mffilename, mfid = mfinfo
mfid = 2
# Remove references to the Visual C runtime
self._remove_visual_c_ref(temp_manifest)
out_arg = '-outputresource:%s;%s' % (output_filename, mfid) out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
try: try:
self.spawn(['mt.exe', '-nologo', '-manifest', self.spawn(['mt.exe', '-nologo', '-manifest',
temp_manifest, out_arg]) mffilename, out_arg])
except DistutilsExecError as msg: except DistutilsExecError as msg:
raise LinkError(msg) raise LinkError(msg)
else: else:
log.debug("skipping %s (up-to-date)", output_filename) log.debug("skipping %s (up-to-date)", output_filename)
def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
# If we need a manifest at all, an embedded manifest is recommended.
# See MSDN article titled
# "How to: Embed a Manifest Inside a C/C++ Application"
# (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
# Ask the linker to generate the manifest in the temp dir, so
# we can check it, and possibly embed it, later.
temp_manifest = os.path.join(
build_temp,
os.path.basename(output_filename) + ".manifest")
ld_args.append('/MANIFESTFILE:' + temp_manifest)
def manifest_get_embed_info(self, target_desc, ld_args):
# If a manifest should be embedded, return a tuple of
# (manifest_filename, resource_id). Returns None if no manifest
# should be embedded. See http://bugs.python.org/issue7833 for why
# we want to avoid any manifest for extension modules if we can)
for arg in ld_args:
if arg.startswith("/MANIFESTFILE:"):
temp_manifest = arg.split(":", 1)[1]
break
else:
# no /MANIFESTFILE so nothing to do.
return None
if target_desc == CCompiler.EXECUTABLE:
# by default, executables always get the manifest with the
# CRT referenced.
mfid = 1
else:
# Extension modules try and avoid any manifest if possible.
mfid = 2
temp_manifest = self._remove_visual_c_ref(temp_manifest)
if temp_manifest is None:
return None
return temp_manifest, mfid
def _remove_visual_c_ref(self, manifest_file): def _remove_visual_c_ref(self, manifest_file):
try: try:
# Remove references to the Visual C runtime, so they will # Remove references to the Visual C runtime, so they will
@ -676,6 +701,8 @@ class MSVCCompiler(CCompiler) :
# runtimes are not in WinSxS folder, but in Python's own # runtimes are not in WinSxS folder, but in Python's own
# folder), the runtimes do not need to be in every folder # folder), the runtimes do not need to be in every folder
# with .pyd's. # with .pyd's.
# Returns either the filename of the modified manifest or
# None if no manifest should be embedded.
manifest_f = open(manifest_file) manifest_f = open(manifest_file)
try: try:
manifest_buf = manifest_f.read() manifest_buf = manifest_f.read()
@ -688,9 +715,18 @@ class MSVCCompiler(CCompiler) :
manifest_buf = re.sub(pattern, "", manifest_buf) manifest_buf = re.sub(pattern, "", manifest_buf)
pattern = "<dependentAssembly>\s*</dependentAssembly>" pattern = "<dependentAssembly>\s*</dependentAssembly>"
manifest_buf = re.sub(pattern, "", manifest_buf) manifest_buf = re.sub(pattern, "", manifest_buf)
# Now see if any other assemblies are referenced - if not, we
# don't want a manifest embedded.
pattern = re.compile(
r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
if re.search(pattern, manifest_buf) is None:
return None
manifest_f = open(manifest_file, 'w') manifest_f = open(manifest_file, 'w')
try: try:
manifest_f.write(manifest_buf) manifest_f.write(manifest_buf)
return manifest_file
finally: finally:
manifest_f.close() manifest_f.close()
except IOError: except IOError:

View File

@ -7,7 +7,36 @@ from distutils.errors import DistutilsPlatformError
from distutils.tests import support from distutils.tests import support
from test.support import run_unittest from test.support import run_unittest
_MANIFEST = """\ # A manifest with the only assembly reference being the msvcrt assembly, so
# should have the assembly completely stripped. Note that although the
# assembly has a <security> reference the assembly is removed - that is
# currently a "feature", not a bug :)
_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false">
</requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
version="9.0.21022.8" processorArchitecture="x86"
publicKeyToken="XXXX">
</assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>
"""
# A manifest with references to assemblies other than msvcrt. When processed,
# this assembly should be returned with just the msvcrt part removed.
_MANIFEST_WITH_MULTIPLE_REFERENCES = """\
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0"> manifestVersion="1.0">
@ -115,7 +144,7 @@ class msvc9compilerTestCase(support.TempdirManager,
manifest = os.path.join(tempdir, 'manifest') manifest = os.path.join(tempdir, 'manifest')
f = open(manifest, 'w') f = open(manifest, 'w')
try: try:
f.write(_MANIFEST) f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES)
finally: finally:
f.close() f.close()
@ -133,6 +162,20 @@ class msvc9compilerTestCase(support.TempdirManager,
# makes sure the manifest was properly cleaned # makes sure the manifest was properly cleaned
self.assertEqual(content, _CLEANED_MANIFEST) self.assertEqual(content, _CLEANED_MANIFEST)
def test_remove_entire_manifest(self):
from distutils.msvc9compiler import MSVCCompiler
tempdir = self.mkdtemp()
manifest = os.path.join(tempdir, 'manifest')
f = open(manifest, 'w')
try:
f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE)
finally:
f.close()
compiler = MSVCCompiler()
got = compiler._remove_visual_c_ref(manifest)
self.assertIs(got, None)
def test_suite(): def test_suite():
return unittest.makeSuite(msvc9compilerTestCase) return unittest.makeSuite(msvc9compilerTestCase)

View File

@ -10,6 +10,10 @@ What's New in Python 3.2.3?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #7833: Extension modules built using distutils on Windows will no
longer include a "manifest" to prevent them failing at import time in some
embedded situations.
- Issue #13063: the Windows error ERROR_NO_DATA (numbered 232 and described - Issue #13063: the Windows error ERROR_NO_DATA (numbered 232 and described
as "The pipe is being closed") is now mapped to POSIX errno EPIPE as "The pipe is being closed") is now mapped to POSIX errno EPIPE
(previously EINVAL). (previously EINVAL).