1# GLibTools.cmake
2#
3# Provides functions to run glib tools.
4#
5# Functions:
6#
7# glib_mkenums_multiple(_output_filename_noext _define_name _enums_header ...)
8#    runs glib-mkenums to generate enumtypes .h and .c files from multiple
9#    _enums_header. It searches for files in the current source directory and
10#    exports to the current binary directory.
11#
12# glib_mkenums(_output_filename_noext _enums_header _define_name)
13#    runs glib-mkenums to generate enumtypes .h and .c files from _enums_header.
14#    It searches for files in the current source directory and exports to the current
15#    binary directory.
16#
17#    An example call is:
18#        glib_mkenums(camel-enumtypes camel-enums.h CAMEL_ENUMTYPES_H)
19#        which uses camel-enums.h as the source of known enums and generates
20#        camel-enumtypes.h which will use the CAMEL_ENUMTYPES_H define
21#        and also generates camel-enumtypes.c with the needed code.
22#
23# glib_genmarshal(_output_filename_noext _prefix _marshallist_filename)
24#    runs glib-genmarshal to process ${_marshallist_filename} to ${_output_filename_noext}.c
25#    and ${_output_filename_noext}.h files in the current binary directory, using
26#    the ${_prefix} as the function prefix.
27#
28# gdbus_codegen(_xml _interface_prefix _c_namespace _files_prefix _list_gens)
29#    runs gdbus-codegen to generate GDBus code from _xml file description,
30#    using _interface_prefix, _c_namespace and _files_prefix as arguments.
31#    The _list_gens is a list variable are stored expected generated files.
32#
33#    An example call is:
34#        set(GENERATED_DBUS_LOCALE
35#               e-dbus-localed.c
36#	        e-dbus-localed.h
37#        )
38#        gdbus_codegen(org.freedesktop.locale1.xml org.freedesktop. E_DBus e-dbus-localed GENERATED_DBUS_LOCALE)
39#
40# gdbus_codegen_custom(_xml _interface_prefix _c_namespace _files_prefix _list_gens _args)
41#    The same as gdbus_codegen() except allows to pass other arguments to the call,
42#    like for example --c-generate-object-manager
43#
44# add_gsettings_schemas(_target _schema0 ...)
45#    Adds one or more GSettings schemas. The extension is supposed to be .gschema.xml. The schema file generation
46#    is added as a dependency of _target.
47#
48# glib_compile_resources _sourcedir _outputprefix _cname _inxml ...deps)
49#    Calls glib-compile-resources as defined in _inxml and using _outputprefix and_cname as other arguments
50#    beside _sourcedir. The optional arguments are other dependencies.
51
52include(PkgConfigEx)
53include(UninstallTarget)
54
55find_program(GLIB_MKENUMS glib-mkenums)
56if(NOT GLIB_MKENUMS)
57	message(FATAL_ERROR "Cannot find glib-mkenums, which is required to build ${PROJECT_NAME}")
58endif(NOT GLIB_MKENUMS)
59
60function(glib_mkenums_multiple _output_filename_noext _define_name _enums_header0)
61	set(HEADER_TMPL "
62/*** BEGIN file-header ***/
63#ifndef ${_define_name}
64#define ${_define_name}
65
66#include <glib-object.h>
67
68G_BEGIN_DECLS
69/*** END file-header ***/
70
71/*** BEGIN file-production ***/
72
73/* Enumerations from \"@basename@\" */
74
75/*** END file-production ***/
76
77/*** BEGIN enumeration-production ***/
78#define @ENUMPREFIX@_TYPE_@ENUMSHORT@	(@enum_name@_get_type())
79GType @enum_name@_get_type	(void) G_GNUC_CONST;
80
81/*** END enumeration-production ***/
82
83/*** BEGIN file-tail ***/
84
85G_END_DECLS
86
87#endif /* ${_define_name} */
88/*** END file-tail ***/")
89
90	file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/enumtypes-${_output_filename_noext}.h.tmpl" "${HEADER_TMPL}\n")
91
92	foreach(_enums_header ${_enums_header0} ${ARGN})
93		list(APPEND _enums_headers "${CMAKE_CURRENT_SOURCE_DIR}/${_enums_header}")
94	endforeach(_enums_header)
95
96	add_custom_command(
97		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
98		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
99		DEPENDS ${_enums_headers}
100	)
101
102set(SOURCE_TMPL "
103/*** BEGIN file-header ***/
104#include \"${_output_filename_noext}.h\"
105/*** END file-header ***/
106
107/*** BEGIN file-production ***/
108/* enumerations from \"@basename@\" */
109#include \"@basename@\"
110
111/*** END file-production ***/
112
113/*** BEGIN value-header ***/
114GType
115@enum_name@_get_type (void)
116{
117	static gsize the_type__volatile = 0;
118
119	if (g_once_init_enter (&the_type__volatile)) {
120		static const G\@Type\@Value values[] = {
121/*** END value-header ***/
122
123/*** BEGIN value-production ***/
124			{ \@VALUENAME\@,
125			  \"@VALUENAME@\",
126			  \"@valuenick@\" },
127/*** END value-production ***/
128
129/*** BEGIN value-tail ***/
130			{ 0, NULL, NULL }
131		};
132		GType the_type = g_\@type\@_register_static (
133			g_intern_static_string (\"@EnumName@\"),
134			values);
135		g_once_init_leave (&the_type__volatile, the_type);
136	}
137	return the_type__volatile;
138}
139
140/*** END value-tail ***/")
141
142	file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/enumtypes-${_output_filename_noext}.c.tmpl" "${SOURCE_TMPL}\n")
143
144	add_custom_command(
145		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
146		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
147		DEPENDS ${_enums_headers}
148	)
149endfunction(glib_mkenums_multiple)
150
151function(glib_mkenums _output_filename_noext _enums_header _define_name)
152	glib_mkenums_multiple (${_output_filename_noext} ${_define_name} ${_enums_header})
153endfunction(glib_mkenums)
154
155find_program(GLIB_GENMARSHAL glib-genmarshal)
156if(NOT GLIB_GENMARSHAL)
157	message(FATAL_ERROR "Cannot find glib-genmarshal, which is required to build ${PROJECT_NAME}")
158endif(NOT GLIB_GENMARSHAL)
159
160function(glib_genmarshal _output_filename_noext _prefix _marshallist_filename)
161	add_custom_command(
162		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
163		COMMAND ${GLIB_GENMARSHAL} --header --skip-source --prefix=${_prefix} "${CMAKE_CURRENT_SOURCE_DIR}/${_marshallist_filename}" >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h.tmp
164		COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h.tmp ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
165		DEPENDS ${_marshallist_filename}
166	)
167
168	add_custom_command(
169		OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
170		COMMAND ${CMAKE_COMMAND} -E echo " #include \\\"${_output_filename_noext}.h\\\"" >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c.tmp
171		COMMAND ${GLIB_GENMARSHAL} --body --skip-source --prefix=${_prefix} "${CMAKE_CURRENT_SOURCE_DIR}/${_marshallist_filename}" >>${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c.tmp
172		COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c.tmp ${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
173		DEPENDS ${_marshallist_filename}
174	)
175endfunction(glib_genmarshal)
176
177find_program(GDBUS_CODEGEN gdbus-codegen)
178if(NOT GDBUS_CODEGEN)
179	message(FATAL_ERROR "Cannot find gdbus-codegen, which is required to build ${PROJECT_NAME}")
180endif(NOT GDBUS_CODEGEN)
181
182function(gdbus_codegen_custom _xml _interface_prefix _c_namespace _files_prefix _list_gens _args)
183	add_custom_command(
184		OUTPUT ${${_list_gens}}
185		COMMAND ${GDBUS_CODEGEN}
186		ARGS --interface-prefix ${_interface_prefix}
187			--c-namespace ${_c_namespace}
188			--generate-c-code ${_files_prefix}
189			--generate-docbook ${_files_prefix}
190			${_args}
191			${CMAKE_CURRENT_SOURCE_DIR}/${_xml}
192		MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${_xml}
193		VERBATIM
194	)
195endfunction(gdbus_codegen_custom)
196
197function(gdbus_codegen _xml _interface_prefix _c_namespace _files_prefix _list_gens)
198	gdbus_codegen_custom(${_xml} ${_interface_prefix} ${_c_namespace} ${_files_prefix} ${_list_gens} "")
199endfunction(gdbus_codegen)
200
201add_printable_option(ENABLE_SCHEMAS_COMPILE "Enable GSettings regeneration of gschemas.compile on install" ON)
202
203if(CMAKE_CROSSCOMPILING)
204	find_program(GLIB_COMPILE_SCHEMAS glib-compile-schemas)
205else(CMAKE_CROSSCOMPILING)
206	pkg_check_variable(GLIB_COMPILE_SCHEMAS gio-2.0 glib_compile_schemas)
207endif(CMAKE_CROSSCOMPILING)
208
209if(NOT GLIB_COMPILE_SCHEMAS)
210	message(FATAL_ERROR "Cannot find glib-compile-schemas, which is required to build ${PROJECT_NAME}")
211endif(NOT GLIB_COMPILE_SCHEMAS)
212
213set(GSETTINGS_SCHEMAS_DIR "${SHARE_INSTALL_PREFIX}/glib-2.0/schemas/")
214
215macro(add_gsettings_schemas _target _schema0)
216	set(_install_code)
217
218	foreach(_schema ${_schema0} ${ARGN})
219		string(REPLACE ".xml" ".valid" _outputfile "${_schema}")
220		get_filename_component(_outputfile "${_outputfile}" NAME)
221
222		get_filename_component(_schema_fullname "${_schema}" DIRECTORY)
223		get_filename_component(_schema_filename "${_schema}" NAME)
224		if(_schema_fullname STREQUAL "")
225			set(_schema_fullname ${CMAKE_CURRENT_SOURCE_DIR}/${_schema})
226		else(_schema_fullname STREQUAL "")
227			set(_schema_fullname ${_schema})
228		endif(_schema_fullname STREQUAL "")
229
230		add_custom_command(
231			OUTPUT ${_outputfile}
232			COMMAND ${GLIB_COMPILE_SCHEMAS} --strict --dry-run --schema-file=${_schema_fullname}
233			COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_schema_fullname}" "${CMAKE_CURRENT_BINARY_DIR}/${_outputfile}"
234			DEPENDS ${_schema_fullname}
235			VERBATIM
236		)
237		add_custom_target(gsettings-schemas-${_schema_filename} ALL DEPENDS ${_outputfile})
238		add_dependencies(${_target} gsettings-schemas-${_schema_filename})
239		if(ENABLE_SCHEMAS_COMPILE)
240			# this is required to compile gsettings schemas like after 'make install,
241			# because there is no better way in CMake to run a code/script after
242			# the whole `make install`
243			set(_install_code "${_install_code}
244				COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_schema_fullname}\" \"${GSETTINGS_SCHEMAS_DIR}\""
245			)
246		endif(ENABLE_SCHEMAS_COMPILE)
247
248		# Do both, to have 'uninstall' working properly
249		install(FILES ${_schema_fullname}
250			DESTINATION ${GSETTINGS_SCHEMAS_DIR})
251	endforeach(_schema)
252
253	if(_install_code)
254		# Compile gsettings schemas and ensure that all of them are in the place.
255		install(CODE
256			"if(\"\$ENV{DESTDIR}\" STREQUAL \"\")
257				execute_process(${_install_code}
258					COMMAND ${CMAKE_COMMAND} -E chdir . \"${GLIB_COMPILE_SCHEMAS}\" \"${GSETTINGS_SCHEMAS_DIR}\"
259				)
260			endif(\"\$ENV{DESTDIR}\" STREQUAL \"\")")
261	endif(_install_code)
262endmacro(add_gsettings_schemas)
263
264# This is called too early, when the schemas are not installed yet during `make install`
265#
266# compile_gsettings_schemas()
267#    Optionally (based on ENABLE_SCHEMAS_COMPILE) recompiles schemas at the destination folder
268#    after install. It's necessary to call it as the last command in the toplevel CMakeLists.txt,
269#    thus the compile runs when all the schemas are installed.
270#
271if(ENABLE_SCHEMAS_COMPILE)
272	add_custom_command(TARGET uninstall POST_BUILD
273		COMMAND ${CMAKE_COMMAND} -E chdir . "${GLIB_COMPILE_SCHEMAS}" "${GSETTINGS_SCHEMAS_DIR}"
274		COMMENT "Recompile GSettings schemas in '${GSETTINGS_SCHEMAS_DIR}'"
275	)
276endif(ENABLE_SCHEMAS_COMPILE)
277
278find_program(GLIB_COMPILE_RESOURCES glib-compile-resources)
279if(NOT GLIB_COMPILE_RESOURCES)
280	message(FATAL_ERROR "Cannot find glib-compile-resources, which is required to build ${PROJECT_NAME}")
281endif(NOT GLIB_COMPILE_RESOURCES)
282
283macro(glib_compile_resources _sourcedir _outputprefix _cname _inxml)
284	add_custom_command(
285		OUTPUT ${_outputprefix}.h
286		COMMAND ${GLIB_COMPILE_RESOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/${_inxml} --target=${_outputprefix}.h --sourcedir=${_sourcedir} --c-name ${_cname} --generate-header
287		DEPENDS ${_inxml} ${ARGN}
288		VERBATIM
289	)
290	add_custom_command(
291		OUTPUT ${_outputprefix}.c
292		COMMAND ${GLIB_COMPILE_RESOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/${_inxml} --target=${_outputprefix}.c --sourcedir=${_sourcedir} --c-name ${_cname} --generate-source
293		DEPENDS ${_inxml} ${ARGN}
294		VERBATIM
295	)
296endmacro(glib_compile_resources)
297