Search code examples
xcodegraphvizmac-app-store

How to configure & package Graphviz for Mac App Store?


I would like to distribute an App through the Mac App Store and will hence need to use Sandboxing. Problem is that I don't know how to configure Graphviz in a Sandbox-compatible way. Graphviz is based on plugins and requires that I:

  1. Use an environment variable (GVBINDIR) to set the plugin directory. The variable has to point to a directory within the Sandbox.
  2. Have a file in GVBINDIR directory location that is called config6 which is used to register plugin libraries. I could copy the file from the Name.app/Contents/Resources there.
  3. Have 2 plugin dylibs in that same GVBINDIR. If I understand it correctly, then the Mach-O Linker settings have to be correctly set for the plugin library prior to deployment and in alignment with the directory.

The following screenshot is based on a run with Xcode instruments, monitoring file access, but filtering to dot_layout plugin. dydl access to one of the libraries As you can see it is found when the app starts (I have it as dependency, you see the FD return "3" - I don't really know what that means). But later, when graphviz triggers the access to the plugin, dyld cannot find it.

What am I doing wrong?

Any ideas? Many thanks in advance!


Solution

  • It took me a while, but in the end I found a solution. I created a script with the following content that I place within the graphviz directory.

    INSTALLER_ROOT="/Users/wizardofkneup"
    GVROOT="${INSTALLER_ROOT}/graphviz"  
    GVTARGET="${INSTALLER_ROOT}/Documents/xcode/VisualThinkingWorkspace/graphviz"
    PLATFORM_DEVELOPER_BIN_DIR="/usr/bin"
    
    ./configure \
      --disable-dependency-tracking \
      --enable-shared=no \
      --enable-static=yes \
      --enable-ltdl=no \
      --enable-swig=no \
      --enable-tcl=no \
      -srcdir=/Users/wizardofkneup/graphviz \
      --with-codegens=no \
      --with-cgraph=yes \
      --with-expat=no \
      --with-fontconfig=no \
      --with-freetype2=no \
      --with-ipsepcola=yes \
      --with-libgd=no \
      --with-xdot=yes \
     --with-quartz=yes \
      --with-visio=yes \
       --with-x=no \
       CC="${PLATFORM_DEVELOPER_BIN_DIR}/clang" \
       CPP="${PLATFORM_DEVELOPER_BIN_DIR}/clang -E" \
       CXX="${PLATFORM_DEVELOPER_BIN_DIR}/clang++" \
       OBJC="${PLATFORM_DEVELOPER_BIN_DIR}/clang" \
       LD="${PLATFORM_DEVELOPER_BIN_DIR}/ld" 
    
    make
    
    rm -rf ${GVTARGET}
    mkdir -p ${GVTARGET}
    find . -type f -name '*_C.a' -exec cp -i {} ${GVTARGET} \;
    find . -type f -name '*.h' -exec cp {} -i ${GVTARGET} \;
    

    A few further comments:

    • Use Homebrew to get the sources for graphviz. brew install --build-from-source graphviz
    • copy & unzip downloaded sources from ~/Library/Caches/Homebrew into GRVIZ directory. (In my case /Users/wizardofkneup/graphviz)
    • run sh autogen.sh
    • compile sources with above script. It copies the files needed by your project going forward to ${GVTARGET}

    Now we can focus on our Swift project within Xcode.

    • Define environment variable GRVIZ, pointing at dev directory . Set header path and swift import path to ${GRVIZ}/**
    • In order to have c++ standard libraries linked (needed for plugins) you need to have a empty .mm file (following walter's idea: Compile errors with C++ static library include in Swift project)
    • Include all _C.a files -> we want to C interface.
    • Create Builtins.c needs to be a c-file in order to avoid name mangling issues. Set this in file inspector
    • For yet unknown reasons do I need both: the GRVIZ and GVTARGET directories.
    • Loading the libraries: Unable to use dot layout (graphviz as a library)

    It looks like current (=2019) graphviz builds have a dependency on glib. In order to have a static glib it needs to be build locally first. For that apply meson _build -Diconv=native when you build it. It might be needed to install libiconv first.

    My builtins.c file:

    #include "builtins.h"
    
    extern gvplugin_library_t gvplugin_dot_layout_LTX_library;
    extern gvplugin_library_t gvplugin_neato_layout_LTX_library;
    extern gvplugin_library_t gvplugin_core_LTX_library;
    extern gvplugin_library_t gvplugin_quartz_LTX_library;
    extern gvplugin_library_t gvplugin_visio_LTX_library;
    
    void loadGraphvizLibraries(GVC_t *gvc) {
        gvAddLibrary(gvc, &gvplugin_dot_layout_LTX_library);
        gvAddLibrary(gvc, &gvplugin_neato_layout_LTX_library);
        gvAddLibrary(gvc, &gvplugin_core_LTX_library);
        gvAddLibrary(gvc, &gvplugin_quartz_LTX_library);
        gvAddLibrary(gvc, &gvplugin_visio_LTX_library);
    }
    

    My builtins.h file:

    #ifndef builtins_h
    #define builtins_h
    
    #include "/Users/wizardofkneup/graphviz/lib/gvc/gvplugin.h" //
    #include "gvc.h"
    
    extern lt_symlist_t lt_preloaded_symbols[];
    void loadGraphvizLibraries(GVC_t *gvc);
    
    #endif /* builtins_h */
    

    the app's Bridging-header.h

    #include <gvc.h>
    #import "builtins.h"
    

    And the configuration in Xcode. enter image description here enter image description here enter image description here

    I needed later on this one: not sure whether that was graphviz related. enter image description here

    I hope this helps. Good luck. Let me know if I missed something.