1 """distutils.bcppcompiler 2 3 Contains BorlandCCompiler, an implementation of the abstract CCompiler class 4 for the Borland C++ compiler. 5 """ 6 7 # This implementation by Lyle Johnson, based on the original msvccompiler.py 8 # module and using the directions originally published by Gordon Williams. 9 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 13 14 __revision__ = "$Id$" 15 16 import os 17 18 from distutils.errors import (DistutilsExecError, CompileError, LibError, 19 LinkError, UnknownFileError) 20 from distutils.ccompiler import CCompiler, gen_preprocess_options 21 from distutils.file_util import write_file 22 from distutils.dep_util import newer 23 from distutils import log 24 25 class BCPPCompiler(CCompiler) : 26 """Concrete class that implements an interface to the Borland C/C++ 27 compiler, as defined by the CCompiler abstract class. 28 """ 29 30 compiler_type = 'bcpp' 31 32 # Just set this so CCompiler's constructor doesn't barf. We currently 33 # don't use the 'set_executables()' bureaucracy provided by CCompiler, 34 # as it really isn't necessary for this sort of single-compiler class. 35 # Would be nice to have a consistent interface with UnixCCompiler, 36 # though, so it's worth thinking about. 37 executables = {} 38 39 # Private class data (need to distinguish C from C++ source for compiler) 40 _c_extensions = ['.c'] 41 _cpp_extensions = ['.cc', '.cpp', '.cxx'] 42 43 # Needed for the filename generation methods provided by the 44 # base class, CCompiler. 45 src_extensions = _c_extensions + _cpp_extensions 46 obj_extension = '.obj' 47 static_lib_extension = '.lib' 48 shared_lib_extension = '.dll' 49 static_lib_format = shared_lib_format = '%s%s' 50 exe_extension = '.exe' 51 52 53 def __init__ (self, 54 verbose=0, 55 dry_run=0, 56 force=0): 57 58 CCompiler.__init__ (self, verbose, dry_run, force) 59 60 # These executables are assumed to all be in the path. 61 # Borland doesn't seem to use any special registry settings to 62 # indicate their installation locations. 63 64 self.cc = "bcc32.exe" 65 self.linker = "ilink32.exe" 66 self.lib = "tlib.exe" 67 68 self.preprocess_options = None 69 self.compile_options = ['/tWM', '/O2', '/q', '/g0'] 70 self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] 71 72 self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] 73 self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] 74 self.ldflags_static = [] 75 self.ldflags_exe = ['/Gn', '/q', '/x'] 76 self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] 77 78 79 # -- Worker methods ------------------------------------------------ 80 81 def compile(self, sources, 82 output_dir=None, macros=None, include_dirs=None, debug=0, 83 extra_preargs=None, extra_postargs=None, depends=None): 84 85 macros, objects, extra_postargs, pp_opts, build = \ 86 self._setup_compile(output_dir, macros, include_dirs, sources, 87 depends, extra_postargs) 88 compile_opts = extra_preargs or [] 89 compile_opts.append ('-c') 90 if debug: 91 compile_opts.extend (self.compile_options_debug) 92 else: 93 compile_opts.extend (self.compile_options) 94 95 for obj in objects: 96 try: 97 src, ext = build[obj] 98 except KeyError: 99 continue 100 # XXX why do the normpath here? 101 src = os.path.normpath(src) 102 obj = os.path.normpath(obj) 103 # XXX _setup_compile() did a mkpath() too but before the normpath. 104 # Is it possible to skip the normpath? 105 self.mkpath(os.path.dirname(obj)) 106 107 if ext == '.res': 108 # This is already a binary file -- skip it. 109 continue # the 'for' loop 110 if ext == '.rc': 111 # This needs to be compiled to a .res file -- do it now. 112 try: 113 self.spawn (["brcc32", "-fo", obj, src]) 114 except DistutilsExecError, msg: 115 raise CompileError, msg 116 continue # the 'for' loop 117 118 # The next two are both for the real compiler. 119 if ext in self._c_extensions: 120 input_opt = "" 121 elif ext in self._cpp_extensions: 122 input_opt = "-P" 123 else: 124 # Unknown file type -- no extra options. The compiler 125 # will probably fail, but let it just in case this is a 126 # file the compiler recognizes even if we don't. 127 input_opt = "" 128 129 output_opt = "-o" + obj 130 131 # Compiler command line syntax is: "bcc32 [options] file(s)". 132 # Note that the source file names must appear at the end of 133 # the command line. 134 try: 135 self.spawn ([self.cc] + compile_opts + pp_opts + 136 [input_opt, output_opt] + 137 extra_postargs + [src]) 138 except DistutilsExecError, msg: 139 raise CompileError, msg 140 141 return objects 142 143 # compile () 144 145 146 def create_static_lib (self, 147 objects, 148 output_libname, 149 output_dir=None, 150 debug=0, 151 target_lang=None): 152 153 (objects, output_dir) = self._fix_object_args (objects, output_dir) 154 output_filename = \ 155 self.library_filename (output_libname, output_dir=output_dir) 156 157 if self._need_link (objects, output_filename): 158 lib_args = [output_filename, '/u'] + objects 159 if debug: 160 pass # XXX what goes here? 161 try: 162 self.spawn ([self.lib] + lib_args) 163 except DistutilsExecError, msg: 164 raise LibError, msg 165 else: 166 log.debug("skipping %s (up-to-date)", output_filename) 167 168 # create_static_lib () 169 170 171 def link (self, 172 target_desc, 173 objects, 174 output_filename, 175 output_dir=None, 176 libraries=None, 177 library_dirs=None, 178 runtime_library_dirs=None, 179 export_symbols=None, 180 debug=0, 181 extra_preargs=None, 182 extra_postargs=None, 183 build_temp=None, 184 target_lang=None): 185 186 # XXX this ignores 'build_temp'! should follow the lead of 187 # msvccompiler.py 188 189 (objects, output_dir) = self._fix_object_args (objects, output_dir) 190 (libraries, library_dirs, runtime_library_dirs) = \ 191 self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) 192 193 if runtime_library_dirs: 194 log.warn("I don't know what to do with 'runtime_library_dirs': %s", 195 str(runtime_library_dirs)) 196 197 if output_dir is not None: 198 output_filename = os.path.join (output_dir, output_filename) 199 200 if self._need_link (objects, output_filename): 201 202 # Figure out linker args based on type of target. 203 if target_desc == CCompiler.EXECUTABLE: 204 startup_obj = 'c0w32' 205 if debug: 206 ld_args = self.ldflags_exe_debug[:] 207 else: 208 ld_args = self.ldflags_exe[:] 209 else: 210 startup_obj = 'c0d32' 211 if debug: 212 ld_args = self.ldflags_shared_debug[:] 213 else: 214 ld_args = self.ldflags_shared[:] 215 216 217 # Create a temporary exports file for use by the linker 218 if export_symbols is None: 219 def_file = '' 220 else: 221 head, tail = os.path.split (output_filename) 222 modname, ext = os.path.splitext (tail) 223 temp_dir = os.path.dirname(objects[0]) # preserve tree structure 224 def_file = os.path.join (temp_dir, '%s.def' % modname) 225 contents = ['EXPORTS'] 226 for sym in (export_symbols or []): 227 contents.append(' %s=_%s' % (sym, sym)) 228 self.execute(write_file, (def_file, contents), 229 "writing %s" % def_file) 230 231 # Borland C++ has problems with '/' in paths 232 objects2 = map(os.path.normpath, objects) 233 # split objects in .obj and .res files 234 # Borland C++ needs them at different positions in the command line 235 objects = [startup_obj] 236 resources = [] 237 for file in objects2: 238 (base, ext) = os.path.splitext(os.path.normcase(file)) 239 if ext == '.res': 240 resources.append(file) 241 else: 242 objects.append(file) 243 244 245 for l in library_dirs: 246 ld_args.append("/L%s" % os.path.normpath(l)) 247 ld_args.append("/L.") # we sometimes use relative paths 248 249 # list of object files 250 ld_args.extend(objects) 251 252 # XXX the command-line syntax for Borland C++ is a bit wonky; 253 # certain filenames are jammed together in one big string, but 254 # comma-delimited. This doesn't mesh too well with the 255 # Unix-centric attitude (with a DOS/Windows quoting hack) of 256 # 'spawn()', so constructing the argument list is a bit 257 # awkward. Note that doing the obvious thing and jamming all 258 # the filenames and commas into one argument would be wrong, 259 # because 'spawn()' would quote any filenames with spaces in 260 # them. Arghghh!. Apparently it works fine as coded... 261 262 # name of dll/exe file 263 ld_args.extend([',',output_filename]) 264 # no map file and start libraries 265 ld_args.append(',,') 266 267 for lib in libraries: 268 # see if we find it and if there is a bcpp specific lib 269 # (xxx_bcpp.lib) 270 libfile = self.find_library_file(library_dirs, lib, debug) 271 if libfile is None: 272 ld_args.append(lib) 273 # probably a BCPP internal library -- don't warn 274 else: 275 # full name which prefers bcpp_xxx.lib over xxx.lib 276 ld_args.append(libfile) 277 278 # some default libraries 279 ld_args.append ('import32') 280 ld_args.append ('cw32mt') 281 282 # def file for export symbols 283 ld_args.extend([',',def_file]) 284 # add resource files 285 ld_args.append(',') 286 ld_args.extend(resources) 287 288 289 if extra_preargs: 290 ld_args[:0] = extra_preargs 291 if extra_postargs: 292 ld_args.extend(extra_postargs) 293 294 self.mkpath (os.path.dirname (output_filename)) 295 try: 296 self.spawn ([self.linker] + ld_args) 297 except DistutilsExecError, msg: 298 raise LinkError, msg 299 300 else: 301 log.debug("skipping %s (up-to-date)", output_filename) 302 303 # link () 304 305 # -- Miscellaneous methods ----------------------------------------- 306 307 308 def find_library_file (self, dirs, lib, debug=0): 309 # List of effective library names to try, in order of preference: 310 # xxx_bcpp.lib is better than xxx.lib 311 # and xxx_d.lib is better than xxx.lib if debug is set 312 # 313 # The "_bcpp" suffix is to handle a Python installation for people 314 # with multiple compilers (primarily Distutils hackers, I suspect 315 # ;-). The idea is they'd have one static library for each 316 # compiler they care about, since (almost?) every Windows compiler 317 # seems to have a different format for static libraries. 318 if debug: 319 dlib = (lib + "_d") 320 try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) 321 else: 322 try_names = (lib + "_bcpp", lib) 323 324 for dir in dirs: 325 for name in try_names: 326 libfile = os.path.join(dir, self.library_filename(name)) 327 if os.path.exists(libfile): 328 return libfile 329 else: 330 # Oops, didn't find it in *any* of 'dirs' 331 return None 332 333 # overwrite the one from CCompiler to support rc and res-files 334 def object_filenames (self, 335 source_filenames, 336 strip_dir=0, 337 output_dir=''): 338 if output_dir is None: output_dir = '' 339 obj_names = [] 340 for src_name in source_filenames: 341 # use normcase to make sure '.rc' is really '.rc' and not '.RC' 342 (base, ext) = os.path.splitext (os.path.normcase(src_name)) 343 if ext not in (self.src_extensions + ['.rc','.res']): 344 raise UnknownFileError, \ 345 "unknown file type '%s' (from '%s')" % \ 346 (ext, src_name) 347 if strip_dir: 348 base = os.path.basename (base) 349 if ext == '.res': 350 # these can go unchanged 351 obj_names.append (os.path.join (output_dir, base + ext)) 352 elif ext == '.rc': 353 # these need to be compiled to .res-files 354 obj_names.append (os.path.join (output_dir, base + '.res')) 355 else: 356 obj_names.append (os.path.join (output_dir, 357 base + self.obj_extension)) 358 return obj_names 359 360 # object_filenames () 361 362 def preprocess (self, 363 source, 364 output_file=None, 365 macros=None, 366 include_dirs=None, 367 extra_preargs=None, 368 extra_postargs=None): 369 370 (_, macros, include_dirs) = \ 371 self._fix_compile_args(None, macros, include_dirs) 372 pp_opts = gen_preprocess_options(macros, include_dirs) 373 pp_args = ['cpp32.exe'] + pp_opts 374 if output_file is not None: 375 pp_args.append('-o' + output_file) 376 if extra_preargs: 377 pp_args[:0] = extra_preargs 378 if extra_postargs: 379 pp_args.extend(extra_postargs) 380 pp_args.append(source) 381 382 # We need to preprocess: either we're being forced to, or the 383 # source file is newer than the target (or the target doesn't 384 # exist). 385 if self.force or output_file is None or newer(source, output_file): 386 if output_file: 387 self.mkpath(os.path.dirname(output_file)) 388 try: 389 self.spawn(pp_args) 390 except DistutilsExecError, msg: 391 print msg 392 raise CompileError, msg 393 394 # preprocess() 395