diff --git a/bash-completion/meson.build b/bash-completion/meson.build new file mode 100644 index 0000000..29796aa --- /dev/null +++ b/bash-completion/meson.build @@ -0,0 +1,4 @@ +install_data( + 'virt-viewer', + install_dir: bash_completion_dir +) diff --git a/build-aux/buildenv.py b/build-aux/buildenv.py new file mode 100644 index 0000000..9d46228 --- /dev/null +++ b/build-aux/buildenv.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 + +import subprocess + +rpms = subprocess.check_output(["rpm", "-qa"]) +rpms = rpms.decode("utf-8").split("\n") +rpms.sort() + +for rpm in rpms: + if rpm == "": + continue + print(rpm, end="\r\n") diff --git a/build-aux/dist.py b/build-aux/dist.py new file mode 100755 index 0000000..a1d36c2 --- /dev/null +++ b/build-aux/dist.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import os +import sys + +meson_build_root = sys.argv[1] +file_name = sys.argv[2] + +meson_dist_root = os.environ['MESON_DIST_ROOT'] + +os.system('cp {0} {1}'.format( + os.path.join(meson_build_root, file_name), + os.path.join(meson_dist_root, file_name) +)) diff --git a/build-aux/gen-authors.py b/build-aux/gen-authors.py new file mode 100755 index 0000000..8551e9e --- /dev/null +++ b/build-aux/gen-authors.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 + +import os + +meson_source_root = os.environ['MESON_SOURCE_ROOT'] + +os.chdir(meson_source_root) +os.system('git log --pretty=format:"* %aN <%aE>" | sort -u') diff --git a/build-aux/msitool.py b/build-aux/msitool.py new file mode 100755 index 0000000..b6270ed --- /dev/null +++ b/build-aux/msitool.py @@ -0,0 +1,80 @@ +#!/usr/bin/python3 + +import os +import subprocess +import sys +import tempfile + +if len(sys.argv) != 13: + print("syntax: %s BUILD-DIR PREFIX WIXL-ARCH MSIFILE WXS " + "BUILDENV WIXL-HEAT-PATH WIXL-PATH " + "HAVE_SPICE HAVE_VNC HAVE_LIBVIRT HAVE_OVIRT" % sys.argv[0]) + sys.exit(1) + +builddir = sys.argv[1] +prefix = sys.argv[2] +arch = sys.argv[3] +msifile = sys.argv[4] +wxs = sys.argv[5] +buildenv = sys.argv[6] + +wixl_heat = sys.argv[7] +wixl = sys.argv[8] + +have_spice = sys.argv[9] +have_vnc = sys.argv[10] +have_libvirt = sys.argv[11] +have_ovirt = sys.argv[12] + +def build_msi(): + if "DESTDIR" not in os.environ: + print("$DESTDIR environment variable missing. " + "Please run 'ninja install' before attempting to " + "build the MSI binary, and set DESTDIR to point " + "to the installation virtual root.", file=sys.stderr) + sys.exit(1) + vroot = os.environ["DESTDIR"] + + manifest = [] + for root, subFolder, files in os.walk(vroot): + for item in files: + path = str(os.path.join(root,item)) + manifest.append(path) + + wxsfiles = subprocess.run( + [ + wixl_heat, + "-p", vroot + prefix + "/", + "--component-group", "CG.virt-viewer", + "--var", "var.DESTDIR", + "--directory-ref", "INSTALLDIR", + ], + input="\n".join(manifest), + encoding="utf8", + check=True, + capture_output=True) + + wxsfilelist = os.path.join(builddir, "data", "virt-viewer-files.wxs") + with open(wxsfilelist, "w") as fh: + print(wxsfiles.stdout, file=fh) + + wixlenv = os.environ + wixlenv["MANUFACTURER"] = "Virt Viewer Project" + + subprocess.run( + [ + wixl, + "-D", "SourceDir=" + prefix, + "-D", "DESTDIR=" + vroot + prefix, + "-D", "HaveSpiceGtk=" + have_spice, + "-D", "HaveGtkVnc=" + have_vnc, + "-D", "HaveLibvirt=" + have_libvirt, + "-D", "HaveOvirt=" + have_ovirt, + "--arch", arch, + "-o", msifile, + wxs, wxsfilelist, + ], + check=True, + env=wixlenv) + +build_msi() diff --git a/build-aux/post_install.py b/build-aux/post_install.py new file mode 100755 index 0000000..a7318c4 --- /dev/null +++ b/build-aux/post_install.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import os +import os.path +import subprocess +import sys + +if "MESON_INSTALL_PREFIX" not in os.environ: + print("This is meant to be run from Meson only", file=sys.stderr) + sys.exit(1) + +# If installing into a DESTDIR we assume +# this is a distro packaging build, so skip actions +if os.environ.get("DESTDIR", "") != "": + sys.exit(0) + +if len(sys.argv) != 4: + print("%s UPDATE-MIME-DATABASE UPDATE-ICON-CACHE UPDATE-DESKTOP-DATABASE") + sys.exit(1) + +prefix = os.environ["MESON_INSTALL_PREFIX"] + +update_mime_database = sys.argv[1] +update_icon_cache = sys.argv[2] +update_desktop_database = sys.argv[3] + +if update_mime_database != "": + print("Updating mime database") + subprocess.run([update_mime_database, + os.path.join(prefix, "share", "mime")], + check=True) +else: + print("Skipping mime database update") + +if update_icon_cache != "": + print("Updating icon cache") + subprocess.run([update_icon_cache, "-qtf", + os.path.join(prefix, "share", "icons", "hicolor")], + check=True) +else: + print("Skipping icon cache update") + +if update_desktop_database != "": + print("Updating desktop database") + subprocess.run([update_desktop_database, "-q", + os.path.join(prefix, "share", "applications")], + check=True) +else: + print("Skipping desktop database update") diff --git a/build-aux/run-windres.py b/build-aux/run-windres.py new file mode 100644 index 0000000..011cab8 --- /dev/null +++ b/build-aux/run-windres.py @@ -0,0 +1,18 @@ +#!/usr/bin/python3 + +import sys +import subprocess + +windres = sys.argv[1] +icondir = sys.argv[2] +manifestdir = sys.argv[3] +rcfile = sys.argv[4] +objfile = sys.argv[5] + +subprocess.check_call([ + windres, + "-DICONDIR=\\\"" + icondir + "\\\"", + "-DMANIFESTDIR=\\\"" + manifestdir + "\\\"", + "-i", rcfile, + "-o", objfile, + ]) diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..1d72f7e --- /dev/null +++ b/config.h.in @@ -0,0 +1,64 @@ +/* Enable compile-time and run-time bounds-checking, and some warnings. */ +#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ +# define _FORTIFY_SOURCE 2 +#endif + +/* Name of package */ +#define PACKAGE "virt-viewer" + +/* GETTEXT package name */ +#define GETTEXT_PACKAGE "virt-viewer" + +/* GLib logging domain */ +#define G_LOG_DOMAIN "virt-viewer" + +/* Enable GNU extensions */ +#define _GNU_SOURCE + +/* Build version details */ +#define BUILDID "-@BUILDID@" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "virt-viewer @VERSION@" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "@VERSION@" + +/* OS ID for this build */ +#define REMOTE_VIEWER_OS_ID "@OS_ID@" + +/* Version number of package */ +#define VERSION "@VERSION@" + +/* Have gtk-vnc? */ +#mesondefine HAVE_GTK_VNC + +/* Have libvirt? */ +#mesondefine HAVE_LIBVIRT + +/* Have libgovirt? */ +#mesondefine HAVE_OVIRT + +/* Define to 1 if you have the `ovirt_api_search_vms' function. */ +#undef HAVE_OVIRT_API_SEARCH_VMS + +/* Define to 1 if you have the `ovirt_cluster_get_data_center' function. */ +#undef HAVE_OVIRT_CLUSTER_GET_DATA_CENTER + +/* Have support for data center */ +#undef HAVE_OVIRT_DATA_CENTER + +/* Define to 1 if you have the `ovirt_host_get_cluster' function. */ +#undef HAVE_OVIRT_HOST_GET_CLUSTER + +/* Define to 1 if you have the `ovirt_storage_domain_get_disks' function. */ +#undef HAVE_OVIRT_STORAGE_DOMAIN_GET_DISKS + +/* Define to 1 if you have the `ovirt_vm_get_host' function. */ +#undef HAVE_OVIRT_VM_GET_HOST + +/* Have spice-gtk? */ +#mesondefine HAVE_SPICE_GTK + +/* Have vte? */ +#mesondefine HAVE_VTE diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 0000000..d718491 --- /dev/null +++ b/data/meson.build @@ -0,0 +1,115 @@ +if host_machine.system() != 'windows' + desktop = 'remote-viewer.desktop' + + i18n.merge_file ( + desktop, + type: 'desktop', + input: desktop + '.in', + output: desktop, + po_dir: po_dir, + install: true, + install_dir: join_paths(datadir, 'applications') + ) + + mimetypes = 'virt-viewer-mime.xml' + + i18n.merge_file ( + mimetypes, + type: 'xml', + input: mimetypes + '.in', + output: mimetypes, + data_dirs: i18n_itsdir, + po_dir: po_dir, + install: true, + install_dir: join_paths(datadir, 'mime', 'packages') + ) + + metainfo = 'remote-viewer.appdata.xml' + + i18n.merge_file ( + metainfo, + type: 'xml', + input: metainfo + '.in', + output: metainfo, + po_dir: po_dir, + install: true, + install_dir: join_paths(datadir, 'metainfo') + ) +endif + +with_msi=false +if host_machine.system() == 'windows' + wixl = find_program('wixl', required: false) + wixl_heat = find_program('wixl-heat', required: false) + + if wixl.found() and wixl_heat.found() + with_msi=true + endif +endif + +if with_msi + buildenv = custom_target( + 'buildenv.txt', + output: ['buildenv.txt'], + command: [ + python3, + join_paths(meson.source_root(), 'build-aux', 'buildenv.py') + ], + capture: true) + + msi_filename = 'virt-viewer-@0@-@1@.msi'.format(wixl_arch, meson.project_version()) + + if libvirt_dep.found() + wixl_libvirt_arg = 'True' + else + wixl_libvirt_arg = 'False' + endif + + if spice_gtk_dep.found() + wixl_spice_gtk_arg = 'True' + else + wixl_spice_gtk_arg = 'False' + endif + + if gtk_vnc_dep.found() + wixl_gtk_vnc_arg = 'True' + else + wixl_gtk_vnc_arg = 'False' + endif + + if govirt_dep.found() + wixl_govirt_arg = 'True' + else + wixl_govirt_arg = 'False' + endif + + wxsfile = configure_file( + input: 'virt-viewer.wxs.in', + output: 'virt-viewer.wxs', + configuration: conf_data + ) + + msi = custom_target( + msi_filename, + input: [wxsfile, buildenv], + output: [msi_filename], + build_by_default: false, + command: [ + python3, + join_paths(meson.source_root(), 'build-aux', 'msitool.py'), + meson.build_root(), + prefix, + wixl_arch, + join_paths(meson.build_root(), 'data', msi_filename), + wxsfile, + buildenv, + wixl_heat, + wixl, + wixl_spice_gtk_arg, + wixl_gtk_vnc_arg, + wixl_libvirt_arg, + wixl_govirt_arg, + ], + ) + +endif diff --git a/icons/meson.build b/icons/meson.build new file mode 100644 index 0000000..a77cda7 --- /dev/null +++ b/icons/meson.build @@ -0,0 +1,62 @@ +logo_icon_sizes = [ + '16x16', + '22x22', + '24x24', + '32x32', + '48x48', + '256x256', + 'scalable', +] + +misc_icon_sizes = [ + '24x24', + 'scalable', +] + +misc_icon_names = [ + 'usb', +] + +foreach icon_size: logo_icon_sizes + if icon_size == 'scalable' + src_icon = 'virt-viewer.svg' + else + src_icon = 'virt-viewer.png' + endif + install_data( + join_paths(icon_size, src_icon), + install_dir: join_paths(datadir, 'icons', 'hicolor', icon_size, 'apps') + ) +endforeach + +foreach icon_size: misc_icon_sizes + foreach icon_name: misc_icon_names + if icon_size == 'scalable' + src_icon = 'virt-viewer-' + icon_name + '.svg' + else + src_icon = 'virt-viewer-' + icon_name + '.png' + endif + install_data( + join_paths(icon_size, src_icon), + install_dir: join_paths(datadir, 'icons', 'hicolor', icon_size, 'devices') + ) + endforeach +endforeach + + +if host_machine.system() == 'windows' + icotool = find_program('icotool') + + infiles = [] + foreach size: ['16', '32', '48', '256'] + infiles += [join_paths('@0@x@0@'.format(size), 'virt-viewer.png')] + endforeach + + outfile = 'virt-viewer.ico' + + icofile = custom_target( + outfile, + output : outfile, + input : infiles, + command : [icotool, '-c', '-o', '@OUTPUT@', '@INPUT@']) +endif diff --git a/man/meson.build b/man/meson.build new file mode 100644 index 0000000..4f8ed85 --- /dev/null +++ b/man/meson.build @@ -0,0 +1,31 @@ +custom_target( + 'viewer-viewer-man', + output : 'virt-viewer.1', + input : 'virt-viewer.pod', + command : [ + pod2man, + '--section=1', + '--center=Virtualization Support', + '--name=Virt-Viewer', + '--release=Virt-Viewer @0@'.format(meson.project_version()), + '@INPUT@', '@OUTPUT@' + ], + install : true, + install_dir : join_paths(mandir, 'man1') +) + +custom_target( + 'remote-viewer-man', + output : 'remote-viewer.1', + input : 'remote-viewer.pod', + command : [ + pod2man, + '--section=1', + '--center=Virtualization Support', + '--name=Remote-Viewer', + '--release=Virt-Viewer @0@'.format(meson.project_version()), + '@INPUT@', '@OUTPUT@' + ], + install : true, + install_dir : join_paths(mandir, 'man1') +) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..a705611 --- /dev/null +++ b/meson.build @@ -0,0 +1,569 @@ +project( + 'virt-viewer','c', + version: '3.0', + license: 'GPLv3+', + default_options: [ + 'buildtype=debugoptimized', + 'b_pie=true', + 'c_std=gnu99', + 'warning_level=1', + ], + meson_version: '>= 0.54.0' +) + +pod2man = find_program('pod2man') + +cc = meson.get_compiler('c') +python3 = find_program('python3', required: true) +# Python3 < 3.7 treats the C locale as 7-bit only. We must force env vars so +# it treats it as UTF-8 regardless of the user's locale. +runutf8 = [ 'LC_ALL=', 'LANG=C', 'LC_CTYPE=en_US.UTF-8' ] +git = run_command('test', '-d', '.git').returncode() == 0 + +conf_data = configuration_data() +conf_data.set('VERSION', meson.project_version()) +conf_data.set('PACKAGE_STRING', 'virt-viewer ' + meson.project_version()) + +# Keep these two definitions in agreement. +glib_min_version='>=2.40' +glib_min_version_symbol='GLIB_VERSION_2_40' + +# Keep these two definitions in agreement. +gtk_min_version='>=3.12' +gtk_min_version_symbol='GDK_VERSION_3_12' + +libxml_min_version='>=2.6.0' +libvirt_min_version='>=1.2.8' +libvirt_glib_min_version='>=0.1.8' +gtk_vnc_min_version='>=0.4.0' +spice_gtk_min_version='>=0.35' +spice_protocol_min_version='>=0.12.7' +govirt_min_version='>=0.3.3' +rest_min_version='>=0.8' +vte_min_version='>=0.46.0' +bash_completion_version='2.0' + +add_global_arguments('-DGLIB_VERSION_MIN_REQUIRED=@0@'.format(glib_min_version_symbol), language: 'c') +add_global_arguments('-DGLIB_VERSION_MAX_ALLOWED=@0@'.format(glib_min_version_symbol), language: 'c') + +add_global_arguments('-DGDK_VERSION_MIN_REQUIRED=@0@'.format(gtk_min_version_symbol), language: 'c') +add_global_arguments('-DGDK_VERSION_MAX_ALLOWED=@0@'.format(gtk_min_version_symbol), language: 'c') + +cc_flags = [] + +git_werror = get_option('git_werror') +if git_werror.enabled() or git_werror.auto() and git + cc_flags += [ '-Werror' ] +endif + +cc_flags += [ + '-fno-common', + '-W', + '-Wabsolute-value', + '-Waddress', + '-Waddress-of-packed-member', + '-Waggressive-loop-optimizations', + '-Wall', + '-Wattribute-warning', + '-Wattributes', + '-Wbool-compare', + '-Wbool-operation', + '-Wbuiltin-declaration-mismatch', + '-Wbuiltin-macro-redefined', + '-Wcannot-profile', + '-Wcast-align', + '-Wcast-align=strict', + '-Wcast-function-type', + '-Wchar-subscripts', + '-Wclobbered', + '-Wcomment', + '-Wcomments', + '-Wcoverage-mismatch', + '-Wcpp', + '-Wdangling-else', + '-Wdate-time', + '-Wdeprecated-declarations', + '-Wdesignated-init', + '-Wdouble-promotion', + '-Wdiscarded-array-qualifiers', + '-Wdiscarded-qualifiers', + '-Wdiv-by-zero', + '-Wduplicated-cond', + '-Wduplicate-decl-specifier', + '-Wempty-body', + '-Wendif-labels', + '-Wexpansion-to-defined', + '-Wextra', + '-Wformat-contains-nul', + '-Wformat-extra-args', + '-Wformat-nonliteral', + '-Wformat-security', + '-Wformat-y2k', + '-Wformat-zero-length', + '-Wframe-address', + '-Wfree-nonheap-object', + '-Whsa', + '-Wif-not-aligned', + '-Wignored-attributes', + '-Wignored-qualifiers', + '-Wimplicit', + '-Wimplicit-function-declaration', + '-Wimplicit-int', + '-Wincompatible-pointer-types', + '-Winit-self', + '-Winline', + '-Wint-conversion', + '-Wint-in-bool-context', + '-Wint-to-pointer-cast', + '-Winvalid-memory-model', + '-Winvalid-pch', + '-Wlogical-not-parentheses', + '-Wlogical-op', + '-Wmain', + '-Wmaybe-uninitialized', + '-Wmemset-elt-size', + '-Wmemset-transposed-args', + '-Wmisleading-indentation', + '-Wmissing-attributes', + '-Wmissing-braces', + '-Wmissing-declarations', + '-Wmissing-field-initializers', + '-Wmissing-include-dirs', + '-Wmissing-parameter-type', + '-Wmissing-profile', + '-Wmissing-prototypes', + '-Wmultichar', + '-Wmultistatement-macros', + '-Wnarrowing', + '-Wnested-externs', + '-Wnonnull', + '-Wnonnull-compare', + '-Wnull-dereference', + '-Wodr', + '-Wold-style-declaration', + '-Wold-style-definition', + '-Wopenmp-simd', + '-Woverflow', + '-Woverride-init', + '-Wpacked-bitfield-compat', + '-Wpacked-not-aligned', + '-Wparentheses', + '-Wpointer-arith', + '-Wpointer-compare', + '-Wpointer-sign', + '-Wpointer-to-int-cast', + '-Wpragmas', + '-Wpsabi', + '-Wrestrict', + '-Wreturn-local-addr', + '-Wreturn-type', + '-Wscalar-storage-order', + '-Wsequence-point', + '-Wshadow', + '-Wshift-count-negative', + '-Wshift-count-overflow', + '-Wshift-negative-value', + '-Wsizeof-array-argument', + '-Wsizeof-pointer-div', + '-Wsizeof-pointer-memaccess', + '-Wstrict-aliasing', + '-Wstrict-prototypes', + '-Wstringop-truncation', + '-Wsuggest-attribute=cold', + '-Wsuggest-attribute=const', + '-Wsuggest-attribute=format', + '-Wsuggest-attribute=noreturn', + '-Wsuggest-attribute=pure', + '-Wsuggest-final-methods', + '-Wsuggest-final-types', + '-Wswitch', + '-Wswitch-bool', + '-Wswitch-unreachable', + '-Wsync-nand', + '-Wtautological-compare', + '-Wtrampolines', + '-Wtrigraphs', + '-Wtype-limits', + '-Wuninitialized', + '-Wunknown-pragmas', + '-Wunused', + '-Wunused-but-set-parameter', + '-Wunused-but-set-variable', + '-Wunused-function', + '-Wunused-label', + '-Wunused-local-typedefs', + '-Wunused-parameter', + '-Wunused-result', + '-Wunused-value', + '-Wunused-variable', + '-Wvarargs', + '-Wvariadic-macros', + '-Wvector-operation-performance', + '-Wvla', + '-Wvolatile-register-var', + '-Wwrite-strings', +] + +# gcc --help=warnings outputs +ptrdiff_max = cc.sizeof('ptrdiff_t', prefix: '#include ') +size_max = cc.sizeof('size_t', prefix: '#include ') +# Compute max safe object size by checking ptrdiff_t and size_t sizes. +# Ideally we would get PTRDIFF_MAX and SIZE_MAX values but it would +# give us (2147483647L) and we would have to remove the () and the suffix +# in order to convert it to numbers to be able to pick the smaller one. +alloc_max = run_command( + 'python3', '-c', + 'print(min(2**(@0@ * 8 - 1) - 1, 2**(@1@ * 8) - 1))'.format(ptrdiff_max, size_max), +) +cc_flags += [ + '-Walloc-size-larger-than=@0@'.format(alloc_max.stdout().strip()), + '-Warray-bounds=2', + '-Wattribute-alias=2', + '-Wformat-overflow=2', + '-Wformat-truncation=2', + '-Wimplicit-fallthrough=5', + '-Wnormalized=nfc', + '-Wshift-overflow=2', + '-Wstringop-overflow=2', + '-Wunused-const-variable=2', + '-Wvla-larger-then=4031', +] + +cc_flags += [ + # So we have -W enabled, and then have to explicitly turn off... + '-Wno-sign-compare', + + # We do "bad" function casts all the time for event callbacks + '-Wno-cast-function-type', + + # Clang incorrectly complains about dup typedefs win gnu99 mode + # so use this Clang-specific arg to keep it quiet + '-Wno-typedef-redefinition', + + # We don't use -Wc++-compat so we have to enable it explicitly + '-Wjump-misses-init', + + # -Wswitch is enabled but that doesn't report missing enums if a default: + # is present + '-Wswitch-enum', + + # -Wformat=2 implies -Wformat-nonliteral so we need to manually exclude it + '-Wno-format-nonliteral', + + # -Wformat enables this by default, and we should keep it, + # but need to rewrite various areas of code first + '-Wno-format-truncation', + + # This should be < 256 really. Currently we're down to 4096, + # but using 1024 bytes sized buffers (mostly for virStrerror) + # stops us from going down further + '-Wframe-larger-than=4096', + + # extra special flags + '-fexceptions', + '-fasynchronous-unwind-tables', + + # Need -fipa-pure-const in order to make -Wsuggest-attribute=pure + # fire even without -O. + '-fipa-pure-const', + + # We should eventually enable this, but right now there are at + # least 75 functions triggering warnings. + '-Wno-suggest-attribute=pure', + '-Wno-suggest-attribute=const', +] + +# on aarch64 error: -fstack-protector not supported for this target +if host_machine.cpu_family() != 'aarch64' + if host_machine.system() in [ 'linux', 'freebsd', 'windows' ] + # we prefer -fstack-protector-strong but fallback to -fstack-protector-all + fstack_cflags = cc.first_supported_argument([ + '-fstack-protector-strong', + '-fstack-protector-all', + ]) + cc_flags += fstack_cflags + + # When building with mingw using -fstack-protector requires libssp library + # which is included by using -fstack-protector with linker. + if fstack_cflags.length() == 1 and host_machine.system() == 'windows' + add_project_link_arguments(fstack_cflags, language: 'c') + endif + endif +endif + +# Clang complains about unused static inline functions which are common +# with G_DEFINE_AUTOPTR_CLEANUP_FUNC. +w_unused_function_args = ['-Wunused-function', '-Werror'] +w_unused_function_code = ''' + static inline void foo(void) {} + + int main(void) { return 0; } +''' +# -Wunused-function is implied by -Wall, we must turn it off explicitly. +if not cc.compiles(w_unused_function_code, args: w_unused_function_args) + cc_flags += ['-Wno-unused-function'] +endif + +cc_flags_disabled = [ + # In meson this is specified using 'c_std=gnu99' in project() function. + '-std=gnu99', + + # don't care about C++ compiler compat + '-Wc++-compat', + '-Wabi', + '-Wdeprecated', + + # Don't care about ancient C standard compat + '-Wtraditional', + '-Wtraditional-conversion', + + # Ignore warnings in /usr/include + '-Wsystem-headers', + + # Happy for compiler to add struct padding + '-Wpadded', + + # GCC very confused with -O2 + '-Wunreachable-code', + + # Too many to deal with + '-Wconversion', + '-Wsign-conversion', + + # Need to allow bad cast for execve() + '-Wcast-qual', + + # We need to use long long in many places + '-Wlong-long', + + # We allow manual list of all enum cases without default + '-Wswitch-default', + + # Not a problem since we don't use -fstrict-overflow + '-Wstrict-overflow', + + # Not a problem since we don't use -funsafe-loop-optimizations + '-Wunsafe-loop-optimizations', + + # gcc 4.4.6 complains this is C++ only; gcc 4.7.0 implies this from -Wall + '-Wenum-compare', + + # gcc 5.1 -Wformat-signedness mishandles enums, not ready for prime time + '-Wformat-signedness', + + # Several conditionals expand the same on both branches depending on the + # particular platform/architecture + '-Wduplicated-branches', + + # > This warning does not generally indicate that there is anything wrong + # > with your code; it merely indicates that GCC's optimizers are unable + # > to handle the code effectively. + # Source: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html + '-Wdisabled-optimization', + + # Various valid glib APIs/macros trigger this warning + '-Wbad-function-cast', + + # We might fundamentally need some of these disabled forever, but + # ideally we'd turn many of them on + '-Wfloat-equal', + '-Wpacked', + '-Wunused-macros', + '-Woverlength-strings', + '-Wstack-protector', + '-Wsuggest-attribute=malloc', + + # g_return_val_if_fail usage violates this often + '-Wdeclaration-after-statement', +] + +foreach flag : cc_flags_disabled + if cc_flags.contains(flag) + error('@0@ is disabled but listed in cc_flags'.format(flag)) + endif +endforeach + +supported_cc_flags = cc.get_supported_arguments(cc_flags) +add_project_arguments(supported_cc_flags, language: 'c') + + +prefix = get_option('prefix') +localedir = join_paths(prefix, get_option('localedir')) +datadir = join_paths(prefix, get_option('datadir')) +mandir = join_paths(prefix, get_option('mandir')) + +add_global_arguments('-DLOCALE_DIR="@0@"'.format(localedir), language: 'c') + +libm_dep = cc.find_library('m', required : true) + +glib_dep = dependency('glib-2.0', version: glib_min_version) +gmodule_dep = dependency('gmodule-2.0', version: glib_min_version) +gtk_dep = dependency('gtk+-3.0', version: gtk_min_version) + +libxml_dep = dependency('libxml-2.0', version: libxml_min_version) + +libvirt_dep = dependency('libvirt', version: libvirt_min_version, required: get_option('libvirt')) +libvirt_glib_dep = dependency('libvirt-glib-1.0', version: libvirt_glib_min_version, required: get_option('libvirt')) +if get_option('libvirt').auto() + if libvirt_dep.found() and not libvirt_glib_dep.found() + libvirt_dep = dependency('', required: false) + endif + if not libvirt_dep.found() and libvirt_glib_dep.found() + libvirt_glib_dep = dependency('', required: false) + endif +endif +if libvirt_dep.found() + conf_data.set('HAVE_LIBVIRT', '1') +endif + +gtk_vnc_dep = dependency('gtk-vnc-2.0', version: gtk_vnc_min_version, required: get_option('vnc')) +if gtk_vnc_dep.found() + conf_data.set('HAVE_GTK_VNC', '1') +endif + +spice_glib_dep = dependency('spice-client-glib-2.0', version: spice_gtk_min_version, required: get_option('spice')) +spice_gtk_dep = dependency('spice-client-gtk-3.0', version: spice_gtk_min_version, required: get_option('spice')) +spice_protocol_dep = dependency('spice-protocol', version: spice_protocol_min_version, required: get_option('spice')) +if get_option('spice').auto() + if not spice_protocol_dep.found() or not spice_gtk_dep.found() or not spice_glib_dep.found() + spice_protocol_dep = dependency('', required: false) + spice_glib_dep = dependency('', required: false) + spice_gtk_dep = dependency('', required: false) + endif +endif +if spice_gtk_dep.found() + conf_data.set('HAVE_SPICE_GTK', '1') +endif + +govirt_dep = dependency('govirt-1.0', version: govirt_min_version, required: get_option('ovirt')) +rest_dep = dependency('rest-0.7', version: rest_min_version, required: get_option('ovirt')) +if get_option('ovirt').auto() + if govirt_dep.found() and not rest_dep.found() + govirt_dep = dependency('', required: false) + endif + if not govirt_dep.found() and rest_dep.found() + rest_dep = dependency('', required: false) + endif +endif +if govirt_dep.found() + conf_data.set('HAVE_OVIRT', '1') +endif + +vte_dep = dependency('vte-2.91', version: vte_min_version, required: get_option('vte')) +if vte_dep.found() + conf_data.set('HAVE_VTE', '1') +endif + +bash_completion_dep = dependency('bash-completion', version: '>=' + bash_completion_version, required: get_option('bash_completion')) + +if bash_completion_dep.found() + bash_completion_dir = get_option('bash_completion_dir') + if bash_completion_dir == '' + bash_completion_dir = bash_completion_dep.get_pkgconfig_variable('completionsdir') + bash_completion_prefix = bash_completion_dep.get_pkgconfig_variable('prefix') + rc = run_command( + 'python3', '-c', + 'print("@0@".replace("@1@", "@2@"))'.format( + bash_completion_dir, bash_completion_prefix, prefix, + ), + check: true, + ) + bash_completion_dir = rc.stdout().strip() + endif +endif + +conf_data.set('BUILDID', get_option('build-id')) +conf_data.set('OS_ID', get_option('os-id')) + +arr_version = meson.project_version().split('.') + +conf_data.set('WINDOWS_PRODUCTVERSION', '@0@.@1@.@2@'.format( + arr_version[0], + arr_version[1], + get_option('build-id'))) + +if host_machine.system() == 'windows' + if host_machine.cpu() != 'x86_64' + wixl_arch = 'x86' + else + wixl_arch = 'x64' + endif + conf_data.set('WIXL_ARCH', wixl_arch) +endif + +configure_file( + input: 'config.h.in', + output: 'config.h', + configuration: conf_data +) + +if git + authors_helper = join_paths(meson.source_root(), 'build-aux', 'gen-authors.py') + authors = run_command(python3.path(), authors_helper, env: runutf8) + authors_file = 'AUTHORS.in' + + authors_conf = configuration_data() + authors_conf.set('authorslist', authors.stdout()) + + configure_file( + input: authors_file, + output: '@BASENAME@', + configuration: authors_conf, + ) + + configure_file( + input: 'virt-viewer.spec.in', + output: 'virt-viewer.spec', + configuration: conf_data + ) + + configure_file( + input: 'mingw-virt-viewer.spec.in', + output: 'mingw-virt-viewer.spec', + configuration: conf_data + ) + + dist_helper = join_paths(meson.source_root(), 'build-aux', 'dist.py') + meson.add_dist_script(python3.path(), dist_helper, meson.build_root(), 'AUTHORS') + meson.add_dist_script(python3.path(), dist_helper, meson.build_root(), 'virt-viewer.spec') +endif + +gnome = import('gnome') +i18n = import('i18n') + +i18n_itsdir = join_paths(meson.source_root(), 'data', 'gettext') +top_include_dir = [include_directories('.')] + +update_mime_database = find_program('update-mime-database', required: false) +update_icon_cache = find_program('gtk-update-icon-cache', required: false) +update_desktop_database = find_program('update-desktop-database', required: false) + +update_mime_database_path = '' +if update_mime_database.found() + update_mime_database_path = update_mime_database.path() +endif + +update_icon_cache_path = '' +if update_icon_cache.found() + update_icon_cache_path = update_icon_cache.path() +endif + +update_desktop_database_path = '' +if update_desktop_database.found() + update_desktop_database_path = update_desktop_database.path() +endif + +meson.add_install_script('build-aux/post_install.py', + update_mime_database_path, + update_icon_cache_path, + update_desktop_database_path) + +subdir('icons') +subdir('src') +subdir('po') +subdir('man') +subdir('tests') +if bash_completion_dep.found() + subdir('bash-completion') +endif +subdir('data') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..5c54162 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,13 @@ +option('git_werror', type: 'feature', value: 'auto', description: 'use -Werror if building from GIT') + +option('libvirt', type: 'feature', value: 'auto', description: 'libvirt support') +option('vnc', type: 'feature', value: 'auto', description: 'VNC support') +option('spice', type: 'feature', value: 'auto', description: 'SPICE support') +option('ovirt', type: 'feature', value: 'auto', description: 'oVirt support') +option('vte', type: 'feature', value: 'auto', description: 'VTE support') + +option('build-id', type: 'string', value: '', description: 'Extra build ID string') +option('os-id', type: 'string', value: '', description: 'Remote viewer OS-ID string') + +option('bash_completion', type: 'feature', value: 'auto', description: 'bash-completion support') +option('bash_completion_dir', type: 'string', value: '', description: 'directory containing bash completion scripts') diff --git a/po/meson.build b/po/meson.build new file mode 100644 index 0000000..2396ddd --- /dev/null +++ b/po/meson.build @@ -0,0 +1,6 @@ +po_dir = meson.current_source_dir() + +i18n.gettext(meson.project_name(), + args: ['--sort-output'], + data_dirs: i18n_itsdir, + preset: 'glib') diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..788a885 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,208 @@ +src_include_dir = [include_directories('.')] + +enum_sources = [ + 'virt-viewer-display.h', +] + +common_enum_headers = gnome.mkenums( + 'virt-viewer-enums.h', + sources: enum_sources, + symbol_prefix: 'virt_viewer', + identifier_prefix: 'VirtViewer', + h_template: 'virt-viewer-enums.h.etemplate', +) + +common_enum_sources = gnome.mkenums( + 'virt-viewer-enums.c', + sources: enum_sources, + symbol_prefix: 'virt_viewer', + identifier_prefix: 'VirtViewer', + c_template: 'virt-viewer-enums.c.etemplate', +) + +ui_resources = gnome.compile_resources( + 'virt-viewer-resources', + 'resources/virt-viewer.gresource.xml', + source_dir: 'resources', +) + +util_sources = [ + 'virt-viewer-util.c' +] + +util_deps = [ + libxml_dep, + glib_dep, gmodule_dep, gtk_dep, +] + + +util_lib = static_library( + 'virt-viewer-util', + util_sources, + dependencies: util_deps, + include_directories: top_include_dir, +) + + +common_sources = [ + common_enum_headers, + common_enum_sources, + ui_resources, + 'glib-compat.c', + 'virt-viewer-auth.c', + 'virt-viewer-app.c', + 'virt-viewer-file.c', + 'virt-viewer-session.c', + 'virt-viewer-display.c', + 'virt-viewer-notebook.c', + 'virt-viewer-window.c', + 'virt-viewer-vm-connection.c', + 'virt-viewer-display-vte.c', + 'virt-viewer-timed-revealer.c', +] + +if gtk_vnc_dep.found() + common_sources += [ + 'virt-viewer-session-vnc.c', + 'virt-viewer-display-vnc.c', + ] +endif + +if spice_gtk_dep.found() + common_sources += [ + 'virt-viewer-session-spice.c', + 'virt-viewer-display-spice.c', + 'virt-viewer-file-transfer-dialog.c', + ] +endif + +if govirt_dep.found() + common_sources += [ + 'ovirt-foreign-menu.c', + 'remote-viewer-iso-list-dialog.c', + ] +endif + +common_deps = [ + libm_dep, + libxml_dep, + glib_dep, gmodule_dep, gtk_dep, + gtk_vnc_dep, + spice_glib_dep, spice_gtk_dep, spice_protocol_dep, + govirt_dep, rest_dep, + vte_dep, +] + + +common_lib = static_library( + 'virt-viewer-common', + common_sources, + dependencies: common_deps, + link_with: [util_lib], + include_directories: top_include_dir, +) + +virt_viewer_sources = [ + common_enum_headers, + 'virt-viewer.c', + 'virt-viewer-main.c' +] + +virt_viewer_deps = common_deps + [ + libvirt_dep, libvirt_glib_dep, +] + +remote_viewer_sources = [ + common_enum_headers, + 'remote-viewer.c', + 'remote-viewer-connect.c', + 'remote-viewer-main.c', +] + +remote_viewer_deps = common_deps + +gui_security_link_args = [] +cui_security_link_args = [] +if host_machine.system() == 'windows' + # binutils does not take into account entry point when + # -pie is used so we need to provide it manually + # The prefix is empty for x86_64, underscore ("_") otherwise + entry_prefix = '' + if host_machine.cpu() != 'x86_64' + entry_prefix = '_' + endif + + # --dynamicbase to enable ASLR protection + # --nxcompat is to enable NX protection + # -pie as --dynamicbase requires relocations + gui_security_link_args += [ + '-Wl,--dynamicbase,-pie,--nxcompat', + '-Wl,-e,@0@WinMainCRTStartup'.format(entry_prefix), + '-mwindows', + ] + cui_security_link_args += [ + '-Wl,--dynamicbase,-pie,--nxcompat', + '-Wl,-e,@0@WinMainCRTStartup'.format(entry_prefix), + '-mconsole', + ] +endif + +if libvirt_dep.found() + executable( + 'virt-viewer', + virt_viewer_sources, + dependencies: virt_viewer_deps, + link_with: common_lib, + link_args: gui_security_link_args, + include_directories: top_include_dir, + install: true, + ) +endif + +executable( + 'remote-viewer', + remote_viewer_sources, + dependencies: remote_viewer_deps, + link_with: common_lib, + link_args: gui_security_link_args, + include_directories: top_include_dir, + install: true, +) + +if host_machine.system() == 'windows' + windres = find_program('windres') + runwindres = join_paths(meson.source_root(), 'build-aux', 'run-windres.py') + + rcfile = configure_file( + input: 'virt-viewer.rc.in', + output: 'virt-viewer.rc', + configuration: conf_data + ) + + rcobj = custom_target( + 'virt-viewer-rc.o', + input: [rcfile, icofile, 'virt-viewer.manifest'], + output: ['virt-viewer-rc.o'], + command : [ + python3, + runwindres, + windres, + join_paths(meson.build_root(), 'icons'), + meson.current_source_dir(), + rcfile, + '@OUTPUT@' + ]) + + wrapper_sources = [ + 'windows-cmdline-wrapper.c', + rcobj, + ] + + executable( + 'windows-cmdline-wrapper', + wrapper_sources, + link_args: ['-lpsapi'] + cui_security_link_args, + include_directories: top_include_dir, + install: true, + ) +endif diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..0409607 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,56 @@ +version_compare_bin = executable( + 'test-version-compare', + sources: ['test-version-compare.c'], + dependencies: [glib_dep, gtk_dep], + include_directories: top_include_dir + src_include_dir, + link_with: [util_lib], +) + +test('test-version-compare', version_compare_bin) + + +monitor_mapping_bin = executable( + 'test-monitor-mapping', + sources: ['test-monitor-mapping.c'], + dependencies: [glib_dep, gtk_dep], + include_directories: top_include_dir + src_include_dir, + link_with: [util_lib], +) + +test('test-monitor-mapping', monitor_mapping_bin) + + +hotkeys_bin = executable( + 'test-hotkeys', + sources: ['test-hotkeys.c', common_enum_headers], + dependencies: [glib_dep, gtk_dep], + include_directories: top_include_dir + src_include_dir, + link_with: [common_lib], +) + +test('test-hotkeys', hotkeys_bin) + + +monitor_alignment_bin = executable( + 'test-monitor-alignment', + sources: ['test-monitor-alignment.c'], + dependencies: [glib_dep, gtk_dep], + include_directories: top_include_dir + src_include_dir, + link_with: [util_lib], +) + +test('test-monitor-alignment', monitor_alignment_bin) + + +if host_machine.system() == 'windows' + redirect_bin = executable( + 'test-redirect', + sources: ['redirect-test.c'], + dependencies: [glib_dep, gtk_dep], + link_with: [util_lib], + link_args: ['-Wl,--subsystem,windows'], + include_directories: top_include_dir + src_include_dir, + ) + + test('test-redirect', redirect_bin) +endif