Search code examples
c++macoscocoagame-engineapp-bundle

Embed Frameworks in Mac Bundle Without Xcode


I am trying to create a game engine right now on macOS and am just trying to set up a makefile to compile the code into a macOS App Bundle without Xcode. I can easily create a bundle if I just link the src code, but I want to embed it as a framework. If I use Xcode Embed Frameworks then it is really easy, but I can't figure out how it achieves this effect. Here is the code for my makefile:

# Fill in Details for Compiler
PRJNAME = YetiGame
GAMEFILES = Example/Game.cpp
COMPILER = clang++

# Don't Change Unless Modifying Engine
LIBFILES = Yeti/Application/Application.cpp
OSXMACROS = -D YETI_PLATFORM_MACOS
SRCPATH = $(shell pwd)/
LIBFLAGS = -I $(SRCPATH) -framework Cocoa -framework Metal -framework Metalkit -framework QuartzCore -framework Foundation -o $(PRJNAME) -F$(SRCPATH) -framework @rpath@executable_path/../Frameworks/Neptune -v
LIBFILES = Yeti/Application/Application.cpp Yeti/Renderer/Metal/2D/metalRenderer2D.mm Yeti/Renderer/Metal/metalView.mm Yeti/Renderer/Metal/metalApplication.mm Yeti/Renderer/Metal/metalApplicationWrapper.mm Yeti/Renderer/Metal/metalDelegate.mm Yeti/Renderer/Metal/metalWindow.mm

app:
    $(COMPILER) $(LIBFILES) $(GAMEFILES) $(LIBFLAGS) $(OSXMACROS)
    mkdir -p _macOS/Contents/macOS/
    mv $(PRJNAME) _macOS/Contents/macOS/YetiExecutable
    cp Resources/Info.plist _macOS/Contents/Info.plist
    mkdir _macOS/Contents/Resources/
    cp Resources/temp.icns _macOS/Contents/Resources/temp.icns
    mv _macOS $(PRJNAME).app

clean:
    rm -R $(PRJNAME).app

Right now I just compiled a framework in Xcode and want to link it to my project to just log something and tested it in Xcode. I want to be able to compile the framework in Xcode and use it from outside of xcode.


Solution

  • There are a few different ways to dynamically link a library on MacOS. One option is to use dlopen(). This allows a user to link to a library manually in the code. Here is an example in use in an OpenGL Function Loader:

    #include <stdlib.h>
    #include <dlfcn.h>
    
    static void* libgl;
    
    void CloseOpenGL()
    {
      dlclose(libgl);
    }
    
    bool LoadOpenGL()
    {
      libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL);
    
      if (!libgl)
        return false;
    
      atexit(CloseOpenGL);
      return true;
    }
    
    void* LoadFunctionProc(const char* name)
    {
      return dlsym(libgl, name);
    }
    

    Another option is to specify the library at link-time. This is what Xcode will do behind the scenes when you link to a dynamic (or static) library. The flag for clang do this is -l.

    In either case, you will need to be careful to make sure that the executable can find the library at runtime. In the case of a system library, the linker should handle it automatically. However, if you create an app bundle you will need to keep the library in the bundle.