Compiling JUCE from Cython?


#1

Hello everyone,

Currently I’m trying to get JUCE to work from Cython. So far, I have a setup.py and a Cython file (shown below) and compiled JUCE as a static library. Compilation of the Cython file goes fine, but the problem starts when I import the created Cython-module, which results in 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

This seems to be due to some missing libraries and I was wondering what the best way would be to resolve this situation (preferably in a mostly portable way). Should I simply install the libraries and if so, which exactly? Or is there a way to compile the JUCE library so that it doesn’t have any other dependencies (perhaps with the Projucer)?

Thanks in advance for your answers.

My 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=["JuceStaticLibrary"], #the name of my static JUCE library
                library_dirs=["src/program/JuceLibraryCode"] #where the static library is placed.

                    )]

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

And my actual 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

#2

You will always have to add the system library, in this case MacOS GUI support…


#3

Thanks for your (very) quick answer. Is that the CoreGraphics framework? If so, how would I add it? Because I have a folder /System/Library/frameworks/CoreGraphics.framework, but inside I can’t find any *.a or *.dylib files. Is there any difference between a framework and a library in this case (as you might have guessed I’m running MacOS)?


#4

A framework is more or less a collection of libraries, so the linker takes care of doing everything properly.
How you write that in setup.py, I don’t know, as it’s MacOS framework specific, and I never had to deal with this combination :confused:


#5

Ah, that’s too bad. I’ve tried simply adding more flags so when I change the compiler arguments to:
compile_args = ['-g', '-std=c++11', '-stdlib=libc++', '-F/System/Library/Frameworks', '-framework', 'CoreFoundation']

I get the following output:
clang: warning: -framework CoreFoundation: ‘linker’ input unused
In file included from src/JUCY/testb.cpp:527:
In file included from src/JUCY/JuceLibraryCode/JuceHeader.h:18:
In file included from JUCE/modules/juce_audio_basics/juce_audio_basics.h:52:
In file included from JUCE/modules/juce_core/juce_core.h:66:
JUCE/modules/juce_core/system/juce_TargetPlatform.h:69:12: fatal error: ‘CoreFoundation/CoreFoundation.h’ file not
found
#include <CoreFoundation/CoreFoundation.h> // (needed to find out what platform we’re using)
^
1 error generated.
error: command ‘clang’ failed with exit status 1

Where I can change “CoreFoundation” to basically any (nonsense) name and get the same output. Does anyone know what causes this, or better yet, how to solve it?


#6

If I remember correctly, the frameworks located in /System/Library/Frameworks are runtime frameworks, not development frameworks, i.e applications use them to run but you cannot use them to compile.

The development frameworks are provided by the OSX SDKs, which are usually installed in the Xcode app bundle. You tell the compiler which OSX SDK you want to use with the -isysroot flag. For instance, when I build with Xcode 7.3, the command line contains -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk, since Xcode 7.3 comes with the 10.11 OSX SDK.

The -framework flag is used by the linker only, thus it should go in extra_link_args and not extra_compile_args.


#7

Thank you very much and of course those are linker flags, should have realised that earlier. -isysroot is also passed as a flag already by Cython itself, both to the compiler and the linker. Now when I add to the setup file (analogous to the compiler_flags):

linker_args = ['-framework', 'Cocoa']
and
extra_link_args = linker_args,

My error is gone (also needed to set JUCE_WEB_BROWSER to disabled under juce_gui_extra, because that needed more frameworks), but now I get:

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

AFAIK I build the JUCE static library in debug mode, and I’m assuming the Cocoa-framework in the SDK is as well, so what is causing this? Are there other frameworks I’m missing?

Or is there perhaps a way to create a static library from JUCE with all the external dependencies already included, so that I only have to reference that and not worry about external frameworks or libraries?


#8

You need to make sure that you pass -D_DEBUG=1 and -DDEBUG=1 when compiling in Debug configuration, and -D_NDEBUG=1 and -DNDEBUG=1 when compiling in Release configuration.


#9

OMG that seems to be working. Thank you so much!


#10

Just one more question; Now that I started working with AudioUnits, I get the same error about missing symbols, this time about _OBJC_CLASS$AUGenericView. I’ve tried both the CoreAudio and AudioUnit frameworks but no luck there. Does anyone know what frameworks/libraries to use in this case? And additionally, is there a way to find out what frameworks to use apart from Googling/guessing?


#11

Each JUCE module documents which OSX frameworks it depends on in its header file. For instance in modules/juce_core/juce_core.h, you can see that juce_core depends on Cocoa and IOKit.

If you are using juce_audio_devices or juce_audio_processors, you might be missing AudioToolbox.


#12

Ah, that certainly seems helpful. However, I checked which modules the juce_audio_processor module depends on (and their dependencies), and I came to: juce_gui_extra, juce_audio_basics, juce_events, juce_graphics juce_data_structures, juce_core. Then I added EVERY framework that those modules depend upon (IOKit, WebKit, Cocoa, CoreAudio, CoreMIDI, Accelerate, AudioToolbox, Carbon, QuartzCore) and I still get the same error. What could be causing this?


#13

Now I remember that Projucer adds some extra OSX frameworks when building AU plugins or an AU plugin host: AudioUnit and CoreAudioKit.

See https://github.com/WeAreROLI/JUCE/blob/master/extras/Projucer/Source/Project%20Saving/jucer_ProjectExport_XCode.h#L1314