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