[Distutils] Source and patch: swig support (and more)

Thomas Heller thomas.heller@ion-tof.com
Fri, 16 Jun 2000 20:19:43 +0200


This is a multi-part message in MIME format.

------=_NextPart_000_035E_01BFD7D0.353E54B0
Content-Type: text/plain;
	charset="Windows-1252"
Content-Transfer-Encoding: 7bit

The included source code and patch implements
a swig class (derived from ccompiler).

Currently the swig options used cannot be configured
with command-line flags or somehow else although they
certainly should.

Simply include 'xxx.i' files into your extension source-files,
and swig will be used to generate 'xxx.cpp' files, which will
then be compiled and linked.

Other stuff included (only useful for the windows crowd)
Resource scripts (*.rc) and message compiler files (*.mc)
can also be used as extension source files, and MSVC
will invoke rc.exe and mc.exe to create .res files
and pass them to the linker.

Comments welcome!

Thomas

------=_NextPart_000_035E_01BFD7D0.353E54B0
Content-Type: text/plain;
	name="swig-diff.txt"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="swig-diff.txt"

Index: ccompiler.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/python/distutils/distutils/ccompiler.py,v
retrieving revision 1.22
diff -c -r1.22 ccompiler.py
*** ccompiler.py	2000/06/07 03:00:05	1.22
--- ccompiler.py	2000/06/16 18:05:42
***************
*** 655,660 ****
--- 655,664 ----
          for src_name in source_filenames:
              (base, ext) =3D os.path.splitext (src_name)
              if ext not in self.src_extensions:
+                 # if extension unknown, return it as object name
+                 # and assume someone other (maybe the linker)
+                 # can use it.
+                 obj_names.append (src_name)
                  continue
              if strip_dir:
                  base =3D os.path.basename (base)
Index: msvccompiler.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/python/distutils/distutils/msvccompiler.py,v
retrieving revision 1.30
diff -c -r1.30 msvccompiler.py
*** msvccompiler.py	2000/05/30 02:02:49	1.30
--- msvccompiler.py	2000/06/16 18:05:43
***************
*** 170,179 ****
      # Private class data (need to distinguish C from C++ source for =
compiler)
      _c_extensions =3D ['.c']
      _cpp_extensions =3D ['.cc','.cpp']
 =20
      # Needed for the filename generation methods provided by the
      # base class, CCompiler.
!     src_extensions =3D _c_extensions + _cpp_extensions
      obj_extension =3D '.obj'
      static_lib_extension =3D '.lib'
      shared_lib_extension =3D '.dll'
--- 170,182 ----
      # Private class data (need to distinguish C from C++ source for =
compiler)
      _c_extensions =3D ['.c']
      _cpp_extensions =3D ['.cc','.cpp']
+     _rc_extensions =3D ['.rc']
+     _mc_extensions =3D ['.mc']
 =20
      # Needed for the filename generation methods provided by the
      # base class, CCompiler.
!     src_extensions =3D _c_extensions + _cpp_extensions + =
_rc_extensions + _mc_extensions
!     res_extension =3D '.res'
      obj_extension =3D '.obj'
      static_lib_extension =3D '.lib'
      shared_lib_extension =3D '.dll'
***************
*** 195,200 ****
--- 198,205 ----
              self.cc   =3D find_exe("cl.exe", version)
              self.link =3D find_exe("link.exe", version)
              self.lib  =3D find_exe("lib.exe", version)
+             self.rc   =3D find_exe("rc.exe", version)     # resource =
compiler
+             self.mc   =3D find_exe("mc.exe", version)     # message =
compiler
              set_path_env_var ('lib', version)
              set_path_env_var ('include', version)
              path=3Dget_msvc_paths('path', version)
***************
*** 209,214 ****
--- 214,221 ----
              self.cc =3D "cl.exe"
              self.link =3D "link.exe"
              self.lib =3D "lib.exe"
+             self.rc =3D "rc.exe"
+             self.mc =3D "mc.exe"
 =20
          self.preprocess_options =3D None
          self.compile_options =3D [ '/nologo', '/Ox', '/MD', '/W3' ]
***************
*** 223,228 ****
--- 230,266 ----
 =20
      # -- Worker methods =
------------------------------------------------
 =20
+     def object_filenames (self,
+                           source_filenames,
+                           strip_dir=3D0,
+                           output_dir=3D''):
+         # Copied from ccompiler.py, extended to return .res as =
'object'-file
+         # for .rc input file
+         if output_dir is None: output_dir =3D ''
+         obj_names =3D []
+         for src_name in source_filenames:
+             (base, ext) =3D os.path.splitext (src_name)
+             if ext not in self.src_extensions:
+                 # Better to raise an exception instead of silently =
continuing
+                 # and later complain about sources and targets having
+                 # different lengths
+                 raise CompileError ("Don't know how to compile %s" % =
src_name)
+             if strip_dir:
+                 base =3D os.path.basename (base)
+             if ext in self._rc_extensions:
+                 obj_names.append (os.path.join (output_dir,
+                                                 base + =
self.res_extension))
+             elif ext in self._mc_extensions:
+                 obj_names.append (os.path.join (output_dir,
+                                                 base + =
self.res_extension))
+             else:
+                 obj_names.append (os.path.join (output_dir,
+                                                 base + =
self.obj_extension))
+         return obj_names
+=20
+     # object_filenames ()
+=20
+=20
      def compile (self,
                   sources,
                   output_dir=3DNone,
***************
*** 254,267 ****
              if skip_sources[src]:
                  self.announce ("skipping %s (%s up-to-date)" % (src, =
obj))
              else:
                  if ext in self._c_extensions:
                      input_opt =3D "/Tc" + src
                  elif ext in self._cpp_extensions:
                      input_opt =3D "/Tp" + src
 =20
                  output_opt =3D "/Fo" + obj
-=20
-                 self.mkpath (os.path.dirname (obj))
                  try:
                      self.spawn ([self.cc] + compile_opts + pp_opts +
                                  [input_opt, output_opt] +
--- 292,345 ----
              if skip_sources[src]:
                  self.announce ("skipping %s (%s up-to-date)" % (src, =
obj))
              else:
+                 self.mkpath (os.path.dirname (obj))
+=20
                  if ext in self._c_extensions:
                      input_opt =3D "/Tc" + src
                  elif ext in self._cpp_extensions:
                      input_opt =3D "/Tp" + src
+                 elif ext in self._rc_extensions:
+                     # compile .RC to .RES file
+                     input_opt =3D src
+                     output_opt =3D "/fo" + obj
+                     try:
+                         self.spawn ([self.rc] +
+                                     [output_opt] + [input_opt])
+                     except DistutilsExecError, msg:
+                         raise CompileError, msg
+                     continue
+                 elif ext in self._mc_extensions:
+                     # compile .MC to .RC file to .RES file
+                     # '-h dir' specifies the directory for the =
generated include file
+                     # '-r dir' specifies the target directory of the =
generated RC file
+                     # and the binary message resource it includes.
+=20
+                     # For now (since there are no options to change =
this),
+                     # we use the source-directory for the include file
+                     # and the build directory for the RC file
+                     # and message resources. This works at least for =
win32all.
+                     h_dir =3D os.path.dirname (src)
+                     rc_dir =3D os.path.dirname (obj)
+                     try:
+                         # first compile .MC to .RC and .H file
+                         self.spawn ([self.mc] +
+                                     ['-h', h_dir, '-r', rc_dir] + =
[src])
+                         base, _ =3D os.path.splitext (os.path.basename =
(src))
+                         rc_file =3D os.path.join (rc_dir, base + =
'.rc')
+                         # then compile .RC to .RES file
+                         self.spawn ([self.rc] +
+                                     ["/fo" + obj] + [rc_file])
+=20
+                     except DistutilsExecError, msg:
+                         raise CompileError, msg
+                     continue
+                 else:
+                     # how to handle this file?
+                     raise CompileError (
+                         "Don't know how to compile %s to %s" % \
+                         (src, obj))
 =20
                  output_opt =3D "/Fo" + obj
                  try:
                      self.spawn ([self.cc] + compile_opts + pp_opts +
                                  [input_opt, output_opt] +
***************
*** 374,382 ****
              if extra_postargs:
                  ld_args.extend (extra_postargs)
 =20
-             print "link_shared_object():"
-             print "  output_filename =3D", output_filename
-             print "  mkpath'ing:", os.path.dirname (output_filename)
              self.mkpath (os.path.dirname (output_filename))
              try:
                  self.spawn ([self.link] + ld_args)
--- 452,457 ----
Index: unixccompiler.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/python/distutils/distutils/unixccompiler.py,v
retrieving revision 1.25
diff -c -r1.25 unixccompiler.py
*** unixccompiler.py	2000/05/30 02:02:49	1.25
--- unixccompiler.py	2000/06/16 18:05:43
***************
*** 131,136 ****
--- 131,139 ----
          # '_prep_compile()'.       =20
          for i in range (len (sources)):
              src =3D sources[i] ; obj =3D objects[i]
+             if src =3D=3D obj:
+                 # this is probably already an object file
+                 continue
              if skip_sources[src]:
                  self.announce ("skipping %s (%s up-to-date)" % (src, =
obj))
              else:
Index: command/build_ext.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/python/distutils/distutils/command/build_ext.py,v
retrieving revision 1.42
diff -c -r1.42 build_ext.py
*** command/build_ext.py	2000/06/07 03:00:06	1.42
--- command/build_ext.py	2000/06/16 18:05:44
***************
*** 147,152 ****
--- 147,153 ----
          # also Python's library directory must be appended to =
library_dirs
          if os.name =3D=3D 'nt':
              self.library_dirs.append (os.path.join(sys.exec_prefix, =
'libs'))
+             self.build_implib =3D self.build_temp
              if self.debug:
                  self.build_temp =3D os.path.join (self.build_temp, =
"Debug")
              else:
***************
*** 157,162 ****
--- 158,164 ----
      def run (self):
 =20
          from distutils.ccompiler import new_compiler
+         from distutils.swig import Swig
 =20
          # 'self.extensions', as supplied by setup.py, is a list of
          # Extension instances.  See the documentation for Extension =
(in
***************
*** 173,178 ****
--- 175,185 ----
          if not self.extensions:
              return
 =20
+         # Setup a swig object to use
+         self.swig =3D Swig(verbose=3Dself.verbose,
+                          dry_run=3Dself.dry_run,
+                          force=3Dself.force)
+=20
          # If we were asked to build any C/C++ libraries, make sure =
that the
          # directory where we put them is in the library search path =
for
          # linking extensions.
***************
*** 366,373 ****
  		continue # 'for' loop over all extensions
  	    else:
          	self.announce ("building '%s' extension" % ext.name)
 =20
!             # First step: compile the source code to object files.  =
This
              # drops the object files in the current directory, =
regardless
              # of where the source is (may be a bad thing, but that's =
how a
              # Makefile.pre.in-based system does it, so at least =
there's a
--- 373,387 ----
  		continue # 'for' loop over all extensions
  	    else:
          	self.announce ("building '%s' extension" % ext.name)
+=20
+             # First step: Pass all sources to swig, let swig generate =
files
+             # as needed, and return a list of files to pass to the =
compiler
+             sources =3D self.swig.compile (sources,
+                                          output_dir=3Dself.build_temp,
+                                          #macros=3Dmacros,
+                                          =
include_dirs=3Dext.include_dirs)
 =20
!             # Second step: compile the source code to object files.  =
This
              # drops the object files in the current directory, =
regardless
              # of where the source is (may be a bad thing, but that's =
how a
              # Makefile.pre.in-based system does it, so at least =
there's a
***************
*** 434,445 ****
                      modname =3D string.split (ext.name, '.')[-1]
                      extra_args.append('/export:init%s'%modname)
 =20
!                 # The MSVC linker generates unneeded .lib and .exp =
files,
!                 # which cannot be suppressed by any linker switches.  =
So
!                 # make sure they are generated in the temporary build
!                 # directory.
                  implib_file =3D os.path.join (
!                     self.build_temp,
                      self.get_ext_libname (ext.name))
                  extra_args.append ('/IMPLIB:' + implib_file)
                  self.mkpath (os.path.dirname (implib_file))
--- 448,461 ----
                      modname =3D string.split (ext.name, '.')[-1]
                      extra_args.append('/export:init%s'%modname)
 =20
!                 # The MSVC linker generates .lib and .exp files,
!                 # which cannot be suppressed by any linker switches. =
The .lib
!                 # files may even be needed! Make sure they are =
generated in
!                 # the temporary build directory. Since they have =
different
!                 # names for debug and release builds, they can go
!                 # into the same directory.
                  implib_file =3D os.path.join (
!                     self.build_implib,
                      self.get_ext_libname (ext.name))
                  extra_args.append ('/IMPLIB:' + implib_file)
                  self.mkpath (os.path.dirname (implib_file))

------=_NextPart_000_035E_01BFD7D0.353E54B0
Content-Type: text/plain;
	name="swig.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="swig.py"

# created 1999/07/05, Thomas Heller

__revision__ = "$Id$"

from distutils.errors import *
from distutils.util import newer_pairwise, newer_group
import string, os
from distutils.ccompiler import CCompiler

def find_exe (exe):
    for p in string.split (os.environ['Path'],';'):
        fn = os.path.join(os.path.abspath(p),exe)
        if os.path.isfile(fn):
            return fn

    # 
    for vers in ("1.3", "1.2", "1.1"):
        fn = os.path.join(r"c:\swig%s" % vers ,exe)
        if os.path.isfile (fn):
            return fn
    return exe


class Swig (CCompiler):

    src_extensions = ['.i']
    obj_extension = '.cpp'
##    obj_extension = '.c'

    def __init__ (self,
                  verbose=0,
                  dry_run=0,
                  force=0):
        CCompiler.__init__ (self, verbose, dry_run, force)
        self.exe = find_exe ("swig.exe")

    def compile (self,
                 sources,
                 output_dir=None,
                 macros=None,
                 include_dirs=None,
                 debug=0,
                 extra_preargs=None,
                 extra_postargs=None):

        (output_dir, macros, include_dirs) = \
            self._fix_compile_args (output_dir, macros, include_dirs)

        (objects, skip_sources) = self._prep_compile (sources, output_dir)

        print "SWIG!", include_dirs
        print "SWIG!", self.include_dirs
        

        for i in range (len (sources)):
            src = sources[i] ; obj = objects[i]
            if src == obj:
                # nothing to do
                continue
            ext = (os.path.splitext (src))[1]

            if skip_sources[src]:
                self.announce ("skipping %s (%s up-to-date)" % (src, obj))
            else:
                self.announce ("swigging %s to %s" % (src, obj))
                self.mkpath (os.path.dirname (obj))
                try:
                    self.spawn ([self.exe] + [
                        "-python",
                        "-dnone",
                        "-c++",
                        "-ISWIG",
                        "-o", obj] + [src])
                except DistutilsExecError, msg:
                    raise CompileError, msg

        return objects

    def object_filenames (self,
                          source_filenames,
                          strip_dir=0,
                          output_dir=''):
        if output_dir is None: output_dir = ''
        obj_names = []
        for src_name in source_filenames:
            (base, ext) = os.path.splitext (src_name)
            if ext not in self.src_extensions:
                obj_names.append (src_name)
                continue
            if strip_dir:
                base = os.path.basename (base)
            obj_names.append (os.path.join (output_dir,
                                            base + self.obj_extension))
        return obj_names

    # object_filenames ()

------=_NextPart_000_035E_01BFD7D0.353E54B0--