diff --git a/README.md b/README.md index 404ee2b..e0d2c1d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,251 @@ # GCR_CMake -A CMake extension supporting the glib-compile-resources tool. +A CMake extension supporting the **glib-compile-resources** tool. + +About +----- + +Inspired from the macros for Vala that I used to build a GTK application, I came +to the point where I needed resources for it. For that purpose the +**glib-compile-resources** tool seemed to be the right choice, but the extra XML +file you need to write bothered me. If I add a resource I don't want to mark it +explicitly in CMake and in the XML file. So I came up with this macro that does +everything for you. You just add your resource to a resource list (with +eventually some attributes like compression) and invoke the resource compilation +function. It generates the XML automatically for you. Quite simple, isn't it? + +Clone +----- + +To clone this repository, just type + +```shell +git clone https://github.com/Makman2/GCR_CMake +``` + +Usage +----- + +Just add the macro directory to your `CMAKE_MODULE_PATH`, include the CMake +file and you are ready to go. + +```cmake +list(APPEND CMAKE_MODULE_PATH + ${PATH_TO_GCR_CMAKE_PARENT_DIR}/GCR_CMake/macros) + +include(GlibCompileResourcesSupport) +``` + +Reference +--------- + +The resource compiling macro is quite powerful and handles as much errors as +possible to make error-finding easier. The function is defined as follows: + +``` +compile_gresources( + [TYPE output_type] + [TARGET output_name] + [RESOURCES [resources...]] + [OPTIONS [command_line_options...]] + [PREFIX resource_prefix] + [SOURCE_DIR resource_directory] + [COMPRESS_ALL] [NO_COMPRESS_ALL] + [STRIPBLANKS_ALL] [NO_STRIPBLANKS_ALL] + [TOPIXDATA_ALL] [NO_TOPIXDATA_ALL]) +``` + +- ***output*** + + The variable name where to set the output file names. Pass this variable to a + target as a dependency (i.e. `add_custom_target`). + +- **TYPE** ***output_type*** + + The resource type to generate. Valid values are `EMBED_C`, `EMBED_H`, `BUNDLE` + or `AUTO`. Anything else will default to `AUTO`. + + - `EMBED_C`: Generate a C code file that can be compiled with your program. + + - `EMBED_H`: Generate a header file to include in your program. + + - `BUNDLE`: Generates a resource disk file to load at runtime. + + - `AUTO` (or anything else): Extract mode from file ending specified in + `TARGET`. + + If `TARGET` contains + an invalid file or file ending not detectable, the function results in a + **FATAL_ERROR**. + + Recognized file formats are: *.gresource*, *.c*, *.h*. + +- **TARGET** ***output_name*** + + Overrides the default output file name. If not specified (and not + `AUTO`-**TYPE** is set) the output name is *resources.[type-ending]*. + +- **RESOURCES** ***[resources...]*** + + The resource list to process. Each resource must be a relative path inside the + source directory. Each file can be preceded with resource flags. + + - `COMPRESS` flag + + Compress the following file. Effectively sets the attribute *compressed* in + the XML file to true. + + - `STRIPBLANKS` flag + + Strip whitespace characters in XML files. Sets the *preprocess* attribute in + XML with *xml-stripblanks* (requires XMLLint). + + - `TOPIXDATA` flag + + Generates a pixdata ready to use in Gdk. Sets the *preprocess* attribute in + XML with *to-pixdata*. + + Note: Using `STRIPBLANKS` and `TOPIXDATA` together results in a + **FATAL_ERROR**. + +- **OPTIONS** ***command_line_options*** + + Extra command line arguments passed to **glib_compile_resources**. For example + `--internal` or `--manual-register`. + +- **PREFIX** ***resource_prefix*** + + Overrides the resource prefix. The resource entries get inside the XML a + prefix that is prepended to each resource file and represents as a whole the + resource path. + +- **SOURCE_DIR** ***resource_directory*** + + The source directory where the resource files are. If not overridden, this + value is set to `CMAKE_SOURCE_DIR`. + +- **COMPRESS_ALL**, **NO_COMPRESS_ALL** + + Overrides the `COMPRESS` flag in all resources. If **COMPRESS_ALL** is + specified, `COMPRESS` is set everywhere regardless of the specified resource + flags. If **NO_COMPRESS_ALL** is specified, compression is deactivated in all + resources. + + Specifying **COMPRESS_ALL** and **NO_COMPRESS_ALL** together results in a + **FATAL_ERROR**. + +- **STRIPBLANKS_ALL**, **NO_STRIPBLANKS_ALL** + + Overrides the `STRIPBLANKS` flag in all resources. If **STRIPBLANKS_ALL** is + specified, `STRIPBLANKS` is set everywhere regardless of the specified + resource flags. If **NO_STRIPBLANKS_ALL** is specified, stripping away + whitespaces is deactivated in all resources. + + Specifying **STRIPBLANKS_ALL** and **NO_STRIPBLANKS_ALL** together results in + a **FATAL_ERROR**. + +- **TOPIXDATA_ALL**, **NO_TOPIXDATA_ALL** + + Overrides the `TOPIXDATA` flag in all resources. If **TOPIXDATA_ALL** is + specified, `TOPIXDATA` is set everywhere regardless of the specified resource + flags. If **NO_TOPIXDATA_ALL** is specified, converting into pixbufs is + deactivated in all resources. + + Specifying **TOPIXDATA_ALL** and **NO_TOPIXDATA_ALL** together results in a + **FATAL_ERROR**. + +Kickstart +--------- + +This is a quick start guide to provide you an easy start with this macro. + +Starting with a simple example: + +```cmake +set(RESOURCE_LIST + info.txt + img/image1.jpg + img/image2.jpg + data.xml) + +compile_gresources(RESOURCE_FILE + TYPE BUNDLE + RESOURCES ${RESOURCE_LIST}) +``` + +What does this snippet do? First it sets some resource files to pack into a +resource file. They are located in the source directory passed to CMake at +invocation (`CMAKE_SOURCE_DIR`). +After that we compile the resources. Means we generate a *.gresource.xml*-file +automatically from our `RESOURCE_LIST` and create a custom command that compiles +the generated *.gresource.xml*-file with the provided resources into a resource +bundle. Since no specific output file is specified via **TARGET** the output +file is placed into the `CMAKE_BINARY_DIR` with the name *resources.gresource*. +The first argument `RESOURCE_FILE` is a variable that is filled with the output +file name, so with *resources.gresource* inside the build directory. This +variable is helpful to create makefile targets (or to process the output file +differently). + +So here comes a full *CMakeLists.txt* that creates the resources from before. + +```cmake +# Minimum support is guaranteed for CMake 2.8. Everything below needs to be +# tested. +cmake_minimum_required(2.8) + +project(MyResourceFile) + +# Include the extension module. +list(APPEND CMAKE_MODULE_PATH + ${PATH_TO_GCR_CMAKE_PARENT_DIR}/GCR_CMake/macros) + +include(GlibCompileResourcesSupport) + +# Set the resources to bundle. +set(RESOURCE_LIST + info.txt + img/image1.jpg + img/image2.jpg + data.xml) + +# Compile the resources. +compile_gresources(RESOURCE_FILE + TYPE BUNDLE + RESOURCES ${RESOURCE_LIST}) + +# Add a custom target to the makefile. Now make builds our resource file. +# It depends on the output RESOURCE_FILE. +add_custom_target(resource ALL DEPENDS ${RESOURCE_FILE}) +``` + +A nice feature of the `compile_gresources`-macro is that it supports +individually setting flags on the resources. So we can extend our resource list +like that: + +```cmake +set(RESOURCE_LIST + info.txt + COMPRESS img/image1.jpg + COMPRESS img/image2.jpg + STRIPBLANKS data.xml) +``` + +This resource list not only simply includes the resources, it specifies also +that the two images should be compressed and in *data.xml* the whitespaces +should be removed. This resource list will include the same files but will +preprocess some of them. + +Very handy are the `COMPRESS_ALL`, `STRIPBLANKS_ALL` or `TOPIXDATA_ALL` +parameters (and their `NO_`-equivalents). If you are too lazy to write before +every file the flag, just invoke `compile_gresources` with them. + +```cmake +# Compile the resources. Compresses all files regardless if you specified it +# explicitly or not. +compile_gresources(RESOURCE_FILE + TYPE BUNDLE + RESOURCES ${RESOURCE_LIST} + COMPRESS_ALL) +``` + +So that's a short introduction into the operating mode of the +`compile-gresources` macro. diff --git a/macros/GlibCompileResourcesSupport.cmake b/macros/GlibCompileResourcesSupport.cmake new file mode 100644 index 0000000..b9abd06 --- /dev/null +++ b/macros/GlibCompileResourcesSupport.cmake @@ -0,0 +1,245 @@ +include(CMakeParseArguments) + +# Finds the glib-compile-resources executable. +find_program(GLIB_COMPILE_RESOURCES_EXECUTABLE glib-compile-resources) +mark_as_advanced(GLIB_COMPILE_RESOURCES_EXECUTABLE) + +# Compiles a gresource resource file from given resource files. Automatically +# creates the XML controlling file. +# The type of resource to generate (header, c-file or bundle) is automatically +# determined from TARGET file ending, if no TYPE is explicitly specified. +# The output file is stored in the provided variable "output". +# If you want to use preprocessing, you need to manually check the existence +# of the tools you use. This function doesn't check this for you, it just +# generates the XML file. glib-compile-resources will then throw a +# warning/error. +function(COMPILE_GRESOURCES output) + # Available options: + # COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all + # registered resources. + # STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all + # registered resources. + # TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all + # registered resources. + set(CG_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL + STRIPBLANKS_ALL NO_STRIPBLANKS_ALL + TOPIXDATA_ALL NO_TOPIXDATA_ALL) + + # Available one value options: + # TYPE Type of resource to create. Valid options are: + # EMBED_C: A C-file that can be compiled with your project. + # EMBED_H: A header that can be included into your project. + # BUNDLE: Generates a resource bundle file that can be loaded + # at runtime. + # AUTO: Determine from target file ending. Need to specify + # target argument. + # PREFIX Overrides the resource prefix that is prepended to each + # relative file name in registered resources. + # SOURCE_DIR Overrides the resources base directory to search for resources. + # Normally this is set to the source directory with that CMake + # was invoked (CMAKE_SOURCE_DIR). + # TARGET Overrides the name of the output file/-s. Normally the output + # names from glib-compile-resources tool is taken. + set(CG_ONEVALUEARGS TYPE PREFIX SOURCE_DIR TARGET) + + # Available multi-value options: + # RESOURCES The list of resource files. Whether absolute or relative path is + # equal, absolute paths are stripped down to relative ones. If the + # absolute path is not inside the given base directory SOURCE_DIR + # or CMAKE_SOURCE_DIR (if SOURCE_DIR is not overriden), this + # function aborts. + # OPTIONS Extra command line options passed to glib-compile-resources. + set(CG_MULTIVALUEARGS RESOURCES OPTIONS) + + # Parse the arguments. + cmake_parse_arguments(CG_ARG + "${CG_OPTIONS}" + "${CG_ONEVALUEARGS}" + "${CG_MULTIVALUEARGS}" + "${ARGN}") + + # Variable to store the double-quote (") string. Since escaping + # double-quotes in strings is not possible we need a helper variable that + # does this job for us. + set(Q \") + + # Check invocation validity with the _UNPARSED_ARGUMENTS variable. + # If other not recognized parameters were passed, throw error. + if (CG_ARG_UNPARSED_ARGUMENTS) + set(CG_WARNMSG + "Invocation of COMPILE_GRESOURCES with unrecognized parameters.") + set(CG_WARNMSG + "${CG_WARNMSG} Parameters are: ${CG_ARG_UNPARSED_ARGUMENTS}.") + message(WARNING ${CG_WARNMSG}) + endif() + + # Check invocation validity depending on generation mode (EMBED_C, EMBED_H + # or BUNDLE). + if ("${CG_ARG_TYPE}" STREQUAL "EMBED_C") + # EMBED_C mode, output compilable C-file. + set(CG_GENERATE_COMMAND_LINE "--generate-source") + set(CG_TARGET_FILE_ENDING "c") + elseif ("${CG_ARG_TYPE}" STREQUAL "EMBED_H") + # EMBED_H mode, output includable header file. + set(CG_GENERATE_COMMAND_LINE "--generate-header") + set(CG_TARGET_FILE_ENDING "h") + elseif ("${CG_ARG_TYPE}" STREQUAL "BUNDLE") + # BUNDLE mode, output resource bundle. Don't do anything since + # glib-compile-resources outputs a bundle when not specifying + # something else. + set(CG_TARGET_FILE_ENDING "gresource") + else() + # Everything else is AUTO mode, determine from target file ending. + if (CG_ARG_TARGET) + set(CG_GENERATE_COMMAND_LINE "--generate") + else() + set(CG_ERRMSG + "AUTO mode given, but no target specified. Can't determine") + set(CG_ERRMSG + "${CG_ERRMSG} output type. In function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + endif() + + # Check flag validity. + if (CG_ARG_COMPRESS_ALL AND CG_ARG_NO_COMPRESS_ALL) + set(CG_ERRMSG + "COMPRESS_ALL and NO_COMPRESS_ALL simultaneously set. In function") + set(CG_ERRMSG + "${CG_ERRMSG} COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + if (CG_ARG_STRIPBLANKS_ALL AND CG_ARG_NO_STRIPBLANKS_ALL) + set(CG_ERRMSG + "STRIPBLANKS_ALL and NO_STRIPBLANKS_ALL simultaneously set. In") + set(CG_ERRMSG + "${CG_ERRMSG} function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + if (CG_ARG_TOPIXDATA_ALL AND CG_ARG_NO_TOPIXDATA_ALL) + set(CG_ERRMSG + "TOPIXDATA_ALL and NO_TOPIXDATA_ALL simultaneously set. In") + set(CG_ERRMSG + "${CG_ERRMSG} function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + + # Check if there are any resources. + if (NOT CG_ARG_RESOURCES) + set(CG_ERRMSG + "No resource files to process. In function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + + # Process resources and generate XML file. + # Begin with the XML header and header nodes. + set(CG_XML_FILE "") + set(CG_XML_FILE "${CG_XML_FILE}") + + # Process each resource. + foreach(res ${CG_ARG_RESOURCES}) + if ("${res}" STREQUAL "COMPRESS") + set(CG_COMPRESSION_FLAG ON) + elseif ("${res}" STREQUAL "STRIPBLANKS") + set(CG_STRIPBLANKS_FLAG ON) + elseif ("${res}" STREQUAL "TOPIXDATA") + set(CG_TOPIXDATA_FLAG ON) + else() + # The file name. + set(CG_RESOURCE_PATH "${res}") + + # Append to real resource file dependency list. + list(APPEND CG_RESOURCES_DEPENDENCIES ${CG_RESOURCE_PATH}) + + # Assemble node. + set(CG_RES_LINE "${CG_RESOURCE_PATH}") + + # Append to file string. + set(CG_XML_FILE + "${CG_XML_FILE}${CG_RES_LINE}") + + # Unset variables. + unset(CG_COMPRESSION_FLAG) + unset(CG_STRIPBLANKS_FLAG) + unset(CG_TOPIXDATA_FLAG) + endif() + + endforeach(res) + + # Append closing nodes. + set(CG_XML_FILE "${CG_XML_FILE}") + + # Use "file" function to generate XML controlling file. + set(CG_XML_FILE_NAME ".gresource.xml") + set(CG_XML_FILE_PATH "${CMAKE_BINARY_DIR}/${CG_XML_FILE_NAME}") + message(STATUS "Generating GResource XML file (${CG_XML_FILE_NAME}).") + file(WRITE ${CG_XML_FILE_PATH} ${CG_XML_FILE}) + + # Create target manually if not set (to make sure glib-compile-resources + # doesn't change behaviour with it's naming standards). + if (NOT CG_ARG_TARGET) + set(CG_ARG_TARGET "${CMAKE_BINARY_DIR}/resources") + set(CG_ARG_TARGET "${CG_ARG_TARGET}.${CG_TARGET_FILE_ENDING}") + endif() + + # Create source directory automatically if not set. + if (NOT CG_ARG_SOURCE_DIR) + set(CG_ARG_SOURCE_DIR "${CMAKE_SOURCE_DIR}") + endif() + + # Add compilation target for resources. + add_custom_command(OUTPUT ${CG_ARG_TARGET} + COMMAND ${GLIB_COMPILE_RESOURCES_EXECUTABLE} + ARGS + ${OPTIONS} + "--target=${Q}${CG_ARG_TARGET}${Q}" + "--sourcedir=${Q}${CG_ARG_SOURCE_DIR}${Q}" + ${CG_GENERATE_COMMAND_LINE} + ${CG_XML_FILE_PATH} + MAIN_DEPENDENCY ${CG_XML_FILE_PATH} + DEPENDS ${CG_RESOURCES_DEPENDENCIES} + WORKING_DIRECTORY ${CMAKE_BUILD_DIR}) + + # Set output to parent scope. + set(${output} ${CG_ARG_TARGET} PARENT_SCOPE) + +endfunction(COMPILE_GRESOURCES)