Avoid trying to fetch metapage of an SPGist partitioned index.
[pgsql.git] / meson.build
blob4e98cbefe4ec76dd927f30468b82895ddfdfe3d5
1 # Copyright (c) 2022-2023, PostgreSQL Global Development Group
3 # Entry point for building PostgreSQL with meson
5 # Good starting points for writing meson.build files are:
6 #  - https://mesonbuild.com/Syntax.html
7 #  - https://mesonbuild.com/Reference-manual.html
9 project('postgresql',
10   ['c'],
11   version: '17devel',
12   license: 'PostgreSQL',
14   # We want < 0.56 for python 3.5 compatibility on old platforms. EPEL for
15   # RHEL 7 has 0.55. < 0.54 would require replacing some uses of the fs
16   # module, < 0.53 all uses of fs. So far there's no need to go to >=0.56.
17   meson_version: '>=0.54',
18   default_options: [
19     'warning_level=1', #-Wall equivalent
20     'b_pch=false',
21     'buildtype=debugoptimized', # -O2 + debug
22     # For compatibility with the autoconf build, set a default prefix. This
23     # works even on windows, where it's a drive-relative path (i.e. when on
24     # d:/somepath it'll install to d:/usr/local/pgsql)
25     'prefix=/usr/local/pgsql',
26   ]
31 ###############################################################
32 # Basic prep
33 ###############################################################
35 fs = import('fs')
36 pkgconfig = import('pkgconfig')
38 host_system = host_machine.system()
39 build_system = build_machine.system()
40 host_cpu = host_machine.cpu_family()
42 cc = meson.get_compiler('c')
44 not_found_dep = dependency('', required: false)
45 thread_dep = dependency('threads')
46 auto_features = get_option('auto_features')
50 ###############################################################
51 # Safety first
52 ###############################################################
54 # It's very easy to get into confusing states when the source directory
55 # contains an in-place build. E.g. the wrong pg_config.h will be used. So just
56 # refuse to build in that case.
58 # There's a more elaborate check later, that checks for conflicts around all
59 # generated files. But we can only do that much further down the line, so this
60 # quick check seems worth it. Adhering to this advice should clean up the
61 # conflict, but won't protect against somebody doing make distclean or just
62 # removing pg_config.h
63 errmsg_nonclean_base = '''
64 ****
65 Non-clean source code directory detected.
67 To build with meson the source tree may not have an in-place, ./configure
68 style, build configured. You can have both meson and ./configure style builds
69 for the same source tree by building out-of-source / VPATH with
70 configure. Alternatively use a separate check out for meson based builds.
72 @0@
73 ****'''
74 if fs.exists(meson.current_source_dir() / 'src' / 'include' / 'pg_config.h')
75   errmsg_cleanup = 'To clean up, run make distclean in the source tree.'
76   error(errmsg_nonclean_base.format(errmsg_cleanup))
77 endif
81 ###############################################################
82 # Variables to be determined
83 ###############################################################
85 postgres_inc_d = ['src/include']
86 postgres_inc_d += get_option('extra_include_dirs')
88 postgres_lib_d = get_option('extra_lib_dirs')
90 cppflags = []
92 cflags = []
93 cxxflags = []
94 cflags_warn = []
95 cxxflags_warn = []
96 cflags_mod = []
97 cxxflags_mod = []
99 ldflags = []
100 ldflags_be = []
101 ldflags_sl = []
102 ldflags_mod = []
104 test_c_args = []
106 os_deps = []
107 backend_both_deps = []
108 backend_deps = []
109 libpq_deps = []
111 pg_sysroot = ''
113 # source of data for pg_config.h etc
114 cdata = configuration_data()
118 ###############################################################
119 # Version and other metadata
120 ###############################################################
122 pg_version = meson.project_version()
124 if pg_version.endswith('devel')
125   pg_version_arr = [pg_version.split('devel')[0], '0']
126 elif pg_version.contains('beta')
127   pg_version_arr = [pg_version.split('beta')[0], '0']
128 elif pg_version.contains('rc')
129   pg_version_arr = [pg_version.split('rc')[0], '0']
130 else
131   pg_version_arr = pg_version.split('.')
132 endif
134 pg_version_major = pg_version_arr[0].to_int()
135 pg_version_minor = pg_version_arr[1].to_int()
136 pg_version_num = (pg_version_major * 10000) + pg_version_minor
138 pg_url = 'https://www.postgresql.org/'
140 cdata.set_quoted('PACKAGE_NAME', 'PostgreSQL')
141 cdata.set_quoted('PACKAGE_BUGREPORT', 'pgsql-bugs@lists.postgresql.org')
142 cdata.set_quoted('PACKAGE_URL', pg_url)
143 cdata.set_quoted('PACKAGE_VERSION', pg_version)
144 cdata.set_quoted('PACKAGE_STRING', 'PostgreSQL @0@'.format(pg_version))
145 cdata.set_quoted('PACKAGE_TARNAME', 'postgresql')
147 pg_version += get_option('extra_version')
148 cdata.set_quoted('PG_VERSION', pg_version)
149 cdata.set_quoted('PG_MAJORVERSION', pg_version_major.to_string())
150 cdata.set('PG_MAJORVERSION_NUM', pg_version_major)
151 cdata.set('PG_MINORVERSION_NUM', pg_version_minor)
152 cdata.set('PG_VERSION_NUM', pg_version_num)
153 # PG_VERSION_STR is built later, it depends on compiler test results
154 cdata.set_quoted('CONFIGURE_ARGS', '')
158 ###############################################################
159 # Basic platform specific configuration
160 ###############################################################
162 # meson's system names don't quite map to our "traditional" names. In some
163 # places we need the "traditional" name, e.g., for mapping
164 # src/include/port/$os.h to src/include/pg_config_os.h. Define portname for
165 # that purpose.
166 portname = host_system
168 exesuffix = '' # overridden below where necessary
169 dlsuffix = '.so' # overridden below where necessary
170 library_path_var = 'LD_LIBRARY_PATH'
172 # Format of file to control exports from libraries, and how to pass them to
173 # the compiler. For export_fmt @0@ is the path to the file export file.
174 export_file_format = 'gnu'
175 export_file_suffix = 'list'
176 export_fmt = '-Wl,--version-script=@0@'
178 # Flags to add when linking a postgres extension, @0@ is path to
179 # the relevant object on the platform.
180 mod_link_args_fmt = []
182 memset_loop_limit = 1024
184 # Choice of shared memory and semaphore implementation
185 shmem_kind = 'sysv'
186 sema_kind = 'sysv'
188 # We implement support for some operating systems by pretending they're
189 # another. Map here, before determining system properties below
190 if host_system == 'dragonfly'
191   # apparently the most similar
192   host_system = 'netbsd'
193 endif
195 if host_system == 'aix'
196   library_path_var = 'LIBPATH'
198   export_file_format = 'aix'
199   export_fmt = '-Wl,-bE:@0@'
200   mod_link_args_fmt = ['-Wl,-bI:@0@']
201   mod_link_with_dir = 'libdir'
202   mod_link_with_name = '@0@.imp'
204   # M:SRE sets a flag indicating that an object is a shared library. Seems to
205   # work in some circumstances without, but required in others.
206   ldflags_sl += '-Wl,-bM:SRE'
207   ldflags_be += '-Wl,-brtllib'
209   # Native memset() is faster, tested on:
210   # - AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
211   # - AIX 5.3 ML3, gcc 4.0.1
212   memset_loop_limit = 0
214 elif host_system == 'cygwin'
215   sema_kind = 'unnamed_posix'
216   cppflags += '-D_GNU_SOURCE'
217   dlsuffix = '.dll'
218   mod_link_args_fmt = ['@0@']
219   mod_link_with_name = 'lib@0@.exe.a'
220   mod_link_with_dir = 'libdir'
222 elif host_system == 'darwin'
223   dlsuffix = '.dylib'
224   library_path_var = 'DYLD_LIBRARY_PATH'
226   export_file_format = 'darwin'
227   export_fmt = '-Wl,-exported_symbols_list,@0@'
229   mod_link_args_fmt = ['-bundle_loader', '@0@']
230   mod_link_with_dir = 'bindir'
231   mod_link_with_name = '@0@'
233   sysroot_args = [files('src/tools/darwin_sysroot'), get_option('darwin_sysroot')]
234   pg_sysroot = run_command(sysroot_args, check:true).stdout().strip()
235   message('darwin sysroot: @0@'.format(pg_sysroot))
236   if pg_sysroot != ''
237     cflags += ['-isysroot', pg_sysroot]
238     ldflags += ['-isysroot', pg_sysroot]
239   endif
240   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
241   # don't want because a) it's different from what we do for autoconf, b) it
242   # causes warnings starting in macOS Ventura
243   ldflags_mod += ['-Wl,-undefined,error']
245 elif host_system == 'freebsd'
246   sema_kind = 'unnamed_posix'
248 elif host_system == 'linux'
249   sema_kind = 'unnamed_posix'
250   cppflags += '-D_GNU_SOURCE'
252 elif host_system == 'netbsd'
253   # We must resolve all dynamic linking in the core server at program start.
254   # Otherwise the postmaster can self-deadlock due to signals interrupting
255   # resolution of calls, since NetBSD's linker takes a lock while doing that
256   # and some postmaster signal handlers do things that will also acquire that
257   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
258   # While there's not a hard reason to adopt these settings for our other
259   # executables, there's also little reason not to, so just add them to
260   # LDFLAGS.
261   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
263 elif host_system == 'openbsd'
264   # you're ok
266 elif host_system == 'sunos'
267   portname = 'solaris'
268   export_fmt = '-Wl,-M@0@'
269   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
271 elif host_system == 'windows'
272   portname = 'win32'
273   exesuffix = '.exe'
274   dlsuffix = '.dll'
275   library_path_var = ''
277   export_file_format = 'win'
278   export_file_suffix = 'def'
279   if cc.get_id() == 'msvc'
280     export_fmt = '/DEF:@0@'
281     mod_link_with_name = '@0@.exe.lib'
282   else
283     export_fmt = '@0@'
284     mod_link_with_name = 'lib@0@.exe.a'
285   endif
286   mod_link_args_fmt = ['@0@']
287   mod_link_with_dir = 'libdir'
289   shmem_kind = 'win32'
290   sema_kind = 'win32'
292   cdata.set('WIN32_STACK_RLIMIT', 4194304)
293   if cc.get_id() == 'msvc'
294     ldflags += '/INCREMENTAL:NO'
295     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
296     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
297   else
298     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
299     # Need to allow multiple definitions, we e.g. want to override getopt.
300     ldflags += '-Wl,--allow-multiple-definition'
301     # Ensure we get MSVC-like linking behavior.
302     ldflags += '-Wl,--disable-auto-import'
303   endif
305   os_deps += cc.find_library('ws2_32', required: true)
306   secur32_dep = cc.find_library('secur32', required: true)
307   backend_deps += secur32_dep
308   libpq_deps += secur32_dep
310   postgres_inc_d += 'src/include/port/win32'
311   if cc.get_id() == 'msvc'
312     postgres_inc_d += 'src/include/port/win32_msvc'
313   endif
315   windows = import('windows')
317 else
318   # XXX: Should we add an option to override the host_system as an escape
319   # hatch?
320   error('unknown host system: @0@'.format(host_system))
321 endif
325 ###############################################################
326 # Program paths
327 ###############################################################
329 # External programs
330 perl = find_program(get_option('PERL'), required: true, native: true)
331 python = find_program(get_option('PYTHON'), required: true, native: true)
332 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
333 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
334 sed = find_program(get_option('SED'), 'sed', native: true, required: false)
335 prove = find_program(get_option('PROVE'), native: true, required: false)
336 tar = find_program(get_option('TAR'), native: true, required: false)
337 gzip = find_program(get_option('GZIP'), native: true, required: false)
338 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
339 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
340 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
341 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
342 missing = find_program('config/missing', native: true)
343 cp = find_program('cp', required: false, native: true)
344 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
345 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
347 bison_flags = []
348 if bison.found()
349   bison_version_c = run_command(bison, '--version', check: true)
350   # bison version string helpfully is something like
351   # >>bison (GNU bison) 3.8.1<<
352   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
353   if bison_version.version_compare('>=3.0')
354     bison_flags += ['-Wno-deprecated']
355   endif
356 endif
357 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
358 bison_kw = {
359   'output': ['@BASENAME@.c', '@BASENAME@.h'],
360   'command': bison_cmd,
363 flex_flags = []
364 if flex.found()
365   flex_version_c = run_command(flex, '--version', check: true)
366   flex_version = flex_version_c.stdout().split(' ')[1].split('\n')[0]
367 endif
368 flex_wrapper = files('src/tools/pgflex')
369 flex_cmd = [python, flex_wrapper,
370   '--builddir', '@BUILD_ROOT@',
371   '--srcdir', '@SOURCE_ROOT@',
372   '--privatedir', '@PRIVATE_DIR@',
373   '--flex', flex, '--perl', perl,
374   '-i', '@INPUT@', '-o', '@OUTPUT0@',
377 wget = find_program('wget', required: false, native: true)
378 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
380 install_files = files('src/tools/install_files')
384 ###############################################################
385 # Path to meson (for tests etc)
386 ###############################################################
388 # NB: this should really be part of meson, see
389 # https://github.com/mesonbuild/meson/issues/8511
390 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
392 if meson_binpath_r.stdout() == ''
393   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
394     meson_binpath_r.returncode(),
395     meson_binpath_r.stdout(),
396     meson_binpath_r.stderr()))
397 endif
399 meson_binpath_s = meson_binpath_r.stdout().split('\n')
400 meson_binpath_len = meson_binpath_s.length()
402 if meson_binpath_len < 1
403   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
404 endif
406 i = 0
407 meson_impl = ''
408 meson_binpath = ''
409 meson_args = []
410 foreach e : meson_binpath_s
411   if i == 0
412     meson_impl = e
413   elif i == 1
414     meson_binpath = e
415   else
416     meson_args += e
417   endif
418   i += 1
419 endforeach
421 if meson_impl not in ['muon', 'meson']
422   error('unknown meson implementation "@0@"'.format(meson_impl))
423 endif
425 meson_bin = find_program(meson_binpath, native: true)
429 ###############################################################
430 # Option Handling
431 ###############################################################
433 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
435 blocksize = get_option('blocksize').to_int() * 1024
437 if get_option('segsize_blocks') != 0
438   if get_option('segsize') != 1
439     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
440   endif
442   segsize = get_option('segsize_blocks')
443 else
444   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
445 endif
447 cdata.set('BLCKSZ', blocksize, description:
448 '''Size of a disk block --- this also limits the size of a tuple. You can set
449    it bigger if you need bigger tuples (although TOAST should reduce the need
450    to have large tuples, since fields can be spread across multiple tuples).
451    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
452    currently 2^15 (32768). This is determined by the 15-bit widths of the
453    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
454    Changing BLCKSZ requires an initdb.''')
456 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
457 cdata.set('RELSEG_SIZE', segsize)
458 cdata.set('DEF_PGPORT', get_option('pgport'))
459 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
460 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
461 if get_option('system_tzdata') != ''
462   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
463 endif
467 ###############################################################
468 # Directories
469 ###############################################################
471 # These are set by the equivalent --xxxdir configure options.  We
472 # append "postgresql" to some of them, if the string does not already
473 # contain "pgsql" or "postgres", in order to avoid directory clutter.
475 pkg = 'postgresql'
477 dir_prefix = get_option('prefix')
479 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
481 dir_bin = get_option('bindir')
483 dir_data = get_option('datadir')
484 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
485   dir_data = dir_data / pkg
486 endif
488 dir_sysconf = get_option('sysconfdir')
489 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
490   dir_sysconf = dir_sysconf / pkg
491 endif
493 dir_lib = get_option('libdir')
495 dir_lib_pkg = dir_lib
496 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
497   dir_lib_pkg = dir_lib_pkg / pkg
498 endif
500 dir_pgxs = dir_lib_pkg / 'pgxs'
502 dir_include = get_option('includedir')
504 dir_include_pkg = dir_include
505 dir_include_pkg_rel = ''
506 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
507   dir_include_pkg = dir_include_pkg / pkg
508   dir_include_pkg_rel = pkg
509 endif
511 dir_man = get_option('mandir')
513 # FIXME: These used to be separately configurable - worth adding?
514 dir_doc = get_option('datadir') / 'doc'
515 if not (dir_prefix_contains_pg or dir_doc.contains('pgsql') or dir_doc.contains('postgres'))
516   dir_doc = dir_doc / pkg
517 endif
518 dir_doc_html = dir_doc / 'html'
520 dir_locale = get_option('localedir')
523 # Derived values
524 dir_bitcode = dir_lib_pkg / 'bitcode'
525 dir_include_internal = dir_include_pkg / 'internal'
526 dir_include_server = dir_include_pkg / 'server'
527 dir_include_extension = dir_include_server / 'extension'
528 dir_data_extension = dir_data / 'extension'
529 dir_doc_extension = dir_doc / 'extension'
533 ###############################################################
534 # Search paths, preparation for compiler tests
536 # NB: Arguments added later are not automatically used for subsequent
537 # configuration-time checks (so they are more isolated). If they should be
538 # used, they need to be added to test_c_args as well.
539 ###############################################################
541 postgres_inc = [include_directories(postgres_inc_d)]
542 test_lib_d = postgres_lib_d
543 test_c_args = cppflags + cflags
547 ###############################################################
548 # Library: bsd-auth
549 ###############################################################
551 bsd_authopt = get_option('bsd_auth')
552 bsd_auth = not_found_dep
553 if cc.check_header('bsd_auth.h', required: bsd_authopt,
554     args: test_c_args, include_directories: postgres_inc)
555   cdata.set('USE_BSD_AUTH', 1)
556   bsd_auth = declare_dependency()
557 endif
561 ###############################################################
562 # Library: bonjour
564 # For now don't search for DNSServiceRegister in a library - only Apple's
565 # Bonjour implementation, which is always linked, works.
566 ###############################################################
568 bonjouropt = get_option('bonjour')
569 bonjour = not_found_dep
570 if cc.check_header('dns_sd.h', required: bonjouropt,
571     args: test_c_args, include_directories: postgres_inc) and \
572    cc.has_function('DNSServiceRegister',
573     args: test_c_args, include_directories: postgres_inc)
574   cdata.set('USE_BONJOUR', 1)
575   bonjour = declare_dependency()
576 endif
580 ###############################################################
581 # Option: docs in HTML and man page format
582 ###############################################################
584 docs_opt = get_option('docs')
585 docs_dep = not_found_dep
586 if not docs_opt.disabled()
587   if xmllint_bin.found() and xsltproc_bin.found()
588     docs_dep = declare_dependency()
589   elif docs_opt.enabled()
590     error('missing required tools for docs in HTML / man page format')
591   endif
592 endif
596 ###############################################################
597 # Option: docs in PDF format
598 ###############################################################
600 docs_pdf_opt = get_option('docs_pdf')
601 docs_pdf_dep = not_found_dep
602 if not docs_pdf_opt.disabled()
603   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
604   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
605     docs_pdf_dep = declare_dependency()
606   elif docs_pdf_opt.enabled()
607     error('missing required tools for docs in PDF format')
608   endif
609 endif
613 ###############################################################
614 # Library: GSSAPI
615 ###############################################################
617 gssapiopt = get_option('gssapi')
618 krb_srvtab = ''
619 have_gssapi = false
620 if not gssapiopt.disabled()
621   gssapi = dependency('krb5-gssapi', required: gssapiopt)
622   have_gssapi = gssapi.found()
624   if not have_gssapi
625   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi, required: false,
626       args: test_c_args, include_directories: postgres_inc)
627     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
628   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
629     cdata.set('HAVE_GSSAPI_H', 1)
630   else
631     have_gssapi = false
632   endif
634   if not have_gssapi
635   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
636       args: test_c_args, include_directories: postgres_inc)
637     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
638   elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
639     cdata.set('HAVE_GSSAPI_EXT_H', 1)
640   else
641     have_gssapi = false
642   endif
644   if not have_gssapi
645   elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
646       args: test_c_args, include_directories: postgres_inc)
647     cdata.set('ENABLE_GSS', 1)
649     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
650     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
651   elif gssapiopt.enabled()
652     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
653   else
654     have_gssapi = false
655   endif
656 endif
657 if not have_gssapi
658   gssapi = not_found_dep
659 endif
663 ###############################################################
664 # Library: ldap
665 ###############################################################
667 ldapopt = get_option('ldap')
668 if ldapopt.disabled()
669   ldap = not_found_dep
670   ldap_r = not_found_dep
671 elif host_system == 'windows'
672   ldap = cc.find_library('wldap32', required: ldapopt)
673   ldap_r = ldap
674 else
675   # macos framework dependency is buggy for ldap (one can argue whether it's
676   # Apple's or meson's fault), leading to an endless recursion with ldap.h
677   # including itself. See https://github.com/mesonbuild/meson/issues/10002
678   # Luckily we only need pkg-config support, so the workaround isn't
679   # complicated.
680   ldap = dependency('ldap', method: 'pkg-config', required: false)
681   ldap_r = ldap
683   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
684   # installed
685   if not ldap.found()
686     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
687       has_headers: 'ldap.h', header_include_directories: postgres_inc)
689     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
690     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
691     # library from a separate OpenLDAP installation).  The most reliable
692     # way to check that is to check for a function introduced in 2.5.
693     if not ldap.found()
694       # don't have ldap, we shouldn't check for ldap_r
695     elif cc.has_function('ldap_verify_credentials',
696         dependencies: ldap, args: test_c_args)
697       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
698     else
700       # Use ldap_r for FE if available, else assume ldap is thread-safe.
701       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
702         has_headers: 'ldap.h', header_include_directories: postgres_inc)
703       if not ldap_r.found()
704         ldap_r = ldap
705       else
706         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
707         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
708       endif
710       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
711       # process.  Check for OpenLDAP versions known not to tolerate doing so;
712       # assume non-OpenLDAP implementations are safe.  The dblink test suite
713       # exercises the hazardous interaction directly.
714       compat_test_code = '''
715 #include <ldap.h>
716 #if !defined(LDAP_VENDOR_VERSION) || \
717      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
718       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
719 choke me
720 #endif
722       if not cc.compiles(compat_test_code,
723           name: 'LDAP implementation compatible',
724           dependencies: ldap, args: test_c_args)
725         warning('''
726 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
727 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
728 *** also uses LDAP will crash on exit.''')
729       endif
730     endif
731   endif
733   if ldap.found() and cc.has_function('ldap_initialize',
734       dependencies: ldap, args: test_c_args)
735     cdata.set('HAVE_LDAP_INITIALIZE', 1)
736   endif
737 endif
739 if ldap.found()
740   assert(ldap_r.found())
741   cdata.set('USE_LDAP', 1)
742 else
743   assert(not ldap_r.found())
744 endif
748 ###############################################################
749 # Library: LLVM
750 ###############################################################
752 llvmopt = get_option('llvm')
753 llvm = not_found_dep
754 if add_languages('cpp', required: llvmopt, native: false)
755   llvm = dependency('llvm', version: '>=3.9', method: 'config-tool', required: llvmopt)
757   if llvm.found()
759     cdata.set('USE_LLVM', 1)
761     cpp = meson.get_compiler('cpp')
763     llvm_binpath = llvm.get_variable(configtool: 'bindir')
765     ccache = find_program('ccache', native: true, required: false)
766     clang = find_program(llvm_binpath / 'clang', required: true)
767   endif
768 elif llvmopt.auto()
769   message('llvm requires a C++ compiler')
770 endif
774 ###############################################################
775 # Library: icu
776 ###############################################################
778 icuopt = get_option('icu')
779 if not icuopt.disabled()
780   icu = dependency('icu-uc', required: icuopt)
781   icu_i18n = dependency('icu-i18n', required: icuopt)
783   if icu.found()
784     cdata.set('USE_ICU', 1)
785   endif
787 else
788   icu = not_found_dep
789   icu_i18n = not_found_dep
790 endif
794 ###############################################################
795 # Library: libxml
796 ###############################################################
798 libxmlopt = get_option('libxml')
799 if not libxmlopt.disabled()
800   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
802   if libxml.found()
803     cdata.set('USE_LIBXML', 1)
804   endif
805 else
806   libxml = not_found_dep
807 endif
811 ###############################################################
812 # Library: libxslt
813 ###############################################################
815 libxsltopt = get_option('libxslt')
816 if not libxsltopt.disabled()
817   libxslt = dependency('libxslt', required: libxsltopt)
819   if libxslt.found()
820     cdata.set('USE_LIBXSLT', 1)
821   endif
822 else
823   libxslt = not_found_dep
824 endif
828 ###############################################################
829 # Library: lz4
830 ###############################################################
832 lz4opt = get_option('lz4')
833 if not lz4opt.disabled()
834   lz4 = dependency('liblz4', required: lz4opt)
836   if lz4.found()
837     cdata.set('USE_LZ4', 1)
838     cdata.set('HAVE_LIBLZ4', 1)
839   endif
841 else
842   lz4 = not_found_dep
843 endif
847 ###############################################################
848 # Library: Tcl (for pltcl)
850 # NB: tclConfig.sh is used in autoconf build for getting
851 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
852 # variables. For now we have not seen a need to copy
853 # that behaviour to the meson build.
854 ###############################################################
856 tclopt = get_option('pltcl')
857 tcl_version = get_option('tcl_version')
858 tcl_dep = not_found_dep
859 if not tclopt.disabled()
861   # via pkg-config
862   tcl_dep = dependency(tcl_version, required: false)
864   if not tcl_dep.found()
865     tcl_dep = cc.find_library(tcl_version,
866       required: tclopt,
867       dirs: test_lib_d)
868   endif
870   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
871     tcl_dep = not_found_dep
872   endif
873 endif
877 ###############################################################
878 # Library: pam
879 ###############################################################
881 pamopt = get_option('pam')
882 if not pamopt.disabled()
883   pam = dependency('pam', required: false)
885   if not pam.found()
886     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
887   endif
889   if pam.found()
890     pam_header_found = false
892     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
893     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
894         args: test_c_args, include_directories: postgres_inc)
895       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
896       pam_header_found = true
897     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
898         args: test_c_args, include_directories: postgres_inc)
899       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
900       pam_header_found = true
901     endif
903     if pam_header_found
904       cdata.set('USE_PAM', 1)
905     else
906       pam = not_found_dep
907     endif
908   endif
909 else
910   pam = not_found_dep
911 endif
915 ###############################################################
916 # Library: Perl (for plperl)
917 ###############################################################
919 perlopt = get_option('plperl')
920 perl_dep = not_found_dep
921 if not perlopt.disabled()
922   perl_may_work = true
924   # First verify that perl has the necessary dependencies installed
925   perl_mods = run_command(
926     [perl,
927      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
928      '-e', ''],
929     check: false)
930   if perl_mods.returncode() != 0
931     perl_may_work = false
932     perl_msg = 'perl installation does not have the required modules'
933   endif
935   # Then inquire perl about its configuration
936   if perl_may_work
937     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
938     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
939     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
940     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
941     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
943     perl_inc_dir = '@0@/CORE'.format(archlibexp)
945     if perlversion.version_compare('< 5.14')
946       perl_may_work = false
947       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
948     elif useshrplib != 'true'
949       perl_may_work = false
950       perl_msg = 'need a shared perl'
951     endif
952   endif
954   if perl_may_work
955     # On most platforms, archlibexp is also where the Perl include files live ...
956     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
957     # ... but on newer macOS versions, we must use -iwithsysroot to look
958     # under sysroot
959     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
960        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
961       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
962     endif
964     # check compiler finds header
965     if not cc.has_header('perl.h', required: false,
966         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
967       perl_may_work = false
968       perl_msg = 'missing perl.h'
969     endif
970   endif
972   if perl_may_work
973     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
975     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
976     foreach flag : perl_ccflags_r.split(' ')
977       if flag.startswith('-D') and \
978           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
979         perl_ccflags += flag
980       endif
981     endforeach
983     if host_system == 'windows'
984       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
986       if cc.get_id() == 'msvc'
987         # prevent binary mismatch between MSVC built plperl and Strawberry or
988         # msys ucrt perl libraries
989         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
990       endif
991     endif
993     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
994     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
996     # We are after Embed's ldopts, but without the subset mentioned in
997     # Config's ccdlflags and ldflags.  (Those are the choices of those who
998     # built the Perl installation, which are not necessarily appropriate
999     # for building PostgreSQL.)
1000     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
1001     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
1002     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
1004     perl_ldopts = []
1005     foreach ldopt : ldopts.split(' ')
1006       if ldopt == '' or ldopt in undesired
1007         continue
1008       endif
1010       perl_ldopts += ldopt.strip('"')
1011     endforeach
1013     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1014     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1016     perl_dep_int = declare_dependency(
1017       compile_args: perl_ccflags,
1018       link_args: perl_ldopts,
1019       version: perlversion,
1020     )
1022     # While we're at it, check that we can link to libperl.
1023     # On most platforms, if perl.h is there then libperl.so will be too, but
1024     # at this writing Debian packages them separately.
1025     perl_link_test = '''
1026 /* see plperl.h */
1027 #ifdef _MSC_VER
1028 #define __inline__ inline
1029 #endif
1030 #include <EXTERN.h>
1031 #include <perl.h>
1032 int main(void)
1034 perl_alloc();
1035 }'''
1036     if not cc.links(perl_link_test, name: 'libperl',
1037           args: test_c_args + perl_ccflags + perl_ldopts,
1038           include_directories: postgres_inc)
1039       perl_may_work = false
1040       perl_msg = 'missing libperl'
1041     endif
1043   endif # perl_may_work
1045   if perl_may_work
1046     perl_dep = perl_dep_int
1047   else
1048     if perlopt.enabled()
1049       error('dependency plperl failed: @0@'.format(perl_msg))
1050     else
1051       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1052     endif
1053   endif
1054 endif
1058 ###############################################################
1059 # Library: Python (for plpython)
1060 ###############################################################
1062 pyopt = get_option('plpython')
1063 python3_dep = not_found_dep
1064 if not pyopt.disabled()
1065   pm = import('python')
1066   python3_inst = pm.find_installation(python.path(), required: pyopt)
1067   if python3_inst.found()
1068     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1069     # Remove this check after we depend on Meson >= 1.1.0
1070     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt)
1071       python3_dep = not_found_dep
1072     endif
1073   endif
1074 endif
1078 ###############################################################
1079 # Library: Readline
1080 ###############################################################
1082 if not get_option('readline').disabled()
1083   libedit_preferred = get_option('libedit_preferred')
1084   # Set the order of readline dependencies
1085   check_readline_deps = libedit_preferred ? \
1086     ['libedit', 'readline'] : ['readline', 'libedit']
1088   foreach readline_dep : check_readline_deps
1089     readline = dependency(readline_dep, required: false)
1090     if not readline.found()
1091       readline = cc.find_library(readline_dep,
1092         required: get_option('readline'),
1093         dirs: test_lib_d)
1094     endif
1095     if readline.found()
1096       break
1097     endif
1098   endforeach
1100   if readline.found()
1101     cdata.set('HAVE_LIBREADLINE', 1)
1103     editline_prefix = {
1104       'header_prefix': 'editline/',
1105       'flag_prefix': 'EDITLINE_',
1106     }
1107     readline_prefix = {
1108       'header_prefix': 'readline/',
1109       'flag_prefix': 'READLINE_',
1110     }
1111     default_prefix = {
1112       'header_prefix': '',
1113       'flag_prefix': '',
1114     }
1116     # Set the order of prefixes
1117     prefixes = libedit_preferred ? \
1118       [editline_prefix, default_prefix, readline_prefix] : \
1119       [readline_prefix, default_prefix, editline_prefix]
1121     at_least_one_header_found = false
1122     foreach header : ['history', 'readline']
1123       is_found = false
1124       foreach prefix : prefixes
1125         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1126         # Check history.h and readline.h
1127         if not is_found and cc.has_header(header_file,
1128             args: test_c_args, include_directories: postgres_inc,
1129             dependencies: [readline], required: false)
1130           if header == 'readline'
1131             readline_h = header_file
1132           endif
1133           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1134           is_found = true
1135           at_least_one_header_found = true
1136         endif
1137       endforeach
1138     endforeach
1140     if not at_least_one_header_found
1141       error('''readline header not found
1142 If you have @0@ already installed, see meson-logs/meson-log.txt for details on the
1143 failure. It is possible the compiler isn't looking in the proper directory.
1144 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1145     endif
1147     check_funcs = [
1148       'append_history',
1149       'history_truncate_file',
1150       'rl_completion_matches',
1151       'rl_filename_completion_function',
1152       'rl_reset_screen_size',
1153       'rl_variable_bind',
1154     ]
1156     foreach func : check_funcs
1157       found = cc.has_function(func, dependencies: [readline],
1158         args: test_c_args, include_directories: postgres_inc)
1159       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1160     endforeach
1162     check_vars = [
1163       'rl_completion_suppress_quote',
1164       'rl_filename_quote_characters',
1165       'rl_filename_quoting_function',
1166     ]
1168     foreach var : check_vars
1169       cdata.set('HAVE_' + var.to_upper(),
1170         cc.has_header_symbol(readline_h, var,
1171           args: test_c_args, include_directories: postgres_inc,
1172           prefix: '#include <stdio.h>',
1173           dependencies: [readline]) ? 1 : false)
1174     endforeach
1176     # If found via cc.find_library() ensure headers are found when using the
1177     # dependency. On meson < 0.57 one cannot do compiler checks using the
1178     # dependency returned by declare_dependency(), so we can't do this above.
1179     if readline.type_name() == 'library'
1180       readline = declare_dependency(dependencies: readline,
1181         include_directories: postgres_inc)
1182     endif
1184     # On windows with mingw readline requires auto-import to successfully
1185     # link, as the headers don't use declspec(dllimport)
1186     if host_system == 'windows' and cc.get_id() != 'msvc'
1187       readline = declare_dependency(dependencies: readline,
1188         link_args: '-Wl,--enable-auto-import')
1189     endif
1190   endif
1192   # XXX: Figure out whether to implement mingw warning equivalent
1193 else
1194   readline = not_found_dep
1195 endif
1199 ###############################################################
1200 # Library: selinux
1201 ###############################################################
1203 selinux = not_found_dep
1204 selinuxopt = get_option('selinux')
1205 if meson.version().version_compare('>=0.59')
1206   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1207 endif
1208 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1209 cdata.set('HAVE_LIBSELINUX',
1210   selinux.found() ? 1 : false)
1214 ###############################################################
1215 # Library: systemd
1216 ###############################################################
1218 systemd = not_found_dep
1219 systemdopt = get_option('systemd')
1220 if meson.version().version_compare('>=0.59')
1221   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1222 endif
1223 systemd = dependency('libsystemd', required: systemdopt)
1224 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1228 ###############################################################
1229 # Library: SSL
1230 ###############################################################
1232 ssl = not_found_dep
1233 ssl_library = 'none'
1234 sslopt = get_option('ssl')
1236 if sslopt == 'auto' and auto_features.disabled()
1237   sslopt = 'none'
1238 endif
1240 if sslopt in ['auto', 'openssl']
1241   openssl_required = (sslopt == 'openssl')
1243   # Try to find openssl via pkg-config et al, if that doesn't work
1244   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1245   # the library names that we know about.
1247   # via pkg-config et al
1248   ssl = dependency('openssl', required: false)
1249   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1250   # we pass cc.find_library() results if necessary
1251   ssl_int = []
1253   # via library + headers
1254   if not ssl.found()
1255     ssl_lib = cc.find_library('ssl',
1256       dirs: test_lib_d,
1257       header_include_directories: postgres_inc,
1258       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1259       required: openssl_required)
1260     crypto_lib = cc.find_library('crypto',
1261       dirs: test_lib_d,
1262       required: openssl_required)
1263     if ssl_lib.found() and crypto_lib.found()
1264       ssl_int = [ssl_lib, crypto_lib]
1265       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1266     endif
1267   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1268        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1269     ssl_int = [ssl]
1270   else
1271     ssl = not_found_dep
1272   endif
1274   if ssl.found()
1275     check_funcs = [
1276       ['CRYPTO_new_ex_data', {'required': true}],
1277       ['SSL_new', {'required': true}],
1279       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1280       ['SSL_CTX_set_cert_cb'],
1282       # Functions introduced in OpenSSL 1.1.0. We used to check for
1283       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1284       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1285       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1286       # functions.
1287       ['OPENSSL_init_ssl'],
1288       ['BIO_meth_new'],
1289       ['ASN1_STRING_get0_data'],
1290       ['HMAC_CTX_new'],
1291       ['HMAC_CTX_free'],
1293       # OpenSSL versions before 1.1.0 required setting callback functions, for
1294       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1295       # function was removed.
1296       ['CRYPTO_lock'],
1298       # Function introduced in OpenSSL 1.1.1
1299       ['X509_get_signature_info'],
1300     ]
1302     are_openssl_funcs_complete = true
1303     foreach c : check_funcs
1304       func = c.get(0)
1305       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1306       required = c.get(1, {}).get('required', false)
1307       if required and not val
1308         are_openssl_funcs_complete = false
1309         if openssl_required
1310           error('openssl function @0@ is required'.format(func))
1311         endif
1312         break
1313       elif not required
1314         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1315       endif
1316     endforeach
1318     if are_openssl_funcs_complete
1319       cdata.set('USE_OPENSSL', 1,
1320                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1321       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1322                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1323       ssl_library = 'openssl'
1324     else
1325       ssl = not_found_dep
1326     endif
1327   endif
1328 endif
1330 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1331   error('no SSL library found')
1332 endif
1336 ###############################################################
1337 # Library: uuid
1338 ###############################################################
1340 uuidopt = get_option('uuid')
1341 if uuidopt != 'none'
1342   uuidname = uuidopt.to_upper()
1343   if uuidopt == 'e2fs'
1344     uuid = dependency('uuid', required: true)
1345     uuidfunc = 'uuid_generate'
1346     uuidheader = 'uuid/uuid.h'
1347   elif uuidopt == 'bsd'
1348     # libc should have uuid function
1349     uuid = declare_dependency()
1350     uuidfunc = 'uuid_to_string'
1351     uuidheader = 'uuid.h'
1352   elif uuidopt == 'ossp'
1353     uuid = dependency('ossp-uuid', required: true)
1354     uuidfunc = 'uuid_export'
1355     uuidheader = 'uuid.h'
1356   else
1357     error('unknown uuid build option value: @0@'.format(uuidopt))
1358   endif
1360   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1361     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1362   endif
1363   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1365   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1366            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1367 else
1368   uuid = not_found_dep
1369 endif
1373 ###############################################################
1374 # Library: zlib
1375 ###############################################################
1377 zlibopt = get_option('zlib')
1378 zlib = not_found_dep
1379 if not zlibopt.disabled()
1380   zlib_t = dependency('zlib', required: zlibopt)
1382   if zlib_t.type_name() == 'internal'
1383     # if fallback was used, we don't need to test if headers are present (they
1384     # aren't built yet, so we can't test)
1385     zlib = zlib_t
1386   elif not zlib_t.found()
1387     warning('did not find zlib')
1388   elif not cc.has_header('zlib.h',
1389       args: test_c_args, include_directories: postgres_inc,
1390       dependencies: [zlib_t], required: zlibopt)
1391     warning('zlib header not found')
1392   else
1393     zlib = zlib_t
1394   endif
1396   if zlib.found()
1397     cdata.set('HAVE_LIBZ', 1)
1398   endif
1399 endif
1403 ###############################################################
1404 # Library: tap test dependencies
1405 ###############################################################
1407 # Check whether tap tests are enabled or not
1408 tap_tests_enabled = false
1409 tapopt = get_option('tap_tests')
1410 if not tapopt.disabled()
1411   # Checking for perl modules for tap tests
1412   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1413   if perl_ipc_run_check.returncode() != 0
1414     message(perl_ipc_run_check.stderr().strip())
1415     if tapopt.enabled()
1416       error('Additional Perl modules are required to run TAP tests.')
1417     else
1418       warning('Additional Perl modules are required to run TAP tests.')
1419     endif
1420   else
1421     tap_tests_enabled = true
1422   endif
1423 endif
1427 ###############################################################
1428 # Library: zstd
1429 ###############################################################
1431 zstdopt = get_option('zstd')
1432 if not zstdopt.disabled()
1433   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1435   if zstd.found()
1436     cdata.set('USE_ZSTD', 1)
1437     cdata.set('HAVE_LIBZSTD', 1)
1438   endif
1440 else
1441   zstd = not_found_dep
1442 endif
1446 ###############################################################
1447 # Compiler tests
1448 ###############################################################
1450 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1451 # unnecessarily, because we optionally rely on newer features.
1452 c99_test = '''
1453 #include <stdbool.h>
1454 #include <complex.h>
1455 #include <tgmath.h>
1456 #include <inttypes.h>
1458 struct named_init_test {
1459   int a;
1460   int b;
1463 extern void structfunc(struct named_init_test);
1465 int main(int argc, char **argv)
1467   struct named_init_test nit = {
1468     .a = 3,
1469     .b = 5,
1470   };
1472   for (int loop_var = 0; loop_var < 3; loop_var++)
1473   {
1474     nit.a += nit.b;
1475   }
1477   structfunc((struct named_init_test){1, 0});
1479   return nit.a != 0;
1483 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1484   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1485         args: test_c_args + ['-std=c99'])
1486     test_c_args += '-std=c99'
1487     cflags += '-std=c99'
1488   else
1489     error('C compiler does not support C99')
1490   endif
1491 endif
1493 sizeof_long = cc.sizeof('long', args: test_c_args)
1494 cdata.set('SIZEOF_LONG', sizeof_long)
1495 if sizeof_long == 8
1496   cdata.set('HAVE_LONG_INT_64', 1)
1497   cdata.set('PG_INT64_TYPE', 'long int')
1498   cdata.set_quoted('INT64_MODIFIER', 'l')
1499 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1500   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1501   cdata.set('PG_INT64_TYPE', 'long long int')
1502   cdata.set_quoted('INT64_MODIFIER', 'll')
1503 else
1504   error('do not know how to get a 64bit int')
1505 endif
1507 if host_machine.endian() == 'big'
1508   cdata.set('WORDS_BIGENDIAN', 1)
1509 endif
1511 alignof_types = ['short', 'int', 'long', 'double']
1512 maxalign = 0
1513 foreach t : alignof_types
1514   align = cc.alignment(t, args: test_c_args)
1515   if maxalign < align
1516     maxalign = align
1517   endif
1518   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1519 endforeach
1520 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1522 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1523 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1526 # Check if __int128 is a working 128 bit integer type, and if so
1527 # define PG_INT128_TYPE to that typename.
1529 # This currently only detects a GCC/clang extension, but support for other
1530 # environments may be added in the future.
1532 # For the moment we only test for support for 128bit math; support for
1533 # 128bit literals and snprintf is not required.
1534 if cc.links('''
1535   /*
1536    * We don't actually run this test, just link it to verify that any support
1537    * functions needed for __int128 are present.
1538    *
1539    * These are globals to discourage the compiler from folding all the
1540    * arithmetic tests down to compile-time constants.  We do not have
1541    * convenient support for 128bit literals at this point...
1542    */
1543   __int128 a = 48828125;
1544   __int128 b = 97656250;
1546   int main(void)
1547   {
1548       __int128 c,d;
1549       a = (a << 12) + 1; /* 200000000001 */
1550       b = (b << 12) + 5; /* 400000000005 */
1551       /* try the most relevant arithmetic ops */
1552       c = a * b;
1553       d = (c + b) / b;
1554       /* must use the results, else compiler may optimize arithmetic away */
1555       return d != a+1;
1556   }''',
1557   name: '__int128',
1558   args: test_c_args)
1560   buggy_int128 = false
1562   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1563   # If not cross-compiling, we can test for bugs and disable use of __int128
1564   # with buggy compilers.  If cross-compiling, hope for the best.
1565   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1566   if not meson.is_cross_build()
1567     r = cc.run('''
1568     /* This must match the corresponding code in c.h: */
1569     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1570     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1571     #elif defined(_MSC_VER)
1572     #define pg_attribute_aligned(a) __declspec(align(a))
1573     #endif
1574     typedef __int128 int128a
1575     #if defined(pg_attribute_aligned)
1576     pg_attribute_aligned(8)
1577     #endif
1578     ;
1580     int128a holder;
1581     void pass_by_val(void *buffer, int128a par) { holder = par; }
1583     int main(void)
1584     {
1585         long int i64 = 97656225L << 12;
1586         int128a q;
1587         pass_by_val(main, (int128a) i64);
1588         q = (int128a) i64;
1589         return q != holder;
1590     }''',
1591     name: '__int128 alignment bug',
1592     args: test_c_args)
1593     assert(r.compiled())
1594     if r.returncode() != 0
1595       buggy_int128 = true
1596       message('__int128 support present but buggy and thus disabled')
1597     endif
1598   endif
1600   if not buggy_int128
1601     cdata.set('PG_INT128_TYPE', '__int128')
1602     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1603   endif
1604 endif
1607 # Check if the C compiler knows computed gotos (gcc extension, also
1608 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1610 # Checking whether computed gotos are supported syntax-wise ought to
1611 # be enough, as the syntax is otherwise illegal.
1612 if cc.compiles('''
1613     static inline int foo(void)
1614     {
1615       void *labeladdrs[] = {&&my_label};
1616       goto *labeladdrs[0];
1617       my_label:
1618       return 1;
1619     }''',
1620     args: test_c_args)
1621   cdata.set('HAVE_COMPUTED_GOTO', 1)
1622 endif
1625 # Check if the C compiler understands _Static_assert(),
1626 # and define HAVE__STATIC_ASSERT if so.
1628 # We actually check the syntax ({ _Static_assert(...) }), because we need
1629 # gcc-style compound expressions to be able to wrap the thing into macros.
1630 if cc.compiles('''
1631     int main(int arg, char **argv)
1632     {
1633         ({ _Static_assert(1, "foo"); });
1634     }
1635     ''',
1636     args: test_c_args)
1637   cdata.set('HAVE__STATIC_ASSERT', 1)
1638 endif
1641 # We use <stdbool.h> if we have it and it declares type bool as having
1642 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1643 if cc.has_type('_Bool', args: test_c_args) \
1644     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1645     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1646   cdata.set('HAVE__BOOL', 1)
1647   cdata.set('PG_USE_STDBOOL', 1)
1648 endif
1651 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1652 # warning for each use of %m.
1653 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1654 testsrc = '''
1655 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1656 static void call_log(void)
1658     emit_log(0, "error: %s: %m", "foo");
1661 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1662 foreach a : printf_attributes
1663   if cc.compiles(testsrc.format(a),
1664       args: test_c_args + attrib_error_args, name: 'format ' + a)
1665     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1666     break
1667   endif
1668 endforeach
1671 if cc.has_function_attribute('visibility:default') and \
1672     cc.has_function_attribute('visibility:hidden')
1673   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1675   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1676   # inlineshidden to C code as well... And either way, we want to put these
1677   # flags into exported files (pgxs, .pc files).
1678   cflags_mod += '-fvisibility=hidden'
1679   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1680   ldflags_mod += '-fvisibility=hidden'
1681 endif
1684 # Check if various builtins exist. Some builtins are tested separately,
1685 # because we want to test something more complicated than the generic case.
1686 builtins = [
1687   'bswap16',
1688   'bswap32',
1689   'bswap64',
1690   'clz',
1691   'ctz',
1692   'constant_p',
1693   'frame_address',
1694   'popcount',
1695   'unreachable',
1698 foreach builtin : builtins
1699   fname = '__builtin_@0@'.format(builtin)
1700   if cc.has_function(fname, args: test_c_args)
1701     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1702   endif
1703 endforeach
1706 # Check if the C compiler understands __builtin_types_compatible_p,
1707 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1709 # We check usage with __typeof__, though it's unlikely any compiler would
1710 # have the former and not the latter.
1711 if cc.compiles('''
1712     static int x;
1713     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1714     ''',
1715     name: '__builtin_types_compatible_p',
1716     args: test_c_args)
1717   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1718 endif
1721 # Check if the C compiler understands __builtin_$op_overflow(),
1722 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1724 # Check for the most complicated case, 64 bit multiplication, as a
1725 # proxy for all of the operations.  To detect the case where the compiler
1726 # knows the function but library support is missing, we must link not just
1727 # compile, and store the results in global variables so the compiler doesn't
1728 # optimize away the call.
1729 if cc.links('''
1730     INT64 a = 1;
1731     INT64 b = 1;
1732     INT64 result;
1734     int main(void)
1735     {
1736         return __builtin_mul_overflow(a, b, &result);
1737     }''',
1738     name: '__builtin_mul_overflow',
1739     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1740     )
1741   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1742 endif
1745 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1746 # here. To prevent problems due to two detection methods working, stop
1747 # checking after one.
1748 if cc.links('''
1749     #include <cpuid.h>
1750     int main(int arg, char **argv)
1751     {
1752         unsigned int exx[4] = {0, 0, 0, 0};
1753         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1754     }
1755     ''', name: '__get_cpuid',
1756     args: test_c_args)
1757   cdata.set('HAVE__GET_CPUID', 1)
1758 elif cc.links('''
1759     #include <intrin.h>
1760     int main(int arg, char **argv)
1761     {
1762         unsigned int exx[4] = {0, 0, 0, 0};
1763         __cpuid(exx, 1);
1764     }
1765     ''', name: '__cpuid',
1766     args: test_c_args)
1767   cdata.set('HAVE__CPUID', 1)
1768 endif
1771 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1772 # versions of clang do not understand -fexcess-precision=standard, the use of
1773 # x87 floating point operations leads to problems like isinf possibly returning
1774 # false for a value that is infinite when converted from the 80bit register to
1775 # the 8byte memory representation.
1777 # Only perform the test if the compiler doesn't understand
1778 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1779 # automatically.
1780 if '-fexcess-precision=standard' not in cflags
1781   if not cc.compiles('''
1782 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1783 choke me
1784 #endif''',
1785       name: '', args: test_c_args)
1786     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1787   endif
1788 endif
1792 ###############################################################
1793 # Compiler flags
1794 ###############################################################
1796 common_functional_flags = [
1797   # Disable strict-aliasing rules; needed for gcc 3.3+
1798   '-fno-strict-aliasing',
1799   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1800   '-fwrapv',
1801   '-fexcess-precision=standard',
1804 cflags += cc.get_supported_arguments(common_functional_flags)
1805 if llvm.found()
1806   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1807 endif
1809 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1810 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1812 common_warning_flags = [
1813   '-Wmissing-prototypes',
1814   '-Wpointer-arith',
1815   # Really don't want VLAs to be used in our dialect of C
1816   '-Werror=vla',
1817   # On macOS, complain about usage of symbols newer than the deployment target
1818   '-Werror=unguarded-availability-new',
1819   '-Wendif-labels',
1820   '-Wmissing-format-attribute',
1821   '-Wimplicit-fallthrough=3',
1822   '-Wcast-function-type',
1823   '-Wshadow=compatible-local',
1824   # This was included in -Wall/-Wformat in older GCC versions
1825   '-Wformat-security',
1828 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1829 if llvm.found()
1830   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1831 endif
1833 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1834 # the result for them
1835 cflags_no_decl_after_statement = []
1836 if cc.has_argument('-Wdeclaration-after-statement')
1837   cflags_warn += '-Wdeclaration-after-statement'
1838   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1839 endif
1842 # The following tests want to suppress various unhelpful warnings by adding
1843 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1844 # switches, so we have to test for the positive form and if that works,
1845 # add the negative form.
1847 negative_warning_flags = [
1848   # Suppress clang's unhelpful unused-command-line-argument warnings.
1849   'unused-command-line-argument',
1851   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1852   # of warnings when building plperl because of usages in the Perl headers.
1853   'compound-token-split-by-macro',
1855   # Similarly disable useless truncation warnings from gcc 8+
1856   'format-truncation',
1857   'stringop-truncation',
1859   # Suppress clang 16's strict warnings about function casts
1860   'cast-function-type-strict',
1862   # To make warning_level=2 / -Wextra work, we'd need at least the following
1863   # 'clobbered',
1864   # 'missing-field-initializers',
1865   # 'sign-compare',
1866   # 'unused-parameter',
1869 foreach w : negative_warning_flags
1870   if cc.has_argument('-W' + w)
1871     cflags_warn += '-Wno-' + w
1872   endif
1873   if llvm.found() and cpp.has_argument('-W' + w)
1874     cxxflags_warn += '-Wno-' + w
1875   endif
1876 endforeach
1879 if cc.get_id() == 'msvc'
1880   cflags_warn += [
1881     '/wd4018', # signed/unsigned mismatch
1882     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1883     '/wd4273', # inconsistent DLL linkage
1884     '/wd4101', # unreferenced local variable
1885     '/wd4102', # unreferenced label
1886     '/wd4090', # different 'modifier' qualifiers
1887     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1888   ]
1890   cppflags += [
1891     '/DWIN32',
1892     '/DWINDOWS',
1893     '/D__WINDOWS__',
1894     '/D__WIN32__',
1895     '/D_CRT_SECURE_NO_DEPRECATE',
1896     '/D_CRT_NONSTDC_NO_DEPRECATE',
1897   ]
1899   # We never need export libraries. As link.exe reports their creation, they
1900   # are unnecessarily noisy. Similarly, we don't need import library for
1901   # modules, we only import them dynamically, and they're also noisy.
1902   ldflags += '/NOEXP'
1903   ldflags_mod += '/NOIMPLIB'
1904 endif
1908 ###############################################################
1909 # Atomics
1910 ###############################################################
1912 if not get_option('spinlocks')
1913   warning('Not using spinlocks will cause poor performance')
1914 else
1915   cdata.set('HAVE_SPINLOCKS', 1)
1916 endif
1918 if not get_option('atomics')
1919   warning('Not using atomics will cause poor performance')
1920 else
1921   # XXX: perhaps we should require some atomics support in this case these
1922   # days?
1923   cdata.set('HAVE_ATOMICS', 1)
1925   atomic_checks = [
1926     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1927      'desc': '__sync_lock_test_and_set(char)',
1928      'test': '''
1929 char lock = 0;
1930 __sync_lock_test_and_set(&lock, 1);
1931 __sync_lock_release(&lock);'''},
1933     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1934      'desc': '__sync_lock_test_and_set(int32)',
1935      'test': '''
1936 int lock = 0;
1937 __sync_lock_test_and_set(&lock, 1);
1938 __sync_lock_release(&lock);'''},
1940     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1941      'desc': '__sync_val_compare_and_swap(int32)',
1942      'test': '''
1943 int val = 0;
1944 __sync_val_compare_and_swap(&val, 0, 37);'''},
1946     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1947      'desc': '__sync_val_compare_and_swap(int64)',
1948      'test': '''
1949 INT64 val = 0;
1950 __sync_val_compare_and_swap(&val, 0, 37);'''},
1952     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1953      'desc': ' __atomic_compare_exchange_n(int32)',
1954      'test': '''
1955 int val = 0;
1956 int expect = 0;
1957 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1959     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1960      'desc': ' __atomic_compare_exchange_n(int64)',
1961      'test': '''
1962 INT64 val = 0;
1963 INT64 expect = 0;
1964 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1965   ]
1967   foreach check : atomic_checks
1968     test = '''
1969 int main(void)
1972 }'''.format(check['test'])
1974     cdata.set(check['name'],
1975       cc.links(test,
1976         name: check['desc'],
1977         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1978     )
1979   endforeach
1981 endif
1985 ###############################################################
1986 # Select CRC-32C implementation.
1988 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1989 # use the special CRC instructions for calculating CRC-32C. If we're not
1990 # targeting such a processor, but we can nevertheless produce code that uses
1991 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1992 # implementations and select which one to use at runtime, depending on whether
1993 # SSE 4.2 is supported by the processor we're running on.
1995 # Similarly, if we are targeting an ARM processor that has the CRC
1996 # instructions that are part of the ARMv8 CRC Extension, use them. And if
1997 # we're not targeting such a processor, but can nevertheless produce code that
1998 # uses the CRC instructions, compile both, and select at runtime.
1999 ###############################################################
2001 have_optimized_crc = false
2002 cflags_crc = []
2003 if host_cpu == 'x86' or host_cpu == 'x86_64'
2005   if cc.get_id() == 'msvc'
2006     cdata.set('USE_SSE42_CRC32C', false)
2007     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2008     have_optimized_crc = true
2009   else
2011     prog = '''
2012 #include <nmmintrin.h>
2014 int main(void)
2016     unsigned int crc = 0;
2017     crc = _mm_crc32_u8(crc, 0);
2018     crc = _mm_crc32_u32(crc, 0);
2019     /* return computed value, to prevent the above being optimized away */
2020     return crc == 0;
2024     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2025           args: test_c_args)
2026       # Use Intel SSE 4.2 unconditionally.
2027       cdata.set('USE_SSE42_CRC32C', 1)
2028       have_optimized_crc = true
2029     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2030           args: test_c_args + ['-msse4.2'])
2031       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2032       # the runtime check.
2033       cflags_crc += '-msse4.2'
2034       cdata.set('USE_SSE42_CRC32C', false)
2035       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2036       have_optimized_crc = true
2037     endif
2039   endif
2041 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2043   prog = '''
2044 #include <arm_acle.h>
2046 int main(void)
2048     unsigned int crc = 0;
2049     crc = __crc32cb(crc, 0);
2050     crc = __crc32ch(crc, 0);
2051     crc = __crc32cw(crc, 0);
2052     crc = __crc32cd(crc, 0);
2054     /* return computed value, to prevent the above being optimized away */
2055     return crc == 0;
2059   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2060       args: test_c_args)
2061     # Use ARM CRC Extension unconditionally
2062     cdata.set('USE_ARMV8_CRC32C', 1)
2063     have_optimized_crc = true
2064   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2065       args: test_c_args + ['-march=armv8-a+crc'])
2066     # Use ARM CRC Extension, with runtime check
2067     cflags_crc += '-march=armv8-a+crc'
2068     cdata.set('USE_ARMV8_CRC32C', false)
2069     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2070     have_optimized_crc = true
2071   endif
2073 elif host_cpu == 'loongarch64'
2075   prog = '''
2076 int main(void)
2078     unsigned int crc = 0;
2079     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2080     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2081     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2082     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2084     /* return computed value, to prevent the above being optimized away */
2085     return crc == 0;
2089   if cc.links(prog, name: '__builtin_loongarch_crcc_w_b_w, __builtin_loongarch_crcc_w_h_w, __builtin_loongarch_crcc_w_w_w, and __builtin_loongarch_crcc_w_d_w',
2090       args: test_c_args)
2091     # Use LoongArch CRC instruction unconditionally
2092     cdata.set('USE_LOONGARCH_CRC32C', 1)
2093     have_optimized_crc = true
2094   endif
2096 endif
2098 if not have_optimized_crc
2099   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2100   # support.
2101   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2102 endif
2106 ###############################################################
2107 # Other CPU specific stuff
2108 ###############################################################
2110 if host_cpu == 'x86_64'
2112   if cc.compiles('''
2113       void main(void)
2114       {
2115           long long x = 1; long long r;
2116           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2117       }''',
2118       name: '@0@: popcntq instruction'.format(host_cpu),
2119       args: test_c_args)
2120     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2121   endif
2123 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2124   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2125   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2126     if cc.compiles('''
2127       static inline int
2128       addi(int ra, int si)
2129       {
2130           int res = 0;
2131           if (__builtin_constant_p(si))
2132               __asm__ __volatile__(
2133                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2134           return res;
2135       }
2136       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2137       ''',
2138       args: test_c_args)
2139       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2140     endif
2141   endif
2142 endif
2146 ###############################################################
2147 # Library / OS tests
2148 ###############################################################
2150 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2151 # unnecessary checks over and over, particularly on windows.
2152 header_checks = [
2153   'atomic.h',
2154   'copyfile.h',
2155   'crtdefs.h',
2156   'execinfo.h',
2157   'getopt.h',
2158   'ifaddrs.h',
2159   'langinfo.h',
2160   'mbarrier.h',
2161   'stdbool.h',
2162   'strings.h',
2163   'sys/epoll.h',
2164   'sys/event.h',
2165   'sys/personality.h',
2166   'sys/prctl.h',
2167   'sys/procctl.h',
2168   'sys/signalfd.h',
2169   'sys/ucred.h',
2170   'termios.h',
2171   'ucred.h',
2174 foreach header : header_checks
2175   varname = 'HAVE_' + header.underscorify().to_upper()
2177   # Emulate autoconf behaviour of not-found->undef, found->1
2178   found = cc.has_header(header,
2179     include_directories: postgres_inc, args: test_c_args)
2180   cdata.set(varname, found ? 1 : false,
2181             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2182 endforeach
2185 decl_checks = [
2186   ['F_FULLFSYNC', 'fcntl.h'],
2187   ['fdatasync', 'unistd.h'],
2188   ['posix_fadvise', 'fcntl.h'],
2189   ['strlcat', 'string.h'],
2190   ['strlcpy', 'string.h'],
2191   ['strnlen', 'string.h'],
2194 # Need to check for function declarations for these functions, because
2195 # checking for library symbols wouldn't handle deployment target
2196 # restrictions on macOS
2197 decl_checks += [
2198   ['preadv', 'sys/uio.h'],
2199   ['pwritev', 'sys/uio.h'],
2202 foreach c : decl_checks
2203   func = c.get(0)
2204   header = c.get(1)
2205   args = c.get(2, {})
2206   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2208   found = cc.has_header_symbol(header, func,
2209     args: test_c_args, include_directories: postgres_inc,
2210     kwargs: args)
2211   cdata.set10(varname, found, description:
2212 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2213    don't.'''.format(func))
2214 endforeach
2217 if cc.has_type('struct option',
2218     args: test_c_args, include_directories: postgres_inc,
2219     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2220   cdata.set('HAVE_STRUCT_OPTION', 1)
2221 endif
2224 foreach c : ['opterr', 'optreset']
2225   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2227   if cc.links('''
2228 #include <unistd.h>
2229 int main(void)
2231     extern int @0@;
2232     @0@ = 1;
2234 '''.format(c), name: c, args: test_c_args)
2235     cdata.set(varname, 1)
2236   else
2237     cdata.set(varname, false)
2238   endif
2239 endforeach
2241 if cc.has_type('socklen_t',
2242     args: test_c_args, include_directories: postgres_inc,
2243     prefix: '''
2244 #include <sys/socket.h>''')
2245   cdata.set('HAVE_SOCKLEN_T', 1)
2246 endif
2248 if cc.has_member('struct sockaddr', 'sa_len',
2249     args: test_c_args, include_directories: postgres_inc,
2250     prefix: '''
2251 #include <sys/types.h>
2252 #include <sys/socket.h>''')
2253   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2254 endif
2256 if cc.has_member('struct tm', 'tm_zone',
2257     args: test_c_args, include_directories: postgres_inc,
2258     prefix: '''
2259 #include <sys/types.h>
2260 #include <time.h>
2261 ''')
2262   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2263 endif
2265 if cc.compiles('''
2266 #include <time.h>
2267 extern int foo(void);
2268 int foo(void)
2270     return timezone / 60;
2272 ''',
2273     name: 'global variable `timezone\' exists',
2274     args: test_c_args, include_directories: postgres_inc)
2275   cdata.set('HAVE_INT_TIMEZONE', 1)
2276 else
2277   cdata.set('HAVE_INT_TIMEZONE', false)
2278 endif
2280 if cc.has_type('union semun',
2281     args: test_c_args,
2282     include_directories: postgres_inc,
2283     prefix: '''
2284 #include <sys/types.h>
2285 #include <sys/ipc.h>
2286 #include <sys/sem.h>
2287 ''')
2288   cdata.set('HAVE_UNION_SEMUN', 1)
2289 endif
2291 if cc.compiles('''
2292 #include <string.h>
2293 int main(void)
2295   char buf[100];
2296   switch (strerror_r(1, buf, sizeof(buf)))
2297   { case 0: break; default: break; }
2298 }''',
2299     name: 'strerror_r',
2300     args: test_c_args, include_directories: postgres_inc)
2301   cdata.set('STRERROR_R_INT', 1)
2302 else
2303   cdata.set('STRERROR_R_INT', false)
2304 endif
2306 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2307 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2308 # we should not use so we check the standard header first.  MSVC has a
2309 # replacement defined in src/include/port/win32_port.h.
2310 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2311    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2312   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2313 endif
2315 # Check if the C compiler understands typeof or a variant.  Define
2316 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2317 foreach kw : ['typeof', '__typeof__', 'decltype']
2318   if cc.compiles('''
2319 int main(void)
2321     int x = 0;
2322     @0@(x) y;
2323     y = x;
2324     return y;
2326 '''.format(kw),
2327     name: 'typeof()',
2328     args: test_c_args, include_directories: postgres_inc)
2330     cdata.set('HAVE_TYPEOF', 1)
2331     if kw != 'typeof'
2332       cdata.set('typeof', kw)
2333     endif
2335     break
2336   endif
2337 endforeach
2340 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2341 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2342 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2343 wcstombs_l_test = '''
2344 #include <stdlib.h>
2345 #include <locale.h>
2348 void main(void)
2350 #ifndef wcstombs_l
2351     (void) wcstombs_l;
2352 #endif
2355 if (not cc.compiles(wcstombs_l_test.format(''),
2356       name: 'wcstombs_l') and
2357     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2358       name: 'wcstombs_l in xlocale.h'))
2359     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2360 endif
2363 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2364 # understands, because it conflicts with __declspec(restrict). Therefore we
2365 # define pg_restrict to the appropriate definition, which presumably won't
2366 # conflict.
2368 # We assume C99 support, so we don't need to make this conditional.
2370 # XXX: Historically we allowed platforms to disable restrict in template
2371 # files, but that was only added for AIX when building with XLC, which we
2372 # don't support yet.
2373 cdata.set('pg_restrict', '__restrict')
2376 # Most libraries are included only if they demonstrably provide a function we
2377 # need, but libm is an exception: always include it, because there are too
2378 # many compilers that play cute optimization games that will break probes for
2379 # standard functions such as pow().
2380 os_deps += cc.find_library('m', required: false)
2382 rt_dep = cc.find_library('rt', required: false)
2384 dl_dep = cc.find_library('dl', required: false)
2386 util_dep = cc.find_library('util', required: false)
2388 getopt_dep = cc.find_library('getopt', required: false)
2389 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2390 # Check if we want to replace getopt/getopt_long even if provided by the system
2391 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2392 #   so always use our version on Windows
2393 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2394 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2395 # - We want to use system's getopt_long() only if the system provides struct
2396 #   option
2397 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2398 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2400 # Required on BSDs
2401 execinfo_dep = cc.find_library('execinfo', required: false)
2403 if host_system == 'cygwin'
2404   cygipc_dep = cc.find_library('cygipc', required: false)
2405 else
2406   cygipc_dep = not_found_dep
2407 endif
2409 if host_system == 'sunos'
2410   socket_dep = cc.find_library('socket', required: false)
2411 else
2412   socket_dep = not_found_dep
2413 endif
2415 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2416 # unnecessary checks over and over, particularly on windows.
2417 func_checks = [
2418   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2419   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2420   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2421   ['copyfile'],
2422   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2423   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2424   # required. Just checking for dlsym() ought to suffice.
2425   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2426   ['explicit_bzero'],
2427   ['getifaddrs'],
2428   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2429   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2430   ['getpeereid'],
2431   ['getpeerucred'],
2432   ['inet_aton'],
2433   ['inet_pton'],
2434   ['kqueue'],
2435   ['mbstowcs_l'],
2436   ['memset_s'],
2437   ['mkdtemp'],
2438   ['posix_fadvise'],
2439   ['posix_fallocate'],
2440   ['ppoll'],
2441   ['pstat'],
2442   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2443   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2444   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2445   ['setproctitle', {'dependencies': [util_dep]}],
2446   ['setproctitle_fast'],
2447   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2448   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2449   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2450   ['socket', {'dependencies': [socket_dep], 'define': false}],
2451   ['strchrnul'],
2452   ['strerror_r', {'dependencies': [thread_dep]}],
2453   ['strlcat'],
2454   ['strlcpy'],
2455   ['strnlen'],
2456   ['strsignal'],
2457   ['sync_file_range'],
2458   ['syncfs'],
2459   ['uselocale'],
2460   ['wcstombs_l'],
2463 func_check_results = {}
2464 foreach c : func_checks
2465   func = c.get(0)
2466   kwargs = c.get(1, {})
2467   deps = kwargs.get('dependencies', [])
2469   if kwargs.get('skip', false)
2470     continue
2471   endif
2473   found = cc.has_function(func, args: test_c_args)
2475   if not found
2476     foreach dep : deps
2477       if not dep.found()
2478         continue
2479       endif
2480       found = cc.has_function(func, args: test_c_args,
2481                               dependencies: [dep])
2482       if found
2483         os_deps += dep
2484         break
2485       endif
2486     endforeach
2487   endif
2489   func_check_results += {func: found}
2491   if kwargs.get('define', true)
2492     # Emulate autoconf behaviour of not-found->undef, found->1
2493     cdata.set('HAVE_' + func.underscorify().to_upper(),
2494               found  ? 1 : false,
2495               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2496   endif
2497 endforeach
2500 if cc.has_function('syslog', args: test_c_args) and \
2501     cc.check_header('syslog.h', args: test_c_args)
2502   cdata.set('HAVE_SYSLOG', 1)
2503 endif
2506 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2507 # semaphores
2508 if sema_kind == 'unnamed_posix' and \
2509    not func_check_results.get('sem_init', false)
2510   sema_kind = 'sysv'
2511 endif
2513 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2514 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2516 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2517 cdata.set_quoted('DLSUFFIX', dlsuffix)
2520 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2521 cdata.set_quoted('PG_VERSION_STR',
2522   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2523     pg_version, host_machine.cpu_family(), host_system,
2524     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2525   )
2529 ###############################################################
2530 # NLS / Gettext
2531 ###############################################################
2533 nlsopt = get_option('nls')
2534 libintl = not_found_dep
2536 if not nlsopt.disabled()
2537   # otherwise there'd be lots of
2538   # "Gettext not found, all translation (po) targets will be ignored."
2539   # warnings if not found.
2540   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2542   # meson 0.59 has this wrapped in dependency('intl')
2543   if (msgfmt.found() and
2544       cc.check_header('libintl.h', required: nlsopt,
2545         args: test_c_args, include_directories: postgres_inc))
2547     # in libc
2548     if cc.has_function('ngettext')
2549       libintl = declare_dependency()
2550     else
2551       libintl = cc.find_library('intl',
2552         has_headers: ['libintl.h'], required: nlsopt,
2553         header_include_directories: postgres_inc,
2554         dirs: test_lib_d)
2555     endif
2556   endif
2558   if libintl.found()
2559     i18n = import('i18n')
2560     cdata.set('ENABLE_NLS', 1)
2561   endif
2562 endif
2566 ###############################################################
2567 # Build
2568 ###############################################################
2570 # Set up compiler / linker arguments to be used everywhere, individual targets
2571 # can add further args directly, or indirectly via dependencies
2572 add_project_arguments(cflags, language: ['c'])
2573 add_project_arguments(cppflags, language: ['c'])
2574 add_project_arguments(cflags_warn, language: ['c'])
2575 add_project_arguments(cxxflags, language: ['cpp'])
2576 add_project_arguments(cppflags, language: ['cpp'])
2577 add_project_arguments(cxxflags_warn, language: ['cpp'])
2578 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2581 # Collect a number of lists of things while recursing through the source
2582 # tree. Later steps then can use those.
2584 # list of targets for various alias targets
2585 backend_targets = []
2586 bin_targets = []
2587 pl_targets = []
2588 contrib_targets = []
2589 testprep_targets = []
2590 nls_targets = []
2593 # Define the tests to distribute them to the correct test styles later
2594 test_deps = []
2595 tests = []
2598 # Default options for targets
2600 # First identify rpaths
2601 bin_install_rpaths = []
2602 lib_install_rpaths = []
2603 mod_install_rpaths = []
2606 # Don't add rpaths on darwin for now - as long as only absolute references to
2607 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2608 # their final destination.
2609 if host_system != 'darwin'
2610   # Add absolute path to libdir to rpath. This ensures installed binaries /
2611   # libraries find our libraries (mainly libpq).
2612   bin_install_rpaths += dir_prefix / dir_lib
2613   lib_install_rpaths += dir_prefix / dir_lib
2614   mod_install_rpaths += dir_prefix / dir_lib
2616   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2617   #
2618   # Not needed on darwin even if we use relative rpaths for our own libraries,
2619   # as the install_name of libraries in extra_lib_dirs will point to their
2620   # location anyway.
2621   bin_install_rpaths += postgres_lib_d
2622   lib_install_rpaths += postgres_lib_d
2623   mod_install_rpaths += postgres_lib_d
2624 endif
2627 # Define arguments for default targets
2629 default_target_args = {
2630   'implicit_include_directories': false,
2631   'install': true,
2634 default_lib_args = default_target_args + {
2635   'name_prefix': '',
2638 internal_lib_args = default_lib_args + {
2639   'build_by_default': false,
2640   'install': false,
2643 default_mod_args = default_lib_args + {
2644   'name_prefix': '',
2645   'install_dir': dir_lib_pkg,
2648 default_bin_args = default_target_args + {
2649   'install_dir': dir_bin,
2652 if get_option('rpath')
2653   default_lib_args += {
2654     'install_rpath': ':'.join(lib_install_rpaths),
2655   }
2657   default_mod_args += {
2658     'install_rpath': ':'.join(mod_install_rpaths),
2659   }
2661   default_bin_args += {
2662     'install_rpath': ':'.join(bin_install_rpaths),
2663   }
2664 endif
2667 # Helper for exporting a limited number of symbols
2668 gen_export_kwargs = {
2669   'input': 'exports.txt',
2670   'output': '@BASENAME@.'+export_file_suffix,
2671   'command': [perl, files('src/tools/gen_export.pl'),
2672    '--format', export_file_format,
2673    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2674   'build_by_default': false,
2675   'install': false,
2681 ### Helpers for custom targets used across the tree
2684 catalog_pm = files('src/backend/catalog/Catalog.pm')
2685 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2686 gen_kwlist_deps = [perfect_hash_pm]
2687 gen_kwlist_cmd = [
2688   perl, '-I', '@SOURCE_ROOT@/src/tools',
2689   files('src/tools/gen_keywordlist.pl'),
2690   '--output', '@OUTDIR@', '@INPUT@']
2695 ### windows resources related stuff
2698 if host_system == 'windows'
2699   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2700   win32ver_rc = files('src/port/win32ver.rc')
2701   rcgen = find_program('src/tools/rcgen', native: true)
2703   rcgen_base_args = [
2704     '--srcdir', '@SOURCE_DIR@',
2705     '--builddir', meson.build_root(),
2706     '--rcout', '@OUTPUT0@',
2707     '--out', '@OUTPUT1@',
2708     '--input', '@INPUT@',
2709     '@EXTRA_ARGS@',
2710   ]
2712   if cc.get_argument_syntax() == 'msvc'
2713     rc = find_program('rc', required: true)
2714     rcgen_base_args += ['--rc', rc.path()]
2715     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2716   else
2717     windres = find_program('windres', required: true)
2718     rcgen_base_args += ['--windres', windres.path()]
2719     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2720   endif
2722   # msbuild backend doesn't support this atm
2723   if meson.backend() == 'ninja'
2724     rcgen_base_args += ['--depfile', '@DEPFILE@']
2725   endif
2727   rcgen_bin_args = rcgen_base_args + [
2728     '--VFT_TYPE', 'VFT_APP',
2729     '--FILEENDING', 'exe',
2730     '--ICO', pg_ico
2731   ]
2733   rcgen_lib_args = rcgen_base_args + [
2734     '--VFT_TYPE', 'VFT_DLL',
2735     '--FILEENDING', 'dll',
2736   ]
2738   rc_bin_gen = generator(rcgen,
2739     depfile: '@BASENAME@.d',
2740     arguments: rcgen_bin_args,
2741     output: rcgen_outputs,
2742   )
2744   rc_lib_gen = generator(rcgen,
2745     depfile: '@BASENAME@.d',
2746     arguments: rcgen_lib_args,
2747     output: rcgen_outputs,
2748   )
2749 endif
2753 # headers that the whole build tree depends on
2754 generated_headers = []
2755 # headers that the backend build depends on
2756 generated_backend_headers = []
2757 # configure_files() output, needs a way of converting to file names
2758 configure_files = []
2760 # generated files that might conflict with a partial in-tree autoconf build
2761 generated_sources = []
2762 # same, for paths that differ between autoconf / meson builds
2763 # elements are [dir, [files]]
2764 generated_sources_ac = {}
2767 # First visit src/include - all targets creating headers are defined
2768 # within. That makes it easy to add the necessary dependencies for the
2769 # subsequent build steps.
2771 subdir('src/include')
2773 subdir('config')
2775 # Then through src/port and src/common, as most other things depend on them
2777 frontend_port_code = declare_dependency(
2778   compile_args: ['-DFRONTEND'],
2779   include_directories: [postgres_inc],
2780   dependencies: os_deps,
2783 backend_port_code = declare_dependency(
2784   compile_args: ['-DBUILDING_DLL'],
2785   include_directories: [postgres_inc],
2786   sources: [errcodes], # errcodes.h is needed due to use of ereport
2787   dependencies: os_deps,
2790 subdir('src/port')
2792 frontend_common_code = declare_dependency(
2793   compile_args: ['-DFRONTEND'],
2794   include_directories: [postgres_inc],
2795   sources: generated_headers,
2796   dependencies: [os_deps, zlib, zstd],
2799 backend_common_code = declare_dependency(
2800   compile_args: ['-DBUILDING_DLL'],
2801   include_directories: [postgres_inc],
2802   sources: generated_headers,
2803   dependencies: [os_deps, zlib, zstd],
2806 subdir('src/common')
2808 # all shared libraries should depend on shlib_code
2809 shlib_code = declare_dependency(
2810   link_args: ldflags_sl,
2813 # all static libraries not part of the backend should depend on this
2814 frontend_stlib_code = declare_dependency(
2815   include_directories: [postgres_inc],
2816   link_with: [common_static, pgport_static],
2817   sources: generated_headers,
2818   dependencies: [os_deps, libintl],
2821 # all shared libraries not part of the backend should depend on this
2822 frontend_shlib_code = declare_dependency(
2823   include_directories: [postgres_inc],
2824   link_with: [common_shlib, pgport_shlib],
2825   sources: generated_headers,
2826   dependencies: [shlib_code, os_deps, libintl],
2829 # Dependencies both for static and shared libpq
2830 libpq_deps += [
2831   thread_dep,
2833   gssapi,
2834   ldap_r,
2835   libintl,
2836   ssl,
2839 subdir('src/interfaces/libpq')
2840 # fe_utils depends on libpq
2841 subdir('src/fe_utils')
2843 # for frontend binaries
2844 frontend_code = declare_dependency(
2845   include_directories: [postgres_inc],
2846   link_with: [fe_utils, common_static, pgport_static],
2847   sources: generated_headers,
2848   dependencies: [os_deps, libintl],
2851 backend_both_deps += [
2852   thread_dep,
2853   bsd_auth,
2854   gssapi,
2855   icu,
2856   icu_i18n,
2857   ldap,
2858   libintl,
2859   libxml,
2860   lz4,
2861   pam,
2862   ssl,
2863   systemd,
2864   zlib,
2865   zstd,
2868 backend_mod_deps = backend_both_deps + os_deps
2870 backend_code = declare_dependency(
2871   compile_args: ['-DBUILDING_DLL'],
2872   include_directories: [postgres_inc],
2873   link_args: ldflags_be,
2874   link_with: [],
2875   sources: generated_headers + generated_backend_headers,
2876   dependencies: os_deps + backend_both_deps + backend_deps,
2879 # install these files only during test, not main install
2880 test_install_data = []
2881 test_install_libs = []
2883 # src/backend/meson.build defines backend_mod_code used for extension
2884 # libraries.
2887 # Then through the main sources. That way contrib can have dependencies on
2888 # main sources. Note that this explicitly doesn't enter src/test, right now a
2889 # few regression tests depend on contrib files.
2891 subdir('src')
2893 subdir('contrib')
2895 subdir('src/test')
2896 subdir('src/interfaces/libpq/test')
2897 subdir('src/interfaces/ecpg/test')
2899 subdir('doc/src/sgml')
2901 generated_sources_ac += {'': ['GNUmakefile']}
2903 # After processing src/test, add test_install_libs to the testprep_targets
2904 # to build them
2905 testprep_targets += test_install_libs
2908 # If there are any files in the source directory that we also generate in the
2909 # build directory, they might get preferred over the newly generated files,
2910 # e.g. because of a #include "file", which always will search in the current
2911 # directory first.
2912 message('checking for file conflicts between source and build directory')
2913 conflicting_files = []
2914 potentially_conflicting_files_t = []
2915 potentially_conflicting_files_t += generated_headers
2916 potentially_conflicting_files_t += generated_backend_headers
2917 potentially_conflicting_files_t += generated_backend_sources
2918 potentially_conflicting_files_t += generated_sources
2920 potentially_conflicting_files = []
2922 # convert all sources of potentially conflicting files into uniform shape
2923 foreach t : potentially_conflicting_files_t
2924   potentially_conflicting_files += t.full_path()
2925 endforeach
2926 foreach t1 : configure_files
2927   if meson.version().version_compare('>=0.59')
2928     t = fs.parent(t1) / fs.name(t1)
2929   else
2930     t = '@0@'.format(t1)
2931   endif
2932   potentially_conflicting_files += meson.current_build_dir() / t
2933 endforeach
2934 foreach sub, fnames : generated_sources_ac
2935   sub = meson.build_root() / sub
2936   foreach fname : fnames
2937     potentially_conflicting_files += sub / fname
2938   endforeach
2939 endforeach
2941 # find and report conflicting files
2942 foreach build_path : potentially_conflicting_files
2943   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2944   # str.replace is in 0.56
2945   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2946   if fs.exists(src_path) or fs.is_symlink(src_path)
2947     conflicting_files += src_path
2948   endif
2949 endforeach
2950 # XXX: Perhaps we should generate a file that would clean these up? The list
2951 # can be long.
2952 if conflicting_files.length() > 0
2953   errmsg_cleanup = '''
2954 Conflicting files in source directory:
2955   @0@
2957 The conflicting files need to be removed, either by removing the files listed
2958 above, or by running configure and then make maintainer-clean.
2960   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2961   error(errmsg_nonclean_base.format(errmsg_cleanup))
2962 endif
2966 ###############################################################
2967 # Install targets
2968 ###############################################################
2971 # We want to define additional install targets beyond what meson provides. For
2972 # that we need to define targets depending on nearly everything. We collected
2973 # the results of i18n.gettext() invocations into nls_targets, that also
2974 # includes maintainer targets though. Collect the ones we want as a dependency.
2976 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2977 # generation happens during install, so that's not a real issue.
2978 nls_mo_targets = []
2979 if libintl.found() and meson.version().version_compare('>=0.60')
2980   # use range() to avoid the flattening of the list that foreach() would do
2981   foreach off : range(0, nls_targets.length())
2982     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2983     # -pot target 3) maintainer -pot target
2984     nls_mo_targets += nls_targets[off][0]
2985   endforeach
2986   alias_target('nls', nls_mo_targets)
2987 endif
2990 all_built = [
2991   backend_targets,
2992   bin_targets,
2993   libpq_st,
2994   pl_targets,
2995   contrib_targets,
2996   nls_mo_targets,
2997   testprep_targets,
2998   ecpg_targets,
3001 # Meson's default install target is quite verbose. Provide one that is quiet.
3002 install_quiet = custom_target('install-quiet',
3003   output: 'install-quiet',
3004   build_always_stale: true,
3005   build_by_default: false,
3006   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3007   depends: all_built,
3010 # Target to install files used for tests, which aren't installed by default
3011 install_test_files_args = [
3012   install_files,
3013   '--prefix', dir_prefix,
3014   '--install', contrib_data_dir, test_install_data,
3015   '--install', dir_lib_pkg, test_install_libs,
3017 run_target('install-test-files',
3018   command: [python] + install_test_files_args,
3019   depends: testprep_targets,
3024 ###############################################################
3025 # Test prep
3026 ###############################################################
3028 # DESTDIR for the installation we'll run tests in
3029 test_install_destdir = meson.build_root() / 'tmp_install/'
3031 # DESTDIR + prefix appropriately munged
3032 if build_system != 'windows'
3033   # On unixoid systems this is trivial, we just prepend the destdir
3034   assert(dir_prefix.startswith('/')) # enforced by meson
3035   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3036 else
3037   # drives, drive-relative paths, etc make this complicated on windows, call
3038   # into a copy of meson's logic for it
3039   command = [
3040     python, '-c',
3041     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3042     test_install_destdir, dir_prefix]
3043   test_install_location = run_command(command, check: true).stdout().strip()
3044 endif
3046 meson_install_args = meson_args + ['install'] + {
3047     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3048     'muon': []
3049 }[meson_impl]
3051 # setup tests should be run first,
3052 # so define priority for these
3053 setup_tests_priority = 100
3054 test('tmp_install',
3055     meson_bin, args: meson_install_args ,
3056     env: {'DESTDIR':test_install_destdir},
3057     priority: setup_tests_priority,
3058     timeout: 300,
3059     is_parallel: false,
3060     suite: ['setup'])
3062 test('install_test_files',
3063     python,
3064     args: install_test_files_args + ['--destdir', test_install_destdir],
3065     priority: setup_tests_priority,
3066     is_parallel: false,
3067     suite: ['setup'])
3069 test_result_dir = meson.build_root() / 'testrun'
3072 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3073 # inevitable conflicts from running tests in parallel, hackishly assign
3074 # different ports for different tests.
3076 testport = 40000
3078 test_env = environment()
3080 temp_install_bindir = test_install_location / get_option('bindir')
3081 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3082 test_env.set('PG_REGRESS', pg_regress.full_path())
3083 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3084 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3086 # Test suites that are not safe by default but can be run if selected
3087 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3088 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3089 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3091 # Add the temporary installation to the library search path on platforms where
3092 # that works (everything but windows, basically). On windows everything
3093 # library-like gets installed into bindir, solving that issue.
3094 if library_path_var != ''
3095   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3096 endif
3099 # Create (and remove old) initdb template directory. Tests use that, where
3100 # possible, to make it cheaper to run tests.
3102 # Use python to remove the old cached initdb, as we cannot rely on a working
3103 # 'rm' binary on windows.
3104 test('initdb_cache',
3105      python,
3106      args: [
3107        '-c', '''
3108 import shutil
3109 import sys
3110 import subprocess
3112 shutil.rmtree(sys.argv[1], ignore_errors=True)
3113 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3114 sys.exit(sp.returncode)
3115 ''',
3116        test_initdb_template,
3117        temp_install_bindir / 'initdb',
3118        '--auth', 'trust', '--no-sync', '--no-instructions', '--no-locale',
3119        '--no-clean'
3120      ],
3121      priority: setup_tests_priority - 1,
3122      timeout: 300,
3123      is_parallel: false,
3124      env: test_env,
3125      suite: ['setup'])
3129 ###############################################################
3130 # Test Generation
3131 ###############################################################
3133 # When using a meson version understanding exclude_suites, define a
3134 # 'tmp_install' test setup (the default) that excludes tests running against a
3135 # pre-existing install and a 'running' setup that conflicts with creation of
3136 # the temporary installation and tap tests (which don't support running
3137 # against a running server).
3139 running_suites = []
3140 install_suites = []
3141 if meson.version().version_compare('>=0.57')
3142   runningcheck = true
3143 else
3144   runningcheck = false
3145 endif
3147 testwrap = files('src/tools/testwrap')
3149 foreach test_dir : tests
3150   testwrap_base = [
3151     testwrap,
3152     '--basedir', meson.build_root(),
3153     '--srcdir', test_dir['sd'],
3154   ]
3156   foreach kind, v : test_dir
3157     if kind in ['sd', 'bd', 'name']
3158       continue
3159     endif
3161     t = test_dir[kind]
3163     if kind in ['regress', 'isolation', 'ecpg']
3164       if kind == 'regress'
3165         runner = pg_regress
3166         fallback_dbname = 'regression_@0@'
3167       elif kind == 'isolation'
3168         runner = pg_isolation_regress
3169         fallback_dbname = 'isolation_regression_@0@'
3170       elif kind == 'ecpg'
3171         runner = pg_regress_ecpg
3172         fallback_dbname = 'ecpg_regression_@0@'
3173       endif
3175       test_group = test_dir['name']
3176       test_group_running = test_dir['name'] + '-running'
3178       test_output = test_result_dir / test_group / kind
3179       test_output_running = test_result_dir / test_group_running/ kind
3181       # Unless specified by the test, choose a non-conflicting database name,
3182       # to avoid conflicts when running against existing server.
3183       dbname = t.get('dbname',
3184         fallback_dbname.format(test_dir['name']))
3186       test_command_base = [
3187         runner.full_path(),
3188         '--inputdir', t.get('inputdir', test_dir['sd']),
3189         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3190         '--bindir', '',
3191         '--dlpath', test_dir['bd'],
3192         '--max-concurrent-tests=20',
3193         '--dbname', dbname,
3194       ] + t.get('regress_args', [])
3196       test_selection = []
3197       if t.has_key('schedule')
3198         test_selection += ['--schedule', t['schedule'],]
3199       endif
3201       if kind == 'isolation'
3202         test_selection += t.get('specs', [])
3203       else
3204         test_selection += t.get('sql', [])
3205       endif
3207       env = test_env
3208       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3210       test_kwargs = {
3211         'protocol': 'tap',
3212         'priority': 10,
3213         'timeout': 1000,
3214         'depends': test_deps + t.get('deps', []),
3215         'env': env,
3216       } + t.get('test_kwargs', {})
3218       test(test_group / kind,
3219         python,
3220         args: [
3221           testwrap_base,
3222           '--testgroup', test_group,
3223           '--testname', kind,
3224           '--',
3225           test_command_base,
3226           '--outputdir', test_output,
3227           '--temp-instance', test_output / 'tmp_check',
3228           '--port', testport.to_string(),
3229           test_selection,
3230         ],
3231         suite: test_group,
3232         kwargs: test_kwargs,
3233       )
3234       install_suites += test_group
3236       # some tests can't support running against running DB
3237       if runningcheck and t.get('runningcheck', true)
3238         test(test_group_running / kind,
3239           python,
3240           args: [
3241             testwrap_base,
3242             '--testgroup', test_group_running,
3243             '--testname', kind,
3244             '--',
3245             test_command_base,
3246             '--outputdir', test_output_running,
3247             test_selection,
3248           ],
3249           is_parallel: t.get('runningcheck-parallel', true),
3250           suite: test_group_running,
3251           kwargs: test_kwargs,
3252         )
3253         running_suites += test_group_running
3254       endif
3256       testport += 1
3257     elif kind == 'tap'
3258       testwrap_tap = testwrap_base
3259       if not tap_tests_enabled
3260         testwrap_tap += ['--skip', 'TAP tests not enabled']
3261       endif
3263       test_command = [
3264         perl.path(),
3265         '-I', meson.source_root() / 'src/test/perl',
3266         '-I', test_dir['sd'],
3267       ]
3269       # Add temporary install, the build directory for non-installed binaries and
3270       # also test/ for non-installed test binaries built separately.
3271       env = test_env
3272       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3274       foreach name, value : t.get('env', {})
3275         env.set(name, value)
3276       endforeach
3278       test_group = test_dir['name']
3279       test_kwargs = {
3280         'protocol': 'tap',
3281         'suite': test_group,
3282         'timeout': 1000,
3283         'depends': test_deps + t.get('deps', []),
3284         'env': env,
3285       } + t.get('test_kwargs', {})
3287       foreach onetap : t['tests']
3288         # Make tap test names prettier, remove t/ and .pl
3289         onetap_p = onetap
3290         if onetap_p.startswith('t/')
3291           onetap_p = onetap.split('t/')[1]
3292         endif
3293         if onetap_p.endswith('.pl')
3294           onetap_p = fs.stem(onetap_p)
3295         endif
3297         test(test_dir['name'] / onetap_p,
3298           python,
3299           kwargs: test_kwargs,
3300           args: testwrap_tap + [
3301             '--testgroup', test_dir['name'],
3302             '--testname', onetap_p,
3303             '--', test_command,
3304             test_dir['sd'] / onetap,
3305           ],
3306         )
3307       endforeach
3308       install_suites += test_group
3309     else
3310       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3311     endif
3313   endforeach # kinds of tests
3315 endforeach # directories with tests
3317 # repeat condition so meson realizes version dependency
3318 if meson.version().version_compare('>=0.57')
3319   add_test_setup('tmp_install',
3320     is_default: true,
3321     exclude_suites: running_suites)
3322   add_test_setup('running',
3323     exclude_suites: ['setup'] + install_suites)
3324 endif
3328 ###############################################################
3329 # Pseudo targets
3330 ###############################################################
3332 alias_target('backend', backend_targets)
3333 alias_target('bin', bin_targets + [libpq_st])
3334 alias_target('pl', pl_targets)
3335 alias_target('contrib', contrib_targets)
3336 alias_target('testprep', testprep_targets)
3338 alias_target('world', all_built, docs)
3339 alias_target('install-world', install_quiet, installdocs)
3341 run_target('help',
3342   command: [
3343     perl, '-ne', 'next if /^#/; print',
3344     files('doc/src/sgml/targets-meson.txt'),
3345   ]
3350 ###############################################################
3351 # The End, The End, My Friend
3352 ###############################################################
3354 if meson.version().version_compare('>=0.57')
3356   summary(
3357     {
3358       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3359       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3360       'segment size': get_option('segsize_blocks') != 0 ?
3361         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3362         '@0@ GB'.format(get_option('segsize')),
3363     },
3364     section: 'Data layout',
3365   )
3367   summary(
3368     {
3369       'host system': '@0@ @1@'.format(host_system, host_cpu),
3370       'build system': '@0@ @1@'.format(build_machine.system(),
3371                                        build_machine.cpu_family()),
3372     },
3373     section: 'System',
3374   )
3376   summary(
3377     {
3378       'linker': '@0@'.format(cc.get_linker_id()),
3379       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3380     },
3381     section: 'Compiler',
3382   )
3384   summary(
3385     {
3386       'CPP FLAGS': ' '.join(cppflags),
3387       'C FLAGS, functional': ' '.join(cflags),
3388       'C FLAGS, warnings': ' '.join(cflags_warn),
3389       'C FLAGS, modules': ' '.join(cflags_mod),
3390       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3391       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3392     },
3393     section: 'Compiler Flags',
3394   )
3396   if llvm.found()
3397     summary(
3398       {
3399         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3400       },
3401       section: 'Compiler',
3402     )
3404     summary(
3405       {
3406         'C++ FLAGS, functional': ' '.join(cxxflags),
3407         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3408         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3409       },
3410       section: 'Compiler Flags',
3411     )
3412   endif
3414   summary(
3415     {
3416       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3417       'dtrace': dtrace,
3418       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3419     },
3420     section: 'Programs',
3421   )
3423   summary(
3424     {
3425       'bonjour': bonjour,
3426       'bsd_auth': bsd_auth,
3427       'docs': docs_dep,
3428       'docs_pdf': docs_pdf_dep,
3429       'gss': gssapi,
3430       'icu': icu,
3431       'ldap': ldap,
3432       'libxml': libxml,
3433       'libxslt': libxslt,
3434       'llvm': llvm,
3435       'lz4': lz4,
3436       'nls': libintl,
3437       'openssl': ssl,
3438       'pam': pam,
3439       'plperl': perl_dep,
3440       'plpython': python3_dep,
3441       'pltcl': tcl_dep,
3442       'readline': readline,
3443       'selinux': selinux,
3444       'systemd': systemd,
3445       'uuid': uuid,
3446       'zlib': zlib,
3447       'zstd': zstd,
3448     },
3449     section: 'External libraries',
3450   )
3452 endif