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