summaryrefslogtreecommitdiff
path: root/cmake/modules/GLibTools.cmake
blob: fc8eb827c868f128c26e07b378e59017eea01720 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# GLibTools.cmake
#
# Provides functions to run glib tools.
#
# Functions:
#
# 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 _output_filename_noext _enums_header _define_name)
	set(HEADER_TMPL "
/*** BEGIN file-header ***/
#ifndef ${_define_name}
#define ${_define_name}
/*** END file-header ***/

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

#include <glib-object.h>

G_BEGIN_DECLS

/* Enumerations from \"@filename@\" */

/*** 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")

	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" "${CMAKE_CURRENT_SOURCE_DIR}/${_enums_header}" >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.h
		DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_enums_header}
	)

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

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

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

/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
	static volatile 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" "${CMAKE_CURRENT_SOURCE_DIR}/${_enums_header}" >${CMAKE_CURRENT_BINARY_DIR}/${_output_filename_noext}.c
		DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_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 --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 --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}
		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
			"execute_process(${_install_code}
				COMMAND ${CMAKE_COMMAND} -E chdir . \"${GLIB_COMPILE_SCHEMAS}\" \"${GSETTINGS_SCHEMAS_DIR}\"
			)")
	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)
	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} ${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} ${ARGN}
		VERBATIM
	)
endmacro(glib_compile_resources)