# Porting from Autotools This page uses [AppStream-glib](https://github.com/hughsie/appstream-glib/) as an example project. AppStream-Glib contains some libraries, GObject Introspection data, tests, man pages, i18n, bash-completion with optional flags to build/not build support for some things. Meson comes with a helper script `ac_converter` that you can use to convert the basic autoconf checks for your project. ## Configure.ac First let's look at `configure.ac` and write the same in `meson.build`. ```autoconf AC_PREREQ(2.63) ``` Meson doesn't provide the same function, so just ignore this. ### Defining variables `configure.ac`: ```autoconf m4_define([as_major_version], [0]) m4_define([as_minor_version], [3]) m4_define([as_micro_version], [6]) m4_define([as_version], [as_major_version.as_minor_version.as_micro_version]) ``` `meson.build`: ```meson as_version = meson.project_version() # set in project() below ver_arr = as_version.split('.') as_major_version = ver_arr[0] as_minor_version = ver_arr[1] as_micro_version = ver_arr[2] ``` ### Initializing project and setting compilers `configure.ac`: ```autoconf AC_INIT([appstream-glib],[as_version]) AC_PROG_CC ``` `meson.build`: ```meson project('appstream-glib', 'c', version : '0.3.6') ``` Note that this must be the first line of your `meson.build` file. ### AC_SUBST `configure.ac`: ```autoconf AC_SUBST(AS_MAJOR_VERSION) AC_SUBST(AS_MINOR_VERSION) AC_SUBST(AS_MICRO_VERSION) AC_SUBST(AS_VERSION) ``` You don't need to do the same in Meson, because it does not have two different types of files (Makefile, configure). ### Auto headers `configure.ac`: ```autoconf AC_CONFIG_HEADERS([config.h]) ``` `meson.build`: ```meson conf = configuration_data() # Surround the version in quotes to make it a C string conf.set_quoted('VERSION', as_version) configure_file(output : 'config.h', configuration : conf) ``` Meson doesn't support autoheaders, you need to manually specify what do you want to see in header file, write `configuration_data()` object and use `configure_file()`. You can also substitute variables of type `@SOME_VAR@` with configure data. The details are on the [configuration page](Configuration.md). ### Finding programs `configure.ac`: ```autoconf AC_PATH_PROG(GPERF, [gperf], [no]) if test x$GPERF != xno ; then AC_DEFINE(HAVE_GPERF,[1], [Use gperf]) fi AM_CONDITIONAL(HAVE_GPERF, [test x$GPERF != xno]) ``` `meson.build`: ```meson gperf = find_program('gperf', required : false) if gperf.found() conf.set('HAVE_GPERF', 1) endif ``` ### Finding pkg-config modules `configure.ac`: ```autoconf PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.24) ``` `meson.build`: ```meson soup = dependency('libsoup-2.4', version : '>= 2.24') ``` ### Arguments `configure.ac`: ```autoconf AC_ARG_ENABLE(dep11, AS_HELP_STRING([--enable-dep11],[enable DEP-11]), enable_dep11=$enableval,enable_dep11=yes) AM_CONDITIONAL(HAVE_DEP11, test x$enable_dep11 = xyes) if test x$enable_dep11 = xyes; then AC_CHECK_HEADER(yaml.h, [], [AC_MSG_ERROR([No yaml.h])]) YAML_LIBS="-lyaml" AC_SUBST(YAML_LIBS) AC_DEFINE(AS_BUILD_DEP11,1,[Build DEP-11 code]) fi ``` `meson.build`: ```meson if get_option('enable-dep11') yaml = dependency('yaml-0.1') conf.set('AS_BUILD_DEP11', 1) else yaml = dependency('yaml-0.1', required : false) endif ``` `meson_options.txt`: ```meson option('enable-dep11', type : 'boolean', value : true, description : 'enable DEP-11') ``` ## Makefile.am Next step is `Makefile.am`. In meson you don't need to have other file, you still use `meson.build`. ### Sub directories `Makefile.am`: ```make SUBDIRS = \ libappstream-glib ``` `meson.build`: ```meson subdir('libappstream-glib') ``` ### *CLEANFILES, EXTRA_DIST, etc. `Makefile.am`: ```make DISTCLEANFILES = \ appstream-glib-*.tar.xz MAINTAINERCLEANFILES = \ *~ \ ABOUT-NLS \ aclocal.m4 \ ChangeLog \ compile \ config.guess \ config.h.* \ config.rpath EXTRA_DIST = \ COPYING \ MAINTAINERS \ AUTHORS \ README.md \ NEWS \ autogen.sh \ config.h ``` In Meson you don't need have `*CLEANFILES`, because in meson you are building in temporary directory (usually called `build`), you manually removing it. You also not need to use `EXTRA_DIST`, because you will make tarballs via `git archive` or something like this. ### glib-compile-resources `Makefile.am`: ```make as-resources.c: appstream-glib.gresource.xml \ as-stock-icons.txt \ as-license-ids.txt \ as-blacklist-ids.txt \ as-category-ids.txt \ as-environment-ids.txt $(AM_V_GEN) \ glib-compile-resources \ --sourcedir=$(srcdir) \ --sourcedir=$(top_builddir)/data \ --target=$@ \ --generate-source \ --c-name as \ $(srcdir)/appstream-glib.gresource.xml as-resources.h: appstream-glib.gresource.xml \ as-stock-icons.txt \ as-license-ids.txt \ as-blacklist-ids.txt \ as-category-ids.txt \ as-environment-ids.txt $(AM_V_GEN) \ glib-compile-resources \ --sourcedir=$(srcdir) \ --sourcedir=$(top_builddir)/data \ --target=$@ \ --generate-header \ --c-name as \ $(srcdir)/appstream-glib.gresource.xml BUILT_SOURCES = \ as-resources.c \ as-resources.h ``` `meson.build`: ```meson asresources = gnome.compile_resources( 'as-resources', 'appstream-glib.gresource.xml', source_dir : '.', c_name : 'as') ``` ### Headers `Makefile.am`: ```make libappstream_glib_includedir = $(includedir)/libappstream-glib libappstream_glib_include_HEADERS = \ appstream-glib.h \ as-app.h \ as-bundle.h \ as-enums.h \ as-icon.h \ as-image.h \ as-inf.h \ as-node.h \ as-problem.h \ as-provide.h \ as-release.h \ as-screenshot.h \ as-store.h \ as-tag.h \ as-utils.h \ as-version.h ``` `meson.build`: ```meson headers = [ 'appstream-glib.h', 'as-app.h', 'as-bundle.h', 'as-enums.h', 'as-icon.h', 'as-image.h', 'as-inf.h', 'as-node.h', 'as-problem.h', 'as-provide.h', 'as-release.h', 'as-screenshot.h', 'as-store.h', 'as-tag.h', 'as-utils.h', 'as-version.h'] install_headers(headers, subdir : 'libappstream-glib') ``` ### Libraries `Makefile.am`: ```make lib_LTLIBRARIES = \ libappstream-glib.la libappstream_glib_la_SOURCES = \ as-app.c \ as-app-desktop.c \ as-app-inf.c \ as-app-private.h \ as-app-validate.c \ as-bundle.c \ as-bundle-private.h \ as-cleanup.h \ as-enums.c \ as-icon.c \ as-icon-private.h \ as-image.c \ as-image-private.h \ as-inf.c \ as-inf.h \ as-node.c \ as-node-private.h \ as-problem.c \ as-problem.h \ as-provide.c \ as-provide-private.h \ as-release.c \ as-release-private.h \ as-resources.c \ as-resources.h \ as-screenshot.c \ as-screenshot-private.h \ as-store.c \ as-tag.c \ as-utils.c \ as-utils-private.h \ as-version.h \ as-yaml.c \ as-yaml.h libappstream_glib_la_LIBADD = \ $(GLIB_LIBS) \ $(GDKPIXBUF_LIBS) \ $(LIBARCHIVE_LIBS) \ $(SOUP_LIBS) \ $(YAML_LIBS) libappstream_glib_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -export-dynamic \ -no-undefined \ -export-symbols-regex '^as_.*' ``` `meson.build`: ```meson sources = [ 'as-app.c', 'as-app-desktop.c', 'as-app-inf.c', 'as-app-private.h', 'as-app-validate.c', 'as-bundle.c', 'as-bundle-private.h', 'as-cleanup.h', 'as-enums.c', 'as-icon.c', 'as-icon-private.h', 'as-image.c', 'as-image-private.h', 'as-inf.c', 'as-inf.h', 'as-node.c', 'as-node-private.h', 'as-problem.c', 'as-problem.h', 'as-provide.c', 'as-provide-private.h', 'as-release.c', 'as-release-private.h', asresources, 'as-screenshot.c', 'as-screenshot-private.h', 'as-store.c', 'as-tag.c', 'as-utils.c', 'as-utils-private.h', 'as-version.h', 'as-yaml.c', 'as-yaml.h'] deps = [glib, gdkpixbuf, libarchive, soup, yaml] mapfile = 'appstream-glib.map' vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile) asglib = shared_library( 'appstream-glib', sources, soversion : lt_current, version : lt_version, dependencies : deps, include_directories : include_directories('@0@/..'.format(meson.current_build_dir())), link_args : ['-Wl,--no-undefined', vflag], link_depends : mapfile, install : true) ``` `appstream-glib.map`: ``` { global: as_*; local: *; }; ``` ### Custom targets `Makefile.am`: ```make if HAVE_GPERF as-tag-private.h: as-tag.gperf $(AM_V_GEN) gperf < $< > $@ libappstream_glib_la_SOURCES += as-tag-private.h BUILT_SOURCES += as-tag-private.h endif ``` `meson.build`: ```meson if gperf.found() astagpriv = custom_target('gperf as-tag', output : 'as-tag-private.h', input : 'as-tag.gperf', command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) sources = sources + [astagpriv] endif ``` ### Global CFLAGS `Makefile.am`: ```make AM_CPPFLAGS = \ -DAS_COMPILATION \ -DLOCALSTATEDIR=\""$(localstatedir)"\" \ -DG_LOG_DOMAIN=\"As\" ``` `meson.build`: ```meson add_project_arguments('-DG_LOG_DOMAIN="As"', language : 'c') add_project_arguments('-DAS_COMPILATION', language : 'c') add_project_arguments('-DLOCALSTATEDIR="/var"', language : 'c') ``` ### Tests `Makefile.am`: ```make check_PROGRAMS = \ as-self-test as_self_test_SOURCES = \ as-self-test.c as_self_test_LDADD = \ $(GLIB_LIBS) \ $(GDKPIXBUF_LIBS) \ $(LIBARCHIVE_LIBS) \ $(SOUP_LIBS) \ $(YAML_LIBS) \ $(lib_LTLIBRARIES) as_self_test_CFLAGS = -DTESTDATADIR=\""$(top_srcdir)/data/tests"\" TESTS = as-self-test ``` `meson.build`: ```meson selftest = executable( 'as-self-test', 'as-self-test.c', include_directories : include_directories('@0@/..'.format(meson.current_build_dir())), dependencies : deps, c_args : '-DTESTDATADIR="@0@/../data/tests"'.format(meson.current_source_dir()), link_with : asglib) test('as-self-test', selftest) ``` ### GObject Introspection `Makefile.am`: ```make introspection_sources = \ as-app.c \ as-app-validate.c \ as-app.h \ as-bundle.c \ as-bundle.h \ as-enums.c \ as-enums.h \ as-icon.c \ as-icon.h \ as-image.c \ as-image.h \ as-inf.c \ as-inf.h \ as-node.c \ as-node.h \ as-problem.c \ as-problem.h \ as-provide.c \ as-provide.h \ as-release.c \ as-release.h \ as-screenshot.c \ as-screenshot.h \ as-store.c \ as-store.h \ as-tag.c \ as-tag.h \ as-utils.c \ as-utils.h \ as-version.h AppStreamGlib-1.0.gir: libappstream-glib.la AppStreamGlib_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 GdkPixbuf-2.0 AppStreamGlib_1_0_gir_CFLAGS = $(AM_CPPFLAGS) AppStreamGlib_1_0_gir_SCANNERFLAGS = --identifier-prefix=As \ --symbol-prefix=as_ \ --warn-all \ --add-include-path=$(srcdir) AppStreamGlib_1_0_gir_EXPORT_PACKAGES = appstream-glib AppStreamGlib_1_0_gir_LIBS = libappstream-glib.la AppStreamGlib_1_0_gir_FILES = $(introspection_sources) INTROSPECTION_GIRS += AppStreamGlib-1.0.gir girdir = $(datadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) typelibdir = $(libdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(gir_DATA) $(typelib_DATA) ``` `meson.build`: ```meson introspection_sources = [ 'as-app.c', 'as-app-validate.c', 'as-app.h', 'as-bundle.c', 'as-bundle.h', 'as-enums.c', 'as-enums.h', 'as-icon.c', 'as-icon.h', 'as-image.c', 'as-image.h', 'as-inf.c', 'as-inf.h', 'as-node.c', 'as-node.h', 'as-problem.c', 'as-problem.h', 'as-provide.c', 'as-provide.h', 'as-release.c', 'as-release.h', 'as-screenshot.c', 'as-screenshot.h', 'as-store.c', 'as-store.h', 'as-tag.c', 'as-tag.h', 'as-utils.c', 'as-utils.h', 'as-version.h'] gnome.generate_gir(asglib, sources : introspection_sources, nsversion : '1.0', namespace : 'AppStreamGlib', symbol_prefix : 'as_', identifier_prefix : 'As', export_packages : 'appstream-glib', includes : ['GObject-2.0', 'Gio-2.0', 'GdkPixbuf-2.0'], install : true ) ``` ### GSettings `configure.ac`: ```sh GLIB_GSETTINGS ``` `Makefile.am`: ```make gsettings_SCHEMAS = foo.gschema.xml @GSETTINGS_RULES@ ``` `meson.build`: ```meson install_data('foo.gschema.xml', install_dir: join_paths(get_option('datadir'), 'glib-2.0', 'schemas')) meson.add_install_script('meson_post_install.py') ``` `meson_post_install.py`: ```python #!/usr/bin/env python3 import os import subprocess schemadir = os.path.join(os.environ['MESON_INSTALL_PREFIX'], 'share', 'glib-2.0', 'schemas') if not os.environ.get('DESTDIR'): print('Compiling gsettings schemas...') subprocess.call(['glib-compile-schemas', schemadir]) ``` ### gettext Note this example does not include `intltool` usage. `configure.ac`: ```m4 AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.19.7]) GETTEXT_PACKAGE=foo AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [The prefix for our gettext translation domains.]) ``` `po/Makevars`: ```make XGETTEXT_OPTIONS = --from-code=UTF-8 --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 --add-comments ``` `Makefile.am`: ```make %.desktop: %.desktop.in $(AM_V_GEN)$(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@ %.appdata.xml: %.appdata.xml.in $(AM_V_GEN)$(MSGFMT) --xml --template $< -d $(top_srcdir)/po -o $@ ``` `meson.build`: ```meson i18n = import('i18n') gettext_package = 'foo' add_project_arguments('-DGETTEXT_PACKAGE=' + gettext_package, language: 'c') subdir('po') i18n.merge_file( input: 'foo.desktop.in', output: 'foo.desktop', type: 'desktop', po_dir: 'po', install: true, install_dir: join_paths(get_option('datadir'), 'applications') ) i18n.merge_file( input: 'foo.appdata.xml.in', output: 'foo.appdata.xml', po_dir: 'po', install: true, install_dir: join_paths(get_option('datadir'), 'appdata') ) ``` `po/meson.build`: ```meson i18n.gettext(gettext_package, preset: 'glib') ```