bcppcompiler.py 15 KB

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