1 """distutils.bcppcompiler
3 Contains BorlandCCompiler, an implementation of the abstract CCompiler class
4 for the Borland C++ compiler.
7 # This implementation by Lyle Johnson, based on the original msvccompiler.py
8 # module and using the directions originally published by Gordon Williams.
10 # XXX looks like there's a LOT of overlap between these two classes:
11 # someone should sit down and factor out the common code as
12 # WindowsCCompiler! --GPW
14 # This module should be kept compatible with Python 2.1.
20 from distutils
.errors
import \
21 DistutilsExecError
, DistutilsPlatformError
, \
22 CompileError
, LibError
, LinkError
, UnknownFileError
23 from distutils
.ccompiler
import \
24 CCompiler
, gen_preprocess_options
, gen_lib_options
25 from distutils
.file_util
import write_file
26 from distutils
.dep_util
import newer
27 from distutils
import log
29 class BCPPCompiler(CCompiler
) :
30 """Concrete class that implements an interface to the Borland C/C++
31 compiler, as defined by the CCompiler abstract class.
34 compiler_type
= 'bcpp'
36 # Just set this so CCompiler's constructor doesn't barf. We currently
37 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
38 # as it really isn't necessary for this sort of single-compiler class.
39 # Would be nice to have a consistent interface with UnixCCompiler,
40 # though, so it's worth thinking about.
43 # Private class data (need to distinguish C from C++ source for compiler)
44 _c_extensions
= ['.c']
45 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
47 # Needed for the filename generation methods provided by the
48 # base class, CCompiler.
49 src_extensions
= _c_extensions
+ _cpp_extensions
50 obj_extension
= '.obj'
51 static_lib_extension
= '.lib'
52 shared_lib_extension
= '.dll'
53 static_lib_format
= shared_lib_format
= '%s%s'
54 exe_extension
= '.exe'
62 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
64 # These executables are assumed to all be in the path.
65 # Borland doesn't seem to use any special registry settings to
66 # indicate their installation locations.
69 self
.linker
= "ilink32.exe"
72 self
.preprocess_options
= None
73 self
.compile_options
= ['/tWM', '/O2', '/q', '/g0']
74 self
.compile_options_debug
= ['/tWM', '/Od', '/q', '/g0']
76 self
.ldflags_shared
= ['/Tpd', '/Gn', '/q', '/x']
77 self
.ldflags_shared_debug
= ['/Tpd', '/Gn', '/q', '/x']
78 self
.ldflags_static
= []
79 self
.ldflags_exe
= ['/Gn', '/q', '/x']
80 self
.ldflags_exe_debug
= ['/Gn', '/q', '/x','/r']
83 # -- Worker methods ------------------------------------------------
85 def compile(self
, sources
,
86 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
87 extra_preargs
=None, extra_postargs
=None, depends
=None):
89 macros
, objects
, extra_postargs
, pp_opts
, build
= \
90 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
91 depends
, extra_postargs
)
92 compile_opts
= extra_preargs
or []
93 compile_opts
.append ('-c')
95 compile_opts
.extend (self
.compile_options_debug
)
97 compile_opts
.extend (self
.compile_options
)
101 src
, ext
= build
[obj
]
104 # XXX why do the normpath here?
105 src
= os
.path
.normpath(src
)
106 obj
= os
.path
.normpath(obj
)
107 # XXX _setup_compile() did a mkpath() too but before the normpath.
108 # Is it possible to skip the normpath?
109 self
.mkpath(os
.path
.dirname(obj
))
112 # This is already a binary file -- skip it.
113 continue # the 'for' loop
115 # This needs to be compiled to a .res file -- do it now.
117 self
.spawn (["brcc32", "-fo", obj
, src
])
118 except DistutilsExecError
, msg
:
119 raise CompileError
, msg
120 continue # the 'for' loop
122 # The next two are both for the real compiler.
123 if ext
in self
._c
_extensions
:
125 elif ext
in self
._cpp
_extensions
:
128 # Unknown file type -- no extra options. The compiler
129 # will probably fail, but let it just in case this is a
130 # file the compiler recognizes even if we don't.
133 output_opt
= "-o" + obj
135 # Compiler command line syntax is: "bcc32 [options] file(s)".
136 # Note that the source file names must appear at the end of
139 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
140 [input_opt
, output_opt
] +
141 extra_postargs
+ [src
])
142 except DistutilsExecError
, msg
:
143 raise CompileError
, msg
150 def create_static_lib (self
,
157 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
159 self
.library_filename (output_libname
, output_dir
=output_dir
)
161 if self
._need
_link
(objects
, output_filename
):
162 lib_args
= [output_filename
, '/u'] + objects
164 pass # XXX what goes here?
166 self
.spawn ([self
.lib
] + lib_args
)
167 except DistutilsExecError
, msg
:
170 log
.debug("skipping %s (up-to-date)", output_filename
)
172 # create_static_lib ()
182 runtime_library_dirs
=None,
190 # XXX this ignores 'build_temp'! should follow the lead of
193 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
194 (libraries
, library_dirs
, runtime_library_dirs
) = \
195 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
197 if runtime_library_dirs
:
198 log
.warn("I don't know what to do with 'runtime_library_dirs': %s",
199 str(runtime_library_dirs
))
201 if output_dir
is not None:
202 output_filename
= os
.path
.join (output_dir
, output_filename
)
204 if self
._need
_link
(objects
, output_filename
):
206 # Figure out linker args based on type of target.
207 if target_desc
== CCompiler
.EXECUTABLE
:
208 startup_obj
= 'c0w32'
210 ld_args
= self
.ldflags_exe_debug
[:]
212 ld_args
= self
.ldflags_exe
[:]
214 startup_obj
= 'c0d32'
216 ld_args
= self
.ldflags_shared_debug
[:]
218 ld_args
= self
.ldflags_shared
[:]
221 # Create a temporary exports file for use by the linker
222 if export_symbols
is None:
225 head
, tail
= os
.path
.split (output_filename
)
226 modname
, ext
= os
.path
.splitext (tail
)
227 temp_dir
= os
.path
.dirname(objects
[0]) # preserve tree structure
228 def_file
= os
.path
.join (temp_dir
, '%s.def' % modname
)
229 contents
= ['EXPORTS']
230 for sym
in (export_symbols
or []):
231 contents
.append(' %s=_%s' % (sym
, sym
))
232 self
.execute(write_file
, (def_file
, contents
),
233 "writing %s" % def_file
)
235 # Borland C++ has problems with '/' in paths
236 objects2
= map(os
.path
.normpath
, objects
)
237 # split objects in .obj and .res files
238 # Borland C++ needs them at different positions in the command line
239 objects
= [startup_obj
]
241 for file in objects2
:
242 (base
, ext
) = os
.path
.splitext(os
.path
.normcase(file))
244 resources
.append(file)
249 for l
in library_dirs
:
250 ld_args
.append("/L%s" % os
.path
.normpath(l
))
251 ld_args
.append("/L.") # we sometimes use relative paths
253 # list of object files
254 ld_args
.extend(objects
)
256 # XXX the command-line syntax for Borland C++ is a bit wonky;
257 # certain filenames are jammed together in one big string, but
258 # comma-delimited. This doesn't mesh too well with the
259 # Unix-centric attitude (with a DOS/Windows quoting hack) of
260 # 'spawn()', so constructing the argument list is a bit
261 # awkward. Note that doing the obvious thing and jamming all
262 # the filenames and commas into one argument would be wrong,
263 # because 'spawn()' would quote any filenames with spaces in
264 # them. Arghghh!. Apparently it works fine as coded...
266 # name of dll/exe file
267 ld_args
.extend([',',output_filename
])
268 # no map file and start libraries
271 for lib
in libraries
:
272 # see if we find it and if there is a bcpp specific lib
274 libfile
= self
.find_library_file(library_dirs
, lib
, debug
)
277 # probably a BCPP internal library -- don't warn
279 # full name which prefers bcpp_xxx.lib over xxx.lib
280 ld_args
.append(libfile
)
282 # some default libraries
283 ld_args
.append ('import32')
284 ld_args
.append ('cw32mt')
286 # def file for export symbols
287 ld_args
.extend([',',def_file
])
290 ld_args
.extend(resources
)
294 ld_args
[:0] = extra_preargs
296 ld_args
.extend(extra_postargs
)
298 self
.mkpath (os
.path
.dirname (output_filename
))
300 self
.spawn ([self
.linker
] + ld_args
)
301 except DistutilsExecError
, msg
:
305 log
.debug("skipping %s (up-to-date)", output_filename
)
309 # -- Miscellaneous methods -----------------------------------------
312 def find_library_file (self
, dirs
, lib
, debug
=0):
313 # List of effective library names to try, in order of preference:
314 # xxx_bcpp.lib is better than xxx.lib
315 # and xxx_d.lib is better than xxx.lib if debug is set
317 # The "_bcpp" suffix is to handle a Python installation for people
318 # with multiple compilers (primarily Distutils hackers, I suspect
319 # ;-). The idea is they'd have one static library for each
320 # compiler they care about, since (almost?) every Windows compiler
321 # seems to have a different format for static libraries.
324 try_names
= (dlib
+ "_bcpp", lib
+ "_bcpp", dlib
, lib
)
326 try_names
= (lib
+ "_bcpp", lib
)
329 for name
in try_names
:
330 libfile
= os
.path
.join(dir, self
.library_filename(name
))
331 if os
.path
.exists(libfile
):
334 # Oops, didn't find it in *any* of 'dirs'
337 # overwrite the one from CCompiler to support rc and res-files
338 def object_filenames (self
,
342 if output_dir
is None: output_dir
= ''
344 for src_name
in source_filenames
:
345 # use normcase to make sure '.rc' is really '.rc' and not '.RC'
346 (base
, ext
) = os
.path
.splitext (os
.path
.normcase(src_name
))
347 if ext
not in (self
.src_extensions
+ ['.rc','.res']):
348 raise UnknownFileError
, \
349 "unknown file type '%s' (from '%s')" % \
352 base
= os
.path
.basename (base
)
354 # these can go unchanged
355 obj_names
.append (os
.path
.join (output_dir
, base
+ ext
))
357 # these need to be compiled to .res-files
358 obj_names
.append (os
.path
.join (output_dir
, base
+ '.res'))
360 obj_names
.append (os
.path
.join (output_dir
,
361 base
+ self
.obj_extension
))
364 # object_filenames ()
366 def preprocess (self
,
372 extra_postargs
=None):
374 (_
, macros
, include_dirs
) = \
375 self
._fix
_compile
_args
(None, macros
, include_dirs
)
376 pp_opts
= gen_preprocess_options(macros
, include_dirs
)
377 pp_args
= ['cpp32.exe'] + pp_opts
378 if output_file
is not None:
379 pp_args
.append('-o' + output_file
)
381 pp_args
[:0] = extra_preargs
383 pp_args
.extend(extra_postargs
)
384 pp_args
.append(source
)
386 # We need to preprocess: either we're being forced to, or the
387 # source file is newer than the target (or the target doesn't
389 if self
.force
or output_file
is None or newer(source
, output_file
):
391 self
.mkpath(os
.path
.dirname(output_file
))
394 except DistutilsExecError
, msg
:
396 raise CompileError
, msg