Pyrex and Distuils: an enhanced build_ext command

Graham Fawcett gmfawcett at operamail.com
Wed Jul 10 16:34:34 EDT 2002


"Thomas Heller" <theller at python.net> wrote in message news:<agh6k6$ltutn$1 at ID-59885.news.dfncis.de>...
> "Graham Fawcett" <gmfawcett at operamail.com> wrote in message news:3d799735.0207100216.4eb8a9cd at posting.google.com...
> >                     # compiling with mingw32 gets an "initializer not
> > a constant" error
> >                     #
> > http://www.python.org/cgi-bin/faqw.py?req=show&file=faq03.024.htp
> >                     # doesn't appear to happen with MSVC
> >                     # so if we are compiling with mingw32, massage the
> > Pyrex-generated
> >                     # C files to compile properly
> >
>
> Isn't this problem (initializer not a constant) solved by compiling
> the file in C++ mode (with .cpp extension)?
>
> Thomas

Yes -- at first I couldn't get c++ compilation to work, so I tried the
other approach suggested in the FAQ.

I just spent some time trying again, and I think I've got the c++
approach working. I still need to massage the Pyrex-generated C files,
but the code is much cleaner.

I also saw your suggestions re: adding command classes using the
canoncial approach, and the extension point at swig_sources(), and
have simplified my code accordingly.

Here are the revised files:

#######################################
#Pyrex.Distutils.__init__.py

from build_ext import build_ext

#######################################
#Pyrex.Distutils.build_ext.py
#
# Subclasses disutils.command.build_ext,
# replacing it with a Pyrex version that compiles pyx->c
# before calling the original build_ext command.
# July 2002, Graham Fawcett
# Pyrex is (c) Greg Ewing.

import distutils.command.build_ext
import Pyrex.Compiler.Main
from Pyrex.Compiler.Errors import PyrexError
from distutils.dep_util import newer
import os
import sys

class build_ext (distutils.command.build_ext.build_ext):

    description = "compile Pyrex scripts, then build C/C++ extensions
(compile/link to build directory)"

    def finalize_options (self):
        distutils.command.build_ext.build_ext.finalize_options(self)

        # compiling with mingw32 gets an "initializer not a constant"
error
        # doesn't appear to happen with MSVC!
        # so if we are compiling with mingw32,
        # switch to C++ mode, to avoid the problem
        if self.compiler == 'mingw32':
            self.swig_cpp = 1

    def swig_sources (self, sources):
        if not self.extensions:
            return

        # collect the names of the source (.pyx) files
        pyx_sources = []
        pyx_sources = [source for source in sources if
source.endswith('.pyx')]

        extension = self.swig_cpp and '.cpp' or '.c'
        for pyx in pyx_sources:
            # should I raise an exception if it doesn't exist?
            if os.path.exists(pyx):
                source = pyx
                target = source.replace('.pyx', extension)
                if newer(source, target) or self.force:
                    self.pyrex_compile(source)

                    if self.swig_cpp:
                        # rename .c to .cpp (Pyrex always builds .c
...)
                        if os.path.exists(target):
                            os.unlink(target)
                        os.rename(source.replace('.pyx', '.c'),
target)
                        # massage the cpp file
                        self.c_to_cpp(target)

        return [src.replace('.pyx', extension) for src in pyx_sources]

    def pyrex_compile(self, source):
        try:
            Pyrex.Compiler.Main.compile(source, c_only=1,
use_listing_file=0)
        except PyrexError, e:
            print e
            sys.exit(1)

    def c_to_cpp(self, filename):
        """touch up the Pyrex generated c/cpp files to meet
mingw32/distutils requirements."""
        f = open(filename, 'r')
        lines = [line for line in f.readlines() if not
line.startswith('staticforward PyTypeObject __pyx_type_')]
        f.close()
        f = open(filename, 'w')
        lines.insert(1, 'extern "C" {\n')
        lines.append('}\n')
        f.write(''.join(lines))
        f.close()

#######################################
# a sample setup.py
from distutils.core import setup
from distutils.extension import Extension
from Pyrex.Distutils import build_ext

setup(name='foo',
    ext_modules=[ Extension("foo", ["foo.pyx"]) ],
    cmdclass = {'build_ext': build_ext}
)

#######################################

Tested on Win32, msvc and mingw32.

I'm happy so far -- the revision is shorter, cleaner and more
explicit.

Comments are welcome!

Cheers,

-- Graham



More information about the Python-list mailing list