Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/bcppcompiler.py
4799 views
"""distutils.bcppcompiler12Contains BorlandCCompiler, an implementation of the abstract CCompiler class3for the Borland C++ compiler.4"""56# This implementation by Lyle Johnson, based on the original msvccompiler.py7# module and using the directions originally published by Gordon Williams.89# XXX looks like there's a LOT of overlap between these two classes:10# someone should sit down and factor out the common code as11# WindowsCCompiler! --GPW121314import os15from distutils.errors import \16DistutilsExecError, \17CompileError, LibError, LinkError, UnknownFileError18from distutils.ccompiler import \19CCompiler, gen_preprocess_options20from distutils.file_util import write_file21from distutils.dep_util import newer22from distutils import log2324class BCPPCompiler(CCompiler) :25"""Concrete class that implements an interface to the Borland C/C++26compiler, as defined by the CCompiler abstract class.27"""2829compiler_type = 'bcpp'3031# Just set this so CCompiler's constructor doesn't barf. We currently32# don't use the 'set_executables()' bureaucracy provided by CCompiler,33# as it really isn't necessary for this sort of single-compiler class.34# Would be nice to have a consistent interface with UnixCCompiler,35# though, so it's worth thinking about.36executables = {}3738# Private class data (need to distinguish C from C++ source for compiler)39_c_extensions = ['.c']40_cpp_extensions = ['.cc', '.cpp', '.cxx']4142# Needed for the filename generation methods provided by the43# base class, CCompiler.44src_extensions = _c_extensions + _cpp_extensions45obj_extension = '.obj'46static_lib_extension = '.lib'47shared_lib_extension = '.dll'48static_lib_format = shared_lib_format = '%s%s'49exe_extension = '.exe'505152def __init__ (self,53verbose=0,54dry_run=0,55force=0):5657super().__init__(verbose, dry_run, force)5859# These executables are assumed to all be in the path.60# Borland doesn't seem to use any special registry settings to61# indicate their installation locations.6263self.cc = "bcc32.exe"64self.linker = "ilink32.exe"65self.lib = "tlib.exe"6667self.preprocess_options = None68self.compile_options = ['/tWM', '/O2', '/q', '/g0']69self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']7071self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']72self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']73self.ldflags_static = []74self.ldflags_exe = ['/Gn', '/q', '/x']75self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r']767778# -- Worker methods ------------------------------------------------7980def compile(self, sources,81output_dir=None, macros=None, include_dirs=None, debug=0,82extra_preargs=None, extra_postargs=None, depends=None):8384macros, objects, extra_postargs, pp_opts, build = \85self._setup_compile(output_dir, macros, include_dirs, sources,86depends, extra_postargs)87compile_opts = extra_preargs or []88compile_opts.append ('-c')89if debug:90compile_opts.extend (self.compile_options_debug)91else:92compile_opts.extend (self.compile_options)9394for obj in objects:95try:96src, ext = build[obj]97except KeyError:98continue99# XXX why do the normpath here?100src = os.path.normpath(src)101obj = os.path.normpath(obj)102# XXX _setup_compile() did a mkpath() too but before the normpath.103# Is it possible to skip the normpath?104self.mkpath(os.path.dirname(obj))105106if ext == '.res':107# This is already a binary file -- skip it.108continue # the 'for' loop109if ext == '.rc':110# This needs to be compiled to a .res file -- do it now.111try:112self.spawn (["brcc32", "-fo", obj, src])113except DistutilsExecError as msg:114raise CompileError(msg)115continue # the 'for' loop116117# The next two are both for the real compiler.118if ext in self._c_extensions:119input_opt = ""120elif ext in self._cpp_extensions:121input_opt = "-P"122else:123# Unknown file type -- no extra options. The compiler124# will probably fail, but let it just in case this is a125# file the compiler recognizes even if we don't.126input_opt = ""127128output_opt = "-o" + obj129130# Compiler command line syntax is: "bcc32 [options] file(s)".131# Note that the source file names must appear at the end of132# the command line.133try:134self.spawn ([self.cc] + compile_opts + pp_opts +135[input_opt, output_opt] +136extra_postargs + [src])137except DistutilsExecError as msg:138raise CompileError(msg)139140return objects141142# compile ()143144145def create_static_lib (self,146objects,147output_libname,148output_dir=None,149debug=0,150target_lang=None):151152(objects, output_dir) = self._fix_object_args (objects, output_dir)153output_filename = \154self.library_filename (output_libname, output_dir=output_dir)155156if self._need_link (objects, output_filename):157lib_args = [output_filename, '/u'] + objects158if debug:159pass # XXX what goes here?160try:161self.spawn ([self.lib] + lib_args)162except DistutilsExecError as msg:163raise LibError(msg)164else:165log.debug("skipping %s (up-to-date)", output_filename)166167# create_static_lib ()168169170def link (self,171target_desc,172objects,173output_filename,174output_dir=None,175libraries=None,176library_dirs=None,177runtime_library_dirs=None,178export_symbols=None,179debug=0,180extra_preargs=None,181extra_postargs=None,182build_temp=None,183target_lang=None):184185# XXX this ignores 'build_temp'! should follow the lead of186# msvccompiler.py187188(objects, output_dir) = self._fix_object_args (objects, output_dir)189(libraries, library_dirs, runtime_library_dirs) = \190self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)191192if runtime_library_dirs:193log.warn("I don't know what to do with 'runtime_library_dirs': %s",194str(runtime_library_dirs))195196if output_dir is not None:197output_filename = os.path.join (output_dir, output_filename)198199if self._need_link (objects, output_filename):200201# Figure out linker args based on type of target.202if target_desc == CCompiler.EXECUTABLE:203startup_obj = 'c0w32'204if debug:205ld_args = self.ldflags_exe_debug[:]206else:207ld_args = self.ldflags_exe[:]208else:209startup_obj = 'c0d32'210if debug:211ld_args = self.ldflags_shared_debug[:]212else:213ld_args = self.ldflags_shared[:]214215216# Create a temporary exports file for use by the linker217if export_symbols is None:218def_file = ''219else:220head, tail = os.path.split (output_filename)221modname, ext = os.path.splitext (tail)222temp_dir = os.path.dirname(objects[0]) # preserve tree structure223def_file = os.path.join (temp_dir, '%s.def' % modname)224contents = ['EXPORTS']225for sym in (export_symbols or []):226contents.append(' %s=_%s' % (sym, sym))227self.execute(write_file, (def_file, contents),228"writing %s" % def_file)229230# Borland C++ has problems with '/' in paths231objects2 = map(os.path.normpath, objects)232# split objects in .obj and .res files233# Borland C++ needs them at different positions in the command line234objects = [startup_obj]235resources = []236for file in objects2:237(base, ext) = os.path.splitext(os.path.normcase(file))238if ext == '.res':239resources.append(file)240else:241objects.append(file)242243244for l in library_dirs:245ld_args.append("/L%s" % os.path.normpath(l))246ld_args.append("/L.") # we sometimes use relative paths247248# list of object files249ld_args.extend(objects)250251# XXX the command-line syntax for Borland C++ is a bit wonky;252# certain filenames are jammed together in one big string, but253# comma-delimited. This doesn't mesh too well with the254# Unix-centric attitude (with a DOS/Windows quoting hack) of255# 'spawn()', so constructing the argument list is a bit256# awkward. Note that doing the obvious thing and jamming all257# the filenames and commas into one argument would be wrong,258# because 'spawn()' would quote any filenames with spaces in259# them. Arghghh!. Apparently it works fine as coded...260261# name of dll/exe file262ld_args.extend([',',output_filename])263# no map file and start libraries264ld_args.append(',,')265266for lib in libraries:267# see if we find it and if there is a bcpp specific lib268# (xxx_bcpp.lib)269libfile = self.find_library_file(library_dirs, lib, debug)270if libfile is None:271ld_args.append(lib)272# probably a BCPP internal library -- don't warn273else:274# full name which prefers bcpp_xxx.lib over xxx.lib275ld_args.append(libfile)276277# some default libraries278ld_args.append ('import32')279ld_args.append ('cw32mt')280281# def file for export symbols282ld_args.extend([',',def_file])283# add resource files284ld_args.append(',')285ld_args.extend(resources)286287288if extra_preargs:289ld_args[:0] = extra_preargs290if extra_postargs:291ld_args.extend(extra_postargs)292293self.mkpath (os.path.dirname (output_filename))294try:295self.spawn ([self.linker] + ld_args)296except DistutilsExecError as msg:297raise LinkError(msg)298299else:300log.debug("skipping %s (up-to-date)", output_filename)301302# link ()303304# -- Miscellaneous methods -----------------------------------------305306307def find_library_file (self, dirs, lib, debug=0):308# List of effective library names to try, in order of preference:309# xxx_bcpp.lib is better than xxx.lib310# and xxx_d.lib is better than xxx.lib if debug is set311#312# The "_bcpp" suffix is to handle a Python installation for people313# with multiple compilers (primarily Distutils hackers, I suspect314# ;-). The idea is they'd have one static library for each315# compiler they care about, since (almost?) every Windows compiler316# seems to have a different format for static libraries.317if debug:318dlib = (lib + "_d")319try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)320else:321try_names = (lib + "_bcpp", lib)322323for dir in dirs:324for name in try_names:325libfile = os.path.join(dir, self.library_filename(name))326if os.path.exists(libfile):327return libfile328else:329# Oops, didn't find it in *any* of 'dirs'330return None331332# overwrite the one from CCompiler to support rc and res-files333def object_filenames (self,334source_filenames,335strip_dir=0,336output_dir=''):337if output_dir is None: output_dir = ''338obj_names = []339for src_name in source_filenames:340# use normcase to make sure '.rc' is really '.rc' and not '.RC'341(base, ext) = os.path.splitext (os.path.normcase(src_name))342if ext not in (self.src_extensions + ['.rc','.res']):343raise UnknownFileError("unknown file type '%s' (from '%s')" % \344(ext, src_name))345if strip_dir:346base = os.path.basename (base)347if ext == '.res':348# these can go unchanged349obj_names.append (os.path.join (output_dir, base + ext))350elif ext == '.rc':351# these need to be compiled to .res-files352obj_names.append (os.path.join (output_dir, base + '.res'))353else:354obj_names.append (os.path.join (output_dir,355base + self.obj_extension))356return obj_names357358# object_filenames ()359360def preprocess (self,361source,362output_file=None,363macros=None,364include_dirs=None,365extra_preargs=None,366extra_postargs=None):367368(_, macros, include_dirs) = \369self._fix_compile_args(None, macros, include_dirs)370pp_opts = gen_preprocess_options(macros, include_dirs)371pp_args = ['cpp32.exe'] + pp_opts372if output_file is not None:373pp_args.append('-o' + output_file)374if extra_preargs:375pp_args[:0] = extra_preargs376if extra_postargs:377pp_args.extend(extra_postargs)378pp_args.append(source)379380# We need to preprocess: either we're being forced to, or the381# source file is newer than the target (or the target doesn't382# exist).383if self.force or output_file is None or newer(source, output_file):384if output_file:385self.mkpath(os.path.dirname(output_file))386try:387self.spawn(pp_args)388except DistutilsExecError as msg:389print(msg)390raise CompileError(msg)391392# preprocess()393394395