Issue
As any GTK project grows, GTK applications tend to be bundled with gresources to separate out code and UI design. This is very useful because UI/UX designers don't need to know code in order to... well design and ultimately contribute their skills and effort to the project.
Not only designers but programmers too benefit a lot! Because code becomes heavily "logic or problem solving" instead of maintaining both UI and logic code together in one single file.
However, to compile our GResource we need glib-compile-resources
utility tool. The command usually goes like this:
glib-compile-resources --generate-source --target=<output-file> <input-file>
But how do I create a build script that compiles our gresource files and link it with our target project? I'm still a newbie learning CMake and I've gotten far enough to know what a target is, how to set a variable, how to link a target, and also how to pull in the required GTK packages for linking. But I don't have any clue how to proceed ahead with solving this :(
Solution
A solution to this is using add_custom_command()
to compile your gresources. But first here's a breakdown of what you need for your CMake script:
- Pull in glib-compile-resources as executable program -
find_program()
- Define how to compile your gresource -
add_custom_command()
- Then define your custom target -
add_custom_target()
- Tell CMake that resource is a generated file -
set_source_files_properties()
- Finally, add your custom target to your project target as a dependency -
add_dependencies()
Here's a sample CMake script:
cmake_minimum_required(VERSION 3.15)
project(dummy)
# Step 1:
find_program(GLIB_COMPILE_RESOURCES NAMES glib-compile-resources REQUIRED)
set(GRESOURCE_C test.gresource.c)
set(GRESOURCE_XML test.gresource.xml)
# Step 2:
add_custom_command(
OUTPUT ${GRESOURCE_C}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GLIB_COMPILE_RESOURCES}
ARGS
--target=${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
${GRESOURCE_XML}
VERBATIM
MAIN_DEPENDENCY ${GRESOURCE_XML}
DEPENDS
for.glade
bar.glade
)
# Step 3:
add_custom_target(
dummy-resource
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
)
# Step 4:
add_executable(${PROJECT_NAME} dummy.c ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C})
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
PROPERTIES GENERATED TRUE
)
# Step 5:
add_dependencies(${PROJECT_NAME} dummy-resource)
Brief explanation
add_custom_command()
OUTPUT
- This is your generated resource fileWORKING_DIRECTORY
- Where your XML and glade files are locatedVERBATIM
- Makes sure ourCOMMAND
receivesARGS
unchangedMAIN_DEPENDENCY
- for glib-compile-resources <input-file>DEPENDS
- Your glade file(s). If any of the file changes then your target build is triggered :)
add_custom_target()
dummy-resource
- That's your custom target nameDEPENDS
- The output your custom target needs in order to trigger your custom command
set_source_files_properties()
When you first generate your build files using cmake
command, your resource file isn't generated yet. So CMake will run into error because it doesn't know where your resource file is or where it's coming from. We need to tell CMake "Don't fail, our resource file is generated later"
Use --generate-dependencies
instead of hard-coding
Now you might notice we are duplicating our effort ie., when we add new glade files or remove existing ones (or any other resources such as icon, sounds, css files, etc) we have to edit both our XML and CMake script files. glib-compile-resources
already provide dependency generation so we can use that in our CMake script and make it smart.
The trick is to change your .xml file to .xml.in as a configuration file. So when that configuration file changes, you call glib tool with --generate-dependencies, get new dependency output values, and send that to add_custom_command(... DEPENDS)
. Now we have an intelligent CMake :)
If you want to approach this method then the below post would be really helpful:
Use list as dependencies on add_custom_command
Good luck :)
Answered By - Keyikedalube Ndang