You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
15 KiB

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