class_docstr: Concrete class that implements an interface to the Borland C/C++
  compiler, as defined by the CCompiler abstract class.
code: def link(  # noqa: C901
      self,
      target_desc,
      objects,
      output_filename,
      output_dir=None,
      libraries=None,
      library_dirs=None,
      runtime_library_dirs=None,
      export_symbols=None,
      debug=0,
      extra_preargs=None,
      extra_postargs=None,
      build_temp=None,
      target_lang=None,
  ):
      # XXX this ignores 'build_temp'!  should follow the lead of
      # msvccompiler.py
  
      (objects, output_dir) = self._fix_object_args(objects, output_dir)
      (
          libraries,
          library_dirs,
          runtime_library_dirs,
      ) = self._fix_lib_args(
          libraries, library_dirs, runtime_library_dirs
      )
  
      if runtime_library_dirs:
          log.warning(
              "I don't know what to do with 'runtime_library_dirs': %s",
              str(runtime_library_dirs),
          )
  
      if output_dir is not None:
          output_filename = os.path.join(output_dir, output_filename)
  
      if self._need_link(objects, output_filename):
          # Figure out linker args based on type of target.
          if target_desc == CCompiler.EXECUTABLE:
              startup_obj = "c0w32"
              if debug:
                  ld_args = self.ldflags_exe_debug[:]
              else:
                  ld_args = self.ldflags_exe[:]
          else:
              startup_obj = "c0d32"
              if debug:
                  ld_args = self.ldflags_shared_debug[:]
              else:
                  ld_args = self.ldflags_shared[:]
  
          # Create a temporary exports file for use by the linker
          if export_symbols is None:
              def_file = ""
          else:
              head, tail = os.path.split(output_filename)
              modname, ext = os.path.splitext(tail)
              temp_dir = os.path.dirname(
                  objects[0]
              )  # preserve tree structure
              def_file = os.path.join(temp_dir, "%s.def" % modname)
              contents = ["EXPORTS"]
              for sym in export_symbols or []:
                  contents.append("  {}=_{}".format(sym, sym))
              self.execute(
                  write_file,
                  (def_file, contents),
                  "writing %s" % def_file,
              )
  
          # Borland C++ has problems with '/' in paths
          objects2 = map(os.path.normpath, objects)
          # split objects in .obj and .res files
          # Borland C++ needs them at different positions in the command line
          objects = [startup_obj]
          resources = []
          for file in objects2:
              (base, ext) = os.path.splitext(os.path.normcase(file))
              if ext == ".res":
                  resources.append(file)
              else:
                  objects.append(file)
  
          for ell in library_dirs:
              ld_args.append("/L%s" % os.path.normpath(ell))
          ld_args.append("/L.")  # we sometimes use relative paths
  
          # list of object files
          ld_args.extend(objects)
  
          # XXX the command-line syntax for Borland C++ is a bit wonky;
          # certain filenames are jammed together in one big string, but
          # comma-delimited.  This doesn't mesh too well with the
          # Unix-centric attitude (with a DOS/Windows quoting hack) of
          # 'spawn()', so constructing the argument list is a bit
          # awkward.  Note that doing the obvious thing and jamming all
          # the filenames and commas into one argument would be wrong,
          # because 'spawn()' would quote any filenames with spaces in
          # them.  Arghghh!.  Apparently it works fine as coded...
  
          # name of dll/exe file
          ld_args.extend([",", output_filename])
          # no map file and start libraries
          ld_args.append(",,")
  
          for lib in libraries:
              # see if we find it and if there is a bcpp specific lib
              # (xxx_bcpp.lib)
              libfile = self.find_library_file(library_dirs, lib, debug)
              if libfile is None:
                  ld_args.append(lib)
                  # probably a BCPP internal library -- don't warn
              else:
                  # full name which prefers bcpp_xxx.lib over xxx.lib
                  ld_args.append(libfile)
  
          # some default libraries
          ld_args.extend(("import32", "cw32mt"))
  
          # def file for export symbols
          ld_args.extend([",", def_file])
          # add resource files
          ld_args.append(",")
          ld_args.extend(resources)
  
          if extra_preargs:
              ld_args[:0] = extra_preargs
          if extra_postargs:
              ld_args.extend(extra_postargs)
  
          self.mkpath(os.path.dirname(output_filename))
          try:
              self.spawn([self.linker] + ld_args)
          except DistutilsExecError as msg:
              raise LinkError(msg)
  
      else:
          log.debug("skipping %s (up-to-date)", output_filename)
  
file_path: catboost/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py
imports: import os
  import warnings
  from .errors import DistutilsExecError, CompileError, LibError, LinkError, UnknownFileError
  from .ccompiler import CCompiler, gen_preprocess_options
  from .file_util import write_file
  from ._modified import newer
  from ._log import log
label_desc: Use image node_id 4 for calling the BCPPCompiler obj's underlying member method code with example usage: obj.link(target_desc, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, extra_preargs, extra_postargs, build_temp, target_lang) without return types