doc: clarify handling of range upper/lower/upper_inf/lower_inf()
[pgsql.git] / meson.build
blob47c8fcdc532b1c995b4bb59d2d947e1a38cefc15
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)
335 prove = find_program(get_option('PROVE'), native: true, required: false)
336 tar = find_program(get_option('TAR'), native: true)
337 gzip = find_program(get_option('GZIP'), native: true)
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(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-log/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_get_data'],
1289       ['BIO_meth_new'],
1290       ['ASN1_STRING_get0_data'],
1291       ['HMAC_CTX_new'],
1292       ['HMAC_CTX_free'],
1294       # OpenSSL versions before 1.1.0 required setting callback functions, for
1295       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1296       # function was removed.
1297       ['CRYPTO_lock'],
1299       # Function introduced in OpenSSL 1.1.1
1300       ['X509_get_signature_info'],
1301     ]
1303     are_openssl_funcs_complete = true
1304     foreach c : check_funcs
1305       func = c.get(0)
1306       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1307       required = c.get(1, {}).get('required', false)
1308       if required and not val
1309         are_openssl_funcs_complete = false
1310         if openssl_required
1311           error('openssl function @0@ is required'.format(func))
1312         endif
1313         break
1314       elif not required
1315         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1316       endif
1317     endforeach
1319     if are_openssl_funcs_complete
1320       cdata.set('USE_OPENSSL', 1,
1321                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1322       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1323                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1324       ssl_library = 'openssl'
1325     else
1326       ssl = not_found_dep
1327     endif
1328   endif
1329 endif
1331 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1332   error('no SSL library found')
1333 endif
1337 ###############################################################
1338 # Library: uuid
1339 ###############################################################
1341 uuidopt = get_option('uuid')
1342 if uuidopt != 'none'
1343   uuidname = uuidopt.to_upper()
1344   if uuidopt == 'e2fs'
1345     uuid = dependency('uuid', required: true)
1346     uuidfunc = 'uuid_generate'
1347     uuidheader = 'uuid/uuid.h'
1348   elif uuidopt == 'bsd'
1349     # libc should have uuid function
1350     uuid = declare_dependency()
1351     uuidfunc = 'uuid_to_string'
1352     uuidheader = 'uuid.h'
1353   elif uuidopt == 'ossp'
1354     uuid = dependency('ossp-uuid', required: true)
1355     uuidfunc = 'uuid_export'
1356     uuidheader = 'uuid.h'
1357   else
1358     error('unknown uuid build option value: @0@'.format(uuidopt))
1359   endif
1361   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1362     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1363   endif
1364   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1366   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1367            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1368 else
1369   uuid = not_found_dep
1370 endif
1374 ###############################################################
1375 # Library: zlib
1376 ###############################################################
1378 zlibopt = get_option('zlib')
1379 zlib = not_found_dep
1380 if not zlibopt.disabled()
1381   zlib_t = dependency('zlib', required: zlibopt)
1383   if zlib_t.type_name() == 'internal'
1384     # if fallback was used, we don't need to test if headers are present (they
1385     # aren't built yet, so we can't test)
1386     zlib = zlib_t
1387   elif not zlib_t.found()
1388     warning('did not find zlib')
1389   elif not cc.has_header('zlib.h',
1390       args: test_c_args, include_directories: postgres_inc,
1391       dependencies: [zlib_t], required: zlibopt)
1392     warning('zlib header not found')
1393   else
1394     zlib = zlib_t
1395   endif
1397   if zlib.found()
1398     cdata.set('HAVE_LIBZ', 1)
1399   endif
1400 endif
1404 ###############################################################
1405 # Library: tap test dependencies
1406 ###############################################################
1408 # Check whether tap tests are enabled or not
1409 tap_tests_enabled = false
1410 tapopt = get_option('tap_tests')
1411 if not tapopt.disabled()
1412   # Checking for perl modules for tap tests
1413   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1414   if perl_ipc_run_check.returncode() != 0
1415     message(perl_ipc_run_check.stderr().strip())
1416     if tapopt.enabled()
1417       error('Additional Perl modules are required to run TAP tests.')
1418     else
1419       warning('Additional Perl modules are required to run TAP tests.')
1420     endif
1421   else
1422     tap_tests_enabled = true
1423   endif
1424 endif
1428 ###############################################################
1429 # Library: zstd
1430 ###############################################################
1432 zstdopt = get_option('zstd')
1433 if not zstdopt.disabled()
1434   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1436   if zstd.found()
1437     cdata.set('USE_ZSTD', 1)
1438     cdata.set('HAVE_LIBZSTD', 1)
1439   endif
1441 else
1442   zstd = not_found_dep
1443 endif
1447 ###############################################################
1448 # Compiler tests
1449 ###############################################################
1451 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1452 # unnecessarily, because we optionally rely on newer features.
1453 c99_test = '''
1454 #include <stdbool.h>
1455 #include <complex.h>
1456 #include <tgmath.h>
1457 #include <inttypes.h>
1459 struct named_init_test {
1460   int a;
1461   int b;
1464 extern void structfunc(struct named_init_test);
1466 int main(int argc, char **argv)
1468   struct named_init_test nit = {
1469     .a = 3,
1470     .b = 5,
1471   };
1473   for (int loop_var = 0; loop_var < 3; loop_var++)
1474   {
1475     nit.a += nit.b;
1476   }
1478   structfunc((struct named_init_test){1, 0});
1480   return nit.a != 0;
1484 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1485   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1486         args: test_c_args + ['-std=c99'])
1487     test_c_args += '-std=c99'
1488     cflags += '-std=c99'
1489   else
1490     error('C compiler does not support C99')
1491   endif
1492 endif
1494 sizeof_long = cc.sizeof('long', args: test_c_args)
1495 cdata.set('SIZEOF_LONG', sizeof_long)
1496 if sizeof_long == 8
1497   cdata.set('HAVE_LONG_INT_64', 1)
1498   cdata.set('PG_INT64_TYPE', 'long int')
1499   cdata.set_quoted('INT64_MODIFIER', 'l')
1500 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1501   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1502   cdata.set('PG_INT64_TYPE', 'long long int')
1503   cdata.set_quoted('INT64_MODIFIER', 'll')
1504 else
1505   error('do not know how to get a 64bit int')
1506 endif
1508 if host_machine.endian() == 'big'
1509   cdata.set('WORDS_BIGENDIAN', 1)
1510 endif
1512 alignof_types = ['short', 'int', 'long', 'double']
1513 maxalign = 0
1514 foreach t : alignof_types
1515   align = cc.alignment(t, args: test_c_args)
1516   if maxalign < align
1517     maxalign = align
1518   endif
1519   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1520 endforeach
1521 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1523 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1524 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1527 # Check if __int128 is a working 128 bit integer type, and if so
1528 # define PG_INT128_TYPE to that typename.
1530 # This currently only detects a GCC/clang extension, but support for other
1531 # environments may be added in the future.
1533 # For the moment we only test for support for 128bit math; support for
1534 # 128bit literals and snprintf is not required.
1535 if cc.links('''
1536   /*
1537    * We don't actually run this test, just link it to verify that any support
1538    * functions needed for __int128 are present.
1539    *
1540    * These are globals to discourage the compiler from folding all the
1541    * arithmetic tests down to compile-time constants.  We do not have
1542    * convenient support for 128bit literals at this point...
1543    */
1544   __int128 a = 48828125;
1545   __int128 b = 97656250;
1547   int main(void)
1548   {
1549       __int128 c,d;
1550       a = (a << 12) + 1; /* 200000000001 */
1551       b = (b << 12) + 5; /* 400000000005 */
1552       /* try the most relevant arithmetic ops */
1553       c = a * b;
1554       d = (c + b) / b;
1555       /* must use the results, else compiler may optimize arithmetic away */
1556       return d != a+1;
1557   }''',
1558   name: '__int128',
1559   args: test_c_args)
1561   buggy_int128 = false
1563   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1564   # If not cross-compiling, we can test for bugs and disable use of __int128
1565   # with buggy compilers.  If cross-compiling, hope for the best.
1566   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1567   if not meson.is_cross_build()
1568     r = cc.run('''
1569     /* This must match the corresponding code in c.h: */
1570     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1571     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1572     #elif defined(_MSC_VER)
1573     #define pg_attribute_aligned(a) __declspec(align(a))
1574     #endif
1575     typedef __int128 int128a
1576     #if defined(pg_attribute_aligned)
1577     pg_attribute_aligned(8)
1578     #endif
1579     ;
1581     int128a holder;
1582     void pass_by_val(void *buffer, int128a par) { holder = par; }
1584     int main(void)
1585     {
1586         long int i64 = 97656225L << 12;
1587         int128a q;
1588         pass_by_val(main, (int128a) i64);
1589         q = (int128a) i64;
1590         return q != holder;
1591     }''',
1592     name: '__int128 alignment bug',
1593     args: test_c_args)
1594     assert(r.compiled())
1595     if r.returncode() != 0
1596       buggy_int128 = true
1597       message('__int128 support present but buggy and thus disabled')
1598     endif
1599   endif
1601   if not buggy_int128
1602     cdata.set('PG_INT128_TYPE', '__int128')
1603     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1604   endif
1605 endif
1608 # Check if the C compiler knows computed gotos (gcc extension, also
1609 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1611 # Checking whether computed gotos are supported syntax-wise ought to
1612 # be enough, as the syntax is otherwise illegal.
1613 if cc.compiles('''
1614     static inline int foo(void)
1615     {
1616       void *labeladdrs[] = {&&my_label};
1617       goto *labeladdrs[0];
1618       my_label:
1619       return 1;
1620     }''',
1621     args: test_c_args)
1622   cdata.set('HAVE_COMPUTED_GOTO', 1)
1623 endif
1626 # Check if the C compiler understands _Static_assert(),
1627 # and define HAVE__STATIC_ASSERT if so.
1629 # We actually check the syntax ({ _Static_assert(...) }), because we need
1630 # gcc-style compound expressions to be able to wrap the thing into macros.
1631 if cc.compiles('''
1632     int main(int arg, char **argv)
1633     {
1634         ({ _Static_assert(1, "foo"); });
1635     }
1636     ''',
1637     args: test_c_args)
1638   cdata.set('HAVE__STATIC_ASSERT', 1)
1639 endif
1642 # We use <stdbool.h> if we have it and it declares type bool as having
1643 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1644 if cc.has_type('_Bool', args: test_c_args) \
1645     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1646     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1647   cdata.set('HAVE__BOOL', 1)
1648   cdata.set('PG_USE_STDBOOL', 1)
1649 endif
1652 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1653 # warning for each use of %m.
1654 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1655 testsrc = '''
1656 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1657 static void call_log(void)
1659     emit_log(0, "error: %s: %m", "foo");
1662 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1663 foreach a : printf_attributes
1664   if cc.compiles(testsrc.format(a),
1665       args: test_c_args + attrib_error_args, name: 'format ' + a)
1666     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1667     break
1668   endif
1669 endforeach
1672 if cc.has_function_attribute('visibility:default') and \
1673     cc.has_function_attribute('visibility:hidden')
1674   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1676   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1677   # inlineshidden to C code as well... And either way, we want to put these
1678   # flags into exported files (pgxs, .pc files).
1679   cflags_mod += '-fvisibility=hidden'
1680   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1681   ldflags_mod += '-fvisibility=hidden'
1682 endif
1685 # Check if various builtins exist. Some builtins are tested separately,
1686 # because we want to test something more complicated than the generic case.
1687 builtins = [
1688   'bswap16',
1689   'bswap32',
1690   'bswap64',
1691   'clz',
1692   'ctz',
1693   'constant_p',
1694   'frame_address',
1695   'popcount',
1696   'unreachable',
1699 foreach builtin : builtins
1700   fname = '__builtin_@0@'.format(builtin)
1701   if cc.has_function(fname, args: test_c_args)
1702     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1703   endif
1704 endforeach
1707 # Check if the C compiler understands __builtin_types_compatible_p,
1708 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1710 # We check usage with __typeof__, though it's unlikely any compiler would
1711 # have the former and not the latter.
1712 if cc.compiles('''
1713     static int x;
1714     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1715     ''',
1716     name: '__builtin_types_compatible_p',
1717     args: test_c_args)
1718   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1719 endif
1722 # Check if the C compiler understands __builtin_$op_overflow(),
1723 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1725 # Check for the most complicated case, 64 bit multiplication, as a
1726 # proxy for all of the operations.  To detect the case where the compiler
1727 # knows the function but library support is missing, we must link not just
1728 # compile, and store the results in global variables so the compiler doesn't
1729 # optimize away the call.
1730 if cc.links('''
1731     INT64 a = 1;
1732     INT64 b = 1;
1733     INT64 result;
1735     int main(void)
1736     {
1737         return __builtin_mul_overflow(a, b, &result);
1738     }''',
1739     name: '__builtin_mul_overflow',
1740     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1741     )
1742   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1743 endif
1746 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1747 # here. To prevent problems due to two detection methods working, stop
1748 # checking after one.
1749 if cc.links('''
1750     #include <cpuid.h>
1751     int main(int arg, char **argv)
1752     {
1753         unsigned int exx[4] = {0, 0, 0, 0};
1754         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1755     }
1756     ''', name: '__get_cpuid',
1757     args: test_c_args)
1758   cdata.set('HAVE__GET_CPUID', 1)
1759 elif cc.links('''
1760     #include <intrin.h>
1761     int main(int arg, char **argv)
1762     {
1763         unsigned int exx[4] = {0, 0, 0, 0};
1764         __cpuid(exx, 1);
1765     }
1766     ''', name: '__cpuid',
1767     args: test_c_args)
1768   cdata.set('HAVE__CPUID', 1)
1769 endif
1772 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1773 # versions of clang do not understand -fexcess-precision=standard, the use of
1774 # x87 floating point operations leads to problems like isinf possibly returning
1775 # false for a value that is infinite when converted from the 80bit register to
1776 # the 8byte memory representation.
1778 # Only perform the test if the compiler doesn't understand
1779 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1780 # automatically.
1781 if '-fexcess-precision=standard' not in cflags
1782   if not cc.compiles('''
1783 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1784 choke me
1785 #endif''',
1786       name: '', args: test_c_args)
1787     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1788   endif
1789 endif
1793 ###############################################################
1794 # Compiler flags
1795 ###############################################################
1797 common_functional_flags = [
1798   # Disable strict-aliasing rules; needed for gcc 3.3+
1799   '-fno-strict-aliasing',
1800   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1801   '-fwrapv',
1802   '-fexcess-precision=standard',
1805 cflags += cc.get_supported_arguments(common_functional_flags)
1806 if llvm.found()
1807   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1808 endif
1810 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1811 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1813 common_warning_flags = [
1814   '-Wmissing-prototypes',
1815   '-Wpointer-arith',
1816   # Really don't want VLAs to be used in our dialect of C
1817   '-Werror=vla',
1818   # On macOS, complain about usage of symbols newer than the deployment target
1819   '-Werror=unguarded-availability-new',
1820   '-Wendif-labels',
1821   '-Wmissing-format-attribute',
1822   '-Wimplicit-fallthrough=3',
1823   '-Wcast-function-type',
1824   '-Wshadow=compatible-local',
1825   # This was included in -Wall/-Wformat in older GCC versions
1826   '-Wformat-security',
1829 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1830 if llvm.found()
1831   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1832 endif
1834 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1835 # the result for them
1836 cflags_no_decl_after_statement = []
1837 if cc.has_argument('-Wdeclaration-after-statement')
1838   cflags_warn += '-Wdeclaration-after-statement'
1839   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1840 endif
1843 # The following tests want to suppress various unhelpful warnings by adding
1844 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1845 # switches, so we have to test for the positive form and if that works,
1846 # add the negative form.
1848 negative_warning_flags = [
1849   # Suppress clang's unhelpful unused-command-line-argument warnings.
1850   'unused-command-line-argument',
1852   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1853   # of warnings when building plperl because of usages in the Perl headers.
1854   'compound-token-split-by-macro',
1856   # Similarly disable useless truncation warnings from gcc 8+
1857   'format-truncation',
1858   'stringop-truncation',
1860   # Suppress clang 16's strict warnings about function casts
1861   'cast-function-type-strict',
1863   # To make warning_level=2 / -Wextra work, we'd need at least the following
1864   # 'clobbered',
1865   # 'missing-field-initializers',
1866   # 'sign-compare',
1867   # 'unused-parameter',
1870 foreach w : negative_warning_flags
1871   if cc.has_argument('-W' + w)
1872     cflags_warn += '-Wno-' + w
1873   endif
1874   if llvm.found() and cpp.has_argument('-W' + w)
1875     cxxflags_warn += '-Wno-' + w
1876   endif
1877 endforeach
1880 # From Project.pm
1881 if cc.get_id() == 'msvc'
1882   cflags_warn += [
1883     '/wd4018', # signed/unsigned mismatch
1884     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1885     '/wd4273', # inconsistent DLL linkage
1886     '/wd4101', # unreferenced local variable
1887     '/wd4102', # unreferenced label
1888     '/wd4090', # different 'modifier' qualifiers
1889     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1890   ]
1892   cppflags += [
1893     '/DWIN32',
1894     '/DWINDOWS',
1895     '/D__WINDOWS__',
1896     '/D__WIN32__',
1897     '/D_CRT_SECURE_NO_DEPRECATE',
1898     '/D_CRT_NONSTDC_NO_DEPRECATE',
1899   ]
1901   # We never need export libraries. As link.exe reports their creation, they
1902   # are unnecessarily noisy. Similarly, we don't need import library for
1903   # modules, we only import them dynamically, and they're also noisy.
1904   ldflags += '/NOEXP'
1905   ldflags_mod += '/NOIMPLIB'
1906 endif
1910 ###############################################################
1911 # Atomics
1912 ###############################################################
1914 if not get_option('spinlocks')
1915   warning('Not using spinlocks will cause poor performance')
1916 else
1917   cdata.set('HAVE_SPINLOCKS', 1)
1918 endif
1920 if not get_option('atomics')
1921   warning('Not using atomics will cause poor performance')
1922 else
1923   # XXX: perhaps we should require some atomics support in this case these
1924   # days?
1925   cdata.set('HAVE_ATOMICS', 1)
1927   atomic_checks = [
1928     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1929      'desc': '__sync_lock_test_and_set(char)',
1930      'test': '''
1931 char lock = 0;
1932 __sync_lock_test_and_set(&lock, 1);
1933 __sync_lock_release(&lock);'''},
1935     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1936      'desc': '__sync_lock_test_and_set(int32)',
1937      'test': '''
1938 int lock = 0;
1939 __sync_lock_test_and_set(&lock, 1);
1940 __sync_lock_release(&lock);'''},
1942     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1943      'desc': '__sync_val_compare_and_swap(int32)',
1944      'test': '''
1945 int val = 0;
1946 __sync_val_compare_and_swap(&val, 0, 37);'''},
1948     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1949      'desc': '__sync_val_compare_and_swap(int64)',
1950      'test': '''
1951 INT64 val = 0;
1952 __sync_val_compare_and_swap(&val, 0, 37);'''},
1954     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1955      'desc': ' __atomic_compare_exchange_n(int32)',
1956      'test': '''
1957 int val = 0;
1958 int expect = 0;
1959 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1961     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1962      'desc': ' __atomic_compare_exchange_n(int64)',
1963      'test': '''
1964 INT64 val = 0;
1965 INT64 expect = 0;
1966 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1967   ]
1969   foreach check : atomic_checks
1970     test = '''
1971 int main(void)
1974 }'''.format(check['test'])
1976     cdata.set(check['name'],
1977       cc.links(test,
1978         name: check['desc'],
1979         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1980     )
1981   endforeach
1983 endif
1987 ###############################################################
1988 # Select CRC-32C implementation.
1990 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1991 # use the special CRC instructions for calculating CRC-32C. If we're not
1992 # targeting such a processor, but we can nevertheless produce code that uses
1993 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1994 # implementations and select which one to use at runtime, depending on whether
1995 # SSE 4.2 is supported by the processor we're running on.
1997 # Similarly, if we are targeting an ARM processor that has the CRC
1998 # instructions that are part of the ARMv8 CRC Extension, use them. And if
1999 # we're not targeting such a processor, but can nevertheless produce code that
2000 # uses the CRC instructions, compile both, and select at runtime.
2001 ###############################################################
2003 have_optimized_crc = false
2004 cflags_crc = []
2005 if host_cpu == 'x86' or host_cpu == 'x86_64'
2007   if cc.get_id() == 'msvc'
2008     cdata.set('USE_SSE42_CRC32C', false)
2009     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2010     have_optimized_crc = true
2011   else
2013     prog = '''
2014 #include <nmmintrin.h>
2016 int main(void)
2018     unsigned int crc = 0;
2019     crc = _mm_crc32_u8(crc, 0);
2020     crc = _mm_crc32_u32(crc, 0);
2021     /* return computed value, to prevent the above being optimized away */
2022     return crc == 0;
2026     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2027           args: test_c_args)
2028       # Use Intel SSE 4.2 unconditionally.
2029       cdata.set('USE_SSE42_CRC32C', 1)
2030       have_optimized_crc = true
2031     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2032           args: test_c_args + ['-msse4.2'])
2033       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2034       # the runtime check.
2035       cflags_crc += '-msse4.2'
2036       cdata.set('USE_SSE42_CRC32C', false)
2037       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2038       have_optimized_crc = true
2039     endif
2041   endif
2043 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2045   prog = '''
2046 #include <arm_acle.h>
2048 int main(void)
2050     unsigned int crc = 0;
2051     crc = __crc32cb(crc, 0);
2052     crc = __crc32ch(crc, 0);
2053     crc = __crc32cw(crc, 0);
2054     crc = __crc32cd(crc, 0);
2056     /* return computed value, to prevent the above being optimized away */
2057     return crc == 0;
2061   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2062       args: test_c_args)
2063     # Use ARM CRC Extension unconditionally
2064     cdata.set('USE_ARMV8_CRC32C', 1)
2065     have_optimized_crc = true
2066   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2067       args: test_c_args + ['-march=armv8-a+crc'])
2068     # Use ARM CRC Extension, with runtime check
2069     cflags_crc += '-march=armv8-a+crc'
2070     cdata.set('USE_ARMV8_CRC32C', false)
2071     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2072     have_optimized_crc = true
2073   endif
2075 elif host_cpu == 'loongarch64'
2077   prog = '''
2078 int main(void)
2080     unsigned int crc = 0;
2081     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2082     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2083     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2084     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2086     /* return computed value, to prevent the above being optimized away */
2087     return crc == 0;
2091   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',
2092       args: test_c_args)
2093     # Use LoongArch CRC instruction unconditionally
2094     cdata.set('USE_LOONGARCH_CRC32C', 1)
2095     have_optimized_crc = true
2096   endif
2098 endif
2100 if not have_optimized_crc
2101   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2102   # support.
2103   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2104 endif
2108 ###############################################################
2109 # Other CPU specific stuff
2110 ###############################################################
2112 if host_cpu == 'x86_64'
2114   if cc.compiles('''
2115       void main(void)
2116       {
2117           long long x = 1; long long r;
2118           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2119       }''',
2120       name: '@0@: popcntq instruction'.format(host_cpu),
2121       args: test_c_args)
2122     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2123   endif
2125 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2126   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2127   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2128     if cc.compiles('''
2129       static inline int
2130       addi(int ra, int si)
2131       {
2132           int res = 0;
2133           if (__builtin_constant_p(si))
2134               __asm__ __volatile__(
2135                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2136           return res;
2137       }
2138       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2139       ''',
2140       args: test_c_args)
2141       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2142     endif
2143   endif
2144 endif
2148 ###############################################################
2149 # Library / OS tests
2150 ###############################################################
2152 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2153 # unnecessary checks over and over, particularly on windows.
2154 header_checks = [
2155   'atomic.h',
2156   'copyfile.h',
2157   'crtdefs.h',
2158   'execinfo.h',
2159   'getopt.h',
2160   'ifaddrs.h',
2161   'langinfo.h',
2162   'mbarrier.h',
2163   'stdbool.h',
2164   'strings.h',
2165   'sys/epoll.h',
2166   'sys/event.h',
2167   'sys/personality.h',
2168   'sys/prctl.h',
2169   'sys/procctl.h',
2170   'sys/signalfd.h',
2171   'sys/ucred.h',
2172   'termios.h',
2173   'ucred.h',
2176 foreach header : header_checks
2177   varname = 'HAVE_' + header.underscorify().to_upper()
2179   # Emulate autoconf behaviour of not-found->undef, found->1
2180   found = cc.has_header(header,
2181     include_directories: postgres_inc, args: test_c_args)
2182   cdata.set(varname, found ? 1 : false,
2183             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2184 endforeach
2187 decl_checks = [
2188   ['F_FULLFSYNC', 'fcntl.h'],
2189   ['fdatasync', 'unistd.h'],
2190   ['posix_fadvise', 'fcntl.h'],
2191   ['strlcat', 'string.h'],
2192   ['strlcpy', 'string.h'],
2193   ['strnlen', 'string.h'],
2196 # Need to check for function declarations for these functions, because
2197 # checking for library symbols wouldn't handle deployment target
2198 # restrictions on macOS
2199 decl_checks += [
2200   ['preadv', 'sys/uio.h'],
2201   ['pwritev', 'sys/uio.h'],
2204 foreach c : decl_checks
2205   func = c.get(0)
2206   header = c.get(1)
2207   args = c.get(2, {})
2208   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2210   found = cc.has_header_symbol(header, func,
2211     args: test_c_args, include_directories: postgres_inc,
2212     kwargs: args)
2213   cdata.set10(varname, found, description:
2214 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2215    don't.'''.format(func))
2216 endforeach
2219 if cc.has_type('struct option',
2220     args: test_c_args, include_directories: postgres_inc,
2221     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2222   cdata.set('HAVE_STRUCT_OPTION', 1)
2223 endif
2226 foreach c : ['opterr', 'optreset']
2227   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2229   if cc.links('''
2230 #include <unistd.h>
2231 int main(void)
2233     extern int @0@;
2234     @0@ = 1;
2236 '''.format(c), name: c, args: test_c_args)
2237     cdata.set(varname, 1)
2238   else
2239     cdata.set(varname, false)
2240   endif
2241 endforeach
2243 if cc.has_type('socklen_t',
2244     args: test_c_args, include_directories: postgres_inc,
2245     prefix: '''
2246 #include <sys/socket.h>''')
2247   cdata.set('HAVE_SOCKLEN_T', 1)
2248 endif
2250 if cc.has_member('struct sockaddr', 'sa_len',
2251     args: test_c_args, include_directories: postgres_inc,
2252     prefix: '''
2253 #include <sys/types.h>
2254 #include <sys/socket.h>''')
2255   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2256 endif
2258 if cc.has_member('struct tm', 'tm_zone',
2259     args: test_c_args, include_directories: postgres_inc,
2260     prefix: '''
2261 #include <sys/types.h>
2262 #include <time.h>
2263 ''')
2264   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2265 endif
2267 if cc.compiles('''
2268 #include <time.h>
2269 extern int foo(void);
2270 int foo(void)
2272     return timezone / 60;
2274 ''',
2275     name: 'global variable `timezone\' exists',
2276     args: test_c_args, include_directories: postgres_inc)
2277   cdata.set('HAVE_INT_TIMEZONE', 1)
2278 else
2279   cdata.set('HAVE_INT_TIMEZONE', false)
2280 endif
2282 if cc.has_type('union semun',
2283     args: test_c_args,
2284     include_directories: postgres_inc,
2285     prefix: '''
2286 #include <sys/types.h>
2287 #include <sys/ipc.h>
2288 #include <sys/sem.h>
2289 ''')
2290   cdata.set('HAVE_UNION_SEMUN', 1)
2291 endif
2293 if cc.compiles('''
2294 #include <string.h>
2295 int main(void)
2297   char buf[100];
2298   switch (strerror_r(1, buf, sizeof(buf)))
2299   { case 0: break; default: break; }
2300 }''',
2301     name: 'strerror_r',
2302     args: test_c_args, include_directories: postgres_inc)
2303   cdata.set('STRERROR_R_INT', 1)
2304 else
2305   cdata.set('STRERROR_R_INT', false)
2306 endif
2308 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2309 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2310 # we should not use so we check the standard header first.  MSVC has a
2311 # replacement defined in src/include/port/win32_port.h.
2312 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2313    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2314   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2315 endif
2317 # Check if the C compiler understands typeof or a variant.  Define
2318 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2319 foreach kw : ['typeof', '__typeof__', 'decltype']
2320   if cc.compiles('''
2321 int main(void)
2323     int x = 0;
2324     @0@(x) y;
2325     y = x;
2326     return y;
2328 '''.format(kw),
2329     name: 'typeof()',
2330     args: test_c_args, include_directories: postgres_inc)
2332     cdata.set('HAVE_TYPEOF', 1)
2333     if kw != 'typeof'
2334       cdata.set('typeof', kw)
2335     endif
2337     break
2338   endif
2339 endforeach
2342 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2343 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2344 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2345 wcstombs_l_test = '''
2346 #include <stdlib.h>
2347 #include <locale.h>
2350 void main(void)
2352 #ifndef wcstombs_l
2353     (void) wcstombs_l;
2354 #endif
2357 if (not cc.compiles(wcstombs_l_test.format(''),
2358       name: 'wcstombs_l') and
2359     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2360       name: 'wcstombs_l in xlocale.h'))
2361     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2362 endif
2365 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2366 # understands, because it conflicts with __declspec(restrict). Therefore we
2367 # define pg_restrict to the appropriate definition, which presumably won't
2368 # conflict.
2370 # We assume C99 support, so we don't need to make this conditional.
2372 # XXX: Historically we allowed platforms to disable restrict in template
2373 # files, but that was only added for AIX when building with XLC, which we
2374 # don't support yet.
2375 cdata.set('pg_restrict', '__restrict')
2378 # Most libraries are included only if they demonstrably provide a function we
2379 # need, but libm is an exception: always include it, because there are too
2380 # many compilers that play cute optimization games that will break probes for
2381 # standard functions such as pow().
2382 os_deps += cc.find_library('m', required: false)
2384 rt_dep = cc.find_library('rt', required: false)
2386 dl_dep = cc.find_library('dl', required: false)
2388 util_dep = cc.find_library('util', required: false)
2390 getopt_dep = cc.find_library('getopt', required: false)
2391 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2392 # Check if we want to replace getopt/getopt_long even if provided by the system
2393 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2394 #   so always use our version on Windows
2395 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2396 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2397 # - We want to use system's getopt_long() only if the system provides struct
2398 #   option
2399 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2400 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2402 # Required on BSDs
2403 execinfo_dep = cc.find_library('execinfo', required: false)
2405 if host_system == 'cygwin'
2406   cygipc_dep = cc.find_library('cygipc', required: false)
2407 else
2408   cygipc_dep = not_found_dep
2409 endif
2411 if host_system == 'sunos'
2412   socket_dep = cc.find_library('socket', required: false)
2413 else
2414   socket_dep = not_found_dep
2415 endif
2417 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2418 # unnecessary checks over and over, particularly on windows.
2419 func_checks = [
2420   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2421   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2422   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2423   ['copyfile'],
2424   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2425   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2426   # required. Just checking for dlsym() ought to suffice.
2427   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2428   ['explicit_bzero'],
2429   ['getifaddrs'],
2430   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2431   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2432   ['getpeereid'],
2433   ['getpeerucred'],
2434   ['inet_aton'],
2435   ['inet_pton'],
2436   ['kqueue'],
2437   ['mbstowcs_l'],
2438   ['memset_s'],
2439   ['mkdtemp'],
2440   ['posix_fadvise'],
2441   ['posix_fallocate'],
2442   ['ppoll'],
2443   ['pstat'],
2444   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2445   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2446   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2447   ['setproctitle', {'dependencies': [util_dep]}],
2448   ['setproctitle_fast'],
2449   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2450   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2451   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2452   ['socket', {'dependencies': [socket_dep], 'define': false}],
2453   ['strchrnul'],
2454   ['strerror_r', {'dependencies': [thread_dep]}],
2455   ['strlcat'],
2456   ['strlcpy'],
2457   ['strnlen'],
2458   ['strsignal'],
2459   ['sync_file_range'],
2460   ['syncfs'],
2461   ['uselocale'],
2462   ['wcstombs_l'],
2465 func_check_results = {}
2466 foreach c : func_checks
2467   func = c.get(0)
2468   kwargs = c.get(1, {})
2469   deps = kwargs.get('dependencies', [])
2471   if kwargs.get('skip', false)
2472     continue
2473   endif
2475   found = cc.has_function(func, args: test_c_args)
2477   if not found
2478     foreach dep : deps
2479       if not dep.found()
2480         continue
2481       endif
2482       found = cc.has_function(func, args: test_c_args,
2483                               dependencies: [dep])
2484       if found
2485         os_deps += dep
2486         break
2487       endif
2488     endforeach
2489   endif
2491   func_check_results += {func: found}
2493   if kwargs.get('define', true)
2494     # Emulate autoconf behaviour of not-found->undef, found->1
2495     cdata.set('HAVE_' + func.underscorify().to_upper(),
2496               found  ? 1 : false,
2497               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2498   endif
2499 endforeach
2502 if cc.has_function('syslog', args: test_c_args) and \
2503     cc.check_header('syslog.h', args: test_c_args)
2504   cdata.set('HAVE_SYSLOG', 1)
2505 endif
2508 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2509 # semaphores
2510 if sema_kind == 'unnamed_posix' and \
2511    not func_check_results.get('sem_init', false)
2512   sema_kind = 'sysv'
2513 endif
2515 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2516 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2518 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2519 cdata.set_quoted('DLSUFFIX', dlsuffix)
2522 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2523 cdata.set_quoted('PG_VERSION_STR',
2524   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2525     pg_version, host_machine.cpu_family(), host_system,
2526     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2527   )
2531 ###############################################################
2532 # NLS / Gettext
2533 ###############################################################
2535 nlsopt = get_option('nls')
2536 libintl = not_found_dep
2538 if not nlsopt.disabled()
2539   # otherwise there'd be lots of
2540   # "Gettext not found, all translation (po) targets will be ignored."
2541   # warnings if not found.
2542   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2544   # meson 0.59 has this wrapped in dependency('intl')
2545   if (msgfmt.found() and
2546       cc.check_header('libintl.h', required: nlsopt,
2547         args: test_c_args, include_directories: postgres_inc))
2549     # in libc
2550     if cc.has_function('ngettext')
2551       libintl = declare_dependency()
2552     else
2553       libintl = cc.find_library('intl',
2554         has_headers: ['libintl.h'], required: nlsopt,
2555         header_include_directories: postgres_inc,
2556         dirs: test_lib_d)
2557     endif
2558   endif
2560   if libintl.found()
2561     i18n = import('i18n')
2562     cdata.set('ENABLE_NLS', 1)
2563   endif
2564 endif
2568 ###############################################################
2569 # Build
2570 ###############################################################
2572 # Set up compiler / linker arguments to be used everywhere, individual targets
2573 # can add further args directly, or indirectly via dependencies
2574 add_project_arguments(cflags, language: ['c'])
2575 add_project_arguments(cppflags, language: ['c'])
2576 add_project_arguments(cflags_warn, language: ['c'])
2577 add_project_arguments(cxxflags, language: ['cpp'])
2578 add_project_arguments(cppflags, language: ['cpp'])
2579 add_project_arguments(cxxflags_warn, language: ['cpp'])
2580 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2583 # Collect a number of lists of things while recursing through the source
2584 # tree. Later steps then can use those.
2586 # list of targets for various alias targets
2587 backend_targets = []
2588 bin_targets = []
2589 pl_targets = []
2590 contrib_targets = []
2591 testprep_targets = []
2592 nls_targets = []
2595 # Define the tests to distribute them to the correct test styles later
2596 test_deps = []
2597 tests = []
2600 # Default options for targets
2602 # First identify rpaths
2603 bin_install_rpaths = []
2604 lib_install_rpaths = []
2605 mod_install_rpaths = []
2608 # Don't add rpaths on darwin for now - as long as only absolute references to
2609 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2610 # their final destination.
2611 if host_system != 'darwin'
2612   # Add absolute path to libdir to rpath. This ensures installed binaries /
2613   # libraries find our libraries (mainly libpq).
2614   bin_install_rpaths += dir_prefix / dir_lib
2615   lib_install_rpaths += dir_prefix / dir_lib
2616   mod_install_rpaths += dir_prefix / dir_lib
2618   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2619   #
2620   # Not needed on darwin even if we use relative rpaths for our own libraries,
2621   # as the install_name of libraries in extra_lib_dirs will point to their
2622   # location anyway.
2623   bin_install_rpaths += postgres_lib_d
2624   lib_install_rpaths += postgres_lib_d
2625   mod_install_rpaths += postgres_lib_d
2626 endif
2629 # Define arguments for default targets
2631 default_target_args = {
2632   'implicit_include_directories': false,
2633   'install': true,
2636 default_lib_args = default_target_args + {
2637   'name_prefix': '',
2640 internal_lib_args = default_lib_args + {
2641   'build_by_default': false,
2642   'install': false,
2645 default_mod_args = default_lib_args + {
2646   'name_prefix': '',
2647   'install_dir': dir_lib_pkg,
2650 default_bin_args = default_target_args + {
2651   'install_dir': dir_bin,
2654 if get_option('rpath')
2655   default_lib_args += {
2656     'install_rpath': ':'.join(lib_install_rpaths),
2657   }
2659   default_mod_args += {
2660     'install_rpath': ':'.join(mod_install_rpaths),
2661   }
2663   default_bin_args += {
2664     'install_rpath': ':'.join(bin_install_rpaths),
2665   }
2666 endif
2669 # Helper for exporting a limited number of symbols
2670 gen_export_kwargs = {
2671   'input': 'exports.txt',
2672   'output': '@BASENAME@.'+export_file_suffix,
2673   'command': [perl, files('src/tools/gen_export.pl'),
2674    '--format', export_file_format,
2675    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2676   'build_by_default': false,
2677   'install': false,
2683 ### Helpers for custom targets used across the tree
2686 catalog_pm = files('src/backend/catalog/Catalog.pm')
2687 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2688 gen_kwlist_deps = [perfect_hash_pm]
2689 gen_kwlist_cmd = [
2690   perl, '-I', '@SOURCE_ROOT@/src/tools',
2691   files('src/tools/gen_keywordlist.pl'),
2692   '--output', '@OUTDIR@', '@INPUT@']
2697 ### windows resources related stuff
2700 if host_system == 'windows'
2701   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2702   win32ver_rc = files('src/port/win32ver.rc')
2703   rcgen = find_program('src/tools/rcgen', native: true)
2705   rcgen_base_args = [
2706     '--srcdir', '@SOURCE_DIR@',
2707     '--builddir', meson.build_root(),
2708     '--rcout', '@OUTPUT0@',
2709     '--out', '@OUTPUT1@',
2710     '--input', '@INPUT@',
2711     '@EXTRA_ARGS@',
2712   ]
2714   if cc.get_argument_syntax() == 'msvc'
2715     rc = find_program('rc', required: true)
2716     rcgen_base_args += ['--rc', rc.path()]
2717     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2718   else
2719     windres = find_program('windres', required: true)
2720     rcgen_base_args += ['--windres', windres.path()]
2721     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2722   endif
2724   # msbuild backend doesn't support this atm
2725   if meson.backend() == 'ninja'
2726     rcgen_base_args += ['--depfile', '@DEPFILE@']
2727   endif
2729   rcgen_bin_args = rcgen_base_args + [
2730     '--VFT_TYPE', 'VFT_APP',
2731     '--FILEENDING', 'exe',
2732     '--ICO', pg_ico
2733   ]
2735   rcgen_lib_args = rcgen_base_args + [
2736     '--VFT_TYPE', 'VFT_DLL',
2737     '--FILEENDING', 'dll',
2738   ]
2740   rc_bin_gen = generator(rcgen,
2741     depfile: '@BASENAME@.d',
2742     arguments: rcgen_bin_args,
2743     output: rcgen_outputs,
2744   )
2746   rc_lib_gen = generator(rcgen,
2747     depfile: '@BASENAME@.d',
2748     arguments: rcgen_lib_args,
2749     output: rcgen_outputs,
2750   )
2751 endif
2755 # headers that the whole build tree depends on
2756 generated_headers = []
2757 # headers that the backend build depends on
2758 generated_backend_headers = []
2759 # configure_files() output, needs a way of converting to file names
2760 configure_files = []
2762 # generated files that might conflict with a partial in-tree autoconf build
2763 generated_sources = []
2764 # same, for paths that differ between autoconf / meson builds
2765 # elements are [dir, [files]]
2766 generated_sources_ac = {}
2769 # First visit src/include - all targets creating headers are defined
2770 # within. That makes it easy to add the necessary dependencies for the
2771 # subsequent build steps.
2773 subdir('src/include')
2775 subdir('config')
2777 # Then through src/port and src/common, as most other things depend on them
2779 frontend_port_code = declare_dependency(
2780   compile_args: ['-DFRONTEND'],
2781   include_directories: [postgres_inc],
2782   dependencies: os_deps,
2785 backend_port_code = declare_dependency(
2786   compile_args: ['-DBUILDING_DLL'],
2787   include_directories: [postgres_inc],
2788   sources: [errcodes], # errcodes.h is needed due to use of ereport
2789   dependencies: os_deps,
2792 subdir('src/port')
2794 frontend_common_code = declare_dependency(
2795   compile_args: ['-DFRONTEND'],
2796   include_directories: [postgres_inc],
2797   sources: generated_headers,
2798   dependencies: [os_deps, zlib, zstd],
2801 backend_common_code = declare_dependency(
2802   compile_args: ['-DBUILDING_DLL'],
2803   include_directories: [postgres_inc],
2804   sources: generated_headers,
2805   dependencies: [os_deps, zlib, zstd],
2808 subdir('src/common')
2810 # all shared libraries should depend on shlib_code
2811 shlib_code = declare_dependency(
2812   link_args: ldflags_sl,
2815 # all static libraries not part of the backend should depend on this
2816 frontend_stlib_code = declare_dependency(
2817   include_directories: [postgres_inc],
2818   link_with: [common_static, pgport_static],
2819   sources: generated_headers,
2820   dependencies: [os_deps, libintl],
2823 # all shared libraries not part of the backend should depend on this
2824 frontend_shlib_code = declare_dependency(
2825   include_directories: [postgres_inc],
2826   link_with: [common_shlib, pgport_shlib],
2827   sources: generated_headers,
2828   dependencies: [shlib_code, os_deps, libintl],
2831 # Dependencies both for static and shared libpq
2832 libpq_deps += [
2833   thread_dep,
2835   gssapi,
2836   ldap_r,
2837   libintl,
2838   ssl,
2841 subdir('src/interfaces/libpq')
2842 # fe_utils depends on libpq
2843 subdir('src/fe_utils')
2845 # for frontend binaries
2846 frontend_code = declare_dependency(
2847   include_directories: [postgres_inc],
2848   link_with: [fe_utils, common_static, pgport_static],
2849   sources: generated_headers,
2850   dependencies: [os_deps, libintl],
2853 backend_both_deps += [
2854   thread_dep,
2855   bsd_auth,
2856   gssapi,
2857   icu,
2858   icu_i18n,
2859   ldap,
2860   libintl,
2861   libxml,
2862   lz4,
2863   pam,
2864   ssl,
2865   systemd,
2866   zlib,
2867   zstd,
2870 backend_mod_deps = backend_both_deps + os_deps
2872 backend_code = declare_dependency(
2873   compile_args: ['-DBUILDING_DLL'],
2874   include_directories: [postgres_inc],
2875   link_args: ldflags_be,
2876   link_with: [],
2877   sources: generated_headers + generated_backend_headers,
2878   dependencies: os_deps + backend_both_deps + backend_deps,
2881 # install these files only during test, not main install
2882 test_install_data = []
2883 test_install_libs = []
2885 # src/backend/meson.build defines backend_mod_code used for extension
2886 # libraries.
2889 # Then through the main sources. That way contrib can have dependencies on
2890 # main sources. Note that this explicitly doesn't enter src/test, right now a
2891 # few regression tests depend on contrib files.
2893 subdir('src')
2895 subdir('contrib')
2897 subdir('src/test')
2898 subdir('src/interfaces/libpq/test')
2899 subdir('src/interfaces/ecpg/test')
2901 subdir('doc/src/sgml')
2903 generated_sources_ac += {'': ['GNUmakefile']}
2905 # After processing src/test, add test_install_libs to the testprep_targets
2906 # to build them
2907 testprep_targets += test_install_libs
2910 # If there are any files in the source directory that we also generate in the
2911 # build directory, they might get preferred over the newly generated files,
2912 # e.g. because of a #include "file", which always will search in the current
2913 # directory first.
2914 message('checking for file conflicts between source and build directory')
2915 conflicting_files = []
2916 potentially_conflicting_files_t = []
2917 potentially_conflicting_files_t += generated_headers
2918 potentially_conflicting_files_t += generated_backend_headers
2919 potentially_conflicting_files_t += generated_backend_sources
2920 potentially_conflicting_files_t += generated_sources
2922 potentially_conflicting_files = []
2924 # convert all sources of potentially conflicting files into uniform shape
2925 foreach t : potentially_conflicting_files_t
2926   potentially_conflicting_files += t.full_path()
2927 endforeach
2928 foreach t : configure_files
2929   t = '@0@'.format(t)
2930   potentially_conflicting_files += meson.current_build_dir() / t
2931 endforeach
2932 foreach sub, fnames : generated_sources_ac
2933   sub = meson.build_root() / sub
2934   foreach fname : fnames
2935     potentially_conflicting_files += sub / fname
2936   endforeach
2937 endforeach
2939 # find and report conflicting files
2940 foreach build_path : potentially_conflicting_files
2941   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2942   # str.replace is in 0.56
2943   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2944   if fs.exists(src_path) or fs.is_symlink(src_path)
2945     conflicting_files += src_path
2946   endif
2947 endforeach
2948 # XXX: Perhaps we should generate a file that would clean these up? The list
2949 # can be long.
2950 if conflicting_files.length() > 0
2951   errmsg_cleanup = '''
2952 Conflicting files in source directory:
2953   @0@
2955 The conflicting files need to be removed, either by removing the files listed
2956 above, or by running configure and then make maintainer-clean.
2958   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2959   error(errmsg_nonclean_base.format(errmsg_cleanup))
2960 endif
2964 ###############################################################
2965 # Install targets
2966 ###############################################################
2969 # We want to define additional install targets beyond what meson provides. For
2970 # that we need to define targets depending on nearly everything. We collected
2971 # the results of i18n.gettext() invocations into nls_targets, that also
2972 # includes maintainer targets though. Collect the ones we want as a dependency.
2974 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2975 # generation happens during install, so that's not a real issue.
2976 nls_mo_targets = []
2977 if libintl.found() and meson.version().version_compare('>=0.60')
2978   # use range() to avoid the flattening of the list that foreach() would do
2979   foreach off : range(0, nls_targets.length())
2980     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2981     # -pot target 3) maintainer -pot target
2982     nls_mo_targets += nls_targets[off][0]
2983   endforeach
2984   alias_target('nls', nls_mo_targets)
2985 endif
2988 all_built = [
2989   backend_targets,
2990   bin_targets,
2991   libpq_st,
2992   pl_targets,
2993   contrib_targets,
2994   nls_mo_targets,
2995   testprep_targets,
2996   ecpg_targets,
2999 # Meson's default install target is quite verbose. Provide one that is quiet.
3000 install_quiet = custom_target('install-quiet',
3001   output: 'install-quiet',
3002   build_always_stale: true,
3003   build_by_default: false,
3004   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3005   depends: all_built,
3008 # Target to install files used for tests, which aren't installed by default
3009 install_test_files_args = [
3010   install_files,
3011   '--prefix', dir_prefix,
3012   '--install', contrib_data_dir, test_install_data,
3013   '--install', dir_lib_pkg, test_install_libs,
3015 run_target('install-test-files',
3016   command: [python] + install_test_files_args,
3017   depends: testprep_targets,
3022 ###############################################################
3023 # Test prep
3024 ###############################################################
3026 # DESTDIR for the installation we'll run tests in
3027 test_install_destdir = meson.build_root() / 'tmp_install/'
3029 # DESTDIR + prefix appropriately munged
3030 if build_system != 'windows'
3031   # On unixoid systems this is trivial, we just prepend the destdir
3032   assert(dir_prefix.startswith('/')) # enforced by meson
3033   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3034 else
3035   # drives, drive-relative paths, etc make this complicated on windows, call
3036   # into a copy of meson's logic for it
3037   command = [
3038     python, '-c',
3039     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3040     test_install_destdir, dir_prefix]
3041   test_install_location = run_command(command, check: true).stdout().strip()
3042 endif
3044 meson_install_args = meson_args + ['install'] + {
3045     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3046     'muon': []
3047 }[meson_impl]
3049 # setup tests should be run first,
3050 # so define priority for these
3051 setup_tests_priority = 100
3052 test('tmp_install',
3053     meson_bin, args: meson_install_args ,
3054     env: {'DESTDIR':test_install_destdir},
3055     priority: setup_tests_priority,
3056     timeout: 300,
3057     is_parallel: false,
3058     suite: ['setup'])
3060 test('install_test_files',
3061     python,
3062     args: install_test_files_args + ['--destdir', test_install_destdir],
3063     priority: setup_tests_priority,
3064     is_parallel: false,
3065     suite: ['setup'])
3067 test_result_dir = meson.build_root() / 'testrun'
3070 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3071 # inevitable conflicts from running tests in parallel, hackishly assign
3072 # different ports for different tests.
3074 testport = 40000
3076 test_env = environment()
3078 temp_install_bindir = test_install_location / get_option('bindir')
3079 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3080 test_env.set('PG_REGRESS', pg_regress.full_path())
3081 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3082 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3084 # Test suites that are not safe by default but can be run if selected
3085 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3086 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3087 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3089 # Add the temporary installation to the library search path on platforms where
3090 # that works (everything but windows, basically). On windows everything
3091 # library-like gets installed into bindir, solving that issue.
3092 if library_path_var != ''
3093   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3094 endif
3097 # Create (and remove old) initdb template directory. Tests use that, where
3098 # possible, to make it cheaper to run tests.
3100 # Use python to remove the old cached initdb, as we cannot rely on a working
3101 # 'rm' binary on windows.
3102 test('initdb_cache',
3103      python,
3104      args: [
3105        '-c', '''
3106 import shutil
3107 import sys
3108 import subprocess
3110 shutil.rmtree(sys.argv[1], ignore_errors=True)
3111 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3112 sys.exit(sp.returncode)
3113 ''',
3114        test_initdb_template,
3115        temp_install_bindir / 'initdb',
3116        '-A', 'trust', '-N', '--no-instructions', '--no-locale'
3117      ],
3118      priority: setup_tests_priority - 1,
3119      timeout: 300,
3120      is_parallel: false,
3121      env: test_env,
3122      suite: ['setup'])
3126 ###############################################################
3127 # Test Generation
3128 ###############################################################
3130 # When using a meson version understanding exclude_suites, define a
3131 # 'tmp_install' test setup (the default) that excludes tests running against a
3132 # pre-existing install and a 'running' setup that conflicts with creation of
3133 # the temporary installation and tap tests (which don't support running
3134 # against a running server).
3136 running_suites = []
3137 install_suites = []
3138 if meson.version().version_compare('>=0.57')
3139   runningcheck = true
3140 else
3141   runningcheck = false
3142 endif
3144 testwrap = files('src/tools/testwrap')
3146 foreach test_dir : tests
3147   testwrap_base = [
3148     testwrap,
3149     '--basedir', meson.build_root(),
3150     '--srcdir', test_dir['sd'],
3151   ]
3153   foreach kind, v : test_dir
3154     if kind in ['sd', 'bd', 'name']
3155       continue
3156     endif
3158     t = test_dir[kind]
3160     if kind in ['regress', 'isolation', 'ecpg']
3161       if kind == 'regress'
3162         runner = pg_regress
3163         fallback_dbname = 'regression_@0@'
3164       elif kind == 'isolation'
3165         runner = pg_isolation_regress
3166         fallback_dbname = 'isolation_regression_@0@'
3167       elif kind == 'ecpg'
3168         runner = pg_regress_ecpg
3169         fallback_dbname = 'ecpg_regression_@0@'
3170       endif
3172       test_group = test_dir['name']
3173       test_group_running = test_dir['name'] + '-running'
3175       test_output = test_result_dir / test_group / kind
3176       test_output_running = test_result_dir / test_group_running/ kind
3178       # Unless specified by the test, choose a non-conflicting database name,
3179       # to avoid conflicts when running against existing server.
3180       dbname = t.get('dbname',
3181         fallback_dbname.format(test_dir['name']))
3183       test_command_base = [
3184         runner.full_path(),
3185         '--inputdir', t.get('inputdir', test_dir['sd']),
3186         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3187         '--bindir', '',
3188         '--dlpath', test_dir['bd'],
3189         '--max-concurrent-tests=20',
3190         '--dbname', dbname,
3191       ] + t.get('regress_args', [])
3193       test_selection = []
3194       if t.has_key('schedule')
3195         test_selection += ['--schedule', t['schedule'],]
3196       endif
3198       if kind == 'isolation'
3199         test_selection += t.get('specs', [])
3200       else
3201         test_selection += t.get('sql', [])
3202       endif
3204       env = test_env
3205       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3207       test_kwargs = {
3208         'protocol': 'tap',
3209         'priority': 10,
3210         'timeout': 1000,
3211         'depends': test_deps + t.get('deps', []),
3212         'env': env,
3213       } + t.get('test_kwargs', {})
3215       test(test_group / kind,
3216         python,
3217         args: [
3218           testwrap_base,
3219           '--testgroup', test_group,
3220           '--testname', kind,
3221           '--',
3222           test_command_base,
3223           '--outputdir', test_output,
3224           '--temp-instance', test_output / 'tmp_check',
3225           '--port', testport.to_string(),
3226           test_selection,
3227         ],
3228         suite: test_group,
3229         kwargs: test_kwargs,
3230       )
3231       install_suites += test_group
3233       # some tests can't support running against running DB
3234       if runningcheck and t.get('runningcheck', true)
3235         test(test_group_running / kind,
3236           python,
3237           args: [
3238             testwrap_base,
3239             '--testgroup', test_group_running,
3240             '--testname', kind,
3241             '--',
3242             test_command_base,
3243             '--outputdir', test_output_running,
3244             test_selection,
3245           ],
3246           is_parallel: t.get('runningcheck-parallel', true),
3247           suite: test_group_running,
3248           kwargs: test_kwargs,
3249         )
3250         running_suites += test_group_running
3251       endif
3253       testport += 1
3254     elif kind == 'tap'
3255       if not tap_tests_enabled
3256         continue
3257       endif
3259       test_command = [
3260         perl.path(),
3261         '-I', meson.source_root() / 'src/test/perl',
3262         '-I', test_dir['sd'],
3263       ]
3265       # Add temporary install, the build directory for non-installed binaries and
3266       # also test/ for non-installed test binaries built separately.
3267       env = test_env
3268       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3270       foreach name, value : t.get('env', {})
3271         env.set(name, value)
3272       endforeach
3274       test_group = test_dir['name']
3275       test_kwargs = {
3276         'protocol': 'tap',
3277         'suite': test_group,
3278         'timeout': 1000,
3279         'depends': test_deps + t.get('deps', []),
3280         'env': env,
3281       } + t.get('test_kwargs', {})
3283       foreach onetap : t['tests']
3284         # Make tap test names prettier, remove t/ and .pl
3285         onetap_p = onetap
3286         if onetap_p.startswith('t/')
3287           onetap_p = onetap.split('t/')[1]
3288         endif
3289         if onetap_p.endswith('.pl')
3290           onetap_p = fs.stem(onetap_p)
3291         endif
3293         test(test_dir['name'] / onetap_p,
3294           python,
3295           kwargs: test_kwargs,
3296           args: testwrap_base + [
3297             '--testgroup', test_dir['name'],
3298             '--testname', onetap_p,
3299             '--', test_command,
3300             test_dir['sd'] / onetap,
3301           ],
3302         )
3303       endforeach
3304       install_suites += test_group
3305     else
3306       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3307     endif
3309   endforeach # kinds of tests
3311 endforeach # directories with tests
3313 # repeat condition so meson realizes version dependency
3314 if meson.version().version_compare('>=0.57')
3315   add_test_setup('tmp_install',
3316     is_default: true,
3317     exclude_suites: running_suites)
3318   add_test_setup('running',
3319     exclude_suites: ['setup'] + install_suites)
3320 endif
3324 ###############################################################
3325 # Pseudo targets
3326 ###############################################################
3328 alias_target('backend', backend_targets)
3329 alias_target('bin', bin_targets + [libpq_st])
3330 alias_target('pl', pl_targets)
3331 alias_target('contrib', contrib_targets)
3332 alias_target('testprep', testprep_targets)
3333 alias_target('install-world', install_quiet, installdocs)
3337 ###############################################################
3338 # The End, The End, My Friend
3339 ###############################################################
3341 if meson.version().version_compare('>=0.57')
3343   summary(
3344     {
3345       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3346       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3347       'segment size': get_option('segsize_blocks') != 0 ?
3348         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3349         '@0@ GB'.format(get_option('segsize')),
3350     },
3351     section: 'Data layout',
3352   )
3354   summary(
3355     {
3356       'host system': '@0@ @1@'.format(host_system, host_cpu),
3357       'build system': '@0@ @1@'.format(build_machine.system(),
3358                                        build_machine.cpu_family()),
3359     },
3360     section: 'System',
3361   )
3363   summary(
3364     {
3365       'linker': '@0@'.format(cc.get_linker_id()),
3366       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3367     },
3368     section: 'Compiler',
3369   )
3371   summary(
3372     {
3373       'CPP FLAGS': ' '.join(cppflags),
3374       'C FLAGS, functional': ' '.join(cflags),
3375       'C FLAGS, warnings': ' '.join(cflags_warn),
3376       'C FLAGS, modules': ' '.join(cflags_mod),
3377       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3378       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3379     },
3380     section: 'Compiler Flags',
3381   )
3383   if llvm.found()
3384     summary(
3385       {
3386         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3387       },
3388       section: 'Compiler',
3389     )
3391     summary(
3392       {
3393         'C++ FLAGS, functional': ' '.join(cxxflags),
3394         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3395         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3396       },
3397       section: 'Compiler Flags',
3398     )
3399   endif
3401   summary(
3402     {
3403       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3404       'dtrace': dtrace,
3405       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3406     },
3407     section: 'Programs',
3408   )
3410   summary(
3411     {
3412       'bonjour': bonjour,
3413       'bsd_auth': bsd_auth,
3414       'docs': docs_dep,
3415       'docs_pdf': docs_pdf_dep,
3416       'gss': gssapi,
3417       'icu': icu,
3418       'ldap': ldap,
3419       'libxml': libxml,
3420       'libxslt': libxslt,
3421       'llvm': llvm,
3422       'lz4': lz4,
3423       'nls': libintl,
3424       'openssl': ssl,
3425       'pam': pam,
3426       'plperl': perl_dep,
3427       'plpython': python3_dep,
3428       'pltcl': tcl_dep,
3429       'readline': readline,
3430       'selinux': selinux,
3431       'systemd': systemd,
3432       'uuid': uuid,
3433       'zlib': zlib,
3434       'zstd': zstd,
3435     },
3436     section: 'External libraries',
3437   )
3439 endif