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
18 from distutils
.errors
import \
19 DistutilsExecError
, DistutilsPlatformError
, \
20 CompileError
, LibError
, LinkError
, UnknownFileError
21 from distutils
.ccompiler
import \
22 CCompiler
, gen_preprocess_options
, gen_lib_options
23 from distutils
.file_util
import write_file
24 from distutils
.dep_util
import newer
25 from distutils
import log
27 class BCPPCompiler(CCompiler
) :
28 """Concrete class that implements an interface to the Borland C/C++
29 compiler, as defined by the CCompiler abstract class.
32 compiler_type
= 'bcpp'
34 # Just set this so CCompiler's constructor doesn't barf. We currently
35 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
36 # as it really isn't necessary for this sort of single-compiler class.
37 # Would be nice to have a consistent interface with UnixCCompiler,
38 # though, so it's worth thinking about.
41 # Private class data (need to distinguish C from C++ source for compiler)
42 _c_extensions
= ['.c']
43 _cpp_extensions
= ['.cc', '.cpp', '.cxx']
45 # Needed for the filename generation methods provided by the
46 # base class, CCompiler.
47 src_extensions
= _c_extensions
+ _cpp_extensions
48 obj_extension
= '.obj'
49 static_lib_extension
= '.lib'
50 shared_lib_extension
= '.dll'
51 static_lib_format
= shared_lib_format
= '%s%s'
52 exe_extension
= '.exe'
60 CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
62 # These executables are assumed to all be in the path.
63 # Borland doesn't seem to use any special registry settings to
64 # indicate their installation locations.
67 self
.linker
= "ilink32.exe"
70 self
.preprocess_options
= None
71 self
.compile_options
= ['/tWM', '/O2', '/q', '/g0']
72 self
.compile_options_debug
= ['/tWM', '/Od', '/q', '/g0']
74 self
.ldflags_shared
= ['/Tpd', '/Gn', '/q', '/x']
75 self
.ldflags_shared_debug
= ['/Tpd', '/Gn', '/q', '/x']
76 self
.ldflags_static
= []
77 self
.ldflags_exe
= ['/Gn', '/q', '/x']
78 self
.ldflags_exe_debug
= ['/Gn', '/q', '/x','/r']
81 # -- Worker methods ------------------------------------------------
83 def compile(self
, sources
,
84 output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
85 extra_preargs
=None, extra_postargs
=None, depends
=None):
87 macros
, objects
, extra_postargs
, pp_opts
, build
= \
88 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
89 depends
, extra_postargs
)
90 compile_opts
= extra_preargs
or []
91 compile_opts
.append ('-c')
93 compile_opts
.extend (self
.compile_options_debug
)
95 compile_opts
.extend (self
.compile_options
)
102 # XXX why do the normpath here?
103 src
= os
.path
.normpath(src
)
104 obj
= os
.path
.normpath(obj
)
105 # XXX _setup_compile() did a mkpath() too but before the normpath.
106 # Is it possible to skip the normpath?
107 self
.mkpath(os
.path
.dirname(obj
))
110 # This is already a binary file -- skip it.
111 continue # the 'for' loop
113 # This needs to be compiled to a .res file -- do it now.
115 self
.spawn (["brcc32", "-fo", obj
, src
])
116 except DistutilsExecError
, msg
:
117 raise CompileError
, msg
118 continue # the 'for' loop
120 # The next two are both for the real compiler.
121 if ext
in self
._c
_extensions
:
123 elif ext
in self
._cpp
_extensions
:
126 # Unknown file type -- no extra options. The compiler
127 # will probably fail, but let it just in case this is a
128 # file the compiler recognizes even if we don't.
131 output_opt
= "-o" + obj
133 # Compiler command line syntax is: "bcc32 [options] file(s)".
134 # Note that the source file names must appear at the end of
137 self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
138 [input_opt
, output_opt
] +
139 extra_postargs
+ [src
])
140 except DistutilsExecError
, msg
:
141 raise CompileError
, msg
148 def create_static_lib (self
,
155 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
157 self
.library_filename (output_libname
, output_dir
=output_dir
)
159 if self
._need
_link
(objects
, output_filename
):
160 lib_args
= [output_filename
, '/u'] + objects
162 pass # XXX what goes here?
164 self
.spawn ([self
.lib
] + lib_args
)
165 except DistutilsExecError
, msg
:
168 log
.debug("skipping %s (up-to-date)", output_filename
)
170 # create_static_lib ()
180 runtime_library_dirs
=None,
188 # XXX this ignores 'build_temp'! should follow the lead of
191 (objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
192 (libraries
, library_dirs
, runtime_library_dirs
) = \
193 self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
195 if runtime_library_dirs
:
196 log
.warn("I don't know what to do with 'runtime_library_dirs': %s",
197 str(runtime_library_dirs
))
199 if output_dir
is not None:
200 output_filename
= os
.path
.join (output_dir
, output_filename
)
202 if self
._need
_link
(objects
, output_filename
):
204 # Figure out linker args based on type of target.
205 if target_desc
== CCompiler
.EXECUTABLE
:
206 startup_obj
= 'c0w32'
208 ld_args
= self
.ldflags_exe_debug
[:]
210 ld_args
= self
.ldflags_exe
[:]
212 startup_obj
= 'c0d32'
214 ld_args
= self
.ldflags_shared_debug
[:]
216 ld_args
= self
.ldflags_shared
[:]
219 # Create a temporary exports file for use by the linker
220 if export_symbols
is None:
223 head
, tail
= os
.path
.split (output_filename
)
224 modname
, ext
= os
.path
.splitext (tail
)
225 temp_dir
= os
.path
.dirname(objects
[0]) # preserve tree structure
226 def_file
= os
.path
.join (temp_dir
, '%s.def' % modname
)
227 contents
= ['EXPORTS']
228 for sym
in (export_symbols
or []):
229 contents
.append(' %s=_%s' % (sym
, sym
))
230 self
.execute(write_file
, (def_file
, contents
),
231 "writing %s" % def_file
)
233 # Borland C++ has problems with '/' in paths
234 objects2
= map(os
.path
.normpath
, objects
)
235 # split objects in .obj and .res files
236 # Borland C++ needs them at different positions in the command line
237 objects
= [startup_obj
]
239 for file in objects2
:
240 (base
, ext
) = os
.path
.splitext(os
.path
.normcase(file))
242 resources
.append(file)
247 for l
in library_dirs
:
248 ld_args
.append("/L%s" % os
.path
.normpath(l
))
249 ld_args
.append("/L.") # we sometimes use relative paths
251 # list of object files
252 ld_args
.extend(objects
)
254 # XXX the command-line syntax for Borland C++ is a bit wonky;
255 # certain filenames are jammed together in one big string, but
256 # comma-delimited. This doesn't mesh too well with the
257 # Unix-centric attitude (with a DOS/Windows quoting hack) of
258 # 'spawn()', so constructing the argument list is a bit
259 # awkward. Note that doing the obvious thing and jamming all
260 # the filenames and commas into one argument would be wrong,
261 # because 'spawn()' would quote any filenames with spaces in
262 # them. Arghghh!. Apparently it works fine as coded...
264 # name of dll/exe file
265 ld_args
.extend([',',output_filename
])
266 # no map file and start libraries
269 for lib
in libraries
:
270 # see if we find it and if there is a bcpp specific lib
272 libfile
= self
.find_library_file(library_dirs
, lib
, debug
)
275 # probably a BCPP internal library -- don't warn
277 # full name which prefers bcpp_xxx.lib over xxx.lib
278 ld_args
.append(libfile
)
280 # some default libraries
281 ld_args
.append ('import32')
282 ld_args
.append ('cw32mt')
284 # def file for export symbols
285 ld_args
.extend([',',def_file
])
288 ld_args
.extend(resources
)
292 ld_args
[:0] = extra_preargs
294 ld_args
.extend(extra_postargs
)
296 self
.mkpath (os
.path
.dirname (output_filename
))
298 self
.spawn ([self
.linker
] + ld_args
)
299 except DistutilsExecError
, msg
:
303 log
.debug("skipping %s (up-to-date)", output_filename
)
307 # -- Miscellaneous methods -----------------------------------------
310 def find_library_file (self
, dirs
, lib
, debug
=0):
311 # List of effective library names to try, in order of preference:
312 # xxx_bcpp.lib is better than xxx.lib
313 # and xxx_d.lib is better than xxx.lib if debug is set
315 # The "_bcpp" suffix is to handle a Python installation for people
316 # with multiple compilers (primarily Distutils hackers, I suspect
317 # ;-). The idea is they'd have one static library for each
318 # compiler they care about, since (almost?) every Windows compiler
319 # seems to have a different format for static libraries.
322 try_names
= (dlib
+ "_bcpp", lib
+ "_bcpp", dlib
, lib
)
324 try_names
= (lib
+ "_bcpp", lib
)
327 for name
in try_names
:
328 libfile
= os
.path
.join(dir, self
.library_filename(name
))
329 if os
.path
.exists(libfile
):
332 # Oops, didn't find it in *any* of 'dirs'
335 # overwrite the one from CCompiler to support rc and res-files
336 def object_filenames (self
,
340 if output_dir
is None: output_dir
= ''
342 for src_name
in source_filenames
:
343 # use normcase to make sure '.rc' is really '.rc' and not '.RC'
344 (base
, ext
) = os
.path
.splitext (os
.path
.normcase(src_name
))
345 if ext
not in (self
.src_extensions
+ ['.rc','.res']):
346 raise UnknownFileError
, \
347 "unknown file type '%s' (from '%s')" % \
350 base
= os
.path
.basename (base
)
352 # these can go unchanged
353 obj_names
.append (os
.path
.join (output_dir
, base
+ ext
))
355 # these need to be compiled to .res-files
356 obj_names
.append (os
.path
.join (output_dir
, base
+ '.res'))
358 obj_names
.append (os
.path
.join (output_dir
,
359 base
+ self
.obj_extension
))
362 # object_filenames ()
364 def preprocess (self
,
370 extra_postargs
=None):
372 (_
, macros
, include_dirs
) = \
373 self
._fix
_compile
_args
(None, macros
, include_dirs
)
374 pp_opts
= gen_preprocess_options(macros
, include_dirs
)
375 pp_args
= ['cpp32.exe'] + pp_opts
376 if output_file
is not None:
377 pp_args
.append('-o' + output_file
)
379 pp_args
[:0] = extra_preargs
381 pp_args
.extend(extra_postargs
)
382 pp_args
.append(source
)
384 # We need to preprocess: either we're being forced to, or the
385 # source file is newer than the target (or the target doesn't
387 if self
.force
or output_file
is None or newer(source
, output_file
):
389 self
.mkpath(os
.path
.dirname(output_file
))
392 except DistutilsExecError
, msg
:
394 raise CompileError
, msg