# GLibTools.cmake
#
# Provides functions to run glib tools.
#
# Functions:
#
# glib_mkenums_multiple(_output_filename_noext _define_name _enums_header ...)
#    runs glib-mkenums to generate enumtypes .h and .c files from multiple
#    _enums_header. It searches for files in the current source directory and
#    exports to the current binary directory.
#
# glib_mkenums(_output_filename_noext _enums_header _define_name)
#    runs glib-mkenums to generate enumtypes .h and .c files from _enums_header.
#    It searches for files in the current source directory and exports to the current
#    binary directory.
#
#    An example call is:
#        glib_mkenums(camel-enumtypes camel-enums.h CAMEL_ENUMTYPES_H)
#        which uses camel-enums.h as the source of known enums and generates
#        camel-enumtypes.h which will use the CAMEL_ENUMTYPES_H define
#        and also generates camel-enumtypes.c with the needed code.
#
# glib_genmarshal(_output_filename_noext _prefix _marshallist_filename)
#    runs glib-genmarshal to process ${_marshallist_filename} to ${_output_filename_noext}.c
#    and ${_output_filename_noext}.h files in the current binary directory, using
#    the ${_prefix} as the function prefix.
#
# gdbus_codegen(_xml _interface_prefix _c_namespace _files_prefix _list_gens)
#    runs gdbus-codegen to generate GDBus code from _xml file description,
#    using _interface_prefix, _c_namespace and _files_prefix as arguments.
#    The _list_gens is a list variable are stored expected generated files.
#
#    An example call is:
#        set(GENERATED_DBUS_LOCALE
#               e-dbus-localed.c
#	        e-dbus-localed.h
#        )
#        gdbus_codegen(org.freedesktop.locale1.xml org.freedesktop. E_DBus e-dbus-localed GENERATED_DBUS_LOCALE)
#
# gdbus_codegen_custom(_xml _interface_prefix _c_namespace _files_prefix _list_gens _args)
#    The same as gdbus_codegen() except allows to pass other arguments to the call,
#    like for example --c-generate-object-manager
#
# add_gsettings_schemas(_target _schema0 ...)
#    Adds one or more GSettings schemas. The extension is supposed to be .gschema.xml. The schema file generation
#    is added as a dependency of _target.
#
# glib_compile_resources _sourcedir _outputprefix _cname _inxml ...deps)
#    Calls glib-compile-resources as defined in _inxml and using _outputprefix and_cname as other arguments
#    beside _sourcedir. The optional arguments are other dependencies.

include(PkgConfigEx)
include(UninstallTarget)

find_program(GLIB_MKENUMS glib-mkenums)
if(NOT GLIB_MKENUMS)
	message(FATAL_ERROR "Cannot find glib-mkenums, which is required to build ${PROJECT_NAME}")
endif(NOT GLIB_MKENUMS)

function(glib_mkenums_multiple _output_filename_noext _define_name _enums_header0)
	set(HEADER_TMPL "
/*** BEGIN file-header ***/
#ifndef ${_define_name}
#define ${_define_name}

#include <glib-object.h>

G_BEGIN_DECLS
/*** END file-header ***/

/*** BEGIN file-production ***/

/* Enumerations from \"@basename@\" */

/*** END file-production ***/

/*** BEGIN enumeration-production ***/
#define @ENUMPREFIX@_TYPE_@ENUMSHORT@	(@enum_name@_get_type())
GType @enum_name@_get_type	(void) G_GNUC_CONST;

/*** END enumeration-production ***/

/*** BEGIN file-tail ***/

G_END_DECLS

#endif /* ${_define_name} */
/*** END file-tail ***/")

	file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/enumtypes-${_output_filename_noext}.h.tmpl" "${HEADER_TMPL}\n")

	foreach(_enums_header ${_enums_header0} ${ARGN})
		list(APPEND _enums_headers "${CMAKE_CURRENT_SOURCE_DIR}/${_enums_header}")
	endforeach(_enums_header)

	add_custom_command(
		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
		COMMAND ${GLIB_MKENUMS} --template "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/enumtypes-${_output_filename_noext}.h.tmpl" ${_enums_headers} >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
		DEPENDS ${_enums_headers}
	)

set(SOURCE_TMPL "
/*** BEGIN file-header ***/
#include \"${_output_filename_noext}.h\"
/*** END file-header ***/

/*** BEGIN file-production ***/
/* enumerations from \"@basename@\" */
#include \"@basename@\"

/*** END file-production ***/

/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
	static gsize the_type__volatile = 0;

	if (g_once_init_enter (&the_type__volatile)) {
		static const G\@Type\@Value values[] = {
/*** END value-header ***/

/*** BEGIN value-production ***/
			{ \@VALUENAME\@,
			  \"@VALUENAME@\",
			  \"@valuenick@\" },
/*** END value-production ***/

/*** BEGIN value-tail ***/
			{ 0, NULL, NULL }
		};
		GType the_type = g_\@type\@_register_static (
			g_intern_static_string (\"@EnumName@\"),
			values);
		g_once_init_leave (&the_type__volatile, the_type);
	}
	return the_type__volatile;
}

/*** END value-tail ***/")

	file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/enumtypes-${_output_filename_noext}.c.tmpl" "${SOURCE_TMPL}\n")

	add_custom_command(
		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
		COMMAND ${GLIB_MKENUMS} --template "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/enumtypes-${_output_filename_noext}.c.tmpl" ${_enums_headers} >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
		DEPENDS ${_enums_headers}
	)
endfunction(glib_mkenums_multiple)

function(glib_mkenums _output_filename_noext _enums_header _define_name)
	glib_mkenums_multiple (${_output_filename_noext} ${_define_name} ${_enums_header})
endfunction(glib_mkenums)

find_program(GLIB_GENMARSHAL glib-genmarshal)
if(NOT GLIB_GENMARSHAL)
	message(FATAL_ERROR "Cannot find glib-genmarshal, which is required to build ${PROJECT_NAME}")
endif(NOT GLIB_GENMARSHAL)

function(glib_genmarshal _output_filename_noext _prefix _marshallist_filename)
	add_custom_command(
		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
		COMMAND ${GLIB_GENMARSHAL} --header --skip-source --prefix=${_prefix} "${CMAKE_CURRENT_SOURCE_DIR}/${_marshallist_filename}" >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h.tmp
		COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h.tmp ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
		DEPENDS ${_marshallist_filename}
	)

	add_custom_command(
		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
		COMMAND ${CMAKE_COMMAND} -E echo " #include \\\"${_output_filename_noext}.h\\\"" >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c.tmp
		COMMAND ${GLIB_GENMARSHAL} --body --skip-source --prefix=${_prefix} "${CMAKE_CURRENT_SOURCE_DIR}/${_marshallist_filename}" >>${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c.tmp
		COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c.tmp ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
		DEPENDS ${_marshallist_filename}
	)
endfunction(glib_genmarshal)

find_program(GDBUS_CODEGEN gdbus-codegen)
if(NOT GDBUS_CODEGEN)
	message(FATAL_ERROR "Cannot find gdbus-codegen, which is required to build ${PROJECT_NAME}")
endif(NOT GDBUS_CODEGEN)

function(gdbus_codegen_custom _xml _interface_prefix _c_namespace _files_prefix _list_gens _args)
	add_custom_command(
		OUTPUT ${${_list_gens}}
		COMMAND ${GDBUS_CODEGEN}
		ARGS --interface-prefix ${_interface_prefix}
			--c-namespace ${_c_namespace}
			--generate-c-code ${_files_prefix}
			--generate-docbook ${_files_prefix}
			${_args}
			${CMAKE_CURRENT_SOURCE_DIR}/${_xml}
		MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${_xml}
		VERBATIM
	)
endfunction(gdbus_codegen_custom)

function(gdbus_codegen _xml _interface_prefix _c_namespace _files_prefix _list_gens)
	gdbus_codegen_custom(${_xml} ${_interface_prefix} ${_c_namespace} ${_files_prefix} ${_list_gens} "")
endfunction(gdbus_codegen)

add_printable_option(ENABLE_SCHEMAS_COMPILE "Enable GSettings regeneration of gschemas.compile on install" ON)

if(CMAKE_CROSSCOMPILING)
	find_program(GLIB_COMPILE_SCHEMAS glib-compile-schemas)
else(CMAKE_CROSSCOMPILING)
	pkg_check_variable(GLIB_COMPILE_SCHEMAS gio-2.0 glib_compile_schemas)
endif(CMAKE_CROSSCOMPILING)

if(NOT GLIB_COMPILE_SCHEMAS)
	message(FATAL_ERROR "Cannot find glib-compile-schemas, which is required to build ${PROJECT_NAME}")
endif(NOT GLIB_COMPILE_SCHEMAS)

set(GSETTINGS_SCHEMAS_DIR "${SHARE_INSTALL_PREFIX}/glib-2.0/schemas/")

macro(add_gsettings_schemas _target _schema0)
	set(_install_code)

	foreach(_schema ${_schema0} ${ARGN})
		string(REPLACE ".xml" ".valid" _outputfile "${_schema}")
		get_filename_component(_outputfile "${_outputfile}" NAME)

		get_filename_component(_schema_fullname "${_schema}" DIRECTORY)
		get_filename_component(_schema_filename "${_schema}" NAME)
		if(_schema_fullname STREQUAL "")
			set(_schema_fullname ${CMAKE_CURRENT_SOURCE_DIR}/${_schema})
		else(_schema_fullname STREQUAL "")
			set(_schema_fullname ${_schema})
		endif(_schema_fullname STREQUAL "")

		add_custom_command(
			OUTPUT ${_outputfile}
			COMMAND ${GLIB_COMPILE_SCHEMAS} --strict --dry-run --schema-file=${_schema_fullname}
			COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_schema_fullname}" "${CMAKE_CURRENT_BINARY_DIR}/${_outputfile}"
			DEPENDS ${_schema_fullname}
			VERBATIM
		)
		add_custom_target(gsettings-schemas-${_schema_filename} ALL DEPENDS ${_outputfile})
		add_dependencies(${_target} gsettings-schemas-${_schema_filename})
		if(ENABLE_SCHEMAS_COMPILE)
			# this is required to compile gsettings schemas like after 'make install,
			# because there is no better way in CMake to run a code/script after
			# the whole `make install`
			set(_install_code "${_install_code}
				COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_schema_fullname}\" \"${GSETTINGS_SCHEMAS_DIR}\""
			)
		endif(ENABLE_SCHEMAS_COMPILE)

		# Do both, to have 'uninstall' working properly
		install(FILES ${_schema_fullname}
			DESTINATION ${GSETTINGS_SCHEMAS_DIR})
	endforeach(_schema)

	if(_install_code)
		# Compile gsettings schemas and ensure that all of them are in the place.
		install(CODE
			"if(\"\$ENV{DESTDIR}\" STREQUAL \"\")
				execute_process(${_install_code}
					COMMAND ${CMAKE_COMMAND} -E chdir . \"${GLIB_COMPILE_SCHEMAS}\" \"${GSETTINGS_SCHEMAS_DIR}\"
				)
			endif(\"\$ENV{DESTDIR}\" STREQUAL \"\")")
	endif(_install_code)
endmacro(add_gsettings_schemas)

# This is called too early, when the schemas are not installed yet during `make install`
#
# compile_gsettings_schemas()
#    Optionally (based on ENABLE_SCHEMAS_COMPILE) recompiles schemas at the destination folder
#    after install. It's necessary to call it as the last command in the toplevel CMakeLists.txt,
#    thus the compile runs when all the schemas are installed.
#
if(ENABLE_SCHEMAS_COMPILE)
	add_custom_command(TARGET uninstall POST_BUILD
		COMMAND ${CMAKE_COMMAND} -E chdir . "${GLIB_COMPILE_SCHEMAS}" "${GSETTINGS_SCHEMAS_DIR}"
		COMMENT "Recompile GSettings schemas in '${GSETTINGS_SCHEMAS_DIR}'"
	)
endif(ENABLE_SCHEMAS_COMPILE)

find_program(GLIB_COMPILE_RESOURCES glib-compile-resources)
if(NOT GLIB_COMPILE_RESOURCES)
	message(FATAL_ERROR "Cannot find glib-compile-resources, which is required to build ${PROJECT_NAME}")
endif(NOT GLIB_COMPILE_RESOURCES)

macro(glib_compile_resources _sourcedir _outputprefix _cname _inxml)
	execute_process(
		COMMAND ${GLIB_COMPILE_RESOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/${_inxml} --sourcedir=${_sourcedir} --generate-dependencies
		OUTPUT_VARIABLE ${_outputprefix}_DEPS
	)
	string(REGEX REPLACE "\n" ";" ${_outputprefix}_DEPS "${${_outputprefix}_DEPS}")
	add_custom_command(
		OUTPUT ${_outputprefix}.h
		COMMAND ${GLIB_COMPILE_RESOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/${_inxml} --target=${_outputprefix}.h --sourcedir=${_sourcedir} --c-name ${_cname} --generate-header
		DEPENDS ${_inxml} ${${_outputprefix}_DEPS} ${ARGN}
		VERBATIM
	)
	add_custom_command(
		OUTPUT ${_outputprefix}.c
		COMMAND ${GLIB_COMPILE_RESOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/${_inxml} --target=${_outputprefix}.c --sourcedir=${_sourcedir} --c-name ${_cname} --generate-source
		DEPENDS ${_inxml} ${${_outputprefix}_DEPS} ${ARGN}
		VERBATIM
	)
endmacro(glib_compile_resources)
