Blame numpy/distutils/mingw32ccompiler.py

Packit 7a8e5e
"""
Packit 7a8e5e
Support code for building Python extensions on Windows.
Packit 7a8e5e
Packit 7a8e5e
    # NT stuff
Packit 7a8e5e
    # 1. Make sure libpython<version>.a exists for gcc.  If not, build it.
Packit 7a8e5e
    # 2. Force windows to use gcc (we're struggling with MSVC and g77 support)
Packit 7a8e5e
    # 3. Force windows to use g77
Packit 7a8e5e
Packit 7a8e5e
"""
Packit 7a8e5e
from __future__ import division, absolute_import, print_function
Packit 7a8e5e
Packit 7a8e5e
import os
Packit 7a8e5e
import sys
Packit 7a8e5e
import subprocess
Packit 7a8e5e
import re
Packit 7a8e5e
Packit 7a8e5e
# Overwrite certain distutils.ccompiler functions:
Packit 7a8e5e
import numpy.distutils.ccompiler
Packit 7a8e5e
Packit 7a8e5e
if sys.version_info[0] < 3:
Packit 7a8e5e
    from . import log
Packit 7a8e5e
else:
Packit 7a8e5e
    from numpy.distutils import log
Packit 7a8e5e
# NT stuff
Packit 7a8e5e
# 1. Make sure libpython<version>.a exists for gcc.  If not, build it.
Packit 7a8e5e
# 2. Force windows to use gcc (we're struggling with MSVC and g77 support)
Packit 7a8e5e
#    --> this is done in numpy/distutils/ccompiler.py
Packit 7a8e5e
# 3. Force windows to use g77
Packit 7a8e5e
Packit 7a8e5e
import distutils.cygwinccompiler
Packit 7a8e5e
from distutils.version import StrictVersion
Packit 7a8e5e
from numpy.distutils.ccompiler import gen_preprocess_options, gen_lib_options
Packit 7a8e5e
from distutils.unixccompiler import UnixCCompiler
Packit 7a8e5e
from distutils.msvccompiler import get_build_version as get_build_msvc_version
Packit 7a8e5e
from distutils.errors import (DistutilsExecError, CompileError,
Packit 7a8e5e
                              UnknownFileError)
Packit 7a8e5e
from numpy.distutils.misc_util import (msvc_runtime_library,
Packit 7a8e5e
                                       msvc_runtime_version,
Packit 7a8e5e
                                       msvc_runtime_major,
Packit 7a8e5e
                                       get_build_architecture)
Packit 7a8e5e
Packit 7a8e5e
def get_msvcr_replacement():
Packit 7a8e5e
    """Replacement for outdated version of get_msvcr from cygwinccompiler"""
Packit 7a8e5e
    msvcr = msvc_runtime_library()
Packit 7a8e5e
    return [] if msvcr is None else [msvcr]
Packit 7a8e5e
Packit 7a8e5e
# monkey-patch cygwinccompiler with our updated version from misc_util
Packit 7a8e5e
# to avoid getting an exception raised on Python 3.5
Packit 7a8e5e
distutils.cygwinccompiler.get_msvcr = get_msvcr_replacement
Packit 7a8e5e
Packit 7a8e5e
# Useful to generate table of symbols from a dll
Packit 7a8e5e
_START = re.compile(r'\[Ordinal/Name Pointer\] Table')
Packit 7a8e5e
_TABLE = re.compile(r'^\s+\[([\s*[0-9]*)\] ([a-zA-Z0-9_]*)')
Packit 7a8e5e
Packit 7a8e5e
# the same as cygwin plus some additional parameters
Packit 7a8e5e
class Mingw32CCompiler(distutils.cygwinccompiler.CygwinCCompiler):
Packit 7a8e5e
    """ A modified MingW32 compiler compatible with an MSVC built Python.
Packit 7a8e5e
Packit 7a8e5e
    """
Packit 7a8e5e
Packit 7a8e5e
    compiler_type = 'mingw32'
Packit 7a8e5e
Packit 7a8e5e
    def __init__ (self,
Packit 7a8e5e
                  verbose=0,
Packit 7a8e5e
                  dry_run=0,
Packit 7a8e5e
                  force=0):
Packit 7a8e5e
Packit 7a8e5e
        distutils.cygwinccompiler.CygwinCCompiler.__init__ (self, verbose,
Packit 7a8e5e
                                                            dry_run, force)
Packit 7a8e5e
Packit 7a8e5e
        # we need to support 3.2 which doesn't match the standard
Packit 7a8e5e
        # get_versions methods regex
Packit 7a8e5e
        if self.gcc_version is None:
Packit 7a8e5e
            import re
Packit 7a8e5e
            p = subprocess.Popen(['gcc', '-dumpversion'], shell=True,
Packit 7a8e5e
                                 stdout=subprocess.PIPE)
Packit 7a8e5e
            out_string = p.stdout.read()
Packit 7a8e5e
            p.stdout.close()
Packit 7a8e5e
            result = re.search(r'(\d+\.\d+)', out_string)
Packit 7a8e5e
            if result:
Packit 7a8e5e
                self.gcc_version = StrictVersion(result.group(1))
Packit 7a8e5e
Packit 7a8e5e
        # A real mingw32 doesn't need to specify a different entry point,
Packit 7a8e5e
        # but cygwin 2.91.57 in no-cygwin-mode needs it.
Packit 7a8e5e
        if self.gcc_version <= "2.91.57":
Packit 7a8e5e
            entry_point = '--entry _DllMain@12'
Packit 7a8e5e
        else:
Packit 7a8e5e
            entry_point = ''
Packit 7a8e5e
Packit 7a8e5e
        if self.linker_dll == 'dllwrap':
Packit 7a8e5e
            # Commented out '--driver-name g++' part that fixes weird
Packit 7a8e5e
            #   g++.exe: g++: No such file or directory
Packit 7a8e5e
            # error (mingw 1.0 in Enthon24 tree, gcc-3.4.5).
Packit 7a8e5e
            # If the --driver-name part is required for some environment
Packit 7a8e5e
            # then make the inclusion of this part specific to that
Packit 7a8e5e
            # environment.
Packit 7a8e5e
            self.linker = 'dllwrap' #  --driver-name g++'
Packit 7a8e5e
        elif self.linker_dll == 'gcc':
Packit 7a8e5e
            self.linker = 'g++'
Packit 7a8e5e
Packit 7a8e5e
        # **changes: eric jones 4/11/01
Packit 7a8e5e
        # 1. Check for import library on Windows.  Build if it doesn't exist.
Packit 7a8e5e
Packit 7a8e5e
        build_import_library()
Packit 7a8e5e
Packit 7a8e5e
        # Check for custom msvc runtime library on Windows. Build if it doesn't exist.
Packit 7a8e5e
        msvcr_success = build_msvcr_library()
Packit 7a8e5e
        msvcr_dbg_success = build_msvcr_library(debug=True)
Packit 7a8e5e
        if msvcr_success or msvcr_dbg_success:
Packit 7a8e5e
            # add preprocessor statement for using customized msvcr lib
Packit 7a8e5e
            self.define_macro('NPY_MINGW_USE_CUSTOM_MSVCR')
Packit 7a8e5e
Packit 7a8e5e
        # Define the MSVC version as hint for MinGW
Packit 7a8e5e
        msvcr_version = msvc_runtime_version()
Packit 7a8e5e
        if msvcr_version:
Packit 7a8e5e
            self.define_macro('__MSVCRT_VERSION__', '0x%04i' % msvcr_version)
Packit 7a8e5e
Packit 7a8e5e
        # MS_WIN64 should be defined when building for amd64 on windows,
Packit 7a8e5e
        # but python headers define it only for MS compilers, which has all
Packit 7a8e5e
        # kind of bad consequences, like using Py_ModuleInit4 instead of
Packit 7a8e5e
        # Py_ModuleInit4_64, etc... So we add it here
Packit 7a8e5e
        if get_build_architecture() == 'AMD64':
Packit 7a8e5e
            if self.gcc_version < "4.0":
Packit 7a8e5e
                self.set_executables(
Packit 7a8e5e
                    compiler='gcc -g -DDEBUG -DMS_WIN64 -mno-cygwin -O0 -Wall',
Packit 7a8e5e
                    compiler_so='gcc -g -DDEBUG -DMS_WIN64 -mno-cygwin -O0'
Packit 7a8e5e
                                ' -Wall -Wstrict-prototypes',
Packit 7a8e5e
                    linker_exe='gcc -g -mno-cygwin',
Packit 7a8e5e
                    linker_so='gcc -g -mno-cygwin -shared')
Packit 7a8e5e
            else:
Packit 7a8e5e
                # gcc-4 series releases do not support -mno-cygwin option
Packit 7a8e5e
                self.set_executables(
Packit 7a8e5e
                    compiler='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall',
Packit 7a8e5e
                    compiler_so='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall -Wstrict-prototypes',
Packit 7a8e5e
                    linker_exe='gcc -g',
Packit 7a8e5e
                    linker_so='gcc -g -shared')
Packit 7a8e5e
        else:
Packit 7a8e5e
            if self.gcc_version <= "3.0.0":
Packit 7a8e5e
                self.set_executables(
Packit 7a8e5e
                    compiler='gcc -mno-cygwin -O2 -w',
Packit 7a8e5e
                    compiler_so='gcc -mno-cygwin -mdll -O2 -w'
Packit 7a8e5e
                                ' -Wstrict-prototypes',
Packit 7a8e5e
                    linker_exe='g++ -mno-cygwin',
Packit 7a8e5e
                    linker_so='%s -mno-cygwin -mdll -static %s' %
Packit 7a8e5e
                              (self.linker, entry_point))
Packit 7a8e5e
            elif self.gcc_version < "4.0":
Packit 7a8e5e
                self.set_executables(
Packit 7a8e5e
                    compiler='gcc -mno-cygwin -O2 -Wall',
Packit 7a8e5e
                    compiler_so='gcc -mno-cygwin -O2 -Wall'
Packit 7a8e5e
                                ' -Wstrict-prototypes',
Packit 7a8e5e
                    linker_exe='g++ -mno-cygwin',
Packit 7a8e5e
                    linker_so='g++ -mno-cygwin -shared')
Packit 7a8e5e
            else:
Packit 7a8e5e
                # gcc-4 series releases do not support -mno-cygwin option
Packit 7a8e5e
                self.set_executables(compiler='gcc -O2 -Wall',
Packit 7a8e5e
                                     compiler_so='gcc -O2 -Wall -Wstrict-prototypes',
Packit 7a8e5e
                                     linker_exe='g++ ',
Packit 7a8e5e
                                     linker_so='g++ -shared')
Packit 7a8e5e
        # added for python2.3 support
Packit 7a8e5e
        # we can't pass it through set_executables because pre 2.2 would fail
Packit 7a8e5e
        self.compiler_cxx = ['g++']
Packit 7a8e5e
Packit 7a8e5e
        # Maybe we should also append -mthreads, but then the finished dlls
Packit 7a8e5e
        # need another dll (mingwm10.dll see Mingw32 docs) (-mthreads: Support
Packit 7a8e5e
        # thread-safe exception handling on `Mingw32')
Packit 7a8e5e
Packit 7a8e5e
        # no additional libraries needed
Packit 7a8e5e
        #self.dll_libraries=[]
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    # __init__ ()
Packit 7a8e5e
Packit 7a8e5e
    def link(self,
Packit 7a8e5e
             target_desc,
Packit 7a8e5e
             objects,
Packit 7a8e5e
             output_filename,
Packit 7a8e5e
             output_dir,
Packit 7a8e5e
             libraries,
Packit 7a8e5e
             library_dirs,
Packit 7a8e5e
             runtime_library_dirs,
Packit 7a8e5e
             export_symbols = None,
Packit 7a8e5e
             debug=0,
Packit 7a8e5e
             extra_preargs=None,
Packit 7a8e5e
             extra_postargs=None,
Packit 7a8e5e
             build_temp=None,
Packit 7a8e5e
             target_lang=None):
Packit 7a8e5e
        # Include the appropriate MSVC runtime library if Python was built
Packit 7a8e5e
        # with MSVC >= 7.0 (MinGW standard is msvcrt)
Packit 7a8e5e
        runtime_library = msvc_runtime_library()
Packit 7a8e5e
        if runtime_library:
Packit 7a8e5e
            if not libraries:
Packit 7a8e5e
                libraries = []
Packit 7a8e5e
            libraries.append(runtime_library)
Packit 7a8e5e
        args = (self,
Packit 7a8e5e
                target_desc,
Packit 7a8e5e
                objects,
Packit 7a8e5e
                output_filename,
Packit 7a8e5e
                output_dir,
Packit 7a8e5e
                libraries,
Packit 7a8e5e
                library_dirs,
Packit 7a8e5e
                runtime_library_dirs,
Packit 7a8e5e
                None, #export_symbols, we do this in our def-file
Packit 7a8e5e
                debug,
Packit 7a8e5e
                extra_preargs,
Packit 7a8e5e
                extra_postargs,
Packit 7a8e5e
                build_temp,
Packit 7a8e5e
                target_lang)
Packit 7a8e5e
        if self.gcc_version < "3.0.0":
Packit 7a8e5e
            func = distutils.cygwinccompiler.CygwinCCompiler.link
Packit 7a8e5e
        else:
Packit 7a8e5e
            func = UnixCCompiler.link
Packit 7a8e5e
        func(*args[:func.__code__.co_argcount])
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    def object_filenames (self,
Packit 7a8e5e
                          source_filenames,
Packit 7a8e5e
                          strip_dir=0,
Packit 7a8e5e
                          output_dir=''):
Packit 7a8e5e
        if output_dir is None: output_dir = ''
Packit 7a8e5e
        obj_names = []
Packit 7a8e5e
        for src_name in source_filenames:
Packit 7a8e5e
            # use normcase to make sure '.rc' is really '.rc' and not '.RC'
Packit 7a8e5e
            (base, ext) = os.path.splitext (os.path.normcase(src_name))
Packit 7a8e5e
Packit 7a8e5e
            # added these lines to strip off windows drive letters
Packit 7a8e5e
            # without it, .o files are placed next to .c files
Packit 7a8e5e
            # instead of the build directory
Packit 7a8e5e
            drv, base = os.path.splitdrive(base)
Packit 7a8e5e
            if drv:
Packit 7a8e5e
                base = base[1:]
Packit 7a8e5e
Packit 7a8e5e
            if ext not in (self.src_extensions + ['.rc', '.res']):
Packit 7a8e5e
                raise UnknownFileError(
Packit 7a8e5e
                      "unknown file type '%s' (from '%s')" % \
Packit 7a8e5e
                      (ext, src_name))
Packit 7a8e5e
            if strip_dir:
Packit 7a8e5e
                base = os.path.basename (base)
Packit 7a8e5e
            if ext == '.res' or ext == '.rc':
Packit 7a8e5e
                # these need to be compiled to object files
Packit 7a8e5e
                obj_names.append (os.path.join (output_dir,
Packit 7a8e5e
                                                base + ext + self.obj_extension))
Packit 7a8e5e
            else:
Packit 7a8e5e
                obj_names.append (os.path.join (output_dir,
Packit 7a8e5e
                                                base + self.obj_extension))
Packit 7a8e5e
        return obj_names
Packit 7a8e5e
Packit 7a8e5e
    # object_filenames ()
Packit 7a8e5e
Packit 7a8e5e
Packit 7a8e5e
def find_python_dll():
Packit 7a8e5e
    # We can't do much here:
Packit 7a8e5e
    # - find it in the virtualenv (sys.prefix)
Packit 7a8e5e
    # - find it in python main dir (sys.base_prefix, if in a virtualenv)
Packit 7a8e5e
    # - sys.real_prefix is main dir for virtualenvs in Python 2.7
Packit 7a8e5e
    # - in system32,
Packit 7a8e5e
    # - ortherwise (Sxs), I don't know how to get it.
Packit 7a8e5e
    stems = [sys.prefix]
Packit 7a8e5e
    if hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix:
Packit 7a8e5e
        stems.append(sys.base_prefix)
Packit 7a8e5e
    elif hasattr(sys, 'real_prefix') and sys.real_prefix != sys.prefix:
Packit 7a8e5e
        stems.append(sys.real_prefix)
Packit 7a8e5e
Packit 7a8e5e
    sub_dirs = ['', 'lib', 'bin']
Packit 7a8e5e
    # generate possible combinations of directory trees and sub-directories
Packit 7a8e5e
    lib_dirs = []
Packit 7a8e5e
    for stem in stems:
Packit 7a8e5e
        for folder in sub_dirs:
Packit 7a8e5e
            lib_dirs.append(os.path.join(stem, folder))
Packit 7a8e5e
Packit 7a8e5e
    # add system directory as well
Packit 7a8e5e
    if 'SYSTEMROOT' in os.environ:
Packit 7a8e5e
        lib_dirs.append(os.path.join(os.environ['SYSTEMROOT'], 'System32'))
Packit 7a8e5e
Packit 7a8e5e
    # search in the file system for possible candidates
Packit 7a8e5e
    major_version, minor_version = tuple(sys.version_info[:2])
Packit 7a8e5e
    patterns = ['python%d%d.dll']
Packit 7a8e5e
Packit 7a8e5e
    for pat in patterns:
Packit 7a8e5e
        dllname = pat % (major_version, minor_version)
Packit 7a8e5e
        print("Looking for %s" % dllname)
Packit 7a8e5e
        for folder in lib_dirs:
Packit 7a8e5e
            dll = os.path.join(folder, dllname)
Packit 7a8e5e
            if os.path.exists(dll):
Packit 7a8e5e
                return dll
Packit 7a8e5e
Packit 7a8e5e
    raise ValueError("%s not found in %s" % (dllname, lib_dirs))
Packit 7a8e5e
Packit 7a8e5e
def dump_table(dll):
Packit 7a8e5e
    st = subprocess.Popen(["objdump.exe", "-p", dll], stdout=subprocess.PIPE)
Packit 7a8e5e
    return st.stdout.readlines()
Packit 7a8e5e
Packit 7a8e5e
def generate_def(dll, dfile):
Packit 7a8e5e
    """Given a dll file location,  get all its exported symbols and dump them
Packit 7a8e5e
    into the given def file.
Packit 7a8e5e
Packit 7a8e5e
    The .def file will be overwritten"""
Packit 7a8e5e
    dump = dump_table(dll)
Packit 7a8e5e
    for i in range(len(dump)):
Packit 7a8e5e
        if _START.match(dump[i].decode()):
Packit 7a8e5e
            break
Packit 7a8e5e
    else:
Packit 7a8e5e
        raise ValueError("Symbol table not found")
Packit 7a8e5e
Packit 7a8e5e
    syms = []
Packit 7a8e5e
    for j in range(i+1, len(dump)):
Packit 7a8e5e
        m = _TABLE.match(dump[j].decode())
Packit 7a8e5e
        if m:
Packit 7a8e5e
            syms.append((int(m.group(1).strip()), m.group(2)))
Packit 7a8e5e
        else:
Packit 7a8e5e
            break
Packit 7a8e5e
Packit 7a8e5e
    if len(syms) == 0:
Packit 7a8e5e
        log.warn('No symbols found in %s' % dll)
Packit 7a8e5e
Packit 7a8e5e
    d = open(dfile, 'w')
Packit 7a8e5e
    d.write('LIBRARY        %s\n' % os.path.basename(dll))
Packit 7a8e5e
    d.write(';CODE          PRELOAD MOVEABLE DISCARDABLE\n')
Packit 7a8e5e
    d.write(';DATA          PRELOAD SINGLE\n')
Packit 7a8e5e
    d.write('\nEXPORTS\n')
Packit 7a8e5e
    for s in syms:
Packit 7a8e5e
        #d.write('@%d    %s\n' % (s[0], s[1]))
Packit 7a8e5e
        d.write('%s\n' % s[1])
Packit 7a8e5e
    d.close()
Packit 7a8e5e
Packit 7a8e5e
def find_dll(dll_name):
Packit 7a8e5e
Packit 7a8e5e
    arch = {'AMD64' : 'amd64',
Packit 7a8e5e
            'Intel' : 'x86'}[get_build_architecture()]
Packit 7a8e5e
Packit 7a8e5e
    def _find_dll_in_winsxs(dll_name):
Packit 7a8e5e
        # Walk through the WinSxS directory to find the dll.
Packit 7a8e5e
        winsxs_path = os.path.join(os.environ.get('WINDIR', r'C:\WINDOWS'),
Packit 7a8e5e
                                   'winsxs')
Packit 7a8e5e
        if not os.path.exists(winsxs_path):
Packit 7a8e5e
            return None
Packit 7a8e5e
        for root, dirs, files in os.walk(winsxs_path):
Packit 7a8e5e
            if dll_name in files and arch in root:
Packit 7a8e5e
                return os.path.join(root, dll_name)
Packit 7a8e5e
        return None
Packit 7a8e5e
Packit 7a8e5e
    def _find_dll_in_path(dll_name):
Packit 7a8e5e
        # First, look in the Python directory, then scan PATH for
Packit 7a8e5e
        # the given dll name.
Packit 7a8e5e
        for path in [sys.prefix] + os.environ['PATH'].split(';'):
Packit 7a8e5e
            filepath = os.path.join(path, dll_name)
Packit 7a8e5e
            if os.path.exists(filepath):
Packit 7a8e5e
                return os.path.abspath(filepath)
Packit 7a8e5e
Packit 7a8e5e
    return _find_dll_in_winsxs(dll_name) or _find_dll_in_path(dll_name)
Packit 7a8e5e
Packit 7a8e5e
def build_msvcr_library(debug=False):
Packit 7a8e5e
    if os.name != 'nt':
Packit 7a8e5e
        return False
Packit 7a8e5e
Packit 7a8e5e
    # If the version number is None, then we couldn't find the MSVC runtime at
Packit 7a8e5e
    # all, because we are running on a Python distribution which is customed
Packit 7a8e5e
    # compiled; trust that the compiler is the same as the one available to us
Packit 7a8e5e
    # now, and that it is capable of linking with the correct runtime without
Packit 7a8e5e
    # any extra options.
Packit 7a8e5e
    msvcr_ver = msvc_runtime_major()
Packit 7a8e5e
    if msvcr_ver is None:
Packit 7a8e5e
        log.debug('Skip building import library: '
Packit 7a8e5e
                  'Runtime is not compiled with MSVC')
Packit 7a8e5e
        return False
Packit 7a8e5e
Packit 7a8e5e
    # Skip using a custom library for versions < MSVC 8.0
Packit 7a8e5e
    if msvcr_ver < 80:
Packit 7a8e5e
        log.debug('Skip building msvcr library:'
Packit 7a8e5e
                  ' custom functionality not present')
Packit 7a8e5e
        return False
Packit 7a8e5e
Packit 7a8e5e
    msvcr_name = msvc_runtime_library()
Packit 7a8e5e
    if debug:
Packit 7a8e5e
        msvcr_name += 'd'
Packit 7a8e5e
Packit 7a8e5e
    # Skip if custom library already exists
Packit 7a8e5e
    out_name = "lib%s.a" % msvcr_name
Packit 7a8e5e
    out_file = os.path.join(sys.prefix, 'libs', out_name)
Packit 7a8e5e
    if os.path.isfile(out_file):
Packit 7a8e5e
        log.debug('Skip building msvcr library: "%s" exists' %
Packit 7a8e5e
                  (out_file,))
Packit 7a8e5e
        return True
Packit 7a8e5e
Packit 7a8e5e
    # Find the msvcr dll
Packit 7a8e5e
    msvcr_dll_name = msvcr_name + '.dll'
Packit 7a8e5e
    dll_file = find_dll(msvcr_dll_name)
Packit 7a8e5e
    if not dll_file:
Packit 7a8e5e
        log.warn('Cannot build msvcr library: "%s" not found' %
Packit 7a8e5e
                 msvcr_dll_name)
Packit 7a8e5e
        return False
Packit 7a8e5e
Packit 7a8e5e
    def_name = "lib%s.def" % msvcr_name
Packit 7a8e5e
    def_file = os.path.join(sys.prefix, 'libs', def_name)
Packit 7a8e5e
Packit 7a8e5e
    log.info('Building msvcr library: "%s" (from %s)' \
Packit 7a8e5e
             % (out_file, dll_file))
Packit 7a8e5e
Packit 7a8e5e
    # Generate a symbol definition file from the msvcr dll
Packit 7a8e5e
    generate_def(dll_file, def_file)
Packit 7a8e5e
Packit 7a8e5e
    # Create a custom mingw library for the given symbol definitions
Packit 7a8e5e
    cmd = ['dlltool', '-d', def_file, '-l', out_file]
Packit 7a8e5e
    retcode = subprocess.call(cmd)
Packit 7a8e5e
Packit 7a8e5e
    # Clean up symbol definitions
Packit 7a8e5e
    os.remove(def_file)
Packit 7a8e5e
Packit 7a8e5e
    return (not retcode)
Packit 7a8e5e
Packit 7a8e5e
def build_import_library():
Packit 7a8e5e
    if os.name != 'nt':
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    arch = get_build_architecture()
Packit 7a8e5e
    if arch == 'AMD64':
Packit 7a8e5e
        return _build_import_library_amd64()
Packit 7a8e5e
    elif arch == 'Intel':
Packit 7a8e5e
        return _build_import_library_x86()
Packit 7a8e5e
    else:
Packit 7a8e5e
        raise ValueError("Unhandled arch %s" % arch)
Packit 7a8e5e
Packit 7a8e5e
def _check_for_import_lib():
Packit 7a8e5e
    """Check if an import library for the Python runtime already exists."""
Packit 7a8e5e
    major_version, minor_version = tuple(sys.version_info[:2])
Packit 7a8e5e
Packit 7a8e5e
    # patterns for the file name of the library itself
Packit 7a8e5e
    patterns = ['libpython%d%d.a',
Packit 7a8e5e
                'libpython%d%d.dll.a',
Packit 7a8e5e
                'libpython%d.%d.dll.a']
Packit 7a8e5e
Packit 7a8e5e
    # directory trees that may contain the library
Packit 7a8e5e
    stems = [sys.prefix]
Packit 7a8e5e
    if hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix:
Packit 7a8e5e
        stems.append(sys.base_prefix)
Packit 7a8e5e
    elif hasattr(sys, 'real_prefix') and sys.real_prefix != sys.prefix:
Packit 7a8e5e
        stems.append(sys.real_prefix)
Packit 7a8e5e
Packit 7a8e5e
    # possible subdirectories within those trees where it is placed
Packit 7a8e5e
    sub_dirs = ['libs', 'lib']
Packit 7a8e5e
Packit 7a8e5e
    # generate a list of candidate locations
Packit 7a8e5e
    candidates = []
Packit 7a8e5e
    for pat in patterns:
Packit 7a8e5e
        filename = pat % (major_version, minor_version)
Packit 7a8e5e
        for stem_dir in stems:
Packit 7a8e5e
            for folder in sub_dirs:
Packit 7a8e5e
                candidates.append(os.path.join(stem_dir, folder, filename))
Packit 7a8e5e
Packit 7a8e5e
    # test the filesystem to see if we can find any of these
Packit 7a8e5e
    for fullname in candidates:
Packit 7a8e5e
        if os.path.isfile(fullname):
Packit 7a8e5e
            # already exists, in location given
Packit 7a8e5e
            return (True, fullname)
Packit 7a8e5e
Packit 7a8e5e
    # needs to be built, preferred location given first
Packit 7a8e5e
    return (False, candidates[0])
Packit 7a8e5e
Packit 7a8e5e
def _build_import_library_amd64():
Packit 7a8e5e
    out_exists, out_file = _check_for_import_lib()
Packit 7a8e5e
    if out_exists:
Packit 7a8e5e
        log.debug('Skip building import library: "%s" exists', out_file)
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    # get the runtime dll for which we are building import library
Packit 7a8e5e
    dll_file = find_python_dll()
Packit 7a8e5e
    log.info('Building import library (arch=AMD64): "%s" (from %s)' %
Packit 7a8e5e
             (out_file, dll_file))
Packit 7a8e5e
Packit 7a8e5e
    # generate symbol list from this library
Packit 7a8e5e
    def_name = "python%d%d.def" % tuple(sys.version_info[:2])
Packit 7a8e5e
    def_file = os.path.join(sys.prefix, 'libs', def_name)
Packit 7a8e5e
    generate_def(dll_file, def_file)
Packit 7a8e5e
Packit 7a8e5e
    # generate import library from this symbol list
Packit 7a8e5e
    cmd = ['dlltool', '-d', def_file, '-l', out_file]
Packit 7a8e5e
    subprocess.Popen(cmd)
Packit 7a8e5e
Packit 7a8e5e
def _build_import_library_x86():
Packit 7a8e5e
    """ Build the import libraries for Mingw32-gcc on Windows
Packit 7a8e5e
    """
Packit 7a8e5e
    out_exists, out_file = _check_for_import_lib()
Packit 7a8e5e
    if out_exists:
Packit 7a8e5e
        log.debug('Skip building import library: "%s" exists', out_file)
Packit 7a8e5e
        return
Packit 7a8e5e
Packit 7a8e5e
    lib_name = "python%d%d.lib" % tuple(sys.version_info[:2])
Packit 7a8e5e
    lib_file = os.path.join(sys.prefix, 'libs', lib_name)
Packit 7a8e5e
    if not os.path.isfile(lib_file):
Packit 7a8e5e
        # didn't find library file in virtualenv, try base distribution, too,
Packit 7a8e5e
        # and use that instead if found there. for Python 2.7 venvs, the base
Packit 7a8e5e
        # directory is in attribute real_prefix instead of base_prefix.
Packit 7a8e5e
        if hasattr(sys, 'base_prefix'):
Packit 7a8e5e
            base_lib = os.path.join(sys.base_prefix, 'libs', lib_name)
Packit 7a8e5e
        elif hasattr(sys, 'real_prefix'):
Packit 7a8e5e
            base_lib = os.path.join(sys.real_prefix, 'libs', lib_name)
Packit 7a8e5e
        else:
Packit 7a8e5e
            base_lib = ''  # os.path.isfile('') == False
Packit 7a8e5e
Packit 7a8e5e
        if os.path.isfile(base_lib):
Packit 7a8e5e
            lib_file = base_lib
Packit 7a8e5e
        else:
Packit 7a8e5e
            log.warn('Cannot build import library: "%s" not found', lib_file)
Packit 7a8e5e
            return
Packit 7a8e5e
    log.info('Building import library (ARCH=x86): "%s"', out_file)
Packit 7a8e5e
Packit 7a8e5e
    from numpy.distutils import lib2def
Packit 7a8e5e
Packit 7a8e5e
    def_name = "python%d%d.def" % tuple(sys.version_info[:2])
Packit 7a8e5e
    def_file = os.path.join(sys.prefix, 'libs', def_name)
Packit 7a8e5e
    nm_cmd = '%s %s' % (lib2def.DEFAULT_NM, lib_file)
Packit 7a8e5e
    nm_output = lib2def.getnm(nm_cmd)
Packit 7a8e5e
    dlist, flist = lib2def.parse_nm(nm_output)
Packit 7a8e5e
    lib2def.output_def(dlist, flist, lib2def.DEF_HEADER, open(def_file, 'w'))
Packit 7a8e5e
Packit 7a8e5e
    dll_name = find_python_dll ()
Packit 7a8e5e
    args = (dll_name, def_file, out_file)
Packit 7a8e5e
    cmd = 'dlltool --dllname "%s" --def "%s" --output-lib "%s"' % args
Packit 7a8e5e
    status = os.system(cmd)
Packit 7a8e5e
    # for now, fail silently
Packit 7a8e5e
    if status:
Packit 7a8e5e
        log.warn('Failed to build import library for gcc. Linking will fail.')
Packit 7a8e5e
    return
Packit 7a8e5e
Packit 7a8e5e
#=====================================
Packit 7a8e5e
# Dealing with Visual Studio MANIFESTS
Packit 7a8e5e
#=====================================
Packit 7a8e5e
Packit 7a8e5e
# Functions to deal with visual studio manifests. Manifest are a mechanism to
Packit 7a8e5e
# enforce strong DLL versioning on windows, and has nothing to do with
Packit 7a8e5e
# distutils MANIFEST. manifests are XML files with version info, and used by
Packit 7a8e5e
# the OS loader; they are necessary when linking against a DLL not in the
Packit 7a8e5e
# system path; in particular, official python 2.6 binary is built against the
Packit 7a8e5e
# MS runtime 9 (the one from VS 2008), which is not available on most windows
Packit 7a8e5e
# systems; python 2.6 installer does install it in the Win SxS (Side by side)
Packit 7a8e5e
# directory, but this requires the manifest for this to work. This is a big
Packit 7a8e5e
# mess, thanks MS for a wonderful system.
Packit 7a8e5e
Packit 7a8e5e
# XXX: ideally, we should use exactly the same version as used by python. I
Packit 7a8e5e
# submitted a patch to get this version, but it was only included for python
Packit 7a8e5e
# 2.6.1 and above. So for versions below, we use a "best guess".
Packit 7a8e5e
_MSVCRVER_TO_FULLVER = {}
Packit 7a8e5e
if sys.platform == 'win32':
Packit 7a8e5e
    try:
Packit 7a8e5e
        import msvcrt
Packit 7a8e5e
        # I took one version in my SxS directory: no idea if it is the good
Packit 7a8e5e
        # one, and we can't retrieve it from python
Packit 7a8e5e
        _MSVCRVER_TO_FULLVER['80'] = "8.0.50727.42"
Packit 7a8e5e
        _MSVCRVER_TO_FULLVER['90'] = "9.0.21022.8"
Packit 7a8e5e
        # Value from msvcrt.CRT_ASSEMBLY_VERSION under Python 3.3.0
Packit 7a8e5e
        # on Windows XP:
Packit 7a8e5e
        _MSVCRVER_TO_FULLVER['100'] = "10.0.30319.460"
Packit 7a8e5e
        if hasattr(msvcrt, "CRT_ASSEMBLY_VERSION"):
Packit 7a8e5e
            major, minor, rest = msvcrt.CRT_ASSEMBLY_VERSION.split(".", 2)
Packit 7a8e5e
            _MSVCRVER_TO_FULLVER[major + minor] = msvcrt.CRT_ASSEMBLY_VERSION
Packit 7a8e5e
            del major, minor, rest
Packit 7a8e5e
    except ImportError:
Packit 7a8e5e
        # If we are here, means python was not built with MSVC. Not sure what
Packit 7a8e5e
        # to do in that case: manifest building will fail, but it should not be
Packit 7a8e5e
        # used in that case anyway
Packit 7a8e5e
        log.warn('Cannot import msvcrt: using manifest will not be possible')
Packit 7a8e5e
Packit 7a8e5e
def msvc_manifest_xml(maj, min):
Packit 7a8e5e
    """Given a major and minor version of the MSVCR, returns the
Packit 7a8e5e
    corresponding XML file."""
Packit 7a8e5e
    try:
Packit 7a8e5e
        fullver = _MSVCRVER_TO_FULLVER[str(maj * 10 + min)]
Packit 7a8e5e
    except KeyError:
Packit 7a8e5e
        raise ValueError("Version %d,%d of MSVCRT not supported yet" %
Packit 7a8e5e
                         (maj, min))
Packit 7a8e5e
    # Don't be fooled, it looks like an XML, but it is not. In particular, it
Packit 7a8e5e
    # should not have any space before starting, and its size should be
Packit 7a8e5e
    # divisible by 4, most likely for alignement constraints when the xml is
Packit 7a8e5e
    # embedded in the binary...
Packit 7a8e5e
    # This template was copied directly from the python 2.6 binary (using
Packit 7a8e5e
    # strings.exe from mingw on python.exe).
Packit 7a8e5e
    template = """\
Packit 7a8e5e
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
Packit 7a8e5e
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
Packit 7a8e5e
    <security>
Packit 7a8e5e
      <requestedPrivileges>
Packit 7a8e5e
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
Packit 7a8e5e
      </requestedPrivileges>
Packit 7a8e5e
    </security>
Packit 7a8e5e
  </trustInfo>
Packit 7a8e5e
  <dependency>
Packit 7a8e5e
    <dependentAssembly>
Packit 7a8e5e
      <assemblyIdentity type="win32" name="Microsoft.VC%(maj)d%(min)d.CRT" version="%(fullver)s" processorArchitecture="*" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
Packit 7a8e5e
    </dependentAssembly>
Packit 7a8e5e
  </dependency>
Packit 7a8e5e
</assembly>"""
Packit 7a8e5e
Packit 7a8e5e
    return template % {'fullver': fullver, 'maj': maj, 'min': min}
Packit 7a8e5e
Packit 7a8e5e
def manifest_rc(name, type='dll'):
Packit 7a8e5e
    """Return the rc file used to generate the res file which will be embedded
Packit 7a8e5e
    as manifest for given manifest file name, of given type ('dll' or
Packit 7a8e5e
    'exe').
Packit 7a8e5e
Packit 7a8e5e
    Parameters
Packit 7a8e5e
    ----------
Packit 7a8e5e
    name : str
Packit 7a8e5e
            name of the manifest file to embed
Packit 7a8e5e
    type : str {'dll', 'exe'}
Packit 7a8e5e
            type of the binary which will embed the manifest
Packit 7a8e5e
Packit 7a8e5e
    """
Packit 7a8e5e
    if type == 'dll':
Packit 7a8e5e
        rctype = 2
Packit 7a8e5e
    elif type == 'exe':
Packit 7a8e5e
        rctype = 1
Packit 7a8e5e
    else:
Packit 7a8e5e
        raise ValueError("Type %s not supported" % type)
Packit 7a8e5e
Packit 7a8e5e
    return """\
Packit 7a8e5e
#include "winuser.h"
Packit 7a8e5e
%d RT_MANIFEST %s""" % (rctype, name)
Packit 7a8e5e
Packit 7a8e5e
def check_embedded_msvcr_match_linked(msver):
Packit 7a8e5e
    """msver is the ms runtime version used for the MANIFEST."""
Packit 7a8e5e
    # check msvcr major version are the same for linking and
Packit 7a8e5e
    # embedding
Packit 7a8e5e
    maj = msvc_runtime_major()
Packit 7a8e5e
    if maj:
Packit 7a8e5e
        if not maj == int(msver):
Packit 7a8e5e
            raise ValueError(
Packit 7a8e5e
                  "Discrepancy between linked msvcr " \
Packit 7a8e5e
                  "(%d) and the one about to be embedded " \
Packit 7a8e5e
                  "(%d)" % (int(msver), maj))
Packit 7a8e5e
Packit 7a8e5e
def configtest_name(config):
Packit 7a8e5e
    base = os.path.basename(config._gen_temp_sourcefile("yo", [], "c"))
Packit 7a8e5e
    return os.path.splitext(base)[0]
Packit 7a8e5e
Packit 7a8e5e
def manifest_name(config):
Packit 7a8e5e
    # Get configest name (including suffix)
Packit 7a8e5e
    root = configtest_name(config)
Packit 7a8e5e
    exext = config.compiler.exe_extension
Packit 7a8e5e
    return root + exext + ".manifest"
Packit 7a8e5e
Packit 7a8e5e
def rc_name(config):
Packit 7a8e5e
    # Get configtest name (including suffix)
Packit 7a8e5e
    root = configtest_name(config)
Packit 7a8e5e
    return root + ".rc"
Packit 7a8e5e
Packit 7a8e5e
def generate_manifest(config):
Packit 7a8e5e
    msver = get_build_msvc_version()
Packit 7a8e5e
    if msver is not None:
Packit 7a8e5e
        if msver >= 8:
Packit 7a8e5e
            check_embedded_msvcr_match_linked(msver)
Packit 7a8e5e
            ma = int(msver)
Packit 7a8e5e
            mi = int((msver - ma) * 10)
Packit 7a8e5e
            # Write the manifest file
Packit 7a8e5e
            manxml = msvc_manifest_xml(ma, mi)
Packit 7a8e5e
            man = open(manifest_name(config), "w")
Packit 7a8e5e
            config.temp_files.append(manifest_name(config))
Packit 7a8e5e
            man.write(manxml)
Packit 7a8e5e
            man.close()