Add support for building with meson/ninja

In a comparison with current autotools build system, meson/ninja
provides a huge improvement in build speed, while keeping the same
functionalities currently available and being considered more user
friendly.

The new system coexists within the same repository with the current one,
so we can do more extensive testing of its functionality before deciding
if the old system can be removed, or for some reason, has to stay for
good.

- Meson: https://mesonbuild.com

  This is the equivalent of autogen/configure step in autotools. It
  generates the files that will be used by ninja to actually build the
  source code.

  The project has received lots of traction recently, with many GNOME
  projects willing to move to this new build system. The following wiki
  page has more details of the status of the many projects being ported:

    https://wiki.gnome.org/Initiatives/GnomeGoals/MesonPorting

  Meson has a python-like syntax, easy to read, and the documentation
  on the project is very complete, with a dedicated page on how to port
  from autotools, explaining how most common use cases can be
  implemented using meson.

    http://mesonbuild.com/Porting-from-autotools.html

  Other important sources of information:

    http://mesonbuild.com/howtox.html
    http://mesonbuild.com/Syntax.html
    http://mesonbuild.com/Reference-manual.html

- Ninja: https://ninja-build.org

  Ninja is the equivalent of make in an autotools setup, which actually
  builds the source code. It has being used by large and complex
  projects such as Google Chrome, Android and LLVM. There is not much to
  say about ninja (other than it is much faster than make) because we
  won't interact directly with it as much, as meson does the middle man
  job here. The reasoning for creating ninja in the first place is
  explained on the following post:

    http://neugierig.org/software/chromium/notes/2011/02/ninja.html

  Also its manual provides more in-depth information about the design
  principles:

    https://ninja-build.org/manual.html

- Basic workflow:

  Meson package is available for most if not all distros, so, taking
  Fedora as an example, we only need to run:

    # dnf -y install meson ninja-build.

  With Meson, building in-tree is not possible at all, so we need to
  pass a directory as argument to meson where we want the build to be
  done. This has the advantage of creating builds with different options
  under the same parent directory, e.g.:

    $ meson ./build --prefix=/usr
    $ meson ./build-extra -Dextra-checks=true -Dalignment-checks=true

  After configuration is done, we call ninja to actually do the build.

    $ ninja -C ./build
    $ ninja -C ./build install

  Ninja defaults to parallel builds, and this can be changed with the -j
  flag.

    $ ninja -j 10 -C ./build

- Hacking:

  * meson.build: Mandatory for the project root and usually found under
                 each directory you want something to be built.

  * meson_options.txt: Options that can interfere with the result of the
                       build.

Signed-off-by: Eduardo Lima (Etrunko) <etrunko@redhat.com>
Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Victor Toso <victortoso@redhat.com>
This commit is contained in:
Eduardo Lima (Etrunko) 2018-03-09 13:40:22 -03:00
parent 4ed9e3b92c
commit 495d1612c4
14 changed files with 628 additions and 0 deletions

View File

@ -17,7 +17,10 @@ DISTCHECK_CONFIGURE_FLAGS = \
$(NULL)
EXTRA_DIST = \
meson.build \
meson_options.txt \
build-aux/git-version-gen \
build-aux/meson/check-spice-common \
.version \
$(NULL)

View File

@ -0,0 +1,5 @@
#!/bin/sh
set -e
if git ls-files --error-unmatch ${MESON_SOURCE_ROOT}/subprojects/spice-common > /dev/null 2>&1; then
git --git-dir="${MESON_SOURCE_ROOT}/.git" submodule update --init --recursive
fi

View File

@ -2,6 +2,7 @@ NULL =
ASCIIDOC_FLAGS = -a icons -a toc
EXTRA_DIST = \
meson.build \
spice_style.html \
spice_style.txt \
spice_threading_model.html \

View File

@ -3,6 +3,7 @@ SUFFIXES = .html
ASCIIDOC_FLAGS = -a icons -a toc
EXTRA_DIST = \
meson.build \
images/icons/important.png \
images/icons/note.png \
manual.html \

18
docs/manual/meson.build Normal file
View File

@ -0,0 +1,18 @@
txt = files('manual.txt')
if asciidoc.found()
custom_target('manual.html',
input : txt,
output : 'manual.html',
build_by_default : true,
command : [asciidoc, '-n', asciidoc_args, '-o', '@OUTPUT@', '@INPUT@'])
endif
a2x = find_program('a2x', required : false)
if a2x.found()
custom_target('manual.chunked',
input : txt,
output : 'manual.chunked',
build_by_default : true,
command : [a2x, '-f', 'chunked', '-D', meson.current_build_dir(), asciidoc_args, '@INPUT@'])
endif

14
docs/meson.build Normal file
View File

@ -0,0 +1,14 @@
if get_option('manual')
asciidoc = find_program('asciidoc', required : false)
if asciidoc.found()
asciidoc_args = ['-a', 'data-uri', '-a', 'icons', '-a', 'toc']
foreach doc : ['style', 'threading_model']
custom_target('spice_@0@.html'.format(doc),
input : 'spice_@0@.txt'.format(doc),
output : 'spice_@0@.html'.format(doc),
build_by_default: true,
command : [asciidoc, '-n', asciidoc_args, '-o', '@OUTPUT@', '@INPUT@'])
endforeach
endif
subdir('manual')
endif

251
meson.build Normal file
View File

@ -0,0 +1,251 @@
#
# project definition
#
project('spice', 'c',
version : run_command('build-aux/git-version-gen', '${MESON_SOURCE_ROOT}/.tarball-version').stdout().strip(),
license : 'LGPLv2.1',
meson_version : '>= 0.47.0')
# double check meson.project_version()
# we can not use 'check' keyword in run_command() for git-version-gen above
# https://github.com/mesonbuild/meson/issues/3944
version = run_command('build-aux/git-version-gen', '${MESON_SOURCE_ROOT}/.tarball-version', check : true).stdout().strip()
if meson.project_version() != version
error('Wrong project version')
endif
message('Updating submodules')
run_command('build-aux/meson/check-spice-common', check : true)
#
# soversion
# The versioning is defined by the forumla (CURRENT-AGE.AGE.REVISION)
#
# XXX: KEEP IN SYNC WITH configure.ac file
#
# Follow the libtool manual for the so version:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
# - If the library source code has changed at all since the last update,
# then increment revision (c:r:a becomes c:r+1:a).
# - If any interfaces have been added, removed, or changed since the last update,
# increment current, and set revision to 0.
# - If any interfaces have been added since the last public release,
# then increment age.
# - If any interfaces have been removed or changed since the last public release,
# then set age to 0.
#
#
spice_server_current = 13
spice_server_revision = 5
spice_server_age = 12
spice_server_so_version = '@0@.@1@.@2@'.format(spice_server_current - spice_server_age,
spice_server_age,
spice_server_revision)
message('libspice.so version: ' + spice_server_so_version)
# some global vars
spice_server_global_cflags = ['-fvisibility=hidden',
'-DSPICE_SERVER_INTERNAL',
'-DG_LOG_DOMAIN="Spice"',
'-DHAVE_CONFIG_H',
#'-Werror',
'-Wall',
'-Wextra',
'-Wno-sign-compare',
'-Wno-unused-parameter']
compiler = meson.get_compiler('c')
spice_server_config_data = configuration_data()
spice_server_include = [include_directories('.')]
spice_server_deps = []
spice_server_link_args = []
spice_server_requires = ''
#
# Spice common subproject
#
spice_common = subproject('spice-common', default_options : 'generate-code=server')
spice_server_config_data.merge_from(spice_common.get_variable('spice_common_config_data'))
spice_server_deps += spice_common.get_variable('spice_common_server_dep')
#
# check for system headers
#
headers = ['sys/time.h',
'execinfo.h',
'linux/sockios.h',
'pthread_np.h']
foreach header : headers
if compiler.has_header(header)
spice_server_config_data.set('HAVE_@0@'.format(header.underscorify().to_upper()), '1')
endif
endforeach
# TCP_KEEPIDLE definition in netinet/tcp.h
if compiler.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE')
spice_server_config_data.set('HAVE_TCP_KEEPIDLE', '1')
endif
#
# check for mandatory dependencies
#
spice_protocol_version='0.12.15'
glib_version = '2.38'
glib_version_info = '>= @0@'.format(glib_version)
pixman_version = '>= 0.17.7'
deps = {'spice-protocol' : '>= @0@'.format(spice_protocol_version),
'glib-2.0' : glib_version_info,
'gio-2.0' : glib_version_info,
'gobject-2.0' : glib_version_info,
'pixman-1' : pixman_version,
'openssl' : '>= 1.0.0'}
foreach dep, version : deps
spice_server_deps += dependency(dep, version : version)
endforeach
# TODO: specify minimum version for jpeg and zlib?
foreach dep : ['libjpeg', 'zlib']
spice_server_deps += dependency(dep)
endforeach
foreach dep : ['librt', 'libm']
spice_server_deps += compiler.find_library(dep)
endforeach
#
# Non-mandatory/optional dependencies
#
optional_deps = {'celt051' : '>= 0.5.1.1',
'opus' : '>= 0.9.14'}
foreach dep, version : optional_deps
d = dependency(dep, required : get_option(dep), version : version)
if d.found()
spice_server_deps += d
spice_server_config_data.set('HAVE_@0@'.format(dep.underscorify().to_upper()), '1')
endif
endforeach
# gstreamer
spice_server_has_gstreamer = false
spice_server_gst_version = get_option('gstreamer')
if spice_server_gst_version != 'no'
gst_deps = ['gstreamer', 'gstreamer-base', 'gstreamer-app', 'gstreamer-video']
foreach dep : gst_deps
dep = '@0@-@1@'.format(dep, spice_server_gst_version)
spice_server_deps += dependency(dep)
endforeach
spice_server_deps += dependency('orc-0.4')
gst_def = 'HAVE_GSTREAMER'
if spice_server_gst_version == '1.0'
gst_def = 'HAVE_GSTREAMER_1_0'
endif
spice_server_config_data.set(gst_def, '1')
spice_server_has_gstreamer = true
endif
# lz4
spice_server_has_lz4 = false
if get_option('lz4')
lz4_dep = dependency('liblz4', required : false, version : '>= 129')
if not lz4_dep.found()
lz4_dep = dependency('liblz4', version : '>= 1.7.3')
endif
if compiler.has_function('LZ4_compress_fast_continue', dependencies : lz4_dep)
spice_server_config_data.set('HAVE_LZ4_COMPRESS_FAST_CONTINUE', '1')
endif
spice_server_deps += lz4_dep
spice_server_config_data.set('USE_LZ4', '1')
spice_server_has_lz4 = true
endif
# sasl
spice_server_has_sasl = false
if get_option('sasl')
spice_server_deps += dependency('libsasl2')
spice_server_config_data.set('HAVE_SASL', '1')
spice_server_has_sasl = true
endif
# smartcard check
spice_server_has_smartcard = false
if get_option('smartcard')
smartcard_dep = dependency('libcacard', required : false, version : '>= 2.5.1')
if smartcard_dep.found()
spice_server_deps += smartcard_dep
spice_server_config_data.set('USE_SMARTCARD', '1')
else
smartcard012_dep = dependency('libcacard', required : false, version : '>= 0.1.2')
if smartcard012_dep.found()
spice_server_deps += smartcard012_dep
spice_server_config_data.set('USE_SMARTCARD_012', '1')
endif
endif
spice_server_has_smartcard = smartcard_dep.found() or smartcard012_dep.found()
if not spice_server_has_smartcard
error('Building with smartcard support but dependency not found')
endif
spice_server_requires += 'libcacard >= 0.1.2 '
endif
#
# global C defines
#
glib_major_minor = glib_version.split('.')
glib_encoded_version = 'GLIB_VERSION_@0@_@1@'.format(glib_major_minor[0], glib_major_minor[1])
spice_server_global_cflags += ['-DGLIB_VERSION_MIN_REQUIRED=@0@'.format(glib_encoded_version),
'-DGLIB_VERSION_MAX_ALLOWED=@0@'.format(glib_encoded_version)]
foreach arg : compiler.get_supported_arguments(spice_server_global_cflags)
add_project_arguments(arg, language : 'c')
endforeach
#
# Subdirectories
#
subdir('server')
subdir('tools')
subdir('docs')
#
# write config.h
#
spice_server_config_data.set_quoted('VERSION', meson.project_version())
spice_server_config_data.set('SPICE_USE_SAFER_CONTAINEROF', '1')
if get_option('statistics')
spice_server_config_data.set('RED_STATISTICS', '1')
endif
configure_file(output : 'config.h',
install : false,
configuration : spice_server_config_data)
#
# write spice-server.pc
#
spice_server_pc = configuration_data()
spice_server_pc.set('prefix', get_option('prefix'))
spice_server_pc.set('exec_prefix', '${prefix}')
spice_server_pc.set('libdir', join_paths('${exec_prefix}', get_option('libdir')))
spice_server_pc.set('includedir', join_paths('${prefix}', get_option('includedir')))
spice_server_pc.set('VERSION', meson.project_version())
spice_server_pc.set('SPICE_PROTOCOL_MIN_VER', spice_protocol_version)
spice_server_requires += 'glib-2.0 @0@ gio-2.0 @0@ gobject-2.0 @0@ pixman-1 @1@ openssl'.format(glib_version_info, pixman_version)
spice_server_pc.set('SPICE_REQUIRES', spice_server_requires)
spice_server_pc.set('SPICE_NONPKGCONFIG_LIBS', '-pthread -lm -lrt')
configure_file(input : 'spice-server.pc.in',
output : 'spice-server.pc',
install_dir : join_paths(get_option('libdir'), 'pkgconfig'),
configuration : spice_server_pc)

47
meson_options.txt Normal file
View File

@ -0,0 +1,47 @@
option('gstreamer',
type : 'combo',
choices : ['1.0', '0.10', 'no'],
description : 'Enable gstreamer support')
option('lz4',
type : 'boolean',
value : true,
description: 'Enable lz4 compression support')
option('sasl',
type : 'boolean',
value : true,
description : 'Use cyrus SASL authentication')
option('celt051',
type : 'feature',
description: 'Enable celt051 audio codec')
option('opus',
type : 'feature',
description: 'Enable Opus audio codec')
option('smartcard',
type : 'boolean',
value : true,
description : 'Enable smartcard support')
option('alignment-checks',
type : 'boolean',
value : false,
description : 'Enable runtime checks for cast alignment')
option('extra-checks',
type : 'boolean',
value: false,
description : 'Enable extra checks on code')
option('statistics',
type : 'boolean',
value: false,
description : 'Build SPICE with statistic code enabled')
option('manual',
type : 'boolean',
value : true,
description : 'Build SPICE manual')

View File

@ -213,6 +213,7 @@ spice-server-enums.h: spice-server.h spice-server-enums.h.tmpl
$(AM_V_GEN)glib-mkenums --template $(srcdir)/spice-server-enums.h.tmpl $< > $@
EXTRA_DIST = \
meson.build \
spice-bitmap-utils.tmpl.c \
cache-item.tmpl.c \
glz-encode-match.tmpl.c \

188
server/meson.build Normal file
View File

@ -0,0 +1,188 @@
spice_server_include += [include_directories('.')]
#
# generate spice-version.h
#
version_info = meson.project_version().split('.')
major = '@0@'.format(version_info[0])
minor = '@0@'.format(version_info[1])
micro = version_info[2].to_int()
if not version_info[3].contains('git')
micro += 1
endif
micro = '@0@'.format(micro)
cmd = run_command('printf', ['0x%02x%02x%02x', major, minor, micro], check : true)
version_data = configuration_data()
version_data.set('SPICE_SERVER_VERSION', cmd.stdout().strip())
spice_version_h = configure_file(input : 'spice-version.h.in',
output : 'spice-version.h',
configuration : version_data)
#
# libspice-server.so
#
spice_server_headers = [
spice_version_h,
'spice-audio.h',
'spice-char.h',
'spice-core.h',
'spice-experimental.h',
'spice-input.h',
'spice-migration.h',
'spice-qxl.h',
'spice-server.h',
'spice-replay.h',
'spice.h',
]
install_headers(spice_server_headers, subdir : 'spice-server')
# generate enums
gnome = import('gnome')
spice_server_enums = gnome.mkenums_simple('spice-server-enums',
sources : 'spice-server.h',
symbol_prefix : 'SPICE')
spice_server_sources = [
spice_server_headers,
spice_server_enums,
'agent-msg-filter.c',
'agent-msg-filter.h',
'cache-item.h',
'char-device.c',
'char-device.h',
'common-graphics-channel.c',
'common-graphics-channel.h',
'cursor-channel.c',
'cursor-channel-client.c',
'cursor-channel-client.h',
'cursor-channel.h',
'dcc.c',
'dcc.h',
'dcc-private.h',
'dcc-send.c',
'demarshallers.h',
'dispatcher.c',
'dispatcher.h',
'display-channel.c',
'display-channel.h',
'display-channel-private.h',
'display-limits.h',
'event-loop.c',
'glib-compat.h',
'glz-encoder.c',
'glz-encoder-dict.c',
'glz-encoder-dict.h',
'glz-encoder.h',
'glz-encoder-priv.h',
'image-cache.c',
'image-cache.h',
'image-encoders.c',
'image-encoders.h',
'inputs-channel.c',
'inputs-channel-client.c',
'inputs-channel-client.h',
'inputs-channel.h',
'jpeg-encoder.c',
'jpeg-encoder.h',
'main-channel.c',
'main-channel-client.c',
'main-channel-client.h',
'main-channel.h',
'main-dispatcher.c',
'main-dispatcher.h',
'memslot.c',
'memslot.h',
'migration-protocol.h',
'mjpeg-encoder.c',
'net-utils.c',
'net-utils.h',
'pixmap-cache.c',
'pixmap-cache.h',
'red-channel.c',
'red-channel-capabilities.c',
'red-channel-capabilities.h',
'red-channel-client.c',
'red-channel-client.h',
'red-channel.h',
'red-client.c',
'red-client.h',
'red-common.h',
'red-parse-qxl.c',
'red-parse-qxl.h',
'red-pipe-item.c',
'red-pipe-item.h',
'red-qxl.c',
'red-qxl.h',
'red-record-qxl.c',
'red-record-qxl.h',
'red-replay-qxl.c',
'reds.c',
'reds.h',
'reds-private.h',
'red-stream.c',
'red-stream.h',
'red-worker.c',
'red-worker.h',
'sound.c',
'sound.h',
'spice-bitmap-utils.c',
'spice-bitmap-utils.h',
'spicevmc.c',
'stat-file.c',
'stat-file.h',
'stat.h',
'stream-channel.c',
'stream-channel.h',
'red-stream-device.c',
'red-stream-device.h',
'sw-canvas.c',
'tree.c',
'tree.h',
'utils.c',
'utils.h',
'video-encoder.h',
'video-stream.c',
'video-stream.h',
'zlib-encoder.c',
'zlib-encoder.h',
]
if spice_server_has_lz4 == true
spice_server_sources += ['lz4-encoder.c',
'lz4-encoder.h']
endif
if spice_server_has_smartcard == true
spice_server_sources += ['smartcard.c',
'smartcard.h',
'smartcard-channel-client.c',
'smartcard-channel-client.h']
endif
if spice_server_has_gstreamer == true
spice_server_sources += ['gstreamer-encoder.c']
endif
#
# custom link_args
#
spice_server_syms = files('spice-server.syms')
spice_server_syms_path = join_paths(meson.current_source_dir(), 'spice-server.syms')
spice_server_link_args = ['-Wl,--no-copy-dt-needed-entries',
'-Wl,-z,relro',
'-Wl,-z,now',
'-Wl,--version-script=@0@'.format(spice_server_syms_path)]
spice_server_libs = both_libraries('spice-server', spice_server_sources,
version : spice_server_so_version,
install : true,
include_directories : spice_server_include,
link_args : compiler.get_supported_link_arguments(spice_server_link_args),
link_depends : spice_server_syms,
dependencies : spice_server_deps)
spice_server_shared_lib = spice_server_libs.get_shared_lib()
spice_server_static_lib = spice_server_libs.get_static_lib()
subdir('tests')

View File

@ -4,6 +4,7 @@ NULL =
VALGRIND_SUPPRESSIONS_FILES = $(srcdir)/valgrind/glib.supp
EXTRA_DIST = \
$(VALGRIND_SUPPRESSIONS_FILES) \
meson.build \
pki/ca-cert.pem \
pki/server-cert.pem \
pki/server-key.pem \

90
server/tests/meson.build Normal file
View File

@ -0,0 +1,90 @@
test_lib_include = [spice_server_include, include_directories('.')]
test_lib_deps = [spice_server_deps, dependency('gio-unix-2.0')]
test_lib_sources = [
'basic-event-loop.c',
'basic-event-loop.h',
'test-display-base.c',
'test-display-base.h',
'test-glib-compat.c',
'test-glib-compat.h',
]
test_libs = []
test_libs += static_library('testlib', test_lib_sources,
link_with: spice_server_static_lib,
include_directories : test_lib_include,
dependencies : test_lib_deps,
install : false)
stat_test_libs = {'testlib_stat1' : ['1', '0', '0'],
'testlib_stat2' : ['2', '0', '1'],
'testlib_stat3' : ['3', '1', '0'],
'testlib_stat4' : ['4', '1', '1']}
foreach lib, params : stat_test_libs
test_libs += static_library(lib, 'stat-test.c',
c_args : ['-DTEST_NAME=stat_test@0@'.format(params[0]),
'-DTEST_COMPRESS_STAT=@0@'.format(params[1]),
'-DTEST_RED_WORKER_STAT=@0@'.format(params[2])],
include_directories : test_lib_include,
dependencies : test_lib_deps,
install : false)
endforeach
tests = [
['test-codecs-parsing', true],
['test-options', true],
['test-stat', true],
['test-stream', true],
['test-agent-msg-filter', true],
['test-loop', true],
['test-qxl-parsing', true],
['test-stat-file', true],
['test-leaks', true],
['test-vdagent', true],
['test-fail-on-null-core-interface', true],
['test-empty-success', true],
['test-channel', true],
['test-stream-device', true],
['test-listen', true],
['test-display-no-ssl', false],
['test-display-streaming', false],
['test-playback', false],
['test-display-resolution-changes', false],
['test-two-servers', false],
['test-display-width-stride', false],
]
if spice_server_has_sasl
tests += [['test-sasl', true]]
endif
if spice_server_has_gstreamer
tests += [['test-gst', false]]
if get_option('extra-checks')
test('video-encoders', files('video-encoders'))
endif
endif
foreach t : tests
test_name = t[0]
is_test = t[1]
exe = executable(test_name,
sources : '@0@.c'.format(test_name),
link_with : test_libs,
include_directories : test_lib_include,
c_args : ['-DSPICE_TOP_SRCDIR="@0@"'.format(meson.source_root())],
dependencies : test_lib_deps,
install : false)
if is_test
test(test_name, exe)
endif
endforeach
executable('spice-server-replay',
sources : ['replay.c', join_paths('..', 'event-loop.c'), 'basic-event-loop.c', 'basic-event-loop.h'],
link_with : spice_server_shared_lib,
include_directories : test_lib_include,
dependencies : test_lib_deps,
install : false)

View File

@ -17,3 +17,7 @@ noinst_PROGRAMS = \
reds_stat_SOURCES = \
reds_stat.c \
$(NULL)
EXTRA_DIST = \
meson.build \
$(NULL)

4
tools/meson.build Normal file
View File

@ -0,0 +1,4 @@
executable('reds_stat', 'reds_stat.c',
install : false,
include_directories : spice_server_include,
dependencies : spice_server_deps)