1project(
2  'virt-viewer','c',
3  version: '11.0',
4  license: 'GPLv3+',
5  default_options: [
6    'buildtype=debugoptimized',
7    'b_pie=true',
8    'c_std=gnu99',
9    'warning_level=1',
10  ],
11  meson_version: '>= 0.54.0'
12)
13
14pod2man = find_program('pod2man')
15
16cc = meson.get_compiler('c')
17python3 = find_program('python3', required: true)
18# Python3 < 3.7 treats the C locale as 7-bit only. We must force env vars so
19# it treats it as UTF-8 regardless of the user's locale.
20runutf8 = [ 'LC_ALL=', 'LANG=C', 'LC_CTYPE=en_US.UTF-8' ]
21git = run_command('test', '-d', '.git').returncode() == 0
22
23conf_data = configuration_data()
24conf_data.set('VERSION', meson.project_version())
25conf_data.set('PACKAGE_STRING', 'virt-viewer ' + meson.project_version())
26
27# Keep these two definitions in agreement.
28glib_min_version='>=2.48'
29glib_min_version_symbol='GLIB_VERSION_2_48'
30
31# Keep these two definitions in agreement.
32gtk_min_version='>=3.18'
33gtk_min_version_symbol='GDK_VERSION_3_18'
34
35libxml_min_version='>=2.6.0'
36libvirt_min_version='>=1.2.8'
37libvirt_glib_min_version='>=0.1.8'
38gtk_vnc_min_version='>=0.4.0'
39spice_gtk_min_version='>=0.35'
40spice_protocol_min_version='>=0.12.7'
41govirt_min_version='>=0.3.7'
42rest_min_version='>=0.8'
43vte_min_version='>=0.46.0'
44bash_completion_version='2.0'
45
46add_global_arguments('-DGLIB_VERSION_MIN_REQUIRED=@0@'.format(glib_min_version_symbol), language: 'c')
47add_global_arguments('-DGLIB_VERSION_MAX_ALLOWED=@0@'.format(glib_min_version_symbol), language: 'c')
48
49add_global_arguments('-DGDK_VERSION_MIN_REQUIRED=@0@'.format(gtk_min_version_symbol), language: 'c')
50add_global_arguments('-DGDK_VERSION_MAX_ALLOWED=@0@'.format(gtk_min_version_symbol), language: 'c')
51
52cc_flags = []
53
54git_werror = get_option('git_werror')
55if git_werror.enabled() or git_werror.auto() and git
56  cc_flags += [ '-Werror' ]
57endif
58
59cc_flags += [
60  '-fno-common',
61  '-W',
62  '-Wabsolute-value',
63  '-Waddress',
64  '-Waddress-of-packed-member',
65  '-Waggressive-loop-optimizations',
66  '-Wall',
67  '-Wattribute-warning',
68  '-Wattributes',
69  '-Wbool-compare',
70  '-Wbool-operation',
71  '-Wbuiltin-declaration-mismatch',
72  '-Wbuiltin-macro-redefined',
73  '-Wcannot-profile',
74  '-Wcast-align',
75  '-Wcast-align=strict',
76  '-Wcast-function-type',
77  '-Wchar-subscripts',
78  '-Wclobbered',
79  '-Wcomment',
80  '-Wcomments',
81  '-Wcoverage-mismatch',
82  '-Wcpp',
83  '-Wdangling-else',
84  '-Wdate-time',
85  '-Wdeprecated-declarations',
86  '-Wdesignated-init',
87  '-Wdouble-promotion',
88  '-Wdiscarded-array-qualifiers',
89  '-Wdiscarded-qualifiers',
90  '-Wdiv-by-zero',
91  '-Wduplicated-cond',
92  '-Wduplicate-decl-specifier',
93  '-Wempty-body',
94  '-Wendif-labels',
95  '-Wexpansion-to-defined',
96  '-Wextra',
97  '-Wformat-contains-nul',
98  '-Wformat-extra-args',
99  '-Wformat-nonliteral',
100  '-Wformat-security',
101  '-Wformat-y2k',
102  '-Wformat-zero-length',
103  '-Wframe-address',
104  '-Wfree-nonheap-object',
105  '-Whsa',
106  '-Wif-not-aligned',
107  '-Wignored-attributes',
108  '-Wignored-qualifiers',
109  '-Wimplicit',
110  '-Wimplicit-function-declaration',
111  '-Wimplicit-int',
112  '-Wincompatible-pointer-types',
113  '-Winit-self',
114  '-Winline',
115  '-Wint-conversion',
116  '-Wint-in-bool-context',
117  '-Wint-to-pointer-cast',
118  '-Winvalid-memory-model',
119  '-Winvalid-pch',
120  '-Wlogical-not-parentheses',
121  '-Wlogical-op',
122  '-Wmain',
123  '-Wmaybe-uninitialized',
124  '-Wmemset-elt-size',
125  '-Wmemset-transposed-args',
126  '-Wmisleading-indentation',
127  '-Wmissing-attributes',
128  '-Wmissing-braces',
129  '-Wmissing-declarations',
130  '-Wmissing-field-initializers',
131  '-Wmissing-include-dirs',
132  '-Wmissing-parameter-type',
133  '-Wmissing-profile',
134  '-Wmissing-prototypes',
135  '-Wmultichar',
136  '-Wmultistatement-macros',
137  '-Wnarrowing',
138  '-Wnested-externs',
139  '-Wnonnull',
140  '-Wnonnull-compare',
141  '-Wnull-dereference',
142  '-Wodr',
143  '-Wold-style-declaration',
144  '-Wold-style-definition',
145  '-Wopenmp-simd',
146  '-Woverflow',
147  '-Woverride-init',
148  '-Wpacked-bitfield-compat',
149  '-Wpacked-not-aligned',
150  '-Wparentheses',
151  '-Wpointer-arith',
152  '-Wpointer-compare',
153  '-Wpointer-sign',
154  '-Wpointer-to-int-cast',
155  '-Wpragmas',
156  '-Wpsabi',
157  '-Wrestrict',
158  '-Wreturn-local-addr',
159  '-Wreturn-type',
160  '-Wscalar-storage-order',
161  '-Wsequence-point',
162  '-Wshadow',
163  '-Wshift-count-negative',
164  '-Wshift-count-overflow',
165  '-Wshift-negative-value',
166  '-Wsizeof-array-argument',
167  '-Wsizeof-pointer-div',
168  '-Wsizeof-pointer-memaccess',
169  '-Wstrict-aliasing',
170  '-Wstrict-prototypes',
171  '-Wstringop-truncation',
172  '-Wsuggest-attribute=cold',
173  '-Wsuggest-attribute=const',
174  '-Wsuggest-attribute=format',
175  '-Wsuggest-attribute=noreturn',
176  '-Wsuggest-attribute=pure',
177  '-Wsuggest-final-methods',
178  '-Wsuggest-final-types',
179  '-Wswitch',
180  '-Wswitch-bool',
181  '-Wswitch-unreachable',
182  '-Wsync-nand',
183  '-Wtautological-compare',
184  '-Wtrampolines',
185  '-Wtrigraphs',
186  '-Wtype-limits',
187  '-Wuninitialized',
188  '-Wunknown-pragmas',
189  '-Wunused',
190  '-Wunused-but-set-parameter',
191  '-Wunused-but-set-variable',
192  '-Wunused-function',
193  '-Wunused-label',
194  '-Wunused-local-typedefs',
195  '-Wunused-parameter',
196  '-Wunused-result',
197  '-Wunused-value',
198  '-Wunused-variable',
199  '-Wvarargs',
200  '-Wvariadic-macros',
201  '-Wvector-operation-performance',
202  '-Wvla',
203  '-Wvolatile-register-var',
204  '-Wwrite-strings',
205]
206
207# gcc --help=warnings outputs
208ptrdiff_max = cc.sizeof('ptrdiff_t', prefix: '#include <stddef.h>')
209size_max = cc.sizeof('size_t', prefix: '#include <stdint.h>')
210# Compute max safe object size by checking ptrdiff_t and size_t sizes.
211# Ideally we would get PTRDIFF_MAX and SIZE_MAX values but it would
212# give us (2147483647L) and we would have to remove the () and the suffix
213# in order to convert it to numbers to be able to pick the smaller one.
214alloc_max = run_command(
215  'python3', '-c',
216  'print(min(2**(@0@ * 8 - 1) - 1, 2**(@1@ * 8) - 1))'.format(ptrdiff_max, size_max),
217)
218cc_flags += [
219  '-Walloc-size-larger-than=@0@'.format(alloc_max.stdout().strip()),
220  '-Warray-bounds=2',
221  '-Wattribute-alias=2',
222  '-Wformat-overflow=2',
223  '-Wformat-truncation=2',
224  '-Wimplicit-fallthrough=5',
225  '-Wnormalized=nfc',
226  '-Wshift-overflow=2',
227  '-Wstringop-overflow=2',
228  '-Wunused-const-variable=2',
229  '-Wvla-larger-then=4031',
230]
231
232cc_flags += [
233  # So we have -W enabled, and then have to explicitly turn off...
234  '-Wno-sign-compare',
235
236  # We do "bad" function casts all the time for event callbacks
237  '-Wno-cast-function-type',
238
239  # Clang incorrectly complains about dup typedefs win gnu99 mode
240  # so use this Clang-specific arg to keep it quiet
241  '-Wno-typedef-redefinition',
242
243  # We don't use -Wc++-compat so we have to enable it explicitly
244  '-Wjump-misses-init',
245
246  # -Wswitch is enabled but that doesn't report missing enums if a default:
247  # is present
248  '-Wswitch-enum',
249
250  # -Wformat=2 implies -Wformat-nonliteral so we need to manually exclude it
251  '-Wno-format-nonliteral',
252
253  # -Wformat enables this by default, and we should keep it,
254  # but need to rewrite various areas of code first
255  '-Wno-format-truncation',
256
257  # This should be < 256 really. Currently we're down to 4096,
258  # but using 1024 bytes sized buffers (mostly for virStrerror)
259  # stops us from going down further
260  '-Wframe-larger-than=4096',
261
262  # extra special flags
263  '-fexceptions',
264  '-fasynchronous-unwind-tables',
265
266  # Need -fipa-pure-const in order to make -Wsuggest-attribute=pure
267  # fire even without -O.
268  '-fipa-pure-const',
269
270  # We should eventually enable this, but right now there are at
271  # least 75 functions triggering warnings.
272  '-Wno-suggest-attribute=pure',
273  '-Wno-suggest-attribute=const',
274]
275
276# on aarch64 error: -fstack-protector not supported for this target
277if host_machine.cpu_family() != 'aarch64'
278  if host_machine.system() in [ 'linux', 'freebsd', 'windows' ]
279    # we prefer -fstack-protector-strong but fallback to -fstack-protector-all
280    fstack_cflags = cc.first_supported_argument([
281      '-fstack-protector-strong',
282      '-fstack-protector-all',
283    ])
284    cc_flags += fstack_cflags
285
286    # When building with mingw using -fstack-protector requires libssp library
287    # which is included by using -fstack-protector with linker.
288    if fstack_cflags.length() == 1 and host_machine.system() == 'windows'
289      add_project_link_arguments(fstack_cflags, language: 'c')
290    endif
291  endif
292endif
293
294# Clang complains about unused static inline functions which are common
295# with G_DEFINE_AUTOPTR_CLEANUP_FUNC.
296w_unused_function_args = ['-Wunused-function', '-Werror']
297w_unused_function_code = '''
298  static inline void foo(void) {}
299
300  int main(void) { return 0; }
301'''
302# -Wunused-function is implied by -Wall, we must turn it off explicitly.
303if not cc.compiles(w_unused_function_code, args: w_unused_function_args)
304  cc_flags += ['-Wno-unused-function']
305endif
306
307cc_flags_disabled = [
308  # In meson this is specified using 'c_std=gnu99' in project() function.
309  '-std=gnu99',
310
311  # don't care about C++ compiler compat
312  '-Wc++-compat',
313  '-Wabi',
314  '-Wdeprecated',
315
316  # Don't care about ancient C standard compat
317  '-Wtraditional',
318  '-Wtraditional-conversion',
319
320  # Ignore warnings in /usr/include
321  '-Wsystem-headers',
322
323  # Happy for compiler to add struct padding
324  '-Wpadded',
325
326  # GCC very confused with -O2
327  '-Wunreachable-code',
328
329  # Too many to deal with
330  '-Wconversion',
331  '-Wsign-conversion',
332
333  # Need to allow bad cast for execve()
334  '-Wcast-qual',
335
336  # We need to use long long in many places
337  '-Wlong-long',
338
339  # We allow manual list of all enum cases without default
340  '-Wswitch-default',
341
342  # Not a problem since we don't use -fstrict-overflow
343  '-Wstrict-overflow',
344
345  # Not a problem since we don't use -funsafe-loop-optimizations
346  '-Wunsafe-loop-optimizations',
347
348  # gcc 4.4.6 complains this is C++ only; gcc 4.7.0 implies this from -Wall
349  '-Wenum-compare',
350
351  # gcc 5.1 -Wformat-signedness mishandles enums, not ready for prime time
352  '-Wformat-signedness',
353
354  # Several conditionals expand the same on both branches depending on the
355  # particular platform/architecture
356  '-Wduplicated-branches',
357
358  # > This warning does not generally indicate that there is anything wrong
359  # > with your code; it merely indicates that GCC's optimizers are unable
360  # > to handle the code effectively.
361  # Source: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
362  '-Wdisabled-optimization',
363
364  # Various valid glib APIs/macros trigger this warning
365  '-Wbad-function-cast',
366
367  # We might fundamentally need some of these disabled forever, but
368  # ideally we'd turn many of them on
369  '-Wfloat-equal',
370  '-Wpacked',
371  '-Wunused-macros',
372  '-Woverlength-strings',
373  '-Wstack-protector',
374  '-Wsuggest-attribute=malloc',
375
376  # g_return_val_if_fail usage violates this often
377  '-Wdeclaration-after-statement',
378]
379
380foreach flag : cc_flags_disabled
381  if cc_flags.contains(flag)
382    error('@0@ is disabled but listed in cc_flags'.format(flag))
383  endif
384endforeach
385
386supported_cc_flags = cc.get_supported_arguments(cc_flags)
387add_project_arguments(supported_cc_flags, language: 'c')
388
389
390prefix = get_option('prefix')
391localedir = join_paths(prefix, get_option('localedir'))
392datadir = join_paths(prefix, get_option('datadir'))
393mandir = join_paths(prefix, get_option('mandir'))
394
395add_global_arguments('-DLOCALE_DIR="@0@"'.format(localedir), language: 'c')
396
397libm_dep = cc.find_library('m', required : true)
398
399glib_dep = dependency('glib-2.0', version: glib_min_version)
400gmodule_dep = dependency('gmodule-2.0', version: glib_min_version)
401gtk_dep = dependency('gtk+-3.0', version: gtk_min_version)
402
403libxml_dep = dependency('libxml-2.0', version: libxml_min_version)
404
405libvirt_dep = dependency('libvirt', version: libvirt_min_version, required: get_option('libvirt'))
406libvirt_glib_dep = dependency('libvirt-glib-1.0', version: libvirt_glib_min_version, required: get_option('libvirt'))
407if get_option('libvirt').auto()
408  if libvirt_dep.found() and not libvirt_glib_dep.found()
409    libvirt_dep = dependency('', required: false)
410  endif
411  if not libvirt_dep.found() and libvirt_glib_dep.found()
412    libvirt_glib_dep = dependency('', required: false)
413  endif
414endif
415if libvirt_dep.found()
416  conf_data.set('HAVE_LIBVIRT', '1')
417endif
418
419gtk_vnc_dep = dependency('gtk-vnc-2.0', version: gtk_vnc_min_version, required: get_option('vnc'))
420if gtk_vnc_dep.found()
421  conf_data.set('HAVE_GTK_VNC', '1')
422endif
423
424spice_glib_dep = dependency('spice-client-glib-2.0', version: spice_gtk_min_version, required: get_option('spice'))
425spice_gtk_dep = dependency('spice-client-gtk-3.0', version: spice_gtk_min_version, required: get_option('spice'))
426spice_protocol_dep = dependency('spice-protocol', version: spice_protocol_min_version, required: get_option('spice'))
427if get_option('spice').auto()
428  if not spice_protocol_dep.found() or not spice_gtk_dep.found() or not spice_glib_dep.found()
429    spice_protocol_dep = dependency('', required: false)
430    spice_glib_dep = dependency('', required: false)
431    spice_gtk_dep = dependency('', required: false)
432  endif
433endif
434if spice_gtk_dep.found()
435  conf_data.set('HAVE_SPICE_GTK', '1')
436endif
437
438govirt_dep = dependency('govirt-1.0', version: govirt_min_version, required: get_option('ovirt'))
439rest_dep = dependency('rest-0.7', version: rest_min_version, required: get_option('ovirt'))
440if get_option('ovirt').auto()
441  if govirt_dep.found() and not rest_dep.found()
442    govirt_dep = dependency('', required: false)
443  endif
444  if not govirt_dep.found() and rest_dep.found()
445    rest_dep = dependency('', required: false)
446  endif
447endif
448if govirt_dep.found()
449  if cc.has_function('ovirt_storage_domain_get_disks', dependencies: govirt_dep)
450    conf_data.set('HAVE_OVIRT_STORAGE_DOMAIN_GET_DISKS', '1')
451  endif
452  conf_data.set('HAVE_OVIRT', '1')
453endif
454
455vte_dep = dependency('vte-2.91', version: vte_min_version, required: get_option('vte'))
456if vte_dep.found()
457  conf_data.set('HAVE_VTE', '1')
458endif
459
460bash_completion_dep = dependency('bash-completion', version: '>=' + bash_completion_version, required: get_option('bash_completion'))
461
462if bash_completion_dep.found()
463  bash_completion_dir = get_option('bash_completion_dir')
464  if bash_completion_dir == ''
465    bash_completion_dir = bash_completion_dep.get_pkgconfig_variable('completionsdir')
466    bash_completion_prefix = bash_completion_dep.get_pkgconfig_variable('prefix')
467    rc = run_command(
468      'python3', '-c',
469      'print("@0@".replace("@1@", "@2@"))'.format(
470        bash_completion_dir, bash_completion_prefix, prefix,
471      ),
472      check: true,
473    )
474    bash_completion_dir = rc.stdout().strip()
475  endif
476endif
477
478id = get_option('build-id')
479if id != ''
480  conf_data.set('BUILDID', '-@0@'.format(id))
481else
482  conf_data.set('BUILDID', '')
483endif
484osid = get_option('os-id')
485if osid != ''
486  conf_data.set('REMOTE_VIEWER_OS_ID', '"' + osid + '"')
487endif
488
489arr_version = meson.project_version().split('.')
490
491conf_data.set('WINDOWS_PRODUCTVERSION', '@0@.@1@.@2@'.format(
492  arr_version[0],
493  arr_version[1],
494  get_option('build-id')))
495
496if host_machine.system() == 'windows'
497  if host_machine.cpu() != 'x86_64'
498    wixl_arch = 'x86'
499  else
500    wixl_arch = 'x64'
501  endif
502  conf_data.set('WIXL_ARCH', wixl_arch)
503endif
504
505configure_file(
506  input: 'config.h.in',
507  output: 'config.h',
508  configuration: conf_data
509)
510
511if git
512  authors_helper = join_paths(meson.source_root(), 'build-aux', 'gen-authors.py')
513  authors = run_command(python3.path(), authors_helper, env: runutf8)
514  authors_file = 'AUTHORS.in'
515
516  authors_conf = configuration_data()
517  authors_conf.set('authorslist', authors.stdout())
518
519  configure_file(
520    input: authors_file,
521    output: '@BASENAME@',
522    configuration: authors_conf,
523  )
524
525  configure_file(
526    input: 'virt-viewer.spec.in',
527    output: 'virt-viewer.spec',
528    configuration: conf_data
529  )
530
531  configure_file(
532    input: 'mingw-virt-viewer.spec.in',
533    output: 'mingw-virt-viewer.spec',
534    configuration: conf_data
535  )
536
537  dist_helper = join_paths(meson.source_root(), 'build-aux', 'dist.py')
538  meson.add_dist_script(python3.path(), dist_helper, meson.build_root(), 'AUTHORS')
539  meson.add_dist_script(python3.path(), dist_helper, meson.build_root(), 'virt-viewer.spec')
540endif
541
542gnome = import('gnome')
543i18n = import('i18n')
544
545i18n_itsdir = join_paths(meson.source_root(), 'data', 'gettext')
546top_include_dir = [include_directories('.')]
547
548update_mime_database = find_program('update-mime-database', required: false)
549update_icon_cache = find_program('gtk-update-icon-cache', required: false)
550update_desktop_database = find_program('update-desktop-database', required: false)
551
552update_mime_database_path = ''
553if update_mime_database.found()
554  update_mime_database_path = update_mime_database.path()
555endif
556
557update_icon_cache_path = ''
558if update_icon_cache.found()
559  update_icon_cache_path = update_icon_cache.path()
560endif
561
562update_desktop_database_path = ''
563if update_desktop_database.found()
564  update_desktop_database_path = update_desktop_database.path()
565endif
566
567meson.add_install_script('build-aux/post_install.py',
568                         update_mime_database_path,
569                         update_icon_cache_path,
570                         update_desktop_database_path)
571
572subdir('icons')
573subdir('src')
574subdir('po')
575subdir('man')
576subdir('tests')
577if bash_completion_dep.found()
578  subdir('bash-completion')
579endif
580subdir('data')
581