[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--