How to compile a storyboard file with dependency tracking

I have a macOS bundle target, and would like to compile a Main.storyboard file and have it installed to Target.app/Contents/Resources/Main.storyboardc.

Normally for similar compile-and-copy actions, I’d do this:

set(input  ${CMAKE_CURRENT_SOURCE_DIR}/Main.storyboard)
set(output ${CMAKE_CURRENT_BINARY_DIR}/Main.storyboardc)
add_custom_command(OUTPUT ${output}
	COMMAND ${IBTOOL} --compile ${output} ${input}
	DEPENDS ${input})
target_sources(MyTarget PRIVATE ${output})
set_source_files_properties(${output} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)

This usually works fine, but it doesn’t work for storyboardc files, because they’re actually directories, and MACOSX_PACKAGE_LOCATION doesn’t recursively copy directories.
Searching online for how to make MACOSX_PACKAGE_LOCATION copy directories only results in people saying to use file(GLOB, which won’t work here, since the directory doesn’t exist until the custom command has run.

Importantly, this does add a dependency from MyTarget to Main.storyboard, so if Main.storyboard is edited, it will be recompiled (and would be recopied if MACOSX_PACKAGE_LOCATION worked on directories)

Instead of using MACOSX_PACKAGE_LOCATION, I can attempt to generate the file in place with

set(input  ${CMAKE_CURRENT_SOURCE_DIR}/Main.storyboard)
set(output $<TARGET_BUNDLE_DIR:MyTarget>/Contents/Resources/Main.storyboardc)
add_custom_command(TARGET MyTarget POST_BUILD
	COMMAND ${IBTOOL} --compile ${output} ${input})

But this doesn’t properly put a dependency on Main.storyboardc (so if it’s edited, MyTarget won’t recompile)

Finally, I tried this

set(input  ${CMAKE_CURRENT_SOURCE_DIR}/Main.storyboard)
set(output ${CMAKE_CURRENT_BINARY_DIR}/Main.storyboardc)
set(install $<TARGET_BUNDLE_DIR:MyTarget>/Contents/Resources/Main.storyboardc)
add_custom_command(OUTPUT ${output}
	COMMAND ${IBTOOL} --compile ${output} ${input}
	DEPENDS ${input})
add_custom_target(copy_storyboard_main
	COMMAND ${CMAKE_COMMAND} -E remove_directory ${install}
	COMMAND ${CMAKE_COMMAND} -E copy_directory ${output} ${install}
	DEPENDS ${output})
add_dependencies(MyTarget copy_storyboard_main)

But it seems TARGET_BUNDLE_DIR adds a dependency on MyTarget, resulting in a mutual dependency.

Does anyone know a good way to make it so the storyboardc is properly installed, and MyTarget has a dependency on Main.storyboard?

If you are only using the Xcode generator, then you can leave most of the work up to Xcode itself.

add_executable(MyApp MACOSX_BUNDLE
    Main.storyboard
    ....
)
set_source_files_properties(Main.storyboard PROPERTIES
    MACOSX_PACKAGE_LOCATION Resources
)
1 Like

We mainly compile with Ninja, though I use Xcode for development

(This is from dolphin emulator, where I recently broke the updater thinking I could use MACOSX_PACKAGE_LOCATION for storyboards. It’s not super important, as no one actually edits the updater’s storyboard files, but it just feels kind of bad to know we have missing dependencies here.)