From 09e4c80b3d325e739bdbe6ac02b46df24c9d0c29 Mon Sep 17 00:00:00 2001
From: Nazir Bilal Yavuz <byavuz81@gmail.com>
Date: Fri, 24 Feb 2023 15:35:32 +0300
Subject: [PATCH v2] meson: Refactor SSL option

---
 .cirrus.yml                      |   7 +-
 meson.build                      | 153 ++++++++++++++++++-------------
 meson_options.txt                |   4 +-
 src/interfaces/libpq/meson.build |   2 +-
 src/makefiles/meson.build        |   2 +-
 src/test/ssl/meson.build         |   2 +-
 6 files changed, 97 insertions(+), 73 deletions(-)

diff --git a/.cirrus.yml b/.cirrus.yml
index f212978752..aaf4066366 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -181,7 +181,7 @@ task:
     su postgres <<-EOF
       meson setup \
         --buildtype=debug \
-        -Dcassert=true -Dssl=openssl -Duuid=bsd -Dtcl_version=tcl86 -Ddtrace=auto \
+        -Dcassert=true -Duuid=bsd -Dtcl_version=tcl86 -Ddtrace=auto \
         -DPG_TEST_EXTRA="$PG_TEST_EXTRA" \
         -Dextra_lib_dirs=/usr/local/lib -Dextra_include_dirs=/usr/local/include/ \
         build
@@ -243,7 +243,6 @@ LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >-
 
 LINUX_MESON_FEATURES: &LINUX_MESON_FEATURES >-
   -Dllvm=enabled
-  -Dssl=openssl
   -Duuid=e2fs
 
 
@@ -497,7 +496,7 @@ task:
       -Dextra_include_dirs=${brewpath}/include \
       -Dextra_lib_dirs=${brewpath}/lib \
       -Dcassert=true \
-      -Dssl=openssl -Duuid=e2fs -Ddtrace=auto \
+      -Duuid=e2fs -Ddtrace=auto \
       -Dsegsize_blocks=6 \
       -DPG_TEST_EXTRA="$PG_TEST_EXTRA" \
       build
@@ -568,7 +567,7 @@ task:
   # Use /DEBUG:FASTLINK to avoid high memory usage during linking
   configure_script: |
     vcvarsall x64
-    meson setup --backend ninja --buildtype debug -Dc_link_args=/DEBUG:FASTLINK -Dcassert=true -Db_pch=true -Dssl=openssl -Dextra_lib_dirs=c:\openssl\1.1\lib -Dextra_include_dirs=c:\openssl\1.1\include -DTAR=%TAR% -DPG_TEST_EXTRA="%PG_TEST_EXTRA%" build
+    meson setup --backend ninja --buildtype debug -Dc_link_args=/DEBUG:FASTLINK -Dcassert=true -Db_pch=true -Dextra_lib_dirs=c:\openssl\1.1\lib -Dextra_include_dirs=c:\openssl\1.1\include -DTAR=%TAR% -DPG_TEST_EXTRA="%PG_TEST_EXTRA%" build
 
   build_script: |
     vcvarsall x64
diff --git a/meson.build b/meson.build
index 656777820c..7fae0e1c49 100644
--- a/meson.build
+++ b/meson.build
@@ -43,6 +43,7 @@ cc = meson.get_compiler('c')
 
 not_found_dep = dependency('', required: false)
 thread_dep = dependency('threads')
+auto_features = get_option('auto_features')
 
 
 
@@ -1171,80 +1172,104 @@ cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
 # Library: SSL
 ###############################################################
 
-if get_option('ssl') == 'openssl'
+ssl = not_found_dep
+ssl_library = 'none'
+sslopt = get_option('ssl')
 
-  # Try to find openssl via pkg-config et al, if that doesn't work
-  # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
-  # the library names that we know about.
+if (sslopt == 'auto' and auto_features.disabled())
+  sslopt = 'none'
+endif
 
-  # via pkg-config et al
-  ssl = dependency('openssl', required: false)
+if sslopt != 'none'
 
-  # via library + headers
-  if not ssl.found()
-    ssl_lib = cc.find_library('ssl',
-      dirs: test_lib_d,
-      header_include_directories: postgres_inc,
-      has_headers: ['openssl/ssl.h', 'openssl/err.h'])
-    crypto_lib = cc.find_library('crypto',
-      dirs: test_lib_d,
-      header_include_directories: postgres_inc)
-    ssl_int = [ssl_lib, crypto_lib]
+  if not ssl.found() and sslopt in ['auto', 'openssl']
+    openssl_required = sslopt == 'openssl'
 
-    ssl = declare_dependency(dependencies: ssl_int,
-                             include_directories: postgres_inc)
-  else
-    cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: true)
-    cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: true)
+    # Try to find openssl via pkg-config et al, if that doesn't work
+    # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
+    # the library names that we know about.
+
+    # via pkg-config et al
+    ssl = dependency('openssl', required: false)
 
+    # via library + headers
+    if not ssl.found()
+      ssl_lib = cc.find_library('ssl',
+        dirs: test_lib_d,
+        header_include_directories: postgres_inc,
+        has_headers: ['openssl/ssl.h', 'openssl/err.h'])
+      crypto_lib = cc.find_library('crypto',
+        dirs: test_lib_d,
+        header_include_directories: postgres_inc)
+      ssl_int = [ssl_lib, crypto_lib]
+
+      ssl = declare_dependency(dependencies: ssl_int,
+                              include_directories: postgres_inc)
+    elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
+         cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
     ssl_int = [ssl]
-  endif
+    endif
 
-  check_funcs = [
-    ['CRYPTO_new_ex_data', {'required': true}],
-    ['SSL_new', {'required': true}],
-
-    # Function introduced in OpenSSL 1.0.2.
-    ['X509_get_signature_nid'],
-
-    # Functions introduced in OpenSSL 1.1.0. We used to check for
-    # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
-    # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
-    # doesn't have these OpenSSL 1.1.0 functions. So check for individual
-    # functions.
-    ['OPENSSL_init_ssl'],
-    ['BIO_get_data'],
-    ['BIO_meth_new'],
-    ['ASN1_STRING_get0_data'],
-    ['HMAC_CTX_new'],
-    ['HMAC_CTX_free'],
-
-    # OpenSSL versions before 1.1.0 required setting callback functions, for
-    # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
-    # function was removed.
-    ['CRYPTO_lock'],
-
-    # Function introduced in OpenSSL 1.1.1
-    ['X509_get_signature_info'],
-  ]
+    if ssl.found()
+      check_funcs = [
+        ['CRYPTO_new_ex_data', {'required': true}],
+        ['SSL_new', {'required': true}],
+
+        # Function introduced in OpenSSL 1.0.2.
+        ['X509_get_signature_nid'],
+
+        # Functions introduced in OpenSSL 1.1.0. We used to check for
+        # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
+        # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
+        # doesn't have these OpenSSL 1.1.0 functions. So check for individual
+        # functions.
+        ['OPENSSL_init_ssl'],
+        ['BIO_get_data'],
+        ['BIO_meth_new'],
+        ['ASN1_STRING_get0_data'],
+        ['HMAC_CTX_new'],
+        ['HMAC_CTX_free'],
+
+        # OpenSSL versions before 1.1.0 required setting callback functions, for
+        # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
+        # function was removed.
+        ['CRYPTO_lock'],
+
+        # Function introduced in OpenSSL 1.1.1
+        ['X509_get_signature_info'],
+      ]
 
-  foreach c : check_funcs
-    func = c.get(0)
-    val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
-    required = c.get(1, {}).get('required', false)
-    if required and not val
-      error('openssl function @0@ is required'.format(func))
-    elif not required
-      cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
+      are_openssl_funcs_complete = true
+      foreach c : check_funcs
+        func = c.get(0)
+        val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
+        required = c.get(1, {}).get('required', false)
+        if required and not val
+          are_openssl_funcs_complete = false
+          openssl_required ? error('openssl function @0@ is required'.format(func)) : \
+                             message('openssl function @0@ is required'.format(func))
+          break
+        elif not required
+          cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
+        endif
+      endforeach
+
+      if are_openssl_funcs_complete
+        cdata.set('USE_OPENSSL', 1,
+                  description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
+        cdata.set('OPENSSL_API_COMPAT', '0x10001000L',
+                  description: '''Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.''')
+        ssl_library = 'openssl'
+      else
+        ssl = not_found_dep
+      endif
     endif
-  endforeach
+  endif
 
-  cdata.set('USE_OPENSSL', 1,
-            description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
-  cdata.set('OPENSSL_API_COMPAT', '0x10001000L',
-            description: '''Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.''')
-else
-  ssl = not_found_dep
+  # At least one SSL library must be found, otherwise throw an error
+  if sslopt == 'auto' and auto_features.enabled()
+    error('SSL Library could not be found')
+  endif
 endif
 
 
diff --git a/meson_options.txt b/meson_options.txt
index 9d3ef4aa20..3edd3e25df 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -127,8 +127,8 @@ option('readline', type : 'feature', value : 'auto',
 option('selinux', type : 'feature', value : 'disabled',
   description: 'build with SELinux support')
 
-option('ssl', type : 'combo', choices : ['none', 'openssl'],
-  value : 'none',
+option('ssl', type : 'combo', choices : ['auto', 'none', 'openssl'],
+  value : 'auto',
   description: 'use LIB for SSL/TLS support (openssl)')
 
 option('systemd', type : 'feature', value: 'auto',
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index 573fd9b6ea..3cd0ddb494 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -117,7 +117,7 @@ tests += {
       't/001_uri.pl',
       't/002_api.pl',
     ],
-    'env': {'with_ssl': get_option('ssl')},
+    'env': {'with_ssl': ssl_library},
   },
 }
 
diff --git a/src/makefiles/meson.build b/src/makefiles/meson.build
index bf7303dc99..98c2bf9ed1 100644
--- a/src/makefiles/meson.build
+++ b/src/makefiles/meson.build
@@ -66,7 +66,7 @@ pgxs_kv = {
   'SUN_STUDIO_CC': 'no', # not supported so far
 
   # want the chosen option, rather than the library
-  'with_ssl' : get_option('ssl'),
+  'with_ssl' : ssl_library,
   'with_uuid': uuidopt,
 
   'default_port': get_option('pgport'),
diff --git a/src/test/ssl/meson.build b/src/test/ssl/meson.build
index a8d9a5424d..4cda81f3bc 100644
--- a/src/test/ssl/meson.build
+++ b/src/test/ssl/meson.build
@@ -6,7 +6,7 @@ tests += {
   'bd': meson.current_build_dir(),
   'tap': {
     'env': {
-      'with_ssl': get_option('ssl'),
+      'with_ssl': ssl_library,
       'OPENSSL': openssl.path(),
     },
     'tests': [
-- 
2.25.1

