0

At the moment I'm trying to get the JUCE audio framework to work from Cython. As such, I first want to get a small and simple example running by showing an AlertWindow through the JUCE framework, but at the moment I seem to be having two small problems: 1. I have a problem with calling an enum from the JUCE framework 2. I don't know how to include the entire framework for compilation and linking.

My setup.py (called with "python3 setup.py build_ext --inplace"):

# Cython compile instructions
from setuptools import setup
from setuptools.extension import Extension
from Cython.Build import cythonize


compile_args = ['-g', '-std=c++11', '-stdlib=libc++']

extensions = [Extension('testb', ['src/JUCE/testb.pyx'],
            extra_compile_args=compile_args,
            include_dirs = ["JUCE/modules"],)]

setup(
    name='My app',
    ext_modules=cythonize(extensions)
)

My testb.pyx (Problem 1 is here):

# distutils: language = c++

cdef extern from "JuceLibraryCode/JuceHeader.h" namespace "juce":
    cdef cppclass AlertWindow:
        AlertWindow(String, String, AlertIconType)

cdef class PyAlertWindow:
    cdef AlertWindow *thisptr
    def __cinit__(self):
        self.thisptr = new AlertWindow("", "", NoIcon) # Don't know how to call the enum right here
    def __dealloc__(self):
        del self.thisptr
    @staticmethod
    def showAlertWindow(b):
        print("at least this works")

Furthermore I continuously get these type of errors, which are as far as I know caused by the fact that the rest of the framework is not getting compiled and included/linked. How would I do this?

ImportError:
dlopen(<project root>/build/lib/testb.cpython-36m-darwin.so, 2): Symbol not found: __ZN4juce6StringC1Ev
Referenced from:
<project root>/build/lib/testb.cpython-36m-darwin.so
Expected in: flat namespace
in <project root>/build/lib/testb.cpython-36m-darwin.so

Also, with the --inplace flag, all compiled files get dumped in my main folder, which does not seem very scaleable, especially with larger frameworks. How do I make sure all .so-files end up in so they are easily referenced? Or are there better ways of dealing with this?


Ok, so with the help of @ead I was able to get a step further. The code now looks as follows:

# setup.py

from setuptools import setup
from setuptools.extension import Extension
from Cython.Build import cythonize

compile_args = ['-g', '-std=c++11', '-stdlib=libc++']

extensions = [Extension('testb', ['src/program/testb.pyx'],
                extra_compile_args=compile_args,
                include_dirs = ["JUCE/modules"],
                libraries=["NewProject"], #the name of your library, WHICH MIGHT BE DIFFERENT FROM THE FILENAME!
                library_dirs=["src/program/JuceLibraryCode"] #where your library is placed.

                    )]

setup(
    name='My app',
    ext_modules=cythonize(extensions)
)

And my Cython file:

# testb.pyx
# distutils: language = c++

from libcpp.string cimport string

cdef extern from "JuceLibraryCode/JuceHeader.h":
    cdef cppclass AlertWindow:
        AlertWindow(String, String, AlertIconType, Component*)

    cdef cppclass Component:
        Component()

    cdef cppclass String:
        String(string)

cdef extern from "JuceLibraryCode/JuceHeader.h" namespace    "juce::AlertWindow":
    ctypedef enum AlertIconType:
        NoIcon
        QuestionIcon
        WarningIcon
        InfoIcon

cdef class PyAlertWindow:
    cdef AlertWindow *thisptr
    def __cinit__(self):
        self.thisptr = new AlertWindow(String(""), String(""), AlertIconType(NoIcon), NULL)
    def __dealloc__(self):
        del self.thisptr

And the example is now compiling fine. However, when I import the resulting package I get the following error:

ImportError: dlopen(<project root>/testb.cpython-36m-darwin.so, 2): Symbol not found: _CGAffineTransformIdentity
  Referenced from: <project root>/testb.cpython-36m-darwin.so
  Expected in: flat namespace
 in <project root>/testb.cpython-36m-darwin.so

Which seems to be related to Cocoa (here) or CoreGraphics (here) (which I also believe is the successor to Cocoa). So how would I solve this? Do I need to include the framework CoreGraphics and if so, how? (simply adding the flag -framework CoreGraphics results in clang: error: unknown argument: '-framework CoreGraphics' unfortunately)

Thanks in advance for your answers!

000
  • 26,951
  • 10
  • 71
  • 101
me me
  • 395
  • 1
  • 3
  • 14

1 Answers1

1

To your first problem: If your enum and function are defined as

//YYY.h
namespace Foo{
  enum Bar {bar1, bar2};
  void do_it(Bar bar);
}

then you need to import the enum-values in cython along with enum-type-name and it should look like:

cdef extern from 'XXX/YYY.h' namespace 'Foo':
    ctypedef enum Bar:
      bar1
      bar2
    cpdef void do_it(Bar a)

Then it can be called via do_it(bar1) or do_it(bar2) somewhere in your cython code. Actually, I learned it from this question here on SO some time ago, so you may consider to up-vote it.

The second problem is that you need to link against the juce-library (right now you only use the includes), for this you need to add the following to your Extension-setup:

Extension('testb', ['src/JUCE/testb.pyx'], ...
          libraries=[juce],#here the right name on your system
          library_dirs=[PATH_TO_JUCE_LIB_ON_YOUR_SYSTEM]
         }

PS: If you don't like the results with the inplace-option, please consult distutils-documentation for the alternatives.

Edit: JUCE has some further dependencies which need to be provided to the linker. I think the best way is to build one of the c++-examples (e.g. HelloWorld) for your system and to see which libraries are needed.

By looking into the Makefile on Linux it seems as if at least the following libraries are needed: freetype2 libcurl x11 xext xinerama webkit2gtk-4.0 gtk+-x11-3.0. Maybe you even need to install them first, I don't think they are default on every system.

Then your setup should look like:

Extension('testb', ['src/JUCE/testb.pyx'], ...
          libraries=['juce', 'x11', 'freetype2', ...], #all needed libraries
          library_dirs=[PATH_TO_JUCE, '/usr/X11R6/lib', '/usr/lib', ...] #all needed library paths
         }
ead
  • 32,758
  • 6
  • 90
  • 153
  • Thank you very much for your answer, it seems to have solved my issue with the enums. It also seems to offer a solution for my other problem, but despite my best efforts I'm still having trouble with it. Could you give me some more details? I have a JuceHeader.h (as seen in the OP) which in turn imports a lot of other headers, and I have the JUCE-library in a *.a-file. How would I then use this is Cython? Simply filling in libraries and library_dirs results in the same error. The strange part is that the compilation goes fine, and I only get a Symbol not found-error during the importing. – me me Aug 01 '17 at 14:57
  • 1
    @meme could you provide your exact setup and the exact error message? Are you sure the missing symbol is provided by the library? – ead Aug 01 '17 at 15:24
  • Thank you, I got a lot (?) further, but I'm running in a related issue (which is extended upon below). Do you know what might cause this? – me me Aug 01 '17 at 20:10