How to add resources to MacOS Bundle

Currently I add resources like this:

cmake_minimum_required(VERSION 3.26)
project(Example VERSION 1.0 LANGUAGES CXX C)

set(SRC
    src/main.cpp
)
set(SHADERS
    shaders/example.frag
)
set(MODELS
    models/cube.obj
)
add_executable(${PROJECT_NAME}
    ${SRC}
    ${SHADERS}
    ${MODELS}
)

if(APPLE)
    # Bundling macOS application
    set_target_properties(${PROJECT_NAME} PROPERTIES
        BUNDLE True
        MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}
        MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION}
        MACOSX_BUNDLE_GUI_IDENTIFIER com.example.${PROJECT_NAME}
        MACOSX_BUNDLE_ICON_FILE AppIcon
        MACOSX_BUNDLE_SHORT_VERSION_STRING ${CMAKE_PROJECT_VERSION}
    )
    set_source_files_properties(${SHADERS}
        PROPERTIES
        MACOSX_PACKAGE_LOCATION "Resources/shaders"
    )
    set_source_files_properties(${MODELS}
        PROPERTIES
        MACOSX_PACKAGE_LOCATION "Resources/models"
    )
    install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION .)
endif()

I understand that add_executable is getting a bit abused in here but even the official CMake example https://cmake.org/cmake/help/latest/prop_tgt/RESOURCE.html adds resource files like that and otherwise my resource files won’t get copied into my .app folder.

With my shader files this works flawlessly though but I get a problem when adding models. Then CMake fails with following error:
[build] ld: unknown file type in '.../models/cube.obj'

I understand that this occurs because add_executable does nothing else than passing the files to the compiler and the compiler probably doesn’t like the .objextension.

My current solution is to simply rename my models, for example to models/cube.model but this is very unsatisfactory, is there another way to add resources that is more elegant? Thanks in advance

Here’s how I do it in my application:

# fonts
set(resource_files
    ${CMAKE_SOURCE_DIR}/JetBrainsMono-ExtraLight.ttf
)

# icon
set(MACOSX_BUNDLE_ICON_FILE "${CMAKE_PROJECT_NAME}.icns")
set(application_icon "${CMAKE_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}")
set_source_files_properties(${application_icon}
    PROPERTIES
        MACOSX_PACKAGE_LOCATION "Resources"
)

# configs
file(GLOB_RECURSE json_configs "${CMAKE_SOURCE_DIR}/configs/*.json")
foreach (FILE ${json_configs})
    file(RELATIVE_PATH NEW_FILE "${CMAKE_SOURCE_DIR}/configs" ${FILE})
    get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
    set_source_files_properties(${FILE}
        PROPERTIES
            MACOSX_PACKAGE_LOCATION "Resources/configs/${NEW_FILE_PATH}"
    )
endforeach()

add_executable(${CMAKE_PROJECT_NAME}
    MACOSX_BUNDLE
    ${application_icon}
    # bundle resources
    "${resource_files}"
    "${json_configs}"
)

set_target_properties(${CMAKE_PROJECT_NAME}
    PROPERTIES # https://cmake.org/cmake/help/latest/prop_tgt/MACOSX_BUNDLE_INFO_PLIST.html
        MACOSX_BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_NAME}" # CFBundleIdentifier
        MACOSX_BUNDLE_GUI_IDENTIFIER "com.our-company"
        MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} # CFBundleLongVersionString, deprecated
        MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION} # CFBundleShortVersionString
        # and bundle resources again
        RESOURCE "${resource_files}"
)

# ...

include(GNUInstallDirs)

install(TARGETS ${CMAKE_PROJECT_NAME}
    BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
)

No issues so far, so maybe you’ll find something there what will help your CMake not to stumble upon model files.

Potentially the *.obj files are being identified as object files (i.e. compiled sources), not OBJ 3D data. You could try setting the HEADER_FILE_ONLY source file property to true on these *.obj files. That might be enough to convince CMake to not treat them as (compiled) object files. Ignore the fact that the property name mentions headers, it’s primary role is to prevent that file from being compiled.

2 Likes