bcppcompiler.py 14 KB

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