mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
initial import of the packaging package in the standard library
This commit is contained in:
parent
566f8a646e
commit
1231a4e097
193 changed files with 30376 additions and 149 deletions
282
Lib/packaging/compiler/__init__.py
Normal file
282
Lib/packaging/compiler/__init__.py
Normal file
|
@ -0,0 +1,282 @@
|
|||
"""Compiler abstraction model used by packaging.
|
||||
|
||||
An abstract base class is defined in the ccompiler submodule, and
|
||||
concrete implementations suitable for various platforms are defined in
|
||||
the other submodules. The extension module is also placed in this
|
||||
package.
|
||||
|
||||
In general, code should not instantiate compiler classes directly but
|
||||
use the new_compiler and customize_compiler functions provided in this
|
||||
module.
|
||||
|
||||
The compiler system has a registration API: get_default_compiler,
|
||||
set_compiler, show_compilers.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
import sysconfig
|
||||
from packaging.util import resolve_name
|
||||
from packaging.errors import PackagingPlatformError
|
||||
|
||||
|
||||
def customize_compiler(compiler):
|
||||
"""Do any platform-specific customization of a CCompiler instance.
|
||||
|
||||
Mainly needed on Unix, so we can plug in the information that
|
||||
varies across Unices and is stored in Python's Makefile.
|
||||
"""
|
||||
if compiler.name == "unix":
|
||||
cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags = (
|
||||
sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
|
||||
'CCSHARED', 'LDSHARED', 'SO', 'AR',
|
||||
'ARFLAGS'))
|
||||
|
||||
if 'CC' in os.environ:
|
||||
cc = os.environ['CC']
|
||||
if 'CXX' in os.environ:
|
||||
cxx = os.environ['CXX']
|
||||
if 'LDSHARED' in os.environ:
|
||||
ldshared = os.environ['LDSHARED']
|
||||
if 'CPP' in os.environ:
|
||||
cpp = os.environ['CPP']
|
||||
else:
|
||||
cpp = cc + " -E" # not always
|
||||
if 'LDFLAGS' in os.environ:
|
||||
ldshared = ldshared + ' ' + os.environ['LDFLAGS']
|
||||
if 'CFLAGS' in os.environ:
|
||||
cflags = opt + ' ' + os.environ['CFLAGS']
|
||||
ldshared = ldshared + ' ' + os.environ['CFLAGS']
|
||||
if 'CPPFLAGS' in os.environ:
|
||||
cpp = cpp + ' ' + os.environ['CPPFLAGS']
|
||||
cflags = cflags + ' ' + os.environ['CPPFLAGS']
|
||||
ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
|
||||
if 'AR' in os.environ:
|
||||
ar = os.environ['AR']
|
||||
if 'ARFLAGS' in os.environ:
|
||||
archiver = ar + ' ' + os.environ['ARFLAGS']
|
||||
else:
|
||||
if ar_flags is not None:
|
||||
archiver = ar + ' ' + ar_flags
|
||||
else:
|
||||
# see if its the proper default value
|
||||
# mmm I don't want to backport the makefile
|
||||
archiver = ar + ' rc'
|
||||
|
||||
cc_cmd = cc + ' ' + cflags
|
||||
compiler.set_executables(
|
||||
preprocessor=cpp,
|
||||
compiler=cc_cmd,
|
||||
compiler_so=cc_cmd + ' ' + ccshared,
|
||||
compiler_cxx=cxx,
|
||||
linker_so=ldshared,
|
||||
linker_exe=cc,
|
||||
archiver=archiver)
|
||||
|
||||
compiler.shared_lib_extension = so_ext
|
||||
|
||||
|
||||
# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
|
||||
# type for that platform. Keys are interpreted as re match
|
||||
# patterns. Order is important; platform mappings are preferred over
|
||||
# OS names.
|
||||
_default_compilers = (
|
||||
|
||||
# Platform string mappings
|
||||
|
||||
# on a cygwin built python we can use gcc like an ordinary UNIXish
|
||||
# compiler
|
||||
('cygwin.*', 'unix'),
|
||||
('os2emx', 'emx'),
|
||||
|
||||
# OS name mappings
|
||||
('posix', 'unix'),
|
||||
('nt', 'msvc'),
|
||||
|
||||
)
|
||||
|
||||
def get_default_compiler(osname=None, platform=None):
|
||||
""" Determine the default compiler to use for the given platform.
|
||||
|
||||
osname should be one of the standard Python OS names (i.e. the
|
||||
ones returned by os.name) and platform the common value
|
||||
returned by sys.platform for the platform in question.
|
||||
|
||||
The default values are os.name and sys.platform in case the
|
||||
parameters are not given.
|
||||
|
||||
"""
|
||||
if osname is None:
|
||||
osname = os.name
|
||||
if platform is None:
|
||||
platform = sys.platform
|
||||
for pattern, compiler in _default_compilers:
|
||||
if re.match(pattern, platform) is not None or \
|
||||
re.match(pattern, osname) is not None:
|
||||
return compiler
|
||||
# Defaults to Unix compiler
|
||||
return 'unix'
|
||||
|
||||
|
||||
# compiler mapping
|
||||
# XXX useful to expose them? (i.e. get_compiler_names)
|
||||
_COMPILERS = {
|
||||
'unix': 'packaging.compiler.unixccompiler.UnixCCompiler',
|
||||
'msvc': 'packaging.compiler.msvccompiler.MSVCCompiler',
|
||||
'cygwin': 'packaging.compiler.cygwinccompiler.CygwinCCompiler',
|
||||
'mingw32': 'packaging.compiler.cygwinccompiler.Mingw32CCompiler',
|
||||
'bcpp': 'packaging.compiler.bcppcompiler.BCPPCompiler',
|
||||
}
|
||||
|
||||
def set_compiler(location):
|
||||
"""Add or change a compiler"""
|
||||
cls = resolve_name(location)
|
||||
# XXX we want to check the class here
|
||||
_COMPILERS[cls.name] = cls
|
||||
|
||||
|
||||
def show_compilers():
|
||||
"""Print list of available compilers (used by the "--help-compiler"
|
||||
options to "build", "build_ext", "build_clib").
|
||||
"""
|
||||
from packaging.fancy_getopt import FancyGetopt
|
||||
compilers = []
|
||||
|
||||
for name, cls in _COMPILERS.items():
|
||||
if isinstance(cls, str):
|
||||
cls = resolve_name(cls)
|
||||
_COMPILERS[name] = cls
|
||||
|
||||
compilers.append(("compiler=" + name, None, cls.description))
|
||||
|
||||
compilers.sort()
|
||||
pretty_printer = FancyGetopt(compilers)
|
||||
pretty_printer.print_help("List of available compilers:")
|
||||
|
||||
|
||||
def new_compiler(plat=None, compiler=None, verbose=0, dry_run=False,
|
||||
force=False):
|
||||
"""Generate an instance of some CCompiler subclass for the supplied
|
||||
platform/compiler combination. 'plat' defaults to 'os.name'
|
||||
(eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
|
||||
for that platform. Currently only 'posix' and 'nt' are supported, and
|
||||
the default compilers are "traditional Unix interface" (UnixCCompiler
|
||||
class) and Visual C++ (MSVCCompiler class). Note that it's perfectly
|
||||
possible to ask for a Unix compiler object under Windows, and a
|
||||
Microsoft compiler object under Unix -- if you supply a value for
|
||||
'compiler', 'plat' is ignored.
|
||||
"""
|
||||
if plat is None:
|
||||
plat = os.name
|
||||
|
||||
try:
|
||||
if compiler is None:
|
||||
compiler = get_default_compiler(plat)
|
||||
|
||||
cls = _COMPILERS[compiler]
|
||||
except KeyError:
|
||||
msg = "don't know how to compile C/C++ code on platform '%s'" % plat
|
||||
if compiler is not None:
|
||||
msg = msg + " with '%s' compiler" % compiler
|
||||
raise PackagingPlatformError(msg)
|
||||
|
||||
if isinstance(cls, str):
|
||||
cls = resolve_name(cls)
|
||||
_COMPILERS[compiler] = cls
|
||||
|
||||
|
||||
# XXX The None is necessary to preserve backwards compatibility
|
||||
# with classes that expect verbose to be the first positional
|
||||
# argument.
|
||||
return cls(None, dry_run, force)
|
||||
|
||||
|
||||
def gen_preprocess_options(macros, include_dirs):
|
||||
"""Generate C pre-processor options (-D, -U, -I) as used by at least
|
||||
two types of compilers: the typical Unix compiler and Visual C++.
|
||||
'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
|
||||
means undefine (-U) macro 'name', and (name,value) means define (-D)
|
||||
macro 'name' to 'value'. 'include_dirs' is just a list of directory
|
||||
names to be added to the header file search path (-I). Returns a list
|
||||
of command-line options suitable for either Unix compilers or Visual
|
||||
C++.
|
||||
"""
|
||||
# XXX it would be nice (mainly aesthetic, and so we don't generate
|
||||
# stupid-looking command lines) to go over 'macros' and eliminate
|
||||
# redundant definitions/undefinitions (ie. ensure that only the
|
||||
# latest mention of a particular macro winds up on the command
|
||||
# line). I don't think it's essential, though, since most (all?)
|
||||
# Unix C compilers only pay attention to the latest -D or -U
|
||||
# mention of a macro on their command line. Similar situation for
|
||||
# 'include_dirs'. I'm punting on both for now. Anyways, weeding out
|
||||
# redundancies like this should probably be the province of
|
||||
# CCompiler, since the data structures used are inherited from it
|
||||
# and therefore common to all CCompiler classes.
|
||||
|
||||
pp_opts = []
|
||||
for macro in macros:
|
||||
|
||||
if not isinstance(macro, tuple) and 1 <= len(macro) <= 2:
|
||||
raise TypeError(
|
||||
"bad macro definition '%s': each element of 'macros'"
|
||||
"list must be a 1- or 2-tuple" % macro)
|
||||
|
||||
if len(macro) == 1: # undefine this macro
|
||||
pp_opts.append("-U%s" % macro[0])
|
||||
elif len(macro) == 2:
|
||||
if macro[1] is None: # define with no explicit value
|
||||
pp_opts.append("-D%s" % macro[0])
|
||||
else:
|
||||
# XXX *don't* need to be clever about quoting the
|
||||
# macro value here, because we're going to avoid the
|
||||
# shell at all costs when we spawn the command!
|
||||
pp_opts.append("-D%s=%s" % macro)
|
||||
|
||||
for dir in include_dirs:
|
||||
pp_opts.append("-I%s" % dir)
|
||||
|
||||
return pp_opts
|
||||
|
||||
|
||||
def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
|
||||
"""Generate linker options for searching library directories and
|
||||
linking with specific libraries.
|
||||
|
||||
'libraries' and 'library_dirs' are, respectively, lists of library names
|
||||
(not filenames!) and search directories. Returns a list of command-line
|
||||
options suitable for use with some compiler (depending on the two format
|
||||
strings passed in).
|
||||
"""
|
||||
lib_opts = []
|
||||
|
||||
for dir in library_dirs:
|
||||
lib_opts.append(compiler.library_dir_option(dir))
|
||||
|
||||
for dir in runtime_library_dirs:
|
||||
opt = compiler.runtime_library_dir_option(dir)
|
||||
if isinstance(opt, list):
|
||||
lib_opts.extend(opt)
|
||||
else:
|
||||
lib_opts.append(opt)
|
||||
|
||||
# XXX it's important that we *not* remove redundant library mentions!
|
||||
# sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
|
||||
# resolve all symbols. I just hope we never have to say "-lfoo obj.o
|
||||
# -lbar" to get things to work -- that's certainly a possibility, but a
|
||||
# pretty nasty way to arrange your C code.
|
||||
|
||||
for lib in libraries:
|
||||
lib_dir, lib_name = os.path.split(lib)
|
||||
if lib_dir != '':
|
||||
lib_file = compiler.find_library_file([lib_dir], lib_name)
|
||||
if lib_file is not None:
|
||||
lib_opts.append(lib_file)
|
||||
else:
|
||||
compiler.warn("no library file corresponding to "
|
||||
"'%s' found (skipping)" % lib)
|
||||
else:
|
||||
lib_opts.append(compiler.library_option(lib))
|
||||
|
||||
return lib_opts
|
356
Lib/packaging/compiler/bcppcompiler.py
Normal file
356
Lib/packaging/compiler/bcppcompiler.py
Normal file
|
@ -0,0 +1,356 @@
|
|||
"""CCompiler implementation for the Borland C++ compiler."""
|
||||
|
||||
# This implementation by Lyle Johnson, based on the original msvccompiler.py
|
||||
# module and using the directions originally published by Gordon Williams.
|
||||
|
||||
# XXX looks like there's a LOT of overlap between these two classes:
|
||||
# someone should sit down and factor out the common code as
|
||||
# WindowsCCompiler! --GPW
|
||||
|
||||
import os
|
||||
|
||||
from packaging.errors import (PackagingExecError, CompileError, LibError,
|
||||
LinkError, UnknownFileError)
|
||||
from packaging.compiler.ccompiler import CCompiler
|
||||
from packaging.compiler import gen_preprocess_options
|
||||
from packaging.file_util import write_file
|
||||
from packaging.dep_util import newer
|
||||
from packaging import logger
|
||||
|
||||
|
||||
class BCPPCompiler(CCompiler) :
|
||||
"""Concrete class that implements an interface to the Borland C/C++
|
||||
compiler, as defined by the CCompiler abstract class.
|
||||
"""
|
||||
|
||||
name = 'bcpp'
|
||||
description = 'Borland C++ Compiler'
|
||||
|
||||
# Just set this so CCompiler's constructor doesn't barf. We currently
|
||||
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
|
||||
# as it really isn't necessary for this sort of single-compiler class.
|
||||
# Would be nice to have a consistent interface with UnixCCompiler,
|
||||
# though, so it's worth thinking about.
|
||||
executables = {}
|
||||
|
||||
# Private class data (need to distinguish C from C++ source for compiler)
|
||||
_c_extensions = ['.c']
|
||||
_cpp_extensions = ['.cc', '.cpp', '.cxx']
|
||||
|
||||
# Needed for the filename generation methods provided by the
|
||||
# base class, CCompiler.
|
||||
src_extensions = _c_extensions + _cpp_extensions
|
||||
obj_extension = '.obj'
|
||||
static_lib_extension = '.lib'
|
||||
shared_lib_extension = '.dll'
|
||||
static_lib_format = shared_lib_format = '%s%s'
|
||||
exe_extension = '.exe'
|
||||
|
||||
|
||||
def __init__(self, verbose=0, dry_run=False, force=False):
|
||||
CCompiler.__init__(self, verbose, dry_run, force)
|
||||
|
||||
# These executables are assumed to all be in the path.
|
||||
# Borland doesn't seem to use any special registry settings to
|
||||
# indicate their installation locations.
|
||||
|
||||
self.cc = "bcc32.exe"
|
||||
self.linker = "ilink32.exe"
|
||||
self.lib = "tlib.exe"
|
||||
|
||||
self.preprocess_options = None
|
||||
self.compile_options = ['/tWM', '/O2', '/q', '/g0']
|
||||
self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']
|
||||
|
||||
self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']
|
||||
self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
|
||||
self.ldflags_static = []
|
||||
self.ldflags_exe = ['/Gn', '/q', '/x']
|
||||
self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r']
|
||||
|
||||
|
||||
# -- Worker methods ------------------------------------------------
|
||||
|
||||
def compile(self, sources,
|
||||
output_dir=None, macros=None, include_dirs=None, debug=False,
|
||||
extra_preargs=None, extra_postargs=None, depends=None):
|
||||
|
||||
macros, objects, extra_postargs, pp_opts, build = \
|
||||
self._setup_compile(output_dir, macros, include_dirs, sources,
|
||||
depends, extra_postargs)
|
||||
compile_opts = extra_preargs or []
|
||||
compile_opts.append('-c')
|
||||
if debug:
|
||||
compile_opts.extend(self.compile_options_debug)
|
||||
else:
|
||||
compile_opts.extend(self.compile_options)
|
||||
|
||||
for obj in objects:
|
||||
try:
|
||||
src, ext = build[obj]
|
||||
except KeyError:
|
||||
continue
|
||||
# XXX why do the normpath here?
|
||||
src = os.path.normpath(src)
|
||||
obj = os.path.normpath(obj)
|
||||
# XXX _setup_compile() did a mkpath() too but before the normpath.
|
||||
# Is it possible to skip the normpath?
|
||||
self.mkpath(os.path.dirname(obj))
|
||||
|
||||
if ext == '.res':
|
||||
# This is already a binary file -- skip it.
|
||||
continue # the 'for' loop
|
||||
if ext == '.rc':
|
||||
# This needs to be compiled to a .res file -- do it now.
|
||||
try:
|
||||
self.spawn(["brcc32", "-fo", obj, src])
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
continue # the 'for' loop
|
||||
|
||||
# The next two are both for the real compiler.
|
||||
if ext in self._c_extensions:
|
||||
input_opt = ""
|
||||
elif ext in self._cpp_extensions:
|
||||
input_opt = "-P"
|
||||
else:
|
||||
# Unknown file type -- no extra options. The compiler
|
||||
# will probably fail, but let it just in case this is a
|
||||
# file the compiler recognizes even if we don't.
|
||||
input_opt = ""
|
||||
|
||||
output_opt = "-o" + obj
|
||||
|
||||
# Compiler command line syntax is: "bcc32 [options] file(s)".
|
||||
# Note that the source file names must appear at the end of
|
||||
# the command line.
|
||||
try:
|
||||
self.spawn([self.cc] + compile_opts + pp_opts +
|
||||
[input_opt, output_opt] +
|
||||
extra_postargs + [src])
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
def create_static_lib(self, objects, output_libname, output_dir=None,
|
||||
debug=False, target_lang=None):
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
output_filename = \
|
||||
self.library_filename(output_libname, output_dir=output_dir)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
lib_args = [output_filename, '/u'] + objects
|
||||
if debug:
|
||||
pass # XXX what goes here?
|
||||
try:
|
||||
self.spawn([self.lib] + lib_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LibError(msg)
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
|
||||
def link(self, target_desc, objects, output_filename, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None):
|
||||
|
||||
# XXX this ignores 'build_temp'! should follow the lead of
|
||||
# msvccompiler.py
|
||||
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
libraries, library_dirs, runtime_library_dirs = \
|
||||
self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
|
||||
|
||||
if runtime_library_dirs:
|
||||
logger.warning("don't know what to do with "
|
||||
"'runtime_library_dirs': %r", runtime_library_dirs)
|
||||
|
||||
if output_dir is not None:
|
||||
output_filename = os.path.join(output_dir, output_filename)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
|
||||
# Figure out linker args based on type of target.
|
||||
if target_desc == CCompiler.EXECUTABLE:
|
||||
startup_obj = 'c0w32'
|
||||
if debug:
|
||||
ld_args = self.ldflags_exe_debug[:]
|
||||
else:
|
||||
ld_args = self.ldflags_exe[:]
|
||||
else:
|
||||
startup_obj = 'c0d32'
|
||||
if debug:
|
||||
ld_args = self.ldflags_shared_debug[:]
|
||||
else:
|
||||
ld_args = self.ldflags_shared[:]
|
||||
|
||||
|
||||
# Create a temporary exports file for use by the linker
|
||||
if export_symbols is None:
|
||||
def_file = ''
|
||||
else:
|
||||
head, tail = os.path.split(output_filename)
|
||||
modname, ext = os.path.splitext(tail)
|
||||
temp_dir = os.path.dirname(objects[0]) # preserve tree structure
|
||||
def_file = os.path.join(temp_dir, '%s.def' % modname)
|
||||
contents = ['EXPORTS']
|
||||
for sym in (export_symbols or []):
|
||||
contents.append(' %s=_%s' % (sym, sym))
|
||||
self.execute(write_file, (def_file, contents),
|
||||
"writing %s" % def_file)
|
||||
|
||||
# Borland C++ has problems with '/' in paths
|
||||
objects2 = [os.path.normpath(o) for o in objects]
|
||||
# split objects in .obj and .res files
|
||||
# Borland C++ needs them at different positions in the command line
|
||||
objects = [startup_obj]
|
||||
resources = []
|
||||
for file in objects2:
|
||||
base, ext = os.path.splitext(os.path.normcase(file))
|
||||
if ext == '.res':
|
||||
resources.append(file)
|
||||
else:
|
||||
objects.append(file)
|
||||
|
||||
|
||||
for l in library_dirs:
|
||||
ld_args.append("/L%s" % os.path.normpath(l))
|
||||
ld_args.append("/L.") # we sometimes use relative paths
|
||||
|
||||
# list of object files
|
||||
ld_args.extend(objects)
|
||||
|
||||
# XXX the command line syntax for Borland C++ is a bit wonky;
|
||||
# certain filenames are jammed together in one big string, but
|
||||
# comma-delimited. This doesn't mesh too well with the
|
||||
# Unix-centric attitude (with a DOS/Windows quoting hack) of
|
||||
# 'spawn()', so constructing the argument list is a bit
|
||||
# awkward. Note that doing the obvious thing and jamming all
|
||||
# the filenames and commas into one argument would be wrong,
|
||||
# because 'spawn()' would quote any filenames with spaces in
|
||||
# them. Arghghh!. Apparently it works fine as coded...
|
||||
|
||||
# name of dll/exe file
|
||||
ld_args.extend((',',output_filename))
|
||||
# no map file and start libraries
|
||||
ld_args.append(',,')
|
||||
|
||||
for lib in libraries:
|
||||
# see if we find it and if there is a bcpp specific lib
|
||||
# (xxx_bcpp.lib)
|
||||
libfile = self.find_library_file(library_dirs, lib, debug)
|
||||
if libfile is None:
|
||||
ld_args.append(lib)
|
||||
# probably a BCPP internal library -- don't warn
|
||||
else:
|
||||
# full name which prefers bcpp_xxx.lib over xxx.lib
|
||||
ld_args.append(libfile)
|
||||
|
||||
# some default libraries
|
||||
ld_args.append('import32')
|
||||
ld_args.append('cw32mt')
|
||||
|
||||
# def file for export symbols
|
||||
ld_args.extend((',',def_file))
|
||||
# add resource files
|
||||
ld_args.append(',')
|
||||
ld_args.extend(resources)
|
||||
|
||||
|
||||
if extra_preargs:
|
||||
ld_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
ld_args.extend(extra_postargs)
|
||||
|
||||
self.mkpath(os.path.dirname(output_filename))
|
||||
try:
|
||||
self.spawn([self.linker] + ld_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LinkError(msg)
|
||||
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
|
||||
|
||||
def find_library_file(self, dirs, lib, debug=False):
|
||||
# List of effective library names to try, in order of preference:
|
||||
# xxx_bcpp.lib is better than xxx.lib
|
||||
# and xxx_d.lib is better than xxx.lib if debug is set
|
||||
#
|
||||
# The "_bcpp" suffix is to handle a Python installation for people
|
||||
# with multiple compilers (primarily Packaging hackers, I suspect
|
||||
# ;-). The idea is they'd have one static library for each
|
||||
# compiler they care about, since (almost?) every Windows compiler
|
||||
# seems to have a different format for static libraries.
|
||||
if debug:
|
||||
dlib = (lib + "_d")
|
||||
try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
|
||||
else:
|
||||
try_names = (lib + "_bcpp", lib)
|
||||
|
||||
for dir in dirs:
|
||||
for name in try_names:
|
||||
libfile = os.path.join(dir, self.library_filename(name))
|
||||
if os.path.exists(libfile):
|
||||
return libfile
|
||||
else:
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
||||
|
||||
# overwrite the one from CCompiler to support rc and res-files
|
||||
def object_filenames(self, source_filenames, strip_dir=False,
|
||||
output_dir=''):
|
||||
if output_dir is None:
|
||||
output_dir = ''
|
||||
obj_names = []
|
||||
for src_name in source_filenames:
|
||||
# use normcase to make sure '.rc' is really '.rc' and not '.RC'
|
||||
base, ext = os.path.splitext(os.path.normcase(src_name))
|
||||
if ext not in (self.src_extensions + ['.rc','.res']):
|
||||
raise UnknownFileError("unknown file type '%s' (from '%s')" % \
|
||||
(ext, src_name))
|
||||
if strip_dir:
|
||||
base = os.path.basename(base)
|
||||
if ext == '.res':
|
||||
# these can go unchanged
|
||||
obj_names.append(os.path.join(output_dir, base + ext))
|
||||
elif ext == '.rc':
|
||||
# these need to be compiled to .res-files
|
||||
obj_names.append(os.path.join(output_dir, base + '.res'))
|
||||
else:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.obj_extension))
|
||||
return obj_names
|
||||
|
||||
|
||||
def preprocess(self, source, output_file=None, macros=None,
|
||||
include_dirs=None, extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
_, macros, include_dirs = \
|
||||
self._fix_compile_args(None, macros, include_dirs)
|
||||
pp_opts = gen_preprocess_options(macros, include_dirs)
|
||||
pp_args = ['cpp32.exe'] + pp_opts
|
||||
if output_file is not None:
|
||||
pp_args.append('-o' + output_file)
|
||||
if extra_preargs:
|
||||
pp_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
pp_args.extend(extra_postargs)
|
||||
pp_args.append(source)
|
||||
|
||||
# We need to preprocess: either we're being forced to, or the
|
||||
# source file is newer than the target (or the target doesn't
|
||||
# exist).
|
||||
if self.force or output_file is None or newer(source, output_file):
|
||||
if output_file:
|
||||
self.mkpath(os.path.dirname(output_file))
|
||||
try:
|
||||
self.spawn(pp_args)
|
||||
except PackagingExecError as msg:
|
||||
print(msg)
|
||||
raise CompileError(msg)
|
868
Lib/packaging/compiler/ccompiler.py
Normal file
868
Lib/packaging/compiler/ccompiler.py
Normal file
|
@ -0,0 +1,868 @@
|
|||
"""Abstract base class for compilers.
|
||||
|
||||
This modules contains CCompiler, an abstract base class that defines the
|
||||
interface for the compiler abstraction model used by packaging.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from shutil import move
|
||||
from packaging import logger
|
||||
from packaging.util import split_quoted, execute, newer_group, spawn
|
||||
from packaging.errors import (CompileError, LinkError, UnknownFileError)
|
||||
from packaging.compiler import gen_preprocess_options
|
||||
|
||||
|
||||
class CCompiler:
|
||||
"""Abstract base class to define the interface that must be implemented
|
||||
by real compiler classes. Also has some utility methods used by
|
||||
several compiler classes.
|
||||
|
||||
The basic idea behind a compiler abstraction class is that each
|
||||
instance can be used for all the compile/link steps in building a
|
||||
single project. Thus, attributes common to all of those compile and
|
||||
link steps -- include directories, macros to define, libraries to link
|
||||
against, etc. -- are attributes of the compiler instance. To allow for
|
||||
variability in how individual files are treated, most of those
|
||||
attributes may be varied on a per-compilation or per-link basis.
|
||||
"""
|
||||
|
||||
# 'name' is a class attribute that identifies this class. It
|
||||
# keeps code that wants to know what kind of compiler it's dealing with
|
||||
# from having to import all possible compiler classes just to do an
|
||||
# 'isinstance'.
|
||||
name = None
|
||||
description = None
|
||||
|
||||
# XXX things not handled by this compiler abstraction model:
|
||||
# * client can't provide additional options for a compiler,
|
||||
# e.g. warning, optimization, debugging flags. Perhaps this
|
||||
# should be the domain of concrete compiler abstraction classes
|
||||
# (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
|
||||
# class should have methods for the common ones.
|
||||
# * can't completely override the include or library searchg
|
||||
# path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
|
||||
# I'm not sure how widely supported this is even by Unix
|
||||
# compilers, much less on other platforms. And I'm even less
|
||||
# sure how useful it is; maybe for cross-compiling, but
|
||||
# support for that is a ways off. (And anyways, cross
|
||||
# compilers probably have a dedicated binary with the
|
||||
# right paths compiled in. I hope.)
|
||||
# * can't do really freaky things with the library list/library
|
||||
# dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
|
||||
# different versions of libfoo.a in different locations. I
|
||||
# think this is useless without the ability to null out the
|
||||
# library search path anyways.
|
||||
|
||||
|
||||
# Subclasses that rely on the standard filename generation methods
|
||||
# implemented below should override these; see the comment near
|
||||
# those methods ('object_filenames()' et. al.) for details:
|
||||
src_extensions = None # list of strings
|
||||
obj_extension = None # string
|
||||
static_lib_extension = None
|
||||
shared_lib_extension = None # string
|
||||
static_lib_format = None # format string
|
||||
shared_lib_format = None # prob. same as static_lib_format
|
||||
exe_extension = None # string
|
||||
|
||||
# Default language settings. language_map is used to detect a source
|
||||
# file or Extension target language, checking source filenames.
|
||||
# language_order is used to detect the language precedence, when deciding
|
||||
# what language to use when mixing source types. For example, if some
|
||||
# extension has two files with ".c" extension, and one with ".cpp", it
|
||||
# is still linked as c++.
|
||||
language_map = {".c": "c",
|
||||
".cc": "c++",
|
||||
".cpp": "c++",
|
||||
".cxx": "c++",
|
||||
".m": "objc",
|
||||
}
|
||||
language_order = ["c++", "objc", "c"]
|
||||
|
||||
def __init__(self, verbose=0, dry_run=False, force=False):
|
||||
self.dry_run = dry_run
|
||||
self.force = force
|
||||
self.verbose = verbose
|
||||
|
||||
# 'output_dir': a common output directory for object, library,
|
||||
# shared object, and shared library files
|
||||
self.output_dir = None
|
||||
|
||||
# 'macros': a list of macro definitions (or undefinitions). A
|
||||
# macro definition is a 2-tuple (name, value), where the value is
|
||||
# either a string or None (no explicit value). A macro
|
||||
# undefinition is a 1-tuple (name,).
|
||||
self.macros = []
|
||||
|
||||
# 'include_dirs': a list of directories to search for include files
|
||||
self.include_dirs = []
|
||||
|
||||
# 'libraries': a list of libraries to include in any link
|
||||
# (library names, not filenames: eg. "foo" not "libfoo.a")
|
||||
self.libraries = []
|
||||
|
||||
# 'library_dirs': a list of directories to search for libraries
|
||||
self.library_dirs = []
|
||||
|
||||
# 'runtime_library_dirs': a list of directories to search for
|
||||
# shared libraries/objects at runtime
|
||||
self.runtime_library_dirs = []
|
||||
|
||||
# 'objects': a list of object files (or similar, such as explicitly
|
||||
# named library files) to include on any link
|
||||
self.objects = []
|
||||
|
||||
for key, value in self.executables.items():
|
||||
self.set_executable(key, value)
|
||||
|
||||
def set_executables(self, **args):
|
||||
"""Define the executables (and options for them) that will be run
|
||||
to perform the various stages of compilation. The exact set of
|
||||
executables that may be specified here depends on the compiler
|
||||
class (via the 'executables' class attribute), but most will have:
|
||||
compiler the C/C++ compiler
|
||||
linker_so linker used to create shared objects and libraries
|
||||
linker_exe linker used to create binary executables
|
||||
archiver static library creator
|
||||
|
||||
On platforms with a command line (Unix, DOS/Windows), each of these
|
||||
is a string that will be split into executable name and (optional)
|
||||
list of arguments. (Splitting the string is done similarly to how
|
||||
Unix shells operate: words are delimited by spaces, but quotes and
|
||||
backslashes can override this. See
|
||||
'distutils.util.split_quoted()'.)
|
||||
"""
|
||||
|
||||
# Note that some CCompiler implementation classes will define class
|
||||
# attributes 'cpp', 'cc', etc. with hard-coded executable names;
|
||||
# this is appropriate when a compiler class is for exactly one
|
||||
# compiler/OS combination (eg. MSVCCompiler). Other compiler
|
||||
# classes (UnixCCompiler, in particular) are driven by information
|
||||
# discovered at run-time, since there are many different ways to do
|
||||
# basically the same things with Unix C compilers.
|
||||
|
||||
for key, value in args.items():
|
||||
if key not in self.executables:
|
||||
raise ValueError("unknown executable '%s' for class %s" % \
|
||||
(key, self.__class__.__name__))
|
||||
self.set_executable(key, value)
|
||||
|
||||
def set_executable(self, key, value):
|
||||
if isinstance(value, str):
|
||||
setattr(self, key, split_quoted(value))
|
||||
else:
|
||||
setattr(self, key, value)
|
||||
|
||||
def _find_macro(self, name):
|
||||
i = 0
|
||||
for defn in self.macros:
|
||||
if defn[0] == name:
|
||||
return i
|
||||
i = i + 1
|
||||
return None
|
||||
|
||||
def _check_macro_definitions(self, definitions):
|
||||
"""Ensures that every element of 'definitions' is a valid macro
|
||||
definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do
|
||||
nothing if all definitions are OK, raise TypeError otherwise.
|
||||
"""
|
||||
for defn in definitions:
|
||||
if not (isinstance(defn, tuple) and
|
||||
(len(defn) == 1 or
|
||||
(len(defn) == 2 and
|
||||
(isinstance(defn[1], str) or defn[1] is None))) and
|
||||
isinstance(defn[0], str)):
|
||||
raise TypeError(("invalid macro definition '%s': " % defn) + \
|
||||
"must be tuple (string,), (string, string), or " + \
|
||||
"(string, None)")
|
||||
|
||||
|
||||
# -- Bookkeeping methods -------------------------------------------
|
||||
|
||||
def define_macro(self, name, value=None):
|
||||
"""Define a preprocessor macro for all compilations driven by this
|
||||
compiler object. The optional parameter 'value' should be a
|
||||
string; if it is not supplied, then the macro will be defined
|
||||
without an explicit value and the exact outcome depends on the
|
||||
compiler used (XXX true? does ANSI say anything about this?)
|
||||
"""
|
||||
# Delete from the list of macro definitions/undefinitions if
|
||||
# already there (so that this one will take precedence).
|
||||
i = self._find_macro(name)
|
||||
if i is not None:
|
||||
del self.macros[i]
|
||||
|
||||
defn = (name, value)
|
||||
self.macros.append(defn)
|
||||
|
||||
def undefine_macro(self, name):
|
||||
"""Undefine a preprocessor macro for all compilations driven by
|
||||
this compiler object. If the same macro is defined by
|
||||
'define_macro()' and undefined by 'undefine_macro()' the last call
|
||||
takes precedence (including multiple redefinitions or
|
||||
undefinitions). If the macro is redefined/undefined on a
|
||||
per-compilation basis (ie. in the call to 'compile()'), then that
|
||||
takes precedence.
|
||||
"""
|
||||
# Delete from the list of macro definitions/undefinitions if
|
||||
# already there (so that this one will take precedence).
|
||||
i = self._find_macro(name)
|
||||
if i is not None:
|
||||
del self.macros[i]
|
||||
|
||||
undefn = (name,)
|
||||
self.macros.append(undefn)
|
||||
|
||||
def add_include_dir(self, dir):
|
||||
"""Add 'dir' to the list of directories that will be searched for
|
||||
header files. The compiler is instructed to search directories in
|
||||
the order in which they are supplied by successive calls to
|
||||
'add_include_dir()'.
|
||||
"""
|
||||
self.include_dirs.append(dir)
|
||||
|
||||
def set_include_dirs(self, dirs):
|
||||
"""Set the list of directories that will be searched to 'dirs' (a
|
||||
list of strings). Overrides any preceding calls to
|
||||
'add_include_dir()'; subsequence calls to 'add_include_dir()' add
|
||||
to the list passed to 'set_include_dirs()'. This does not affect
|
||||
any list of standard include directories that the compiler may
|
||||
search by default.
|
||||
"""
|
||||
self.include_dirs = dirs[:]
|
||||
|
||||
def add_library(self, libname):
|
||||
"""Add 'libname' to the list of libraries that will be included in
|
||||
all links driven by this compiler object. Note that 'libname'
|
||||
should *not* be the name of a file containing a library, but the
|
||||
name of the library itself: the actual filename will be inferred by
|
||||
the linker, the compiler, or the compiler class (depending on the
|
||||
platform).
|
||||
|
||||
The linker will be instructed to link against libraries in the
|
||||
order they were supplied to 'add_library()' and/or
|
||||
'set_libraries()'. It is perfectly valid to duplicate library
|
||||
names; the linker will be instructed to link against libraries as
|
||||
many times as they are mentioned.
|
||||
"""
|
||||
self.libraries.append(libname)
|
||||
|
||||
def set_libraries(self, libnames):
|
||||
"""Set the list of libraries to be included in all links driven by
|
||||
this compiler object to 'libnames' (a list of strings). This does
|
||||
not affect any standard system libraries that the linker may
|
||||
include by default.
|
||||
"""
|
||||
self.libraries = libnames[:]
|
||||
|
||||
|
||||
def add_library_dir(self, dir):
|
||||
"""Add 'dir' to the list of directories that will be searched for
|
||||
libraries specified to 'add_library()' and 'set_libraries()'. The
|
||||
linker will be instructed to search for libraries in the order they
|
||||
are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
|
||||
"""
|
||||
self.library_dirs.append(dir)
|
||||
|
||||
def set_library_dirs(self, dirs):
|
||||
"""Set the list of library search directories to 'dirs' (a list of
|
||||
strings). This does not affect any standard library search path
|
||||
that the linker may search by default.
|
||||
"""
|
||||
self.library_dirs = dirs[:]
|
||||
|
||||
def add_runtime_library_dir(self, dir):
|
||||
"""Add 'dir' to the list of directories that will be searched for
|
||||
shared libraries at runtime.
|
||||
"""
|
||||
self.runtime_library_dirs.append(dir)
|
||||
|
||||
def set_runtime_library_dirs(self, dirs):
|
||||
"""Set the list of directories to search for shared libraries at
|
||||
runtime to 'dirs' (a list of strings). This does not affect any
|
||||
standard search path that the runtime linker may search by
|
||||
default.
|
||||
"""
|
||||
self.runtime_library_dirs = dirs[:]
|
||||
|
||||
def add_link_object(self, object):
|
||||
"""Add 'object' to the list of object files (or analogues, such as
|
||||
explicitly named library files or the output of "resource
|
||||
compilers") to be included in every link driven by this compiler
|
||||
object.
|
||||
"""
|
||||
self.objects.append(object)
|
||||
|
||||
def set_link_objects(self, objects):
|
||||
"""Set the list of object files (or analogues) to be included in
|
||||
every link to 'objects'. This does not affect any standard object
|
||||
files that the linker may include by default (such as system
|
||||
libraries).
|
||||
"""
|
||||
self.objects = objects[:]
|
||||
|
||||
|
||||
# -- Private utility methods --------------------------------------
|
||||
# (here for the convenience of subclasses)
|
||||
|
||||
# Helper method to prep compiler in subclass compile() methods
|
||||
def _setup_compile(self, outdir, macros, incdirs, sources, depends,
|
||||
extra):
|
||||
"""Process arguments and decide which source files to compile."""
|
||||
if outdir is None:
|
||||
outdir = self.output_dir
|
||||
elif not isinstance(outdir, str):
|
||||
raise TypeError("'output_dir' must be a string or None")
|
||||
|
||||
if macros is None:
|
||||
macros = self.macros
|
||||
elif isinstance(macros, list):
|
||||
macros = macros + (self.macros or [])
|
||||
else:
|
||||
raise TypeError("'macros' (if supplied) must be a list of tuples")
|
||||
|
||||
if incdirs is None:
|
||||
incdirs = self.include_dirs
|
||||
elif isinstance(incdirs, (list, tuple)):
|
||||
incdirs = list(incdirs) + (self.include_dirs or [])
|
||||
else:
|
||||
raise TypeError(
|
||||
"'include_dirs' (if supplied) must be a list of strings")
|
||||
|
||||
if extra is None:
|
||||
extra = []
|
||||
|
||||
# Get the list of expected output (object) files
|
||||
objects = self.object_filenames(sources,
|
||||
strip_dir=False,
|
||||
output_dir=outdir)
|
||||
assert len(objects) == len(sources)
|
||||
|
||||
pp_opts = gen_preprocess_options(macros, incdirs)
|
||||
|
||||
build = {}
|
||||
for i in range(len(sources)):
|
||||
src = sources[i]
|
||||
obj = objects[i]
|
||||
ext = os.path.splitext(src)[1]
|
||||
self.mkpath(os.path.dirname(obj))
|
||||
build[obj] = (src, ext)
|
||||
|
||||
return macros, objects, extra, pp_opts, build
|
||||
|
||||
def _get_cc_args(self, pp_opts, debug, before):
|
||||
# works for unixccompiler, emxccompiler, cygwinccompiler
|
||||
cc_args = pp_opts + ['-c']
|
||||
if debug:
|
||||
cc_args[:0] = ['-g']
|
||||
if before:
|
||||
cc_args[:0] = before
|
||||
return cc_args
|
||||
|
||||
def _fix_compile_args(self, output_dir, macros, include_dirs):
|
||||
"""Typecheck and fix-up some of the arguments to the 'compile()'
|
||||
method, and return fixed-up values. Specifically: if 'output_dir'
|
||||
is None, replaces it with 'self.output_dir'; ensures that 'macros'
|
||||
is a list, and augments it with 'self.macros'; ensures that
|
||||
'include_dirs' is a list, and augments it with 'self.include_dirs'.
|
||||
Guarantees that the returned values are of the correct type,
|
||||
i.e. for 'output_dir' either string or None, and for 'macros' and
|
||||
'include_dirs' either list or None.
|
||||
"""
|
||||
if output_dir is None:
|
||||
output_dir = self.output_dir
|
||||
elif not isinstance(output_dir, str):
|
||||
raise TypeError("'output_dir' must be a string or None")
|
||||
|
||||
if macros is None:
|
||||
macros = self.macros
|
||||
elif isinstance(macros, list):
|
||||
macros = macros + (self.macros or [])
|
||||
else:
|
||||
raise TypeError("'macros' (if supplied) must be a list of tuples")
|
||||
|
||||
if include_dirs is None:
|
||||
include_dirs = self.include_dirs
|
||||
elif isinstance(include_dirs, (list, tuple)):
|
||||
include_dirs = list(include_dirs) + (self.include_dirs or [])
|
||||
else:
|
||||
raise TypeError(
|
||||
"'include_dirs' (if supplied) must be a list of strings")
|
||||
|
||||
return output_dir, macros, include_dirs
|
||||
|
||||
def _fix_object_args(self, objects, output_dir):
|
||||
"""Typecheck and fix up some arguments supplied to various methods.
|
||||
Specifically: ensure that 'objects' is a list; if output_dir is
|
||||
None, replace with self.output_dir. Return fixed versions of
|
||||
'objects' and 'output_dir'.
|
||||
"""
|
||||
if not isinstance(objects, (list, tuple)):
|
||||
raise TypeError("'objects' must be a list or tuple of strings")
|
||||
objects = list(objects)
|
||||
|
||||
if output_dir is None:
|
||||
output_dir = self.output_dir
|
||||
elif not isinstance(output_dir, str):
|
||||
raise TypeError("'output_dir' must be a string or None")
|
||||
|
||||
return objects, output_dir
|
||||
|
||||
def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
|
||||
"""Typecheck and fix up some of the arguments supplied to the
|
||||
'link_*' methods. Specifically: ensure that all arguments are
|
||||
lists, and augment them with their permanent versions
|
||||
(eg. 'self.libraries' augments 'libraries'). Return a tuple with
|
||||
fixed versions of all arguments.
|
||||
"""
|
||||
if libraries is None:
|
||||
libraries = self.libraries
|
||||
elif isinstance(libraries, (list, tuple)):
|
||||
libraries = list(libraries) + (self.libraries or [])
|
||||
else:
|
||||
raise TypeError(
|
||||
"'libraries' (if supplied) must be a list of strings")
|
||||
|
||||
if library_dirs is None:
|
||||
library_dirs = self.library_dirs
|
||||
elif isinstance(library_dirs, (list, tuple)):
|
||||
library_dirs = list(library_dirs) + (self.library_dirs or [])
|
||||
else:
|
||||
raise TypeError(
|
||||
"'library_dirs' (if supplied) must be a list of strings")
|
||||
|
||||
if runtime_library_dirs is None:
|
||||
runtime_library_dirs = self.runtime_library_dirs
|
||||
elif isinstance(runtime_library_dirs, (list, tuple)):
|
||||
runtime_library_dirs = (list(runtime_library_dirs) +
|
||||
(self.runtime_library_dirs or []))
|
||||
else:
|
||||
raise TypeError("'runtime_library_dirs' (if supplied) "
|
||||
"must be a list of strings")
|
||||
|
||||
return libraries, library_dirs, runtime_library_dirs
|
||||
|
||||
def _need_link(self, objects, output_file):
|
||||
"""Return true if we need to relink the files listed in 'objects'
|
||||
to recreate 'output_file'.
|
||||
"""
|
||||
if self.force:
|
||||
return True
|
||||
else:
|
||||
if self.dry_run:
|
||||
newer = newer_group(objects, output_file, missing='newer')
|
||||
else:
|
||||
newer = newer_group(objects, output_file)
|
||||
return newer
|
||||
|
||||
def detect_language(self, sources):
|
||||
"""Detect the language of a given file, or list of files. Uses
|
||||
language_map, and language_order to do the job.
|
||||
"""
|
||||
if not isinstance(sources, list):
|
||||
sources = [sources]
|
||||
lang = None
|
||||
index = len(self.language_order)
|
||||
for source in sources:
|
||||
base, ext = os.path.splitext(source)
|
||||
extlang = self.language_map.get(ext)
|
||||
try:
|
||||
extindex = self.language_order.index(extlang)
|
||||
if extindex < index:
|
||||
lang = extlang
|
||||
index = extindex
|
||||
except ValueError:
|
||||
pass
|
||||
return lang
|
||||
|
||||
# -- Worker methods ------------------------------------------------
|
||||
# (must be implemented by subclasses)
|
||||
|
||||
def preprocess(self, source, output_file=None, macros=None,
|
||||
include_dirs=None, extra_preargs=None, extra_postargs=None):
|
||||
"""Preprocess a single C/C++ source file, named in 'source'.
|
||||
Output will be written to file named 'output_file', or stdout if
|
||||
'output_file' not supplied. 'macros' is a list of macro
|
||||
definitions as for 'compile()', which will augment the macros set
|
||||
with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a
|
||||
list of directory names that will be added to the default list.
|
||||
|
||||
Raises PreprocessError on failure.
|
||||
"""
|
||||
pass
|
||||
|
||||
def compile(self, sources, output_dir=None, macros=None,
|
||||
include_dirs=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, depends=None):
|
||||
"""Compile one or more source files.
|
||||
|
||||
'sources' must be a list of filenames, most likely C/C++
|
||||
files, but in reality anything that can be handled by a
|
||||
particular compiler and compiler class (eg. MSVCCompiler can
|
||||
handle resource files in 'sources'). Return a list of object
|
||||
filenames, one per source filename in 'sources'. Depending on
|
||||
the implementation, not all source files will necessarily be
|
||||
compiled, but all corresponding object filenames will be
|
||||
returned.
|
||||
|
||||
If 'output_dir' is given, object files will be put under it, while
|
||||
retaining their original path component. That is, "foo/bar.c"
|
||||
normally compiles to "foo/bar.o" (for a Unix implementation); if
|
||||
'output_dir' is "build", then it would compile to
|
||||
"build/foo/bar.o".
|
||||
|
||||
'macros', if given, must be a list of macro definitions. A macro
|
||||
definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
|
||||
The former defines a macro; if the value is None, the macro is
|
||||
defined without an explicit value. The 1-tuple case undefines a
|
||||
macro. Later definitions/redefinitions/ undefinitions take
|
||||
precedence.
|
||||
|
||||
'include_dirs', if given, must be a list of strings, the
|
||||
directories to add to the default include file search path for this
|
||||
compilation only.
|
||||
|
||||
'debug' is a boolean; if true, the compiler will be instructed to
|
||||
output debug symbols in (or alongside) the object file(s).
|
||||
|
||||
'extra_preargs' and 'extra_postargs' are implementation- dependent.
|
||||
On platforms that have the notion of a command line (e.g. Unix,
|
||||
DOS/Windows), they are most likely lists of strings: extra
|
||||
command-line arguments to prepand/append to the compiler command
|
||||
line. On other platforms, consult the implementation class
|
||||
documentation. In any event, they are intended as an escape hatch
|
||||
for those occasions when the abstract compiler framework doesn't
|
||||
cut the mustard.
|
||||
|
||||
'depends', if given, is a list of filenames that all targets
|
||||
depend on. If a source file is older than any file in
|
||||
depends, then the source file will be recompiled. This
|
||||
supports dependency tracking, but only at a coarse
|
||||
granularity.
|
||||
|
||||
Raises CompileError on failure.
|
||||
"""
|
||||
# A concrete compiler class can either override this method
|
||||
# entirely or implement _compile().
|
||||
|
||||
macros, objects, extra_postargs, pp_opts, build = \
|
||||
self._setup_compile(output_dir, macros, include_dirs, sources,
|
||||
depends, extra_postargs)
|
||||
cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
|
||||
|
||||
for obj in objects:
|
||||
try:
|
||||
src, ext = build[obj]
|
||||
except KeyError:
|
||||
continue
|
||||
self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
|
||||
|
||||
# Return *all* object filenames, not just the ones we just built.
|
||||
return objects
|
||||
|
||||
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
|
||||
"""Compile 'src' to product 'obj'."""
|
||||
|
||||
# A concrete compiler class that does not override compile()
|
||||
# should implement _compile().
|
||||
pass
|
||||
|
||||
def create_static_lib(self, objects, output_libname, output_dir=None,
|
||||
debug=False, target_lang=None):
|
||||
"""Link a bunch of stuff together to create a static library file.
|
||||
The "bunch of stuff" consists of the list of object files supplied
|
||||
as 'objects', the extra object files supplied to
|
||||
'add_link_object()' and/or 'set_link_objects()', the libraries
|
||||
supplied to 'add_library()' and/or 'set_libraries()', and the
|
||||
libraries supplied as 'libraries' (if any).
|
||||
|
||||
'output_libname' should be a library name, not a filename; the
|
||||
filename will be inferred from the library name. 'output_dir' is
|
||||
the directory where the library file will be put.
|
||||
|
||||
'debug' is a boolean; if true, debugging information will be
|
||||
included in the library (note that on most platforms, it is the
|
||||
compile step where this matters: the 'debug' flag is included here
|
||||
just for consistency).
|
||||
|
||||
'target_lang' is the target language for which the given objects
|
||||
are being compiled. This allows specific linkage time treatment of
|
||||
certain languages.
|
||||
|
||||
Raises LibError on failure.
|
||||
"""
|
||||
pass
|
||||
|
||||
# values for target_desc parameter in link()
|
||||
SHARED_OBJECT = "shared_object"
|
||||
SHARED_LIBRARY = "shared_library"
|
||||
EXECUTABLE = "executable"
|
||||
|
||||
def link(self, target_desc, objects, output_filename, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None):
|
||||
"""Link a bunch of stuff together to create an executable or
|
||||
shared library file.
|
||||
|
||||
The "bunch of stuff" consists of the list of object files supplied
|
||||
as 'objects'. 'output_filename' should be a filename. If
|
||||
'output_dir' is supplied, 'output_filename' is relative to it
|
||||
(i.e. 'output_filename' can provide directory components if
|
||||
needed).
|
||||
|
||||
'libraries' is a list of libraries to link against. These are
|
||||
library names, not filenames, since they're translated into
|
||||
filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
|
||||
on Unix and "foo.lib" on DOS/Windows). However, they can include a
|
||||
directory component, which means the linker will look in that
|
||||
specific directory rather than searching all the normal locations.
|
||||
|
||||
'library_dirs', if supplied, should be a list of directories to
|
||||
search for libraries that were specified as bare library names
|
||||
(ie. no directory component). These are on top of the system
|
||||
default and those supplied to 'add_library_dir()' and/or
|
||||
'set_library_dirs()'. 'runtime_library_dirs' is a list of
|
||||
directories that will be embedded into the shared library and used
|
||||
to search for other shared libraries that *it* depends on at
|
||||
run-time. (This may only be relevant on Unix.)
|
||||
|
||||
'export_symbols' is a list of symbols that the shared library will
|
||||
export. (This appears to be relevant only on Windows.)
|
||||
|
||||
'debug' is as for 'compile()' and 'create_static_lib()', with the
|
||||
slight distinction that it actually matters on most platforms (as
|
||||
opposed to 'create_static_lib()', which includes a 'debug' flag
|
||||
mostly for form's sake).
|
||||
|
||||
'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
|
||||
of course that they supply command-line arguments for the
|
||||
particular linker being used).
|
||||
|
||||
'target_lang' is the target language for which the given objects
|
||||
are being compiled. This allows specific linkage time treatment of
|
||||
certain languages.
|
||||
|
||||
Raises LinkError on failure.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
# Old 'link_*()' methods, rewritten to use the new 'link()' method.
|
||||
|
||||
def link_shared_lib(self, objects, output_libname, output_dir=None,
|
||||
libraries=None, library_dirs=None,
|
||||
runtime_library_dirs=None, export_symbols=None,
|
||||
debug=False, extra_preargs=None, extra_postargs=None,
|
||||
build_temp=None, target_lang=None):
|
||||
self.link(CCompiler.SHARED_LIBRARY, objects,
|
||||
self.library_filename(output_libname, lib_type='shared'),
|
||||
output_dir,
|
||||
libraries, library_dirs, runtime_library_dirs,
|
||||
export_symbols, debug,
|
||||
extra_preargs, extra_postargs, build_temp, target_lang)
|
||||
|
||||
def link_shared_object(self, objects, output_filename, output_dir=None,
|
||||
libraries=None, library_dirs=None,
|
||||
runtime_library_dirs=None, export_symbols=None,
|
||||
debug=False, extra_preargs=None, extra_postargs=None,
|
||||
build_temp=None, target_lang=None):
|
||||
self.link(CCompiler.SHARED_OBJECT, objects,
|
||||
output_filename, output_dir,
|
||||
libraries, library_dirs, runtime_library_dirs,
|
||||
export_symbols, debug,
|
||||
extra_preargs, extra_postargs, build_temp, target_lang)
|
||||
|
||||
def link_executable(self, objects, output_progname, output_dir=None,
|
||||
libraries=None, library_dirs=None,
|
||||
runtime_library_dirs=None, debug=False,
|
||||
extra_preargs=None, extra_postargs=None,
|
||||
target_lang=None):
|
||||
self.link(CCompiler.EXECUTABLE, objects,
|
||||
self.executable_filename(output_progname), output_dir,
|
||||
libraries, library_dirs, runtime_library_dirs, None,
|
||||
debug, extra_preargs, extra_postargs, None, target_lang)
|
||||
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
# These are all used by the 'gen_lib_options() function; there is
|
||||
# no appropriate default implementation so subclasses should
|
||||
# implement all of these.
|
||||
|
||||
def library_dir_option(self, dir):
|
||||
"""Return the compiler option to add 'dir' to the list of
|
||||
directories searched for libraries.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def runtime_library_dir_option(self, dir):
|
||||
"""Return the compiler option to add 'dir' to the list of
|
||||
directories searched for runtime libraries.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def library_option(self, lib):
|
||||
"""Return the compiler option to add 'dir' to the list of libraries
|
||||
linked into the shared library or executable.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def has_function(self, funcname, includes=None, include_dirs=None,
|
||||
libraries=None, library_dirs=None):
|
||||
"""Return a boolean indicating whether funcname is supported on
|
||||
the current platform. The optional arguments can be used to
|
||||
augment the compilation environment.
|
||||
"""
|
||||
|
||||
# this can't be included at module scope because it tries to
|
||||
# import math which might not be available at that point - maybe
|
||||
# the necessary logic should just be inlined?
|
||||
import tempfile
|
||||
if includes is None:
|
||||
includes = []
|
||||
if include_dirs is None:
|
||||
include_dirs = []
|
||||
if libraries is None:
|
||||
libraries = []
|
||||
if library_dirs is None:
|
||||
library_dirs = []
|
||||
fd, fname = tempfile.mkstemp(".c", funcname, text=True)
|
||||
f = os.fdopen(fd, "w")
|
||||
try:
|
||||
for incl in includes:
|
||||
f.write("""#include "%s"\n""" % incl)
|
||||
f.write("""\
|
||||
main (int argc, char **argv) {
|
||||
%s();
|
||||
}
|
||||
""" % funcname)
|
||||
finally:
|
||||
f.close()
|
||||
try:
|
||||
objects = self.compile([fname], include_dirs=include_dirs)
|
||||
except CompileError:
|
||||
return False
|
||||
|
||||
try:
|
||||
self.link_executable(objects, "a.out",
|
||||
libraries=libraries,
|
||||
library_dirs=library_dirs)
|
||||
except (LinkError, TypeError):
|
||||
return False
|
||||
return True
|
||||
|
||||
def find_library_file(self, dirs, lib, debug=False):
|
||||
"""Search the specified list of directories for a static or shared
|
||||
library file 'lib' and return the full path to that file. If
|
||||
'debug' is true, look for a debugging version (if that makes sense on
|
||||
the current platform). Return None if 'lib' wasn't found in any of
|
||||
the specified directories.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
# -- Filename generation methods -----------------------------------
|
||||
|
||||
# The default implementation of the filename generating methods are
|
||||
# prejudiced towards the Unix/DOS/Windows view of the world:
|
||||
# * object files are named by replacing the source file extension
|
||||
# (eg. .c/.cpp -> .o/.obj)
|
||||
# * library files (shared or static) are named by plugging the
|
||||
# library name and extension into a format string, eg.
|
||||
# "lib%s.%s" % (lib_name, ".a") for Unix static libraries
|
||||
# * executables are named by appending an extension (possibly
|
||||
# empty) to the program name: eg. progname + ".exe" for
|
||||
# Windows
|
||||
#
|
||||
# To reduce redundant code, these methods expect to find
|
||||
# several attributes in the current object (presumably defined
|
||||
# as class attributes):
|
||||
# * src_extensions -
|
||||
# list of C/C++ source file extensions, eg. ['.c', '.cpp']
|
||||
# * obj_extension -
|
||||
# object file extension, eg. '.o' or '.obj'
|
||||
# * static_lib_extension -
|
||||
# extension for static library files, eg. '.a' or '.lib'
|
||||
# * shared_lib_extension -
|
||||
# extension for shared library/object files, eg. '.so', '.dll'
|
||||
# * static_lib_format -
|
||||
# format string for generating static library filenames,
|
||||
# eg. 'lib%s.%s' or '%s.%s'
|
||||
# * shared_lib_format
|
||||
# format string for generating shared library filenames
|
||||
# (probably same as static_lib_format, since the extension
|
||||
# is one of the intended parameters to the format string)
|
||||
# * exe_extension -
|
||||
# extension for executable files, eg. '' or '.exe'
|
||||
|
||||
def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
|
||||
if output_dir is None:
|
||||
output_dir = ''
|
||||
obj_names = []
|
||||
for src_name in source_filenames:
|
||||
base, ext = os.path.splitext(src_name)
|
||||
base = os.path.splitdrive(base)[1] # Chop off the drive
|
||||
base = base[os.path.isabs(base):] # If abs, chop off leading /
|
||||
if ext not in self.src_extensions:
|
||||
raise UnknownFileError("unknown file type '%s' (from '%s')" %
|
||||
(ext, src_name))
|
||||
if strip_dir:
|
||||
base = os.path.basename(base)
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.obj_extension))
|
||||
return obj_names
|
||||
|
||||
def shared_object_filename(self, basename, strip_dir=False, output_dir=''):
|
||||
assert output_dir is not None
|
||||
if strip_dir:
|
||||
basename = os.path.basename(basename)
|
||||
return os.path.join(output_dir, basename + self.shared_lib_extension)
|
||||
|
||||
def executable_filename(self, basename, strip_dir=False, output_dir=''):
|
||||
assert output_dir is not None
|
||||
if strip_dir:
|
||||
basename = os.path.basename(basename)
|
||||
return os.path.join(output_dir, basename + (self.exe_extension or ''))
|
||||
|
||||
def library_filename(self, libname, lib_type='static', # or 'shared'
|
||||
strip_dir=False, output_dir=''):
|
||||
assert output_dir is not None
|
||||
if lib_type not in ("static", "shared", "dylib"):
|
||||
raise ValueError(
|
||||
"'lib_type' must be 'static', 'shared' or 'dylib'")
|
||||
fmt = getattr(self, lib_type + "_lib_format")
|
||||
ext = getattr(self, lib_type + "_lib_extension")
|
||||
|
||||
dir, base = os.path.split(libname)
|
||||
filename = fmt % (base, ext)
|
||||
if strip_dir:
|
||||
dir = ''
|
||||
|
||||
return os.path.join(output_dir, dir, filename)
|
||||
|
||||
|
||||
# -- Utility methods -----------------------------------------------
|
||||
|
||||
def execute(self, func, args, msg=None, level=1):
|
||||
execute(func, args, msg, self.dry_run)
|
||||
|
||||
def spawn(self, cmd):
|
||||
spawn(cmd, dry_run=self.dry_run)
|
||||
|
||||
def move_file(self, src, dst):
|
||||
logger.info("moving %r to %r", src, dst)
|
||||
if self.dry_run:
|
||||
return
|
||||
return move(src, dst)
|
||||
|
||||
def mkpath(self, name, mode=0o777):
|
||||
name = os.path.normpath(name)
|
||||
if os.path.isdir(name) or name == '':
|
||||
return
|
||||
if self.dry_run:
|
||||
head = ''
|
||||
for part in name.split(os.sep):
|
||||
logger.info("created directory %s%s", head, part)
|
||||
head += part + os.sep
|
||||
return
|
||||
os.makedirs(name, mode)
|
355
Lib/packaging/compiler/cygwinccompiler.py
Normal file
355
Lib/packaging/compiler/cygwinccompiler.py
Normal file
|
@ -0,0 +1,355 @@
|
|||
"""CCompiler implementations for Cygwin and mingw32 versions of GCC.
|
||||
|
||||
This module contains the CygwinCCompiler class, a subclass of
|
||||
UnixCCompiler that handles the Cygwin port of the GNU C compiler to
|
||||
Windows, and the Mingw32CCompiler class which handles the mingw32 port
|
||||
of GCC (same as cygwin in no-cygwin mode).
|
||||
"""
|
||||
|
||||
# problems:
|
||||
#
|
||||
# * if you use a msvc compiled python version (1.5.2)
|
||||
# 1. you have to insert a __GNUC__ section in its config.h
|
||||
# 2. you have to generate a import library for its dll
|
||||
# - create a def-file for python??.dll
|
||||
# - create a import library using
|
||||
# dlltool --dllname python15.dll --def python15.def \
|
||||
# --output-lib libpython15.a
|
||||
#
|
||||
# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
|
||||
#
|
||||
# * We put export_symbols in a def-file, and don't use
|
||||
# --export-all-symbols because it doesn't worked reliable in some
|
||||
# tested configurations. And because other windows compilers also
|
||||
# need their symbols specified this no serious problem.
|
||||
#
|
||||
# tested configurations:
|
||||
#
|
||||
# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works
|
||||
# (after patching python's config.h and for C++ some other include files)
|
||||
# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
|
||||
# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works
|
||||
# (ld doesn't support -shared, so we use dllwrap)
|
||||
# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now
|
||||
# - its dllwrap doesn't work, there is a bug in binutils 2.10.90
|
||||
# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html
|
||||
# - using gcc -mdll instead dllwrap doesn't work without -static because
|
||||
# it tries to link against dlls instead their import libraries. (If
|
||||
# it finds the dll first.)
|
||||
# By specifying -static we force ld to link against the import libraries,
|
||||
# this is windows standard and there are normally not the necessary symbols
|
||||
# in the dlls.
|
||||
# *** only the version of June 2000 shows these problems
|
||||
# * cygwin gcc 3.2/ld 2.13.90 works
|
||||
# (ld supports -shared)
|
||||
# * mingw gcc 3.2/ld 2.13 works
|
||||
# (ld supports -shared)
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import copy
|
||||
|
||||
from packaging import logger
|
||||
from packaging.compiler.unixccompiler import UnixCCompiler
|
||||
from packaging.util import write_file
|
||||
from packaging.errors import PackagingExecError, CompileError, UnknownFileError
|
||||
from packaging.util import get_compiler_versions
|
||||
import sysconfig
|
||||
|
||||
|
||||
def get_msvcr():
|
||||
"""Include the appropriate MSVC runtime library if Python was built
|
||||
with MSVC 7.0 or later.
|
||||
"""
|
||||
msc_pos = sys.version.find('MSC v.')
|
||||
if msc_pos != -1:
|
||||
msc_ver = sys.version[msc_pos+6:msc_pos+10]
|
||||
if msc_ver == '1300':
|
||||
# MSVC 7.0
|
||||
return ['msvcr70']
|
||||
elif msc_ver == '1310':
|
||||
# MSVC 7.1
|
||||
return ['msvcr71']
|
||||
elif msc_ver == '1400':
|
||||
# VS2005 / MSVC 8.0
|
||||
return ['msvcr80']
|
||||
elif msc_ver == '1500':
|
||||
# VS2008 / MSVC 9.0
|
||||
return ['msvcr90']
|
||||
else:
|
||||
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
|
||||
|
||||
|
||||
class CygwinCCompiler(UnixCCompiler):
|
||||
""" Handles the Cygwin port of the GNU C compiler to Windows.
|
||||
"""
|
||||
name = 'cygwin'
|
||||
description = 'Cygwin port of GNU C Compiler for Win32'
|
||||
obj_extension = ".o"
|
||||
static_lib_extension = ".a"
|
||||
shared_lib_extension = ".dll"
|
||||
static_lib_format = "lib%s%s"
|
||||
shared_lib_format = "%s%s"
|
||||
exe_extension = ".exe"
|
||||
|
||||
def __init__(self, verbose=0, dry_run=False, force=False):
|
||||
|
||||
UnixCCompiler.__init__(self, verbose, dry_run, force)
|
||||
|
||||
status, details = check_config_h()
|
||||
logger.debug("Python's GCC status: %s (details: %s)", status, details)
|
||||
if status is not CONFIG_H_OK:
|
||||
self.warn(
|
||||
"Python's pyconfig.h doesn't seem to support your compiler. "
|
||||
"Reason: %s. "
|
||||
"Compiling may fail because of undefined preprocessor macros."
|
||||
% details)
|
||||
|
||||
self.gcc_version, self.ld_version, self.dllwrap_version = \
|
||||
get_compiler_versions()
|
||||
logger.debug(self.name + ": gcc %s, ld %s, dllwrap %s\n",
|
||||
self.gcc_version,
|
||||
self.ld_version,
|
||||
self.dllwrap_version)
|
||||
|
||||
# ld_version >= "2.10.90" and < "2.13" should also be able to use
|
||||
# gcc -mdll instead of dllwrap
|
||||
# Older dllwraps had own version numbers, newer ones use the
|
||||
# same as the rest of binutils ( also ld )
|
||||
# dllwrap 2.10.90 is buggy
|
||||
if self.ld_version >= "2.10.90":
|
||||
self.linker_dll = "gcc"
|
||||
else:
|
||||
self.linker_dll = "dllwrap"
|
||||
|
||||
# ld_version >= "2.13" support -shared so use it instead of
|
||||
# -mdll -static
|
||||
if self.ld_version >= "2.13":
|
||||
shared_option = "-shared"
|
||||
else:
|
||||
shared_option = "-mdll -static"
|
||||
|
||||
# Hard-code GCC because that's what this is all about.
|
||||
# XXX optimization, warnings etc. should be customizable.
|
||||
self.set_executables(compiler='gcc -mcygwin -O -Wall',
|
||||
compiler_so='gcc -mcygwin -mdll -O -Wall',
|
||||
compiler_cxx='g++ -mcygwin -O -Wall',
|
||||
linker_exe='gcc -mcygwin',
|
||||
linker_so=('%s -mcygwin %s' %
|
||||
(self.linker_dll, shared_option)))
|
||||
|
||||
# cygwin and mingw32 need different sets of libraries
|
||||
if self.gcc_version == "2.91.57":
|
||||
# cygwin shouldn't need msvcrt, but without the dlls will crash
|
||||
# (gcc version 2.91.57) -- perhaps something about initialization
|
||||
self.dll_libraries=["msvcrt"]
|
||||
self.warn(
|
||||
"Consider upgrading to a newer version of gcc")
|
||||
else:
|
||||
# Include the appropriate MSVC runtime library if Python was built
|
||||
# with MSVC 7.0 or later.
|
||||
self.dll_libraries = get_msvcr()
|
||||
|
||||
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
|
||||
"""Compile the source by spawning GCC and windres if needed."""
|
||||
if ext == '.rc' or ext == '.res':
|
||||
# gcc needs '.res' and '.rc' compiled to object files !!!
|
||||
try:
|
||||
self.spawn(["windres", "-i", src, "-o", obj])
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
else: # for other files use the C-compiler
|
||||
try:
|
||||
self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
|
||||
extra_postargs)
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
|
||||
def link(self, target_desc, objects, output_filename, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None):
|
||||
"""Link the objects."""
|
||||
# use separate copies, so we can modify the lists
|
||||
extra_preargs = copy.copy(extra_preargs or [])
|
||||
libraries = copy.copy(libraries or [])
|
||||
objects = copy.copy(objects or [])
|
||||
|
||||
# Additional libraries
|
||||
libraries.extend(self.dll_libraries)
|
||||
|
||||
# handle export symbols by creating a def-file
|
||||
# with executables this only works with gcc/ld as linker
|
||||
if ((export_symbols is not None) and
|
||||
(target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
|
||||
# (The linker doesn't do anything if output is up-to-date.
|
||||
# So it would probably better to check if we really need this,
|
||||
# but for this we had to insert some unchanged parts of
|
||||
# UnixCCompiler, and this is not what we want.)
|
||||
|
||||
# we want to put some files in the same directory as the
|
||||
# object files are, build_temp doesn't help much
|
||||
# where are the object files
|
||||
temp_dir = os.path.dirname(objects[0])
|
||||
# name of dll to give the helper files the same base name
|
||||
dll_name, dll_extension = os.path.splitext(
|
||||
os.path.basename(output_filename))
|
||||
|
||||
# generate the filenames for these files
|
||||
def_file = os.path.join(temp_dir, dll_name + ".def")
|
||||
lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a")
|
||||
|
||||
# Generate .def file
|
||||
contents = [
|
||||
"LIBRARY %s" % os.path.basename(output_filename),
|
||||
"EXPORTS"]
|
||||
for sym in export_symbols:
|
||||
contents.append(sym)
|
||||
self.execute(write_file, (def_file, contents),
|
||||
"writing %s" % def_file)
|
||||
|
||||
# next add options for def-file and to creating import libraries
|
||||
|
||||
# dllwrap uses different options than gcc/ld
|
||||
if self.linker_dll == "dllwrap":
|
||||
extra_preargs.extend(("--output-lib", lib_file))
|
||||
# for dllwrap we have to use a special option
|
||||
extra_preargs.extend(("--def", def_file))
|
||||
# we use gcc/ld here and can be sure ld is >= 2.9.10
|
||||
else:
|
||||
# doesn't work: bfd_close build\...\libfoo.a: Invalid operation
|
||||
#extra_preargs.extend(("-Wl,--out-implib,%s" % lib_file))
|
||||
# for gcc/ld the def-file is specified as any object files
|
||||
objects.append(def_file)
|
||||
|
||||
#end: if ((export_symbols is not None) and
|
||||
# (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
|
||||
|
||||
# who wants symbols and a many times larger output file
|
||||
# should explicitly switch the debug mode on
|
||||
# otherwise we let dllwrap/ld strip the output file
|
||||
# (On my machine: 10KB < stripped_file < ??100KB
|
||||
# unstripped_file = stripped_file + XXX KB
|
||||
# ( XXX=254 for a typical python extension))
|
||||
if not debug:
|
||||
extra_preargs.append("-s")
|
||||
|
||||
UnixCCompiler.link(self, target_desc, objects, output_filename,
|
||||
output_dir, libraries, library_dirs,
|
||||
runtime_library_dirs,
|
||||
None, # export_symbols, we do this in our def-file
|
||||
debug, extra_preargs, extra_postargs, build_temp,
|
||||
target_lang)
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
|
||||
def object_filenames(self, source_filenames, strip_dir=False,
|
||||
output_dir=''):
|
||||
"""Adds supports for rc and res files."""
|
||||
if output_dir is None:
|
||||
output_dir = ''
|
||||
obj_names = []
|
||||
for src_name in source_filenames:
|
||||
# use normcase to make sure '.rc' is really '.rc' and not '.RC'
|
||||
base, ext = os.path.splitext(os.path.normcase(src_name))
|
||||
if ext not in (self.src_extensions + ['.rc','.res']):
|
||||
raise UnknownFileError("unknown file type '%s' (from '%s')" % (ext, src_name))
|
||||
if strip_dir:
|
||||
base = os.path.basename (base)
|
||||
if ext in ('.res', '.rc'):
|
||||
# these need to be compiled to object files
|
||||
obj_names.append (os.path.join(output_dir,
|
||||
base + ext + self.obj_extension))
|
||||
else:
|
||||
obj_names.append (os.path.join(output_dir,
|
||||
base + self.obj_extension))
|
||||
return obj_names
|
||||
|
||||
# the same as cygwin plus some additional parameters
|
||||
class Mingw32CCompiler(CygwinCCompiler):
|
||||
""" Handles the Mingw32 port of the GNU C compiler to Windows.
|
||||
"""
|
||||
name = 'mingw32'
|
||||
description = 'MinGW32 compiler'
|
||||
|
||||
def __init__(self, verbose=0, dry_run=False, force=False):
|
||||
|
||||
CygwinCCompiler.__init__ (self, verbose, dry_run, force)
|
||||
|
||||
# ld_version >= "2.13" support -shared so use it instead of
|
||||
# -mdll -static
|
||||
if self.ld_version >= "2.13":
|
||||
shared_option = "-shared"
|
||||
else:
|
||||
shared_option = "-mdll -static"
|
||||
|
||||
# A real mingw32 doesn't need to specify a different entry point,
|
||||
# but cygwin 2.91.57 in no-cygwin-mode needs it.
|
||||
if self.gcc_version <= "2.91.57":
|
||||
entry_point = '--entry _DllMain@12'
|
||||
else:
|
||||
entry_point = ''
|
||||
|
||||
self.set_executables(compiler='gcc -mno-cygwin -O -Wall',
|
||||
compiler_so='gcc -mno-cygwin -mdll -O -Wall',
|
||||
compiler_cxx='g++ -mno-cygwin -O -Wall',
|
||||
linker_exe='gcc -mno-cygwin',
|
||||
linker_so='%s -mno-cygwin %s %s'
|
||||
% (self.linker_dll, shared_option,
|
||||
entry_point))
|
||||
# Maybe we should also append -mthreads, but then the finished
|
||||
# dlls need another dll (mingwm10.dll see Mingw32 docs)
|
||||
# (-mthreads: Support thread-safe exception handling on `Mingw32')
|
||||
|
||||
# no additional libraries needed
|
||||
self.dll_libraries=[]
|
||||
|
||||
# Include the appropriate MSVC runtime library if Python was built
|
||||
# with MSVC 7.0 or later.
|
||||
self.dll_libraries = get_msvcr()
|
||||
|
||||
# Because these compilers aren't configured in Python's pyconfig.h file by
|
||||
# default, we should at least warn the user if he is using a unmodified
|
||||
# version.
|
||||
|
||||
CONFIG_H_OK = "ok"
|
||||
CONFIG_H_NOTOK = "not ok"
|
||||
CONFIG_H_UNCERTAIN = "uncertain"
|
||||
|
||||
def check_config_h():
|
||||
"""Check if the current Python installation appears amenable to building
|
||||
extensions with GCC.
|
||||
|
||||
Returns a tuple (status, details), where 'status' is one of the following
|
||||
constants:
|
||||
|
||||
- CONFIG_H_OK: all is well, go ahead and compile
|
||||
- CONFIG_H_NOTOK: doesn't look good
|
||||
- CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
|
||||
|
||||
'details' is a human-readable string explaining the situation.
|
||||
|
||||
Note there are two ways to conclude "OK": either 'sys.version' contains
|
||||
the string "GCC" (implying that this Python was built with GCC), or the
|
||||
installed "pyconfig.h" contains the string "__GNUC__".
|
||||
"""
|
||||
|
||||
# XXX since this function also checks sys.version, it's not strictly a
|
||||
# "pyconfig.h" check -- should probably be renamed...
|
||||
# if sys.version contains GCC then python was compiled with GCC, and the
|
||||
# pyconfig.h file should be OK
|
||||
if "GCC" in sys.version:
|
||||
return CONFIG_H_OK, "sys.version mentions 'GCC'"
|
||||
|
||||
# let's see if __GNUC__ is mentioned in python.h
|
||||
fn = sysconfig.get_config_h_filename()
|
||||
try:
|
||||
with open(fn) as config_h:
|
||||
if "__GNUC__" in config_h.read():
|
||||
return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn
|
||||
else:
|
||||
return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
|
||||
except IOError as exc:
|
||||
return (CONFIG_H_UNCERTAIN,
|
||||
"couldn't read '%s': %s" % (fn, exc.strerror))
|
121
Lib/packaging/compiler/extension.py
Normal file
121
Lib/packaging/compiler/extension.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
"""Class representing C/C++ extension modules."""
|
||||
|
||||
from packaging import logger
|
||||
|
||||
# This class is really only used by the "build_ext" command, so it might
|
||||
# make sense to put it in distutils.command.build_ext. However, that
|
||||
# module is already big enough, and I want to make this class a bit more
|
||||
# complex to simplify some common cases ("foo" module in "foo.c") and do
|
||||
# better error-checking ("foo.c" actually exists).
|
||||
#
|
||||
# Also, putting this in build_ext.py means every setup script would have to
|
||||
# import that large-ish module (indirectly, through distutils.core) in
|
||||
# order to do anything.
|
||||
|
||||
|
||||
class Extension:
|
||||
"""Just a collection of attributes that describes an extension
|
||||
module and everything needed to build it (hopefully in a portable
|
||||
way, but there are hooks that let you be as unportable as you need).
|
||||
|
||||
Instance attributes:
|
||||
name : string
|
||||
the full name of the extension, including any packages -- ie.
|
||||
*not* a filename or pathname, but Python dotted name
|
||||
sources : [string]
|
||||
list of source filenames, relative to the distribution root
|
||||
(where the setup script lives), in Unix form (slash-separated)
|
||||
for portability. Source files may be C, C++, SWIG (.i),
|
||||
platform-specific resource files, or whatever else is recognized
|
||||
by the "build_ext" command as source for a Python extension.
|
||||
include_dirs : [string]
|
||||
list of directories to search for C/C++ header files (in Unix
|
||||
form for portability)
|
||||
define_macros : [(name : string, value : string|None)]
|
||||
list of macros to define; each macro is defined using a 2-tuple,
|
||||
where 'value' is either the string to define it to or None to
|
||||
define it without a particular value (equivalent of "#define
|
||||
FOO" in source or -DFOO on Unix C compiler command line)
|
||||
undef_macros : [string]
|
||||
list of macros to undefine explicitly
|
||||
library_dirs : [string]
|
||||
list of directories to search for C/C++ libraries at link time
|
||||
libraries : [string]
|
||||
list of library names (not filenames or paths) to link against
|
||||
runtime_library_dirs : [string]
|
||||
list of directories to search for C/C++ libraries at run time
|
||||
(for shared extensions, this is when the extension is loaded)
|
||||
extra_objects : [string]
|
||||
list of extra files to link with (eg. object files not implied
|
||||
by 'sources', static library that must be explicitly specified,
|
||||
binary resource files, etc.)
|
||||
extra_compile_args : [string]
|
||||
any extra platform- and compiler-specific information to use
|
||||
when compiling the source files in 'sources'. For platforms and
|
||||
compilers where "command line" makes sense, this is typically a
|
||||
list of command-line arguments, but for other platforms it could
|
||||
be anything.
|
||||
extra_link_args : [string]
|
||||
any extra platform- and compiler-specific information to use
|
||||
when linking object files together to create the extension (or
|
||||
to create a new static Python interpreter). Similar
|
||||
interpretation as for 'extra_compile_args'.
|
||||
export_symbols : [string]
|
||||
list of symbols to be exported from a shared extension. Not
|
||||
used on all platforms, and not generally necessary for Python
|
||||
extensions, which typically export exactly one symbol: "init" +
|
||||
extension_name.
|
||||
swig_opts : [string]
|
||||
any extra options to pass to SWIG if a source file has the .i
|
||||
extension.
|
||||
depends : [string]
|
||||
list of files that the extension depends on
|
||||
language : string
|
||||
extension language (i.e. "c", "c++", "objc"). Will be detected
|
||||
from the source extensions if not provided.
|
||||
optional : boolean
|
||||
specifies that a build failure in the extension should not abort the
|
||||
build process, but simply not install the failing extension.
|
||||
"""
|
||||
|
||||
# **kwargs are allowed so that a warning is emitted instead of an
|
||||
# exception
|
||||
def __init__(self, name, sources, include_dirs=None, define_macros=None,
|
||||
undef_macros=None, library_dirs=None, libraries=None,
|
||||
runtime_library_dirs=None, extra_objects=None,
|
||||
extra_compile_args=None, extra_link_args=None,
|
||||
export_symbols=None, swig_opts=None, depends=None,
|
||||
language=None, optional=None, **kw):
|
||||
if not isinstance(name, str):
|
||||
raise AssertionError("'name' must be a string")
|
||||
|
||||
if not isinstance(sources, list):
|
||||
raise AssertionError("'sources' must be a list of strings")
|
||||
|
||||
for v in sources:
|
||||
if not isinstance(v, str):
|
||||
raise AssertionError("'sources' must be a list of strings")
|
||||
|
||||
self.name = name
|
||||
self.sources = sources
|
||||
self.include_dirs = include_dirs or []
|
||||
self.define_macros = define_macros or []
|
||||
self.undef_macros = undef_macros or []
|
||||
self.library_dirs = library_dirs or []
|
||||
self.libraries = libraries or []
|
||||
self.runtime_library_dirs = runtime_library_dirs or []
|
||||
self.extra_objects = extra_objects or []
|
||||
self.extra_compile_args = extra_compile_args or []
|
||||
self.extra_link_args = extra_link_args or []
|
||||
self.export_symbols = export_symbols or []
|
||||
self.swig_opts = swig_opts or []
|
||||
self.depends = depends or []
|
||||
self.language = language
|
||||
self.optional = optional
|
||||
|
||||
# If there are unknown keyword options, warn about them
|
||||
if len(kw) > 0:
|
||||
options = [repr(option) for option in kw]
|
||||
options = ', '.join(sorted(options))
|
||||
logger.warning(
|
||||
'unknown arguments given to Extension: %s', options)
|
720
Lib/packaging/compiler/msvc9compiler.py
Normal file
720
Lib/packaging/compiler/msvc9compiler.py
Normal file
|
@ -0,0 +1,720 @@
|
|||
"""CCompiler implementation for the Microsoft Visual Studio 2008 compiler.
|
||||
|
||||
The MSVCCompiler class is compatible with VS 2005 and VS 2008. Legacy
|
||||
support for older versions of VS are in the msvccompiler module.
|
||||
"""
|
||||
|
||||
# Written by Perry Stoll
|
||||
# hacked by Robin Becker and Thomas Heller to do a better job of
|
||||
# finding DevStudio (through the registry)
|
||||
# ported to VS2005 and VS 2008 by Christian Heimes
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
|
||||
from packaging.errors import (PackagingExecError, PackagingPlatformError,
|
||||
CompileError, LibError, LinkError)
|
||||
from packaging.compiler.ccompiler import CCompiler
|
||||
from packaging.compiler import gen_lib_options
|
||||
from packaging import logger
|
||||
from packaging.util import get_platform
|
||||
|
||||
import winreg
|
||||
|
||||
RegOpenKeyEx = winreg.OpenKeyEx
|
||||
RegEnumKey = winreg.EnumKey
|
||||
RegEnumValue = winreg.EnumValue
|
||||
RegError = winreg.error
|
||||
|
||||
HKEYS = (winreg.HKEY_USERS,
|
||||
winreg.HKEY_CURRENT_USER,
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
winreg.HKEY_CLASSES_ROOT)
|
||||
|
||||
VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
|
||||
WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
|
||||
NET_BASE = r"Software\Microsoft\.NETFramework"
|
||||
|
||||
# A map keyed by get_platform() return values to values accepted by
|
||||
# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
|
||||
# the param to cross-compile on x86 targetting amd64.)
|
||||
PLAT_TO_VCVARS = {
|
||||
'win32' : 'x86',
|
||||
'win-amd64' : 'amd64',
|
||||
'win-ia64' : 'ia64',
|
||||
}
|
||||
|
||||
|
||||
class Reg:
|
||||
"""Helper class to read values from the registry
|
||||
"""
|
||||
|
||||
def get_value(cls, path, key):
|
||||
for base in HKEYS:
|
||||
d = cls.read_values(base, path)
|
||||
if d and key in d:
|
||||
return d[key]
|
||||
raise KeyError(key)
|
||||
get_value = classmethod(get_value)
|
||||
|
||||
def read_keys(cls, base, key):
|
||||
"""Return list of registry keys."""
|
||||
try:
|
||||
handle = RegOpenKeyEx(base, key)
|
||||
except RegError:
|
||||
return None
|
||||
L = []
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
k = RegEnumKey(handle, i)
|
||||
except RegError:
|
||||
break
|
||||
L.append(k)
|
||||
i += 1
|
||||
return L
|
||||
read_keys = classmethod(read_keys)
|
||||
|
||||
def read_values(cls, base, key):
|
||||
"""Return dict of registry keys and values.
|
||||
|
||||
All names are converted to lowercase.
|
||||
"""
|
||||
try:
|
||||
handle = RegOpenKeyEx(base, key)
|
||||
except RegError:
|
||||
return None
|
||||
d = {}
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
name, value, type = RegEnumValue(handle, i)
|
||||
except RegError:
|
||||
break
|
||||
name = name.lower()
|
||||
d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
|
||||
i += 1
|
||||
return d
|
||||
read_values = classmethod(read_values)
|
||||
|
||||
def convert_mbcs(s):
|
||||
dec = getattr(s, "decode", None)
|
||||
if dec is not None:
|
||||
try:
|
||||
s = dec("mbcs")
|
||||
except UnicodeError:
|
||||
pass
|
||||
return s
|
||||
convert_mbcs = staticmethod(convert_mbcs)
|
||||
|
||||
class MacroExpander:
|
||||
|
||||
def __init__(self, version):
|
||||
self.macros = {}
|
||||
self.vsbase = VS_BASE % version
|
||||
self.load_macros(version)
|
||||
|
||||
def set_macro(self, macro, path, key):
|
||||
self.macros["$(%s)" % macro] = Reg.get_value(path, key)
|
||||
|
||||
def load_macros(self, version):
|
||||
self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
|
||||
self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
|
||||
self.set_macro("FrameworkDir", NET_BASE, "installroot")
|
||||
try:
|
||||
if version >= 8.0:
|
||||
self.set_macro("FrameworkSDKDir", NET_BASE,
|
||||
"sdkinstallrootv2.0")
|
||||
else:
|
||||
raise KeyError("sdkinstallrootv2.0")
|
||||
except KeyError:
|
||||
raise PackagingPlatformError(
|
||||
"""Python was built with Visual Studio 2008;
|
||||
extensions must be built with a compiler than can generate compatible binaries.
|
||||
Visual Studio 2008 was not found on this system. If you have Cygwin installed,
|
||||
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
|
||||
|
||||
if version >= 9.0:
|
||||
self.set_macro("FrameworkVersion", self.vsbase, "clr version")
|
||||
self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
|
||||
else:
|
||||
p = r"Software\Microsoft\NET Framework Setup\Product"
|
||||
for base in HKEYS:
|
||||
try:
|
||||
h = RegOpenKeyEx(base, p)
|
||||
except RegError:
|
||||
continue
|
||||
key = RegEnumKey(h, 0)
|
||||
d = Reg.get_value(base, r"%s\%s" % (p, key))
|
||||
self.macros["$(FrameworkVersion)"] = d["version"]
|
||||
|
||||
def sub(self, s):
|
||||
for k, v in self.macros.items():
|
||||
s = s.replace(k, v)
|
||||
return s
|
||||
|
||||
def get_build_version():
|
||||
"""Return the version of MSVC that was used to build Python.
|
||||
|
||||
For Python 2.3 and up, the version number is included in
|
||||
sys.version. For earlier versions, assume the compiler is MSVC 6.
|
||||
"""
|
||||
prefix = "MSC v."
|
||||
i = sys.version.find(prefix)
|
||||
if i == -1:
|
||||
return 6
|
||||
i = i + len(prefix)
|
||||
s, rest = sys.version[i:].split(" ", 1)
|
||||
majorVersion = int(s[:-2]) - 6
|
||||
minorVersion = int(s[2:3]) / 10.0
|
||||
# I don't think paths are affected by minor version in version 6
|
||||
if majorVersion == 6:
|
||||
minorVersion = 0
|
||||
if majorVersion >= 6:
|
||||
return majorVersion + minorVersion
|
||||
# else we don't know what version of the compiler this is
|
||||
return None
|
||||
|
||||
def normalize_and_reduce_paths(paths):
|
||||
"""Return a list of normalized paths with duplicates removed.
|
||||
|
||||
The current order of paths is maintained.
|
||||
"""
|
||||
# Paths are normalized so things like: /a and /a/ aren't both preserved.
|
||||
reduced_paths = []
|
||||
for p in paths:
|
||||
np = os.path.normpath(p)
|
||||
# XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
|
||||
if np not in reduced_paths:
|
||||
reduced_paths.append(np)
|
||||
return reduced_paths
|
||||
|
||||
def removeDuplicates(variable):
|
||||
"""Remove duplicate values of an environment variable.
|
||||
"""
|
||||
oldList = variable.split(os.pathsep)
|
||||
newList = []
|
||||
for i in oldList:
|
||||
if i not in newList:
|
||||
newList.append(i)
|
||||
newVariable = os.pathsep.join(newList)
|
||||
return newVariable
|
||||
|
||||
def find_vcvarsall(version):
|
||||
"""Find the vcvarsall.bat file
|
||||
|
||||
At first it tries to find the productdir of VS 2008 in the registry. If
|
||||
that fails it falls back to the VS90COMNTOOLS env var.
|
||||
"""
|
||||
vsbase = VS_BASE % version
|
||||
try:
|
||||
productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
|
||||
"productdir")
|
||||
except KeyError:
|
||||
logger.debug("Unable to find productdir in registry")
|
||||
productdir = None
|
||||
|
||||
if not productdir or not os.path.isdir(productdir):
|
||||
toolskey = "VS%0.f0COMNTOOLS" % version
|
||||
toolsdir = os.environ.get(toolskey, None)
|
||||
|
||||
if toolsdir and os.path.isdir(toolsdir):
|
||||
productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
|
||||
productdir = os.path.abspath(productdir)
|
||||
if not os.path.isdir(productdir):
|
||||
logger.debug("%s is not a valid directory", productdir)
|
||||
return None
|
||||
else:
|
||||
logger.debug("env var %s is not set or invalid", toolskey)
|
||||
if not productdir:
|
||||
logger.debug("no productdir found")
|
||||
return None
|
||||
vcvarsall = os.path.join(productdir, "vcvarsall.bat")
|
||||
if os.path.isfile(vcvarsall):
|
||||
return vcvarsall
|
||||
logger.debug("unable to find vcvarsall.bat")
|
||||
return None
|
||||
|
||||
def query_vcvarsall(version, arch="x86"):
|
||||
"""Launch vcvarsall.bat and read the settings from its environment
|
||||
"""
|
||||
vcvarsall = find_vcvarsall(version)
|
||||
interesting = set(("include", "lib", "libpath", "path"))
|
||||
result = {}
|
||||
|
||||
if vcvarsall is None:
|
||||
raise PackagingPlatformError("Unable to find vcvarsall.bat")
|
||||
logger.debug("calling 'vcvarsall.bat %s' (version=%s)", arch, version)
|
||||
popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
stdout, stderr = popen.communicate()
|
||||
if popen.wait() != 0:
|
||||
raise PackagingPlatformError(stderr.decode("mbcs"))
|
||||
|
||||
stdout = stdout.decode("mbcs")
|
||||
for line in stdout.split("\n"):
|
||||
line = Reg.convert_mbcs(line)
|
||||
if '=' not in line:
|
||||
continue
|
||||
line = line.strip()
|
||||
key, value = line.split('=', 1)
|
||||
key = key.lower()
|
||||
if key in interesting:
|
||||
if value.endswith(os.pathsep):
|
||||
value = value[:-1]
|
||||
result[key] = removeDuplicates(value)
|
||||
|
||||
if len(result) != len(interesting):
|
||||
raise ValueError(str(list(result)))
|
||||
|
||||
return result
|
||||
|
||||
# More globals
|
||||
VERSION = get_build_version()
|
||||
if VERSION < 8.0:
|
||||
raise PackagingPlatformError("VC %0.1f is not supported by this module" % VERSION)
|
||||
# MACROS = MacroExpander(VERSION)
|
||||
|
||||
class MSVCCompiler(CCompiler) :
|
||||
"""Concrete class that implements an interface to Microsoft Visual C++,
|
||||
as defined by the CCompiler abstract class."""
|
||||
|
||||
name = 'msvc'
|
||||
description = 'Microsoft Visual C++'
|
||||
|
||||
# Just set this so CCompiler's constructor doesn't barf. We currently
|
||||
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
|
||||
# as it really isn't necessary for this sort of single-compiler class.
|
||||
# Would be nice to have a consistent interface with UnixCCompiler,
|
||||
# though, so it's worth thinking about.
|
||||
executables = {}
|
||||
|
||||
# Private class data (need to distinguish C from C++ source for compiler)
|
||||
_c_extensions = ['.c']
|
||||
_cpp_extensions = ['.cc', '.cpp', '.cxx']
|
||||
_rc_extensions = ['.rc']
|
||||
_mc_extensions = ['.mc']
|
||||
|
||||
# Needed for the filename generation methods provided by the
|
||||
# base class, CCompiler.
|
||||
src_extensions = (_c_extensions + _cpp_extensions +
|
||||
_rc_extensions + _mc_extensions)
|
||||
res_extension = '.res'
|
||||
obj_extension = '.obj'
|
||||
static_lib_extension = '.lib'
|
||||
shared_lib_extension = '.dll'
|
||||
static_lib_format = shared_lib_format = '%s%s'
|
||||
exe_extension = '.exe'
|
||||
|
||||
def __init__(self, verbose=0, dry_run=False, force=False):
|
||||
CCompiler.__init__(self, verbose, dry_run, force)
|
||||
self.__version = VERSION
|
||||
self.__root = r"Software\Microsoft\VisualStudio"
|
||||
# self.__macros = MACROS
|
||||
self.__paths = []
|
||||
# target platform (.plat_name is consistent with 'bdist')
|
||||
self.plat_name = None
|
||||
self.__arch = None # deprecated name
|
||||
self.initialized = False
|
||||
|
||||
def initialize(self, plat_name=None):
|
||||
# multi-init means we would need to check platform same each time...
|
||||
assert not self.initialized, "don't init multiple times"
|
||||
if plat_name is None:
|
||||
plat_name = get_platform()
|
||||
# sanity check for platforms to prevent obscure errors later.
|
||||
ok_plats = 'win32', 'win-amd64', 'win-ia64'
|
||||
if plat_name not in ok_plats:
|
||||
raise PackagingPlatformError("--plat-name must be one of %s" %
|
||||
(ok_plats,))
|
||||
|
||||
if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
|
||||
# Assume that the SDK set up everything alright; don't try to be
|
||||
# smarter
|
||||
self.cc = "cl.exe"
|
||||
self.linker = "link.exe"
|
||||
self.lib = "lib.exe"
|
||||
self.rc = "rc.exe"
|
||||
self.mc = "mc.exe"
|
||||
else:
|
||||
# On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
|
||||
# to cross compile, you use 'x86_amd64'.
|
||||
# On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
|
||||
# compile use 'x86' (ie, it runs the x86 compiler directly)
|
||||
# No idea how itanium handles this, if at all.
|
||||
if plat_name == get_platform() or plat_name == 'win32':
|
||||
# native build or cross-compile to win32
|
||||
plat_spec = PLAT_TO_VCVARS[plat_name]
|
||||
else:
|
||||
# cross compile from win32 -> some 64bit
|
||||
plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
|
||||
PLAT_TO_VCVARS[plat_name]
|
||||
|
||||
vc_env = query_vcvarsall(VERSION, plat_spec)
|
||||
|
||||
# take care to only use strings in the environment.
|
||||
self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep)
|
||||
os.environ['lib'] = vc_env['lib'].encode('mbcs')
|
||||
os.environ['include'] = vc_env['include'].encode('mbcs')
|
||||
|
||||
if len(self.__paths) == 0:
|
||||
raise PackagingPlatformError("Python was built with %s, "
|
||||
"and extensions need to be built with the same "
|
||||
"version of the compiler, but it isn't installed."
|
||||
% self.__product)
|
||||
|
||||
self.cc = self.find_exe("cl.exe")
|
||||
self.linker = self.find_exe("link.exe")
|
||||
self.lib = self.find_exe("lib.exe")
|
||||
self.rc = self.find_exe("rc.exe") # resource compiler
|
||||
self.mc = self.find_exe("mc.exe") # message compiler
|
||||
#self.set_path_env_var('lib')
|
||||
#self.set_path_env_var('include')
|
||||
|
||||
# extend the MSVC path with the current path
|
||||
try:
|
||||
for p in os.environ['path'].split(';'):
|
||||
self.__paths.append(p)
|
||||
except KeyError:
|
||||
pass
|
||||
self.__paths = normalize_and_reduce_paths(self.__paths)
|
||||
os.environ['path'] = ";".join(self.__paths)
|
||||
|
||||
self.preprocess_options = None
|
||||
if self.__arch == "x86":
|
||||
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
|
||||
'/DNDEBUG']
|
||||
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
|
||||
'/Z7', '/D_DEBUG']
|
||||
else:
|
||||
# Win64
|
||||
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
|
||||
'/DNDEBUG']
|
||||
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
|
||||
'/Z7', '/D_DEBUG']
|
||||
|
||||
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
|
||||
if self.__version >= 7:
|
||||
self.ldflags_shared_debug = [
|
||||
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
|
||||
]
|
||||
self.ldflags_static = [ '/nologo']
|
||||
|
||||
self.initialized = True
|
||||
|
||||
# -- Worker methods ------------------------------------------------
|
||||
|
||||
def object_filenames(self,
|
||||
source_filenames,
|
||||
strip_dir=False,
|
||||
output_dir=''):
|
||||
# Copied from ccompiler.py, extended to return .res as 'object'-file
|
||||
# for .rc input file
|
||||
if output_dir is None: output_dir = ''
|
||||
obj_names = []
|
||||
for src_name in source_filenames:
|
||||
base, ext = os.path.splitext(src_name)
|
||||
base = os.path.splitdrive(base)[1] # Chop off the drive
|
||||
base = base[os.path.isabs(base):] # If abs, chop off leading /
|
||||
if ext not in self.src_extensions:
|
||||
# Better to raise an exception instead of silently continuing
|
||||
# and later complain about sources and targets having
|
||||
# different lengths
|
||||
raise CompileError("Don't know how to compile %s" % src_name)
|
||||
if strip_dir:
|
||||
base = os.path.basename(base)
|
||||
if ext in self._rc_extensions:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.res_extension))
|
||||
elif ext in self._mc_extensions:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.res_extension))
|
||||
else:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.obj_extension))
|
||||
return obj_names
|
||||
|
||||
|
||||
def compile(self, sources,
|
||||
output_dir=None, macros=None, include_dirs=None, debug=False,
|
||||
extra_preargs=None, extra_postargs=None, depends=None):
|
||||
|
||||
if not self.initialized:
|
||||
self.initialize()
|
||||
compile_info = self._setup_compile(output_dir, macros, include_dirs,
|
||||
sources, depends, extra_postargs)
|
||||
macros, objects, extra_postargs, pp_opts, build = compile_info
|
||||
|
||||
compile_opts = extra_preargs or []
|
||||
compile_opts.append('/c')
|
||||
if debug:
|
||||
compile_opts.extend(self.compile_options_debug)
|
||||
else:
|
||||
compile_opts.extend(self.compile_options)
|
||||
|
||||
for obj in objects:
|
||||
try:
|
||||
src, ext = build[obj]
|
||||
except KeyError:
|
||||
continue
|
||||
if debug:
|
||||
# pass the full pathname to MSVC in debug mode,
|
||||
# this allows the debugger to find the source file
|
||||
# without asking the user to browse for it
|
||||
src = os.path.abspath(src)
|
||||
|
||||
if ext in self._c_extensions:
|
||||
input_opt = "/Tc" + src
|
||||
elif ext in self._cpp_extensions:
|
||||
input_opt = "/Tp" + src
|
||||
elif ext in self._rc_extensions:
|
||||
# compile .RC to .RES file
|
||||
input_opt = src
|
||||
output_opt = "/fo" + obj
|
||||
try:
|
||||
self.spawn([self.rc] + pp_opts +
|
||||
[output_opt] + [input_opt])
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
continue
|
||||
elif ext in self._mc_extensions:
|
||||
# Compile .MC to .RC file to .RES file.
|
||||
# * '-h dir' specifies the directory for the
|
||||
# generated include file
|
||||
# * '-r dir' specifies the target directory of the
|
||||
# generated RC file and the binary message resource
|
||||
# it includes
|
||||
#
|
||||
# For now (since there are no options to change this),
|
||||
# we use the source-directory for the include file and
|
||||
# the build directory for the RC file and message
|
||||
# resources. This works at least for win32all.
|
||||
h_dir = os.path.dirname(src)
|
||||
rc_dir = os.path.dirname(obj)
|
||||
try:
|
||||
# first compile .MC to .RC and .H file
|
||||
self.spawn([self.mc] +
|
||||
['-h', h_dir, '-r', rc_dir] + [src])
|
||||
base, _ = os.path.splitext(os.path.basename(src))
|
||||
rc_file = os.path.join(rc_dir, base + '.rc')
|
||||
# then compile .RC to .RES file
|
||||
self.spawn([self.rc] +
|
||||
["/fo" + obj] + [rc_file])
|
||||
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
continue
|
||||
else:
|
||||
# how to handle this file?
|
||||
raise CompileError("Don't know how to compile %s to %s"
|
||||
% (src, obj))
|
||||
|
||||
output_opt = "/Fo" + obj
|
||||
try:
|
||||
self.spawn([self.cc] + compile_opts + pp_opts +
|
||||
[input_opt, output_opt] +
|
||||
extra_postargs)
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
def create_static_lib(self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
debug=False,
|
||||
target_lang=None):
|
||||
|
||||
if not self.initialized:
|
||||
self.initialize()
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
output_filename = self.library_filename(output_libname,
|
||||
output_dir=output_dir)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
lib_args = objects + ['/OUT:' + output_filename]
|
||||
if debug:
|
||||
pass # XXX what goes here?
|
||||
try:
|
||||
self.spawn([self.lib] + lib_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LibError(msg)
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
|
||||
def link(self, target_desc, objects, output_filename, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None):
|
||||
if not self.initialized:
|
||||
self.initialize()
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
fixed_args = self._fix_lib_args(libraries, library_dirs,
|
||||
runtime_library_dirs)
|
||||
libraries, library_dirs, runtime_library_dirs = fixed_args
|
||||
|
||||
if runtime_library_dirs:
|
||||
self.warn("don't know what to do with 'runtime_library_dirs': "
|
||||
+ str(runtime_library_dirs))
|
||||
|
||||
lib_opts = gen_lib_options(self,
|
||||
library_dirs, runtime_library_dirs,
|
||||
libraries)
|
||||
if output_dir is not None:
|
||||
output_filename = os.path.join(output_dir, output_filename)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
if target_desc == CCompiler.EXECUTABLE:
|
||||
if debug:
|
||||
ldflags = self.ldflags_shared_debug[1:]
|
||||
else:
|
||||
ldflags = self.ldflags_shared[1:]
|
||||
else:
|
||||
if debug:
|
||||
ldflags = self.ldflags_shared_debug
|
||||
else:
|
||||
ldflags = self.ldflags_shared
|
||||
|
||||
export_opts = []
|
||||
for sym in (export_symbols or []):
|
||||
export_opts.append("/EXPORT:" + sym)
|
||||
|
||||
ld_args = (ldflags + lib_opts + export_opts +
|
||||
objects + ['/OUT:' + output_filename])
|
||||
|
||||
# The MSVC linker generates .lib and .exp files, which cannot be
|
||||
# suppressed by any linker switches. The .lib files may even be
|
||||
# needed! Make sure they are generated in the temporary build
|
||||
# directory. Since they have different names for debug and release
|
||||
# builds, they can go into the same directory.
|
||||
build_temp = os.path.dirname(objects[0])
|
||||
if export_symbols is not None:
|
||||
dll_name, dll_ext = os.path.splitext(
|
||||
os.path.basename(output_filename))
|
||||
implib_file = os.path.join(
|
||||
build_temp,
|
||||
self.library_filename(dll_name))
|
||||
ld_args.append('/IMPLIB:' + implib_file)
|
||||
|
||||
# Embedded manifests are 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 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:
|
||||
ld_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
ld_args.extend(extra_postargs)
|
||||
|
||||
self.mkpath(os.path.dirname(output_filename))
|
||||
try:
|
||||
self.spawn([self.linker] + ld_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LinkError(msg)
|
||||
|
||||
# embed the manifest
|
||||
# XXX - this is somewhat fragile - if mt.exe fails, distutils
|
||||
# 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
|
||||
# implies a build environment error that shouldn't go undetected.
|
||||
if target_desc == CCompiler.EXECUTABLE:
|
||||
mfid = 1
|
||||
else:
|
||||
mfid = 2
|
||||
self._remove_visual_c_ref(temp_manifest)
|
||||
out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
|
||||
try:
|
||||
self.spawn(['mt.exe', '-nologo', '-manifest',
|
||||
temp_manifest, out_arg])
|
||||
except PackagingExecError as msg:
|
||||
raise LinkError(msg)
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
def _remove_visual_c_ref(self, manifest_file):
|
||||
try:
|
||||
# Remove references to the Visual C runtime, so they will
|
||||
# fall through to the Visual C dependency of Python.exe.
|
||||
# This way, when installed for a restricted user (e.g.
|
||||
# runtimes are not in WinSxS folder, but in Python's own
|
||||
# folder), the runtimes do not need to be in every folder
|
||||
# with .pyd's.
|
||||
with open(manifest_file) as manifest_f:
|
||||
manifest_buf = manifest_f.read()
|
||||
pattern = re.compile(
|
||||
r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
|
||||
r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
|
||||
re.DOTALL)
|
||||
manifest_buf = re.sub(pattern, "", manifest_buf)
|
||||
pattern = "<dependentAssembly>\s*</dependentAssembly>"
|
||||
manifest_buf = re.sub(pattern, "", manifest_buf)
|
||||
with open(manifest_file, 'w') as manifest_f:
|
||||
manifest_f.write(manifest_buf)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
# These are all used by the 'gen_lib_options() function, in
|
||||
# ccompiler.py.
|
||||
|
||||
def library_dir_option(self, dir):
|
||||
return "/LIBPATH:" + dir
|
||||
|
||||
def runtime_library_dir_option(self, dir):
|
||||
raise PackagingPlatformError(
|
||||
"don't know how to set runtime library search path for MSVC++")
|
||||
|
||||
def library_option(self, lib):
|
||||
return self.library_filename(lib)
|
||||
|
||||
|
||||
def find_library_file(self, dirs, lib, debug=False):
|
||||
# Prefer a debugging library if found (and requested), but deal
|
||||
# with it if we don't have one.
|
||||
if debug:
|
||||
try_names = [lib + "_d", lib]
|
||||
else:
|
||||
try_names = [lib]
|
||||
for dir in dirs:
|
||||
for name in try_names:
|
||||
libfile = os.path.join(dir, self.library_filename(name))
|
||||
if os.path.exists(libfile):
|
||||
return libfile
|
||||
else:
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
||||
|
||||
# Helper methods for using the MSVC registry settings
|
||||
|
||||
def find_exe(self, exe):
|
||||
"""Return path to an MSVC executable program.
|
||||
|
||||
Tries to find the program in several places: first, one of the
|
||||
MSVC program search paths from the registry; next, the directories
|
||||
in the PATH environment variable. If any of those work, return an
|
||||
absolute path that is known to exist. If none of them work, just
|
||||
return the original program name, 'exe'.
|
||||
"""
|
||||
for p in self.__paths:
|
||||
fn = os.path.join(os.path.abspath(p), exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
# didn't find it; try existing path
|
||||
for p in os.environ['Path'].split(';'):
|
||||
fn = os.path.join(os.path.abspath(p),exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
return exe
|
636
Lib/packaging/compiler/msvccompiler.py
Normal file
636
Lib/packaging/compiler/msvccompiler.py
Normal file
|
@ -0,0 +1,636 @@
|
|||
"""CCompiler implementation for old Microsoft Visual Studio compilers.
|
||||
|
||||
For a compiler compatible with VS 2005 and 2008, use msvc9compiler.
|
||||
"""
|
||||
|
||||
# Written by Perry Stoll
|
||||
# hacked by Robin Becker and Thomas Heller to do a better job of
|
||||
# finding DevStudio (through the registry)
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from packaging.errors import (PackagingExecError, PackagingPlatformError,
|
||||
CompileError, LibError, LinkError)
|
||||
from packaging.compiler.ccompiler import CCompiler
|
||||
from packaging.compiler import gen_lib_options
|
||||
from packaging import logger
|
||||
|
||||
_can_read_reg = False
|
||||
try:
|
||||
import winreg
|
||||
|
||||
_can_read_reg = True
|
||||
hkey_mod = winreg
|
||||
|
||||
RegOpenKeyEx = winreg.OpenKeyEx
|
||||
RegEnumKey = winreg.EnumKey
|
||||
RegEnumValue = winreg.EnumValue
|
||||
RegError = winreg.error
|
||||
|
||||
except ImportError:
|
||||
try:
|
||||
import win32api
|
||||
import win32con
|
||||
_can_read_reg = True
|
||||
hkey_mod = win32con
|
||||
|
||||
RegOpenKeyEx = win32api.RegOpenKeyEx
|
||||
RegEnumKey = win32api.RegEnumKey
|
||||
RegEnumValue = win32api.RegEnumValue
|
||||
RegError = win32api.error
|
||||
|
||||
except ImportError:
|
||||
logger.warning(
|
||||
"can't read registry to find the necessary compiler setting;\n"
|
||||
"make sure that Python modules _winreg, win32api or win32con "
|
||||
"are installed.")
|
||||
|
||||
if _can_read_reg:
|
||||
HKEYS = (hkey_mod.HKEY_USERS,
|
||||
hkey_mod.HKEY_CURRENT_USER,
|
||||
hkey_mod.HKEY_LOCAL_MACHINE,
|
||||
hkey_mod.HKEY_CLASSES_ROOT)
|
||||
|
||||
|
||||
def read_keys(base, key):
|
||||
"""Return list of registry keys."""
|
||||
|
||||
try:
|
||||
handle = RegOpenKeyEx(base, key)
|
||||
except RegError:
|
||||
return None
|
||||
L = []
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
k = RegEnumKey(handle, i)
|
||||
except RegError:
|
||||
break
|
||||
L.append(k)
|
||||
i = i + 1
|
||||
return L
|
||||
|
||||
|
||||
def read_values(base, key):
|
||||
"""Return dict of registry keys and values.
|
||||
|
||||
All names are converted to lowercase.
|
||||
"""
|
||||
try:
|
||||
handle = RegOpenKeyEx(base, key)
|
||||
except RegError:
|
||||
return None
|
||||
d = {}
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
name, value, type = RegEnumValue(handle, i)
|
||||
except RegError:
|
||||
break
|
||||
name = name.lower()
|
||||
d[convert_mbcs(name)] = convert_mbcs(value)
|
||||
i = i + 1
|
||||
return d
|
||||
|
||||
|
||||
def convert_mbcs(s):
|
||||
enc = getattr(s, "encode", None)
|
||||
if enc is not None:
|
||||
try:
|
||||
s = enc("mbcs")
|
||||
except UnicodeError:
|
||||
pass
|
||||
return s
|
||||
|
||||
|
||||
class MacroExpander:
|
||||
|
||||
def __init__(self, version):
|
||||
self.macros = {}
|
||||
self.load_macros(version)
|
||||
|
||||
def set_macro(self, macro, path, key):
|
||||
for base in HKEYS:
|
||||
d = read_values(base, path)
|
||||
if d:
|
||||
self.macros["$(%s)" % macro] = d[key]
|
||||
break
|
||||
|
||||
def load_macros(self, version):
|
||||
vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
|
||||
self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
|
||||
self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
|
||||
net = r"Software\Microsoft\.NETFramework"
|
||||
self.set_macro("FrameworkDir", net, "installroot")
|
||||
try:
|
||||
if version > 7.0:
|
||||
self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
|
||||
else:
|
||||
self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
|
||||
except KeyError:
|
||||
raise PackagingPlatformError(
|
||||
"""Python was built with Visual Studio 2003; extensions must be built with
|
||||
a compiler than can generate compatible binaries. Visual Studio 2003 was
|
||||
not found on this system. If you have Cygwin installed, you can try
|
||||
compiling with MingW32, by passing "-c mingw32" to setup.py.""")
|
||||
# XXX update this comment for setup.cfg
|
||||
|
||||
p = r"Software\Microsoft\NET Framework Setup\Product"
|
||||
for base in HKEYS:
|
||||
try:
|
||||
h = RegOpenKeyEx(base, p)
|
||||
except RegError:
|
||||
continue
|
||||
key = RegEnumKey(h, 0)
|
||||
d = read_values(base, r"%s\%s" % (p, key))
|
||||
self.macros["$(FrameworkVersion)"] = d["version"]
|
||||
|
||||
def sub(self, s):
|
||||
for k, v in self.macros.items():
|
||||
s = s.replace(k, v)
|
||||
return s
|
||||
|
||||
|
||||
def get_build_version():
|
||||
"""Return the version of MSVC that was used to build Python.
|
||||
|
||||
For Python 2.3 and up, the version number is included in
|
||||
sys.version. For earlier versions, assume the compiler is MSVC 6.
|
||||
"""
|
||||
|
||||
prefix = "MSC v."
|
||||
i = sys.version.find(prefix)
|
||||
if i == -1:
|
||||
return 6
|
||||
i = i + len(prefix)
|
||||
s, rest = sys.version[i:].split(" ", 1)
|
||||
majorVersion = int(s[:-2]) - 6
|
||||
minorVersion = int(s[2:3]) / 10.0
|
||||
# I don't think paths are affected by minor version in version 6
|
||||
if majorVersion == 6:
|
||||
minorVersion = 0
|
||||
if majorVersion >= 6:
|
||||
return majorVersion + minorVersion
|
||||
# else we don't know what version of the compiler this is
|
||||
return None
|
||||
|
||||
|
||||
def get_build_architecture():
|
||||
"""Return the processor architecture.
|
||||
|
||||
Possible results are "Intel", "Itanium", or "AMD64".
|
||||
"""
|
||||
|
||||
prefix = " bit ("
|
||||
i = sys.version.find(prefix)
|
||||
if i == -1:
|
||||
return "Intel"
|
||||
j = sys.version.find(")", i)
|
||||
return sys.version[i+len(prefix):j]
|
||||
|
||||
|
||||
def normalize_and_reduce_paths(paths):
|
||||
"""Return a list of normalized paths with duplicates removed.
|
||||
|
||||
The current order of paths is maintained.
|
||||
"""
|
||||
# Paths are normalized so things like: /a and /a/ aren't both preserved.
|
||||
reduced_paths = []
|
||||
for p in paths:
|
||||
np = os.path.normpath(p)
|
||||
# XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
|
||||
if np not in reduced_paths:
|
||||
reduced_paths.append(np)
|
||||
return reduced_paths
|
||||
|
||||
|
||||
class MSVCCompiler(CCompiler):
|
||||
"""Concrete class that implements an interface to Microsoft Visual C++,
|
||||
as defined by the CCompiler abstract class."""
|
||||
|
||||
name = 'msvc'
|
||||
description = "Microsoft Visual C++"
|
||||
|
||||
# Just set this so CCompiler's constructor doesn't barf. We currently
|
||||
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
|
||||
# as it really isn't necessary for this sort of single-compiler class.
|
||||
# Would be nice to have a consistent interface with UnixCCompiler,
|
||||
# though, so it's worth thinking about.
|
||||
executables = {}
|
||||
|
||||
# Private class data (need to distinguish C from C++ source for compiler)
|
||||
_c_extensions = ['.c']
|
||||
_cpp_extensions = ['.cc', '.cpp', '.cxx']
|
||||
_rc_extensions = ['.rc']
|
||||
_mc_extensions = ['.mc']
|
||||
|
||||
# Needed for the filename generation methods provided by the
|
||||
# base class, CCompiler.
|
||||
src_extensions = (_c_extensions + _cpp_extensions +
|
||||
_rc_extensions + _mc_extensions)
|
||||
res_extension = '.res'
|
||||
obj_extension = '.obj'
|
||||
static_lib_extension = '.lib'
|
||||
shared_lib_extension = '.dll'
|
||||
static_lib_format = shared_lib_format = '%s%s'
|
||||
exe_extension = '.exe'
|
||||
|
||||
def __init__(self, verbose=0, dry_run=False, force=False):
|
||||
CCompiler.__init__(self, verbose, dry_run, force)
|
||||
self.__version = get_build_version()
|
||||
self.__arch = get_build_architecture()
|
||||
if self.__arch == "Intel":
|
||||
# x86
|
||||
if self.__version >= 7:
|
||||
self.__root = r"Software\Microsoft\VisualStudio"
|
||||
self.__macros = MacroExpander(self.__version)
|
||||
else:
|
||||
self.__root = r"Software\Microsoft\Devstudio"
|
||||
self.__product = "Visual Studio version %s" % self.__version
|
||||
else:
|
||||
# Win64. Assume this was built with the platform SDK
|
||||
self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
|
||||
|
||||
self.initialized = False
|
||||
|
||||
def initialize(self):
|
||||
self.__paths = []
|
||||
if ("DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and
|
||||
self.find_exe("cl.exe")):
|
||||
# Assume that the SDK set up everything alright; don't try to be
|
||||
# smarter
|
||||
self.cc = "cl.exe"
|
||||
self.linker = "link.exe"
|
||||
self.lib = "lib.exe"
|
||||
self.rc = "rc.exe"
|
||||
self.mc = "mc.exe"
|
||||
else:
|
||||
self.__paths = self.get_msvc_paths("path")
|
||||
|
||||
if len(self.__paths) == 0:
|
||||
raise PackagingPlatformError("Python was built with %s "
|
||||
"and extensions need to be built with the same "
|
||||
"version of the compiler, but it isn't installed." %
|
||||
self.__product)
|
||||
|
||||
self.cc = self.find_exe("cl.exe")
|
||||
self.linker = self.find_exe("link.exe")
|
||||
self.lib = self.find_exe("lib.exe")
|
||||
self.rc = self.find_exe("rc.exe") # resource compiler
|
||||
self.mc = self.find_exe("mc.exe") # message compiler
|
||||
self.set_path_env_var('lib')
|
||||
self.set_path_env_var('include')
|
||||
|
||||
# extend the MSVC path with the current path
|
||||
try:
|
||||
for p in os.environ['path'].split(';'):
|
||||
self.__paths.append(p)
|
||||
except KeyError:
|
||||
pass
|
||||
self.__paths = normalize_and_reduce_paths(self.__paths)
|
||||
os.environ['path'] = ';'.join(self.__paths)
|
||||
|
||||
self.preprocess_options = None
|
||||
if self.__arch == "Intel":
|
||||
self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GX',
|
||||
'/DNDEBUG']
|
||||
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
|
||||
'/Z7', '/D_DEBUG']
|
||||
else:
|
||||
# Win64
|
||||
self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GS-',
|
||||
'/DNDEBUG']
|
||||
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
|
||||
'/Z7', '/D_DEBUG']
|
||||
|
||||
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
|
||||
if self.__version >= 7:
|
||||
self.ldflags_shared_debug = [
|
||||
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
|
||||
]
|
||||
else:
|
||||
self.ldflags_shared_debug = [
|
||||
'/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
|
||||
]
|
||||
self.ldflags_static = [ '/nologo']
|
||||
|
||||
self.initialized = True
|
||||
|
||||
# -- Worker methods ------------------------------------------------
|
||||
|
||||
def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
|
||||
# Copied from ccompiler.py, extended to return .res as 'object'-file
|
||||
# for .rc input file
|
||||
if output_dir is None:
|
||||
output_dir = ''
|
||||
obj_names = []
|
||||
for src_name in source_filenames:
|
||||
base, ext = os.path.splitext(src_name)
|
||||
base = os.path.splitdrive(base)[1] # Chop off the drive
|
||||
base = base[os.path.isabs(base):] # If abs, chop off leading /
|
||||
if ext not in self.src_extensions:
|
||||
# Better to raise an exception instead of silently continuing
|
||||
# and later complain about sources and targets having
|
||||
# different lengths
|
||||
raise CompileError("Don't know how to compile %s" % src_name)
|
||||
if strip_dir:
|
||||
base = os.path.basename(base)
|
||||
if ext in self._rc_extensions:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.res_extension))
|
||||
elif ext in self._mc_extensions:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.res_extension))
|
||||
else:
|
||||
obj_names.append(os.path.join(output_dir,
|
||||
base + self.obj_extension))
|
||||
return obj_names
|
||||
|
||||
def compile(self, sources,
|
||||
output_dir=None, macros=None, include_dirs=None, debug=False,
|
||||
extra_preargs=None, extra_postargs=None, depends=None):
|
||||
|
||||
if not self.initialized:
|
||||
self.initialize()
|
||||
macros, objects, extra_postargs, pp_opts, build = \
|
||||
self._setup_compile(output_dir, macros, include_dirs, sources,
|
||||
depends, extra_postargs)
|
||||
|
||||
compile_opts = extra_preargs or []
|
||||
compile_opts.append('/c')
|
||||
if debug:
|
||||
compile_opts.extend(self.compile_options_debug)
|
||||
else:
|
||||
compile_opts.extend(self.compile_options)
|
||||
|
||||
for obj in objects:
|
||||
try:
|
||||
src, ext = build[obj]
|
||||
except KeyError:
|
||||
continue
|
||||
if debug:
|
||||
# pass the full pathname to MSVC in debug mode,
|
||||
# this allows the debugger to find the source file
|
||||
# without asking the user to browse for it
|
||||
src = os.path.abspath(src)
|
||||
|
||||
if ext in self._c_extensions:
|
||||
input_opt = "/Tc" + src
|
||||
elif ext in self._cpp_extensions:
|
||||
input_opt = "/Tp" + src
|
||||
elif ext in self._rc_extensions:
|
||||
# compile .RC to .RES file
|
||||
input_opt = src
|
||||
output_opt = "/fo" + obj
|
||||
try:
|
||||
self.spawn([self.rc] + pp_opts +
|
||||
[output_opt] + [input_opt])
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
continue
|
||||
elif ext in self._mc_extensions:
|
||||
|
||||
# Compile .MC to .RC file to .RES file.
|
||||
# * '-h dir' specifies the directory for the
|
||||
# generated include file
|
||||
# * '-r dir' specifies the target directory of the
|
||||
# generated RC file and the binary message resource
|
||||
# it includes
|
||||
#
|
||||
# For now (since there are no options to change this),
|
||||
# we use the source-directory for the include file and
|
||||
# the build directory for the RC file and message
|
||||
# resources. This works at least for win32all.
|
||||
|
||||
h_dir = os.path.dirname(src)
|
||||
rc_dir = os.path.dirname(obj)
|
||||
try:
|
||||
# first compile .MC to .RC and .H file
|
||||
self.spawn([self.mc] +
|
||||
['-h', h_dir, '-r', rc_dir] + [src])
|
||||
base, _ = os.path.splitext(os.path.basename(src))
|
||||
rc_file = os.path.join(rc_dir, base + '.rc')
|
||||
# then compile .RC to .RES file
|
||||
self.spawn([self.rc] +
|
||||
["/fo" + obj] + [rc_file])
|
||||
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
continue
|
||||
else:
|
||||
# how to handle this file?
|
||||
raise CompileError(
|
||||
"Don't know how to compile %s to %s" %
|
||||
(src, obj))
|
||||
|
||||
output_opt = "/Fo" + obj
|
||||
try:
|
||||
self.spawn([self.cc] + compile_opts + pp_opts +
|
||||
[input_opt, output_opt] +
|
||||
extra_postargs)
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
|
||||
return objects
|
||||
|
||||
def create_static_lib(self, objects, output_libname, output_dir=None,
|
||||
debug=False, target_lang=None):
|
||||
if not self.initialized:
|
||||
self.initialize()
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
output_filename = \
|
||||
self.library_filename(output_libname, output_dir=output_dir)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
lib_args = objects + ['/OUT:' + output_filename]
|
||||
if debug:
|
||||
pass # XXX what goes here?
|
||||
try:
|
||||
self.spawn([self.lib] + lib_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LibError(msg)
|
||||
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
def link(self, target_desc, objects, output_filename, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None):
|
||||
|
||||
if not self.initialized:
|
||||
self.initialize()
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
libraries, library_dirs, runtime_library_dirs = \
|
||||
self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
|
||||
|
||||
if runtime_library_dirs:
|
||||
self.warn("don't know what to do with 'runtime_library_dirs': %s"
|
||||
% (runtime_library_dirs,))
|
||||
|
||||
lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
|
||||
libraries)
|
||||
if output_dir is not None:
|
||||
output_filename = os.path.join(output_dir, output_filename)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
|
||||
if target_desc == CCompiler.EXECUTABLE:
|
||||
if debug:
|
||||
ldflags = self.ldflags_shared_debug[1:]
|
||||
else:
|
||||
ldflags = self.ldflags_shared[1:]
|
||||
else:
|
||||
if debug:
|
||||
ldflags = self.ldflags_shared_debug
|
||||
else:
|
||||
ldflags = self.ldflags_shared
|
||||
|
||||
export_opts = []
|
||||
for sym in (export_symbols or []):
|
||||
export_opts.append("/EXPORT:" + sym)
|
||||
|
||||
ld_args = (ldflags + lib_opts + export_opts +
|
||||
objects + ['/OUT:' + output_filename])
|
||||
|
||||
# The MSVC linker generates .lib and .exp files, which cannot be
|
||||
# suppressed by any linker switches. The .lib files may even be
|
||||
# needed! Make sure they are generated in the temporary build
|
||||
# directory. Since they have different names for debug and release
|
||||
# builds, they can go into the same directory.
|
||||
if export_symbols is not None:
|
||||
dll_name, dll_ext = os.path.splitext(
|
||||
os.path.basename(output_filename))
|
||||
implib_file = os.path.join(
|
||||
os.path.dirname(objects[0]),
|
||||
self.library_filename(dll_name))
|
||||
ld_args.append('/IMPLIB:' + implib_file)
|
||||
|
||||
if extra_preargs:
|
||||
ld_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
ld_args.extend(extra_postargs)
|
||||
|
||||
self.mkpath(os.path.dirname(output_filename))
|
||||
try:
|
||||
self.spawn([self.linker] + ld_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LinkError(msg)
|
||||
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
# These are all used by the 'gen_lib_options() function, in
|
||||
# ccompiler.py.
|
||||
|
||||
def library_dir_option(self, dir):
|
||||
return "/LIBPATH:" + dir
|
||||
|
||||
def runtime_library_dir_option(self, dir):
|
||||
raise PackagingPlatformError("don't know how to set runtime library search path for MSVC++")
|
||||
|
||||
def library_option(self, lib):
|
||||
return self.library_filename(lib)
|
||||
|
||||
def find_library_file(self, dirs, lib, debug=False):
|
||||
# Prefer a debugging library if found (and requested), but deal
|
||||
# with it if we don't have one.
|
||||
if debug:
|
||||
try_names = [lib + "_d", lib]
|
||||
else:
|
||||
try_names = [lib]
|
||||
for dir in dirs:
|
||||
for name in try_names:
|
||||
libfile = os.path.join(dir, self.library_filename(name))
|
||||
if os.path.exists(libfile):
|
||||
return libfile
|
||||
else:
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
||||
|
||||
# Helper methods for using the MSVC registry settings
|
||||
|
||||
def find_exe(self, exe):
|
||||
"""Return path to an MSVC executable program.
|
||||
|
||||
Tries to find the program in several places: first, one of the
|
||||
MSVC program search paths from the registry; next, the directories
|
||||
in the PATH environment variable. If any of those work, return an
|
||||
absolute path that is known to exist. If none of them work, just
|
||||
return the original program name, 'exe'.
|
||||
"""
|
||||
|
||||
for p in self.__paths:
|
||||
fn = os.path.join(os.path.abspath(p), exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
# didn't find it; try existing path
|
||||
for p in os.environ['Path'].split(';'):
|
||||
fn = os.path.join(os.path.abspath(p), exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
return exe
|
||||
|
||||
def get_msvc_paths(self, path, platform='x86'):
|
||||
"""Get a list of devstudio directories (include, lib or path).
|
||||
|
||||
Return a list of strings. The list will be empty if unable to
|
||||
access the registry or appropriate registry keys not found.
|
||||
"""
|
||||
|
||||
if not _can_read_reg:
|
||||
return []
|
||||
|
||||
path = path + " dirs"
|
||||
if self.__version >= 7:
|
||||
key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
|
||||
% (self.__root, self.__version))
|
||||
else:
|
||||
key = (r"%s\6.0\Build System\Components\Platforms"
|
||||
r"\Win32 (%s)\Directories" % (self.__root, platform))
|
||||
|
||||
for base in HKEYS:
|
||||
d = read_values(base, key)
|
||||
if d:
|
||||
if self.__version >= 7:
|
||||
return self.__macros.sub(d[path]).split(";")
|
||||
else:
|
||||
return d[path].split(";")
|
||||
# MSVC 6 seems to create the registry entries we need only when
|
||||
# the GUI is run.
|
||||
if self.__version == 6:
|
||||
for base in HKEYS:
|
||||
if read_values(base, r"%s\6.0" % self.__root) is not None:
|
||||
self.warn("It seems you have Visual Studio 6 installed, "
|
||||
"but the expected registry settings are not present.\n"
|
||||
"You must at least run the Visual Studio GUI once "
|
||||
"so that these entries are created.")
|
||||
break
|
||||
return []
|
||||
|
||||
def set_path_env_var(self, name):
|
||||
"""Set environment variable 'name' to an MSVC path type value.
|
||||
|
||||
This is equivalent to a SET command prior to execution of spawned
|
||||
commands.
|
||||
"""
|
||||
|
||||
if name == "lib":
|
||||
p = self.get_msvc_paths("library")
|
||||
else:
|
||||
p = self.get_msvc_paths(name)
|
||||
if p:
|
||||
os.environ[name] = ';'.join(p)
|
||||
|
||||
|
||||
if get_build_version() >= 8.0:
|
||||
logger.debug("importing new compiler from distutils.msvc9compiler")
|
||||
OldMSVCCompiler = MSVCCompiler
|
||||
from packaging.compiler.msvc9compiler import MSVCCompiler
|
||||
# get_build_architecture not really relevant now we support cross-compile
|
||||
from packaging.compiler.msvc9compiler import MacroExpander
|
339
Lib/packaging/compiler/unixccompiler.py
Normal file
339
Lib/packaging/compiler/unixccompiler.py
Normal file
|
@ -0,0 +1,339 @@
|
|||
"""CCompiler implementation for Unix compilers.
|
||||
|
||||
This module contains the UnixCCompiler class, a subclass of CCompiler
|
||||
that handles the "typical" Unix-style command-line C compiler:
|
||||
* macros defined with -Dname[=value]
|
||||
* macros undefined with -Uname
|
||||
* include search directories specified with -Idir
|
||||
* libraries specified with -lllib
|
||||
* library search directories specified with -Ldir
|
||||
* compile handled by 'cc' (or similar) executable with -c option:
|
||||
compiles .c to .o
|
||||
* link static library handled by 'ar' command (possibly with 'ranlib')
|
||||
* link shared library handled by 'cc -shared'
|
||||
"""
|
||||
|
||||
import os, sys
|
||||
|
||||
from packaging.util import newer
|
||||
from packaging.compiler.ccompiler import CCompiler
|
||||
from packaging.compiler import gen_preprocess_options, gen_lib_options
|
||||
from packaging.errors import (PackagingExecError, CompileError,
|
||||
LibError, LinkError)
|
||||
from packaging import logger
|
||||
import sysconfig
|
||||
|
||||
|
||||
# XXX Things not currently handled:
|
||||
# * optimization/debug/warning flags; we just use whatever's in Python's
|
||||
# Makefile and live with it. Is this adequate? If not, we might
|
||||
# have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
|
||||
# SunCCompiler, and I suspect down that road lies madness.
|
||||
# * even if we don't know a warning flag from an optimization flag,
|
||||
# we need some way for outsiders to feed preprocessor/compiler/linker
|
||||
# flags in to us -- eg. a sysadmin might want to mandate certain flags
|
||||
# via a site config file, or a user might want to set something for
|
||||
# compiling this module distribution only via the setup.py command
|
||||
# line, whatever. As long as these options come from something on the
|
||||
# current system, they can be as system-dependent as they like, and we
|
||||
# should just happily stuff them into the preprocessor/compiler/linker
|
||||
# options and carry on.
|
||||
|
||||
def _darwin_compiler_fixup(compiler_so, cc_args):
|
||||
"""
|
||||
This function will strip '-isysroot PATH' and '-arch ARCH' from the
|
||||
compile flags if the user has specified one them in extra_compile_flags.
|
||||
|
||||
This is needed because '-arch ARCH' adds another architecture to the
|
||||
build, without a way to remove an architecture. Furthermore GCC will
|
||||
barf if multiple '-isysroot' arguments are present.
|
||||
"""
|
||||
stripArch = stripSysroot = False
|
||||
|
||||
compiler_so = list(compiler_so)
|
||||
kernel_version = os.uname()[2] # 8.4.3
|
||||
major_version = int(kernel_version.split('.')[0])
|
||||
|
||||
if major_version < 8:
|
||||
# OSX before 10.4.0, these don't support -arch and -isysroot at
|
||||
# all.
|
||||
stripArch = stripSysroot = True
|
||||
else:
|
||||
stripArch = '-arch' in cc_args
|
||||
stripSysroot = '-isysroot' in cc_args
|
||||
|
||||
if stripArch or 'ARCHFLAGS' in os.environ:
|
||||
while True:
|
||||
try:
|
||||
index = compiler_so.index('-arch')
|
||||
# Strip this argument and the next one:
|
||||
del compiler_so[index:index+2]
|
||||
except ValueError:
|
||||
break
|
||||
|
||||
if 'ARCHFLAGS' in os.environ and not stripArch:
|
||||
# User specified different -arch flags in the environ,
|
||||
# see also the sysconfig
|
||||
compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
|
||||
|
||||
if stripSysroot:
|
||||
try:
|
||||
index = compiler_so.index('-isysroot')
|
||||
# Strip this argument and the next one:
|
||||
del compiler_so[index:index+2]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Check if the SDK that is used during compilation actually exists,
|
||||
# the universal build requires the usage of a universal SDK and not all
|
||||
# users have that installed by default.
|
||||
sysroot = None
|
||||
if '-isysroot' in cc_args:
|
||||
idx = cc_args.index('-isysroot')
|
||||
sysroot = cc_args[idx+1]
|
||||
elif '-isysroot' in compiler_so:
|
||||
idx = compiler_so.index('-isysroot')
|
||||
sysroot = compiler_so[idx+1]
|
||||
|
||||
if sysroot and not os.path.isdir(sysroot):
|
||||
logger.warning(
|
||||
"compiling with an SDK that doesn't seem to exist: %r;\n"
|
||||
"please check your Xcode installation", sysroot)
|
||||
|
||||
return compiler_so
|
||||
|
||||
class UnixCCompiler(CCompiler):
|
||||
|
||||
name = 'unix'
|
||||
description = 'Standard UNIX-style compiler'
|
||||
|
||||
# These are used by CCompiler in two places: the constructor sets
|
||||
# instance attributes 'preprocessor', 'compiler', etc. from them, and
|
||||
# 'set_executable()' allows any of these to be set. The defaults here
|
||||
# are pretty generic; they will probably have to be set by an outsider
|
||||
# (eg. using information discovered by the sysconfig about building
|
||||
# Python extensions).
|
||||
executables = {'preprocessor' : None,
|
||||
'compiler' : ["cc"],
|
||||
'compiler_so' : ["cc"],
|
||||
'compiler_cxx' : ["cc"],
|
||||
'linker_so' : ["cc", "-shared"],
|
||||
'linker_exe' : ["cc"],
|
||||
'archiver' : ["ar", "-cr"],
|
||||
'ranlib' : None,
|
||||
}
|
||||
|
||||
if sys.platform[:6] == "darwin":
|
||||
executables['ranlib'] = ["ranlib"]
|
||||
|
||||
# Needed for the filename generation methods provided by the base
|
||||
# class, CCompiler. NB. whoever instantiates/uses a particular
|
||||
# UnixCCompiler instance should set 'shared_lib_ext' -- we set a
|
||||
# reasonable common default here, but it's not necessarily used on all
|
||||
# Unices!
|
||||
|
||||
src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"]
|
||||
obj_extension = ".o"
|
||||
static_lib_extension = ".a"
|
||||
shared_lib_extension = ".so"
|
||||
dylib_lib_extension = ".dylib"
|
||||
static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
|
||||
if sys.platform == "cygwin":
|
||||
exe_extension = ".exe"
|
||||
|
||||
def preprocess(self, source,
|
||||
output_file=None, macros=None, include_dirs=None,
|
||||
extra_preargs=None, extra_postargs=None):
|
||||
ignore, macros, include_dirs = \
|
||||
self._fix_compile_args(None, macros, include_dirs)
|
||||
pp_opts = gen_preprocess_options(macros, include_dirs)
|
||||
pp_args = self.preprocessor + pp_opts
|
||||
if output_file:
|
||||
pp_args.extend(('-o', output_file))
|
||||
if extra_preargs:
|
||||
pp_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
pp_args.extend(extra_postargs)
|
||||
pp_args.append(source)
|
||||
|
||||
# We need to preprocess: either we're being forced to, or we're
|
||||
# generating output to stdout, or there's a target output file and
|
||||
# the source file is newer than the target (or the target doesn't
|
||||
# exist).
|
||||
if self.force or output_file is None or newer(source, output_file):
|
||||
if output_file:
|
||||
self.mkpath(os.path.dirname(output_file))
|
||||
try:
|
||||
self.spawn(pp_args)
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
|
||||
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
|
||||
compiler_so = self.compiler_so
|
||||
if sys.platform == 'darwin':
|
||||
compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs)
|
||||
try:
|
||||
self.spawn(compiler_so + cc_args + [src, '-o', obj] +
|
||||
extra_postargs)
|
||||
except PackagingExecError as msg:
|
||||
raise CompileError(msg)
|
||||
|
||||
def create_static_lib(self, objects, output_libname,
|
||||
output_dir=None, debug=False, target_lang=None):
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
|
||||
output_filename = \
|
||||
self.library_filename(output_libname, output_dir=output_dir)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
self.mkpath(os.path.dirname(output_filename))
|
||||
self.spawn(self.archiver +
|
||||
[output_filename] +
|
||||
objects + self.objects)
|
||||
|
||||
# Not many Unices required ranlib anymore -- SunOS 4.x is, I
|
||||
# think the only major Unix that does. Maybe we need some
|
||||
# platform intelligence here to skip ranlib if it's not
|
||||
# needed -- or maybe Python's configure script took care of
|
||||
# it for us, hence the check for leading colon.
|
||||
if self.ranlib:
|
||||
try:
|
||||
self.spawn(self.ranlib + [output_filename])
|
||||
except PackagingExecError as msg:
|
||||
raise LibError(msg)
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
def link(self, target_desc, objects,
|
||||
output_filename, output_dir=None, libraries=None,
|
||||
library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=False, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None):
|
||||
objects, output_dir = self._fix_object_args(objects, output_dir)
|
||||
libraries, library_dirs, runtime_library_dirs = \
|
||||
self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
|
||||
|
||||
lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
|
||||
libraries)
|
||||
if type(output_dir) not in (str, type(None)):
|
||||
raise TypeError("'output_dir' must be a string or None")
|
||||
if output_dir is not None:
|
||||
output_filename = os.path.join(output_dir, output_filename)
|
||||
|
||||
if self._need_link(objects, output_filename):
|
||||
ld_args = (objects + self.objects +
|
||||
lib_opts + ['-o', output_filename])
|
||||
if debug:
|
||||
ld_args[:0] = ['-g']
|
||||
if extra_preargs:
|
||||
ld_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
ld_args.extend(extra_postargs)
|
||||
self.mkpath(os.path.dirname(output_filename))
|
||||
try:
|
||||
if target_desc == CCompiler.EXECUTABLE:
|
||||
linker = self.linker_exe[:]
|
||||
else:
|
||||
linker = self.linker_so[:]
|
||||
if target_lang == "c++" and self.compiler_cxx:
|
||||
# skip over environment variable settings if /usr/bin/env
|
||||
# is used to set up the linker's environment.
|
||||
# This is needed on OSX. Note: this assumes that the
|
||||
# normal and C++ compiler have the same environment
|
||||
# settings.
|
||||
i = 0
|
||||
if os.path.basename(linker[0]) == "env":
|
||||
i = 1
|
||||
while '=' in linker[i]:
|
||||
i = i + 1
|
||||
|
||||
linker[i] = self.compiler_cxx[i]
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
linker = _darwin_compiler_fixup(linker, ld_args)
|
||||
|
||||
self.spawn(linker + ld_args)
|
||||
except PackagingExecError as msg:
|
||||
raise LinkError(msg)
|
||||
else:
|
||||
logger.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
# These are all used by the 'gen_lib_options() function, in
|
||||
# ccompiler.py.
|
||||
|
||||
def library_dir_option(self, dir):
|
||||
return "-L" + dir
|
||||
|
||||
def _is_gcc(self, compiler_name):
|
||||
return "gcc" in compiler_name or "g++" in compiler_name
|
||||
|
||||
def runtime_library_dir_option(self, dir):
|
||||
# XXX Hackish, at the very least. See Python bug #445902:
|
||||
# http://sourceforge.net/tracker/index.php
|
||||
# ?func=detail&aid=445902&group_id=5470&atid=105470
|
||||
# Linkers on different platforms need different options to
|
||||
# specify that directories need to be added to the list of
|
||||
# directories searched for dependencies when a dynamic library
|
||||
# is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to
|
||||
# be told to pass the -R option through to the linker, whereas
|
||||
# other compilers and gcc on other systems just know this.
|
||||
# Other compilers may need something slightly different. At
|
||||
# this time, there's no way to determine this information from
|
||||
# the configuration data stored in the Python installation, so
|
||||
# we use this hack.
|
||||
|
||||
compiler = os.path.basename(sysconfig.get_config_var("CC"))
|
||||
if sys.platform[:6] == "darwin":
|
||||
# MacOSX's linker doesn't understand the -R flag at all
|
||||
return "-L" + dir
|
||||
elif sys.platform[:5] == "hp-ux":
|
||||
if self._is_gcc(compiler):
|
||||
return ["-Wl,+s", "-L" + dir]
|
||||
return ["+s", "-L" + dir]
|
||||
elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5":
|
||||
return ["-rpath", dir]
|
||||
elif self._is_gcc(compiler):
|
||||
# gcc on non-GNU systems does not need -Wl, but can
|
||||
# use it anyway. Since distutils has always passed in
|
||||
# -Wl whenever gcc was used in the past it is probably
|
||||
# safest to keep doing so.
|
||||
if sysconfig.get_config_var("GNULD") == "yes":
|
||||
# GNU ld needs an extra option to get a RUNPATH
|
||||
# instead of just an RPATH.
|
||||
return "-Wl,--enable-new-dtags,-R" + dir
|
||||
else:
|
||||
return "-Wl,-R" + dir
|
||||
elif sys.platform[:3] == "aix":
|
||||
return "-blibpath:" + dir
|
||||
else:
|
||||
# No idea how --enable-new-dtags would be passed on to
|
||||
# ld if this system was using GNU ld. Don't know if a
|
||||
# system like this even exists.
|
||||
return "-R" + dir
|
||||
|
||||
def library_option(self, lib):
|
||||
return "-l" + lib
|
||||
|
||||
def find_library_file(self, dirs, lib, debug=False):
|
||||
shared_f = self.library_filename(lib, lib_type='shared')
|
||||
dylib_f = self.library_filename(lib, lib_type='dylib')
|
||||
static_f = self.library_filename(lib, lib_type='static')
|
||||
|
||||
for dir in dirs:
|
||||
shared = os.path.join(dir, shared_f)
|
||||
dylib = os.path.join(dir, dylib_f)
|
||||
static = os.path.join(dir, static_f)
|
||||
# We're second-guessing the linker here, with not much hard
|
||||
# data to go on: GCC seems to prefer the shared library, so I'm
|
||||
# assuming that *all* Unix C compilers do. And of course I'm
|
||||
# ignoring even GCC's "-static" option. So sue me.
|
||||
if os.path.exists(dylib):
|
||||
return dylib
|
||||
elif os.path.exists(shared):
|
||||
return shared
|
||||
elif os.path.exists(static):
|
||||
return static
|
||||
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
Loading…
Add table
Add a link
Reference in a new issue