"""distutils.bcppcompiler
Contains BorlandCCompiler, an implementation of the abstract CCompiler class
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
# This module should be kept compatible with Python 2.1.
__revision__
= "$Id: bcppcompiler.py,v 1.18 2004/11/10 22:23:13 loewis Exp $"
from distutils
.errors
import \
DistutilsExecError
, DistutilsPlatformError
, \
CompileError
, LibError
, LinkError
, UnknownFileError
from distutils
.ccompiler
import \
CCompiler
, gen_preprocess_options
, gen_lib_options
from distutils
.file_util
import write_file
from distutils
.dep_util
import newer
from distutils
import log
class BCPPCompiler(CCompiler
) :
"""Concrete class that implements an interface to the Borland C/C++
compiler, as defined by the CCompiler abstract class.
# 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.
# Private class data (need to distinguish C from C++ source for compiler)
_cpp_extensions
= ['.cc', '.cpp', '.cxx']
# Needed for the filename generation methods provided by the
src_extensions
= _c_extensions
+ _cpp_extensions
static_lib_extension
= '.lib'
shared_lib_extension
= '.dll'
static_lib_format
= shared_lib_format
= '%s%s'
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
.linker
= "ilink32.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_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
=0,
extra_preargs
=None, extra_postargs
=None, depends
=None):
macros
, objects
, extra_postargs
, pp_opts
, build
= \
self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
compile_opts
= extra_preargs
or []
compile_opts
.append ('-c')
compile_opts
.extend (self
.compile_options_debug
)
compile_opts
.extend (self
.compile_options
)
# 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
))
# This is already a binary file -- skip it.
continue # the 'for' loop
# This needs to be compiled to a .res file -- do it now.
self
.spawn (["brcc32", "-fo", obj
, src
])
except DistutilsExecError
, msg
:
continue # the 'for' loop
# The next two are both for the real compiler.
if ext
in self
._c
_extensions
:
elif ext
in self
._cpp
_extensions
:
# 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.
# Compiler command line syntax is: "bcc32 [options] file(s)".
# Note that the source file names must appear at the end of
self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
[input_opt
, output_opt
] +
except DistutilsExecError
, msg
:
def create_static_lib (self
,
(objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
self
.library_filename (output_libname
, output_dir
=output_dir
)
if self
._need
_link
(objects
, output_filename
):
lib_args
= [output_filename
, '/u'] + objects
pass # XXX what goes here?
self
.spawn ([self
.lib
] + lib_args
)
except DistutilsExecError
, msg
:
log
.debug("skipping %s (up-to-date)", output_filename
)
runtime_library_dirs
=None,
# XXX this ignores 'build_temp'! should follow the lead of
(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
)
log
.warn("I don't know what to do with 'runtime_library_dirs': %s",
str(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
:
ld_args
= self
.ldflags_exe_debug
[:]
ld_args
= self
.ldflags_exe
[:]
ld_args
= self
.ldflags_shared_debug
[:]
ld_args
= self
.ldflags_shared
[:]
# Create a temporary exports file for use by the linker
if export_symbols
is None:
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
)
for sym
in (export_symbols
or []):
contents
.append(' %s=_%s' % (sym
, sym
))
self
.execute(write_file
, (def_file
, contents
),
# Borland C++ has problems with '/' in paths
objects2
= map(os
.path
.normpath
, objects
)
# split objects in .obj and .res files
# Borland C++ needs them at different positions in the command line
(base
, ext
) = os
.path
.splitext(os
.path
.normcase(file))
ld_args
.append("/L%s" % os
.path
.normpath(l
))
ld_args
.append("/L.") # we sometimes use relative paths
# 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...
ld_args
.extend([',',output_filename
])
# no map file and start libraries
# see if we find it and if there is a bcpp specific lib
libfile
= self
.find_library_file(library_dirs
, lib
, debug
)
# probably a BCPP internal library -- don't warn
# full name which prefers bcpp_xxx.lib over xxx.lib
ld_args
.append ('import32')
ld_args
.append ('cw32mt')
# def file for export symbols
ld_args
.extend([',',def_file
])
ld_args
.extend(resources
)
ld_args
[:0] = extra_preargs
ld_args
.extend(extra_postargs
)
self
.mkpath (os
.path
.dirname (output_filename
))
self
.spawn ([self
.linker
] + ld_args
)
except DistutilsExecError
, msg
:
log
.debug("skipping %s (up-to-date)", output_filename
)
# -- Miscellaneous methods -----------------------------------------
def find_library_file (self
, dirs
, lib
, debug
=0):
# 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 Distutils 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.
try_names
= (dlib
+ "_bcpp", lib
+ "_bcpp", dlib
, lib
)
try_names
= (lib
+ "_bcpp", lib
)
libfile
= os
.path
.join(dir, self
.library_filename(name
))
if os
.path
.exists(libfile
):
# Oops, didn't find it in *any* of 'dirs'
# overwrite the one from CCompiler to support rc and res-files
def object_filenames (self
,
if output_dir
is None: output_dir
= ''
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')" % \
base
= os
.path
.basename (base
)
obj_names
.append (os
.path
.join (output_dir
, base
+ ext
))
# these need to be compiled to .res-files
obj_names
.append (os
.path
.join (output_dir
, base
+ '.res'))
obj_names
.append (os
.path
.join (output_dir
,
base
+ self
.obj_extension
))
(_
, 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
)
pp_args
[:0] = extra_preargs
pp_args
.extend(extra_postargs
)
# We need to preprocess: either we're being forced to, or the
# source file is newer than the target (or the target doesn't
if self
.force
or output_file
is None or newer(source
, output_file
):
self
.mkpath(os
.path
.dirname(output_file
))
except DistutilsExecError
, msg
: