[gobject-introspection/wip/meson: 8/23] meson: Rewrite glib gir generation



commit 86dc3cdbc4636ebfb505de1b67d758552ec8d826
Author: Nirbheek Chauhan <nirbheek centricular com>
Date:   Thu Nov 23 13:48:13 2017 +0530

    meson: Rewrite glib gir generation
    
    The previous build files had a bunch of problems:
    
    1. It assumed that glib would only be sourced via pkg-config
    2. It was using the system gobject-introspection-1.0.pc file while
       building GIRepository-1.0.gir
    3. It wasn't ignoring the *-autocleanup.h headers properly
    
    Now you can build glib as a subproject and generate girs against the
    in-tree sources. This also yields more accurate girs because they
    document platform-specific features that are actually enabled in
    the glib build we are linking against.

 gir/meson.build   |  334 +++++++++++++++++++++++++++++++++-------------------
 meson.build       |    1 +
 tools/meson.build |    2 +-
 3 files changed, 214 insertions(+), 123 deletions(-)
---
diff --git a/gir/meson.build b/gir/meson.build
index 5229207..5ae0acd 100644
--- a/gir/meson.build
+++ b/gir/meson.build
@@ -43,199 +43,289 @@ scanner_command = [
   'UNINSTALLED_INTROSPECTION_BUILDDIR=' + meson.build_root(),
   girscanner,
   '--output=@OUTPUT@',
-  '--warn-all',
   '--no-libtool',
-  '--external-library',
   '--reparse-validate',
 ]
 
+# Take a glob and print to newlines
+globber = '''
+from glob import glob
+
+for f in glob('@0@'):
+  print(f)
+'''
+
 # GLib
-glib_includedir = join_paths(gobject_dep.get_pkgconfig_variable('includedir'), 'glib-2.0')
-glib_libincludedir = join_paths(gobject_dep.get_pkgconfig_variable('libdir'), 'glib-2.0/include')
-glib_srcdir = get_option('glib-src-dir')
+glib_files = files('glib-2.0.c')
+glib_command = scanner_command + [
+  '--identifier-prefix=G',
+  '--symbol-prefix=g',
+  '--symbol-prefix=glib',
+  '--c-include=glib.h',
+  '--namespace=GLib',
+  '--nsversion=2.0',
+  '--library=glib-2.0',
+  '--library=gobject-2.0',
+]
 
-glib_glob = glib_includedir + '/glib/*.h'
-if glib_srcdir != ''
-  glib_glob += ',' + glib_srcdir + '/glib/*.c'
+dep_type = glib_dep.type_name()
+if dep_type == 'pkgconfig'
+  glib_command += ['--external-library', '--pkg=glib-2.0']
+  glib_libdir = glib_dep.get_pkgconfig_variable('libdir')
+  glib_incdir = join_paths(glib_dep.get_pkgconfig_variable('includedir'), 'glib-2.0')
+  glib_libincdir = join_paths(glib_libdir, 'glib-2.0', 'include')
+  glib_files += join_paths(glib_incdir, 'gobject', 'glib-types.h')
+  glib_files += join_paths(glib_libincdir, 'glibconfig.h')
+  if giounix_dep.found()
+    glib_files += join_paths(glib_incdir, 'glib-unix.h')
+  endif
+  # Parse glob to get installed header list
+  ret = run_command(python3, '-c', globber.format(join_paths(glib_incdir, 'glib', '*.h')))
+  if ret.returncode() != 0
+    error('Failed to get glib header list')
+  endif
+  glib_headers = ret.stdout().strip().split('\n')
+  # Get a list of all source files
+  glib_srcdir = get_option('glib-src-dir')
+  if glib_srcdir != ''
+    ret = run_command(python3, '-c', globber.format(join_paths(glib_srcdir, 'glib', '*.c')))
+    if ret.returncode() != 0
+      error('Failed to get glib source list')
+    endif
+    glib_files += ret.stdout().strip().split('\n')
+  endif
+  glib_includes = ['-I' + glib_incdir, '-I' + glib_libincdir]
+elif dep_type == 'internal'
+  glib_command += ['--pkg-export=glib-2.0']
+  # XXX: Assumes that the builddir layout is 'mirror'
+  glib_libdir = join_paths(meson.build_root(), 'subprojects', 'glib', 'glib')
+  # XXX: Assumes the location of the glib subproject dir
+  # We should add API to meson to get a specific file from a specific subproject
+  glibproj_incdir = join_paths(meson.source_root(), 'subprojects', 'glib')
+  glib_incdir = join_paths(glibproj_incdir, 'glib')
+  glib_libincdir = glib_libdir
+  glib_files += join_paths(glibproj_incdir, 'gobject', 'glib-types.h')
+  glib_files += join_paths(glib_libincdir, 'glibconfig.h')
+  if giounix_dep.found()
+    glib_files += join_paths(glib_incdir, 'glib-unix.h')
+  endif
+  # We know exactly what headers will be installed, so just fetch that
+  glib_subproject = subproject('glib')
+  glib_headers = glib_subproject.get_variable('glib_sub_headers')
+  glib_files += glib_subproject.get_variable('glib_sources')
+  # XXX: Assumes that the builddir layout is 'mirror'
+  gobject_libdir = join_paths(meson.build_root(), 'subprojects', 'glib', 'gobject')
+  gmodule_libdir = join_paths(meson.build_root(), 'subprojects', 'glib', 'gmodule')
+  gio_libdir = join_paths(meson.build_root(), 'subprojects', 'glib', 'gio')
+  glib_libpaths = ['-L' + glib_libdir, '-L' + gobject_libdir,
+                   '-L' + gmodule_libdir, '-L' + gio_libdir]
+  glib_command += glib_libpaths
+  glib_includes = ['-I' + glibproj_incdir, '-I' + glib_incdir, '-I' + glib_libincdir]
+else
+  error('Unknown glib dependency type: ' + dep_type)
 endif
 
-glib_sources_file = custom_target('glib-sources-file',
-  input: 'glib-2.0.c',
-  output: 'glib-sources.list',
-  command: [
-    find_program('generate_source_list.py'),
-    '@OUTPUT@',
-    glib_glob,
-    'glib-autocleanups.h',
-    glib_libincludedir + '/glibconfig.h',
-    glib_includedir + '/gobject/glib-types.h',
-    '@INPUT@',
-  ]
-)
+foreach h : glib_headers
+  hstr = '@0@'.format(h)
+  if not hstr.endswith('autocleanups.h')
+    glib_files += h
+  endif
+endforeach
 
 glib_gir = custom_target('gir-glib',
-  input: glib_sources_file,
+  input: glib_files,
   output: 'GLib-2.0.gir',
   depends: giscanner_pymod,
   install: true,
   install_dir: girdir,
-  command: scanner_command + [
-    '--identifier-prefix=G',
-    '--symbol-prefix=g',
-    '--symbol-prefix=glib',
-    '--c-include=glib.h',
-    '--namespace=GLib',
-    '--nsversion=2.0',
-    '--pkg=glib-2.0',
-    '--library=glib-2.0',
-    '--library=gobject-2.0',
-    '--cflags-begin',
-    '-I' + glib_includedir,
-    '-I' + glib_libincludedir,
+  command: glib_command + [
+    '--cflags-begin'] + glib_includes + [
     '-DGLIB_COMPILATION',
     '-D__G_I18N_LIB_H__',
     '-DGETTEXT_PACKAGE=Dummy',
     '--cflags-end',
-    '--filelist=@INPUT@',
+    '@INPUT@',
   ]
 )
 
 gir_files += glib_gir
 
 # GObject
-gobject_glob = glib_includedir + '/gobject/*.h'
-if glib_srcdir != ''
-  gobject_glob += ',' + glib_srcdir + '/gobject/*.c'
+gobject_files = files('gobject-2.0.c')
+gobject_command = scanner_command + [
+  '--identifier-prefix=G',
+  '--c-include=glib-object.h',
+  '--namespace=GObject',
+  '--nsversion=2.0',
+  '--library=gobject-2.0',
+]
+
+if dep_type == 'pkgconfig'
+  gobject_command += ['--external-library', '--pkg=gobject-2.0']
+  # Get the installed header list
+  ret = run_command(python3, '-c', globber.format(join_paths(glib_incdir, 'gobject', '*.h')))
+  if ret.returncode() != 0
+    error('Failed to get gobject header list')
+  endif
+  gobject_headers = ret.stdout().strip().split('\n')
+  if glib_srcdir != ''
+    ret = run_command(python3, '-c', globber.format(join_paths(glib_srcdir, 'gobject', '*.c')))
+    if ret.returncode() != 0
+      error('Failed to get gobject source list')
+    endif
+    gobject_files += ret.stdout().strip().split('\n')
+  endif
+else
+  gobject_command += ['--pkg-export=gobject-2.0']
+  gobject_headers = glib_subproject.get_variable('gobject_install_headers')
+  gobject_files += glib_subproject.get_variable('gobject_sources')
+  gobject_command += glib_libpaths
 endif
 
-gobject_sources_file = custom_target('gobject-sources-file',
-  input: 'gobject-2.0.c',
-  output: 'gobject-sources.list',
-  command: [
-    find_program('generate_source_list.py'),
-    '@OUTPUT@',
-    gobject_glob,
-    'glib-types.h',
-    '@INPUT@',
-  ]
-)
+foreach h : gobject_headers
+  hstr = '@0@'.format(h)
+  if not hstr.endswith('autocleanups.h') and not hstr.endswith('glib-types.h')
+    gobject_files += h
+  endif
+endforeach
 
 gobject_gir = custom_target('gir-gobject',
-  input: gobject_sources_file,
+  input: gobject_files,
   output: 'GObject-2.0.gir',
   depends: [glib_gir, giscanner_pymod],
   install: true,
   install_dir: girdir,
-  command: scanner_command + [
-    '--identifier-prefix=G',
-    '--symbol-prefix=g',
-    '--c-include=glib-object.h',
-    '--namespace=GObject',
-    '--nsversion=2.0',
-    '--pkg=gobject-2.0',
-    '--library=gobject-2.0',
+  command: gobject_command + [
     '--include-uninstalled=' + glib_gir.full_path(),
-    '--cflags-begin',
-    '-I' + glib_includedir,
-    '-I' + glib_libincludedir,
+    '--cflags-begin'] + glib_includes + [
     '-DGOBJECT_COMPILATION',
     '--cflags-end',
-    '--filelist=@INPUT@',
+    '@INPUT@',
   ]
 )
 
 gir_files += gobject_gir
 
 # GModule
+gmodule_files = files('gmodule-2.0.c')
+gmodule_command = scanner_command + [
+  '--identifier-prefix=G',
+  '--symbol-prefix=g',
+  '--c-include=gmodule.h',
+  '--namespace=GModule',
+  '--nsversion=2.0',
+  '--library=gmodule-2.0',
+]
+
+if dep_type == 'pkgconfig'
+  gmodule_command += ['--external-library', '--pkg=gmodule-2.0']
+  gmodule_files += join_paths(glib_incdir, 'gmodule.h')
+  if glib_srcdir != ''
+    gmodule_files += join_paths(glib_srcdir, 'gmodule', 'gmodule.c')
+  endif
+else
+  gmodule_command += ['--pkg-export=gmodule-2.0']
+  gmodule_command += glib_libpaths
+  gmodule_files += [join_paths(glibproj_incdir, 'gmodule', 'gmodule.h'),
+                    join_paths(glibproj_incdir, 'gmodule', 'gmodule.c')]
+endif
+
 gir_files += custom_target('gir-gmodule',
-  input: 'gmodule-2.0.c', # TODO: glib sources
+  input: gmodule_files,
   output: 'GModule-2.0.gir',
   depends: [glib_gir, giscanner_pymod],
   install: true,
   install_dir: girdir,
-  command: scanner_command + [
-    '--identifier-prefix=G',
-    '--symbol-prefix=g',
-    '--c-include=gmodule.h',
-    '--namespace=GModule',
-    '--nsversion=2.0',
-    '--pkg=gmodule-2.0',
-    '--library=gmodule-2.0',
+  command: gmodule_command + [
     '--include-uninstalled=' + glib_gir.full_path(),
-    '--cflags-begin',
-    '-I' + glib_includedir,
-    '-I' + glib_libincludedir,
+    '--cflags-begin'] + glib_includes + [
     '--cflags-end',
-    glib_includedir + '/gmodule.h',
     '@INPUT@',
   ]
 )
 
-# Gio
+## Gio
+gio_files = files('gio-2.0.c')
 gio_command = scanner_command + [
   '--identifier-prefix=G',
   '--symbol-prefix=g',
   '--c-include=gio/gio.h',
   '--namespace=Gio',
   '--nsversion=2.0',
-  '--pkg=gio-2.0',
   '--library=gio-2.0',
-  '--include-uninstalled=' + gobject_gir.full_path(),
-  '--cflags-begin',
-  '-DGIO_COMPILATION',
-  '-DG_SETTINGS_ENABLE_BACKEND',
-  '-I' + glib_includedir,
-  '--cflags-end',
-  '--filelist=@INPUT@',
 ]
 
-gio_glob = glib_includedir + '/gio/*.h'
-if glib_srcdir != ''
-  gio_glob += ',' + glib_srcdir + '/gio/*.c'
+if dep_type == 'pkgconfig'
+  gio_command += ['--external-library', '--pkg=gio-2.0']
+  # Get the installed header list
+  ret = run_command(python3, '-c', globber.format(join_paths(glib_incdir, 'gio', '*.h')))
+  if ret.returncode() != 0
+    error('Failed to get gio header list')
+  endif
+  gio_headers = ret.stdout().strip().split('\n')
+  # Get all gio (and gio-unix) sources. This is not entirely correct, but it's
+  # probably fine since it matches what Autotools does. We are more exact in
+  # the subproject case.
+  if glib_srcdir != ''
+    ret = run_command(python3, '-c', globber.format(join_paths(glib_srcdir, 'gio', '*.c')))
+    if ret.returncode() != 0
+      error('Failed to get gio source list')
+    endif
+    gio_files += ret.stdout().strip().split('\n')
+  endif
+else
+  gio_command += ['--pkg-export=gio-2.0']
+  gio_headers = glib_subproject.get_variable('gio_headers')
+  gio_files += glib_subproject.get_variable('gio_sources')
+  gio_command += glib_libpaths
 endif
 
-if giounix_dep.found()
-  giounix_includedir = giounix_dep.get_pkgconfig_variable('includedir') + '/gio-unix-2.0'
-  gio_glob += ',' + giounix_includedir + '/gio/*.h'
-
-  get_header_list = '''
-from glob import glob
-from os.path import basename
-
-print(','.join(basename(f) for f in glob('@0@/gio/*.h')))
-'''.format(giounix_includedir)
+foreach h : gio_headers
+  hstr = '@0@'.format(h)
+  if not hstr.endswith('autocleanups.h')
+    gio_files += h
+  endif
+endforeach
 
-  ret = run_command(py3.find_python(), '-c', get_header_list)
-  if ret.returncode() != 0
-    error('Failed to get gio header list')
+if giounix_dep.found()
+  if dep_type == 'pkgconfig'
+    gio_command += ['--pkg=gio-unix-2.0']
+    giounix_includedir = join_paths(giounix_dep.get_pkgconfig_variable('includedir'), 'gio-unix-2.0')
+    # Get the installed gio-unix header list
+    ret = run_command(python3, '-c', globber.format(join_paths(giounix_includedir, 'gio', '*.h')))
+    if ret.returncode() != 0
+      error('Failed to get gio-unix header list')
+    endif
+    giounix_headers = ret.stdout().strip().split('\n')
+  else
+    gio_command += ['--pkg-export=gio-unix-2.0']
+    giounix_headers = glib_subproject.get_variable('gio_unix_include_headers')
   endif
-  gio_headers = ret.stdout().strip().split(',')
-  foreach header : gio_headers
-    gio_command += '--c-include=gio/' + header
+  # No filtering needed
+  gio_files += giounix_headers
+  # GIO Unix headers must be included explicitly since there is no catch-all
+  # header that includes all of them unlike gio/gio.h above
+  foreach header : giounix_headers
+    hstr = '@0@'.format(header)
+    hbase = hstr.split('/')[-1]
+    gio_command += '--c-include=gio/@0@'.format(hbase)
   endforeach
-
-  gio_command += '--pkg=gio-unix-2.0'
 endif
 
-gio_sources_file = custom_target('gio-sources-file',
-  input: 'gio-2.0.c',
-  output: 'gio-sources.list',
-  command: [
-    find_program('generate_source_list.py'),
-    '@OUTPUT@',
-    gio_glob,
-    ' ',
-    '@INPUT@',
-  ]
-)
-
 gir_files += custom_target('gir-gio',
-  input: gio_sources_file,
+  input: gio_files,
   output: 'Gio-2.0.gir',
   depends: [gobject_gir, giscanner_pymod],
   install: true,
   install_dir: girdir,
-  command: gio_command,
+  command: gio_command + [
+    '--include-uninstalled=' + gobject_gir.full_path(),
+    '--cflags-begin'] + glib_includes + [
+    '-DGIO_COMPILATION',
+    '-DG_SETTINGS_ENABLE_BACKEND',
+    '--cflags-end',
+    '@INPUT@',
+  ]
 )
 
 # GIRepository
@@ -251,12 +341,12 @@ gir_files += custom_target('gir-girepository',
     '--c-include=girepository.h',
     '--namespace=GIRepository',
     '--nsversion=2.0',
-    '--pkg=gobject-introspection-1.0',
+    '--pkg-export=gobject-introspection-1.0',
     '--library=girepository-1.0',
     '--include-uninstalled=' + gobject_gir.full_path(),
     '--cflags-begin',
     '-DGI_COMPILATION',
-    '-I' + meson.source_root() + '/girepository',
+    '-I' + meson.current_source_dir() + '/../girepository',
     '--cflags-end',
     '@INPUT@',
   ]
diff --git a/meson.build b/meson.build
index 2573776..7ae703f 100644
--- a/meson.build
+++ b/meson.build
@@ -13,6 +13,7 @@ gi_versions = meson.project_version().split('.')
 configinc = include_directories('.')
 
 py3 = import('python3')
+python3 = py3.find_python()
 
 cc = meson.get_compiler('c')
 config = configuration_data()
diff --git a/tools/meson.build b/tools/meson.build
index 73817ec..e32937b 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -1,6 +1,6 @@
 libdir_abs = join_paths(get_option('prefix'), get_option('libdir'))
 datadir_abs = join_paths(get_option('prefix'), get_option('datadir'))
-python_path = py3.find_python().path()
+python_path = python3.path()
 
 tools = [
   ['g-ir-scanner', 'scannermain', 'scanner_main'],


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]