From e0c99ade3957847e2c028fd4b673ec75db1b4abc Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Mon, 29 Dec 2025 04:47:32 +0200
Subject: [PATCH 50/50] Meson: Add support for dynamically loadable AI modules

See RM #1853

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 INSTALL                          |  4 +++
 gen_headers/meson_fc_config.h.in |  6 ++++
 meson.build                      | 59 ++++++++++++++++++--------------
 meson_options.txt                |  5 +++
 4 files changed, 49 insertions(+), 25 deletions(-)

diff --git a/INSTALL b/INSTALL
index 4a38a14f85..3ec6a6ae5a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -221,6 +221,10 @@ aimodules (array):
    * stub
   Default is to build classic and tex.
 
+dynaimod (boolean):
+  Whether to build im support for dynamic loading of AI modules.
+  Default is not to build the support.
+
 appimage (boolean):
   Make build suitable for AppImage packaging. This affects paths from
   which various resources are looked from, and produces build that
diff --git a/gen_headers/meson_fc_config.h.in b/gen_headers/meson_fc_config.h.in
index af0c2c441b..c765da5786 100644
--- a/gen_headers/meson_fc_config.h.in
+++ b/gen_headers/meson_fc_config.h.in
@@ -21,6 +21,12 @@
 #mesondefine AI_MOD_STATIC_TEX
 #mesondefine AI_MOD_STATIC_STUB
 
+/* Enable AI modules */
+#mesondefine AI_MODULES
+
+/* Directory to search AI modules from */
+#mesondefine AI_MODULEDIR
+
 #mesondefine DEFAULT_SOCK_PORT
 
 #define INIT_BRACE_BEGIN {
diff --git a/meson.build b/meson.build
index b1e82e42e7..cf61964ce8 100644
--- a/meson.build
+++ b/meson.build
@@ -238,6 +238,30 @@ else
   priv_conf_data.set('LOCALEDIR', join_paths(localeprefix, get_option('localedir')))
 endif
 
+if meson.is_cross_build()
+  cross_inc_str = meson.get_external_property('cross_inc_path', '')
+  cross_inc_path = [cross_inc_str]
+  cross_lib_path = [meson.get_external_property('cross_lib_path', '')]
+  crosser = meson.get_external_property('crosser', false)
+
+  # emscripten build is always cross-build, so check it only here
+  if c_compiler.compiles(
+'''#ifndef __EMSCRIPTEN__
+  error fail
+#endif''',
+  name: 'emscripten')
+    emscripten = true
+  else
+    emscripten = false
+  endif
+else
+  cross_inc_str = ''
+  cross_inc_path = []
+  cross_lib_path = []
+  crosser = false
+  emscripten = false
+endif
+
 server_type = get_option('server')
 
 if server_type == 'freeciv-web'
@@ -257,6 +281,15 @@ else
   aimodcount = 0
   aisrc = []
 
+  if get_option('dynaimod')
+    priv_conf_data.set('AI_MODULES', 1)
+    priv_conf_data.set('AI_MODULEDIR',
+                       '"' + join_paths(get_option('libdir'), 'fcai') + '"')
+    dynai_dep = c_compiler.find_library('ltdl', dirs: cross_lib_path)
+  else
+    dynai_dep = []
+  endif
+
   if get_option('aimodules').contains('classic')
     priv_conf_data.set('AI_MOD_STATIC_CLASSIC', 1)
     priv_conf_data.set('AI_MOD_DEFAULT', '"classic"')
@@ -291,30 +324,6 @@ else
   pub_conf_data.set('FREECIV_AI_MOD_LAST', aimodcount)
 endif
 
-if meson.is_cross_build()
-  cross_inc_str = meson.get_external_property('cross_inc_path', '')
-  cross_inc_path = [cross_inc_str]
-  cross_lib_path = [meson.get_external_property('cross_lib_path', '')]
-  crosser = meson.get_external_property('crosser', false)
-
-  # emscripten build is always cross-build, so check it only here
-  if c_compiler.compiles(
-'''#ifndef __EMSCRIPTEN__
-  error fail
-#endif''',
-  name: 'emscripten')
-    emscripten = true
-  else
-    emscripten = false
-  endif
-else
-  cross_inc_str = ''
-  cross_inc_path = []
-  cross_lib_path = []
-  crosser = false
-  emscripten = false
-endif
-
 if get_option('appimage')
   pub_conf_data.set('FREECIV_APPIMAGE', 1)
 endif
@@ -1675,7 +1684,7 @@ server_lib = static_library('fc_server',
   sources: [ verhdr, pack_server,
              tolua.process('server/scripting/tolua_fcdb.pkg',
                            'server/scripting/tolua_server.pkg')],
-  dependencies: lua_dep
+  dependencies: [ lua_dep, dynai_dep ]
   )
 
 endif
diff --git a/meson_options.txt b/meson_options.txt
index 11244ce401..61c0c143e6 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -79,6 +79,11 @@ option('aimodules',
        value: ['classic', 'tex'],
        description: 'AI modules to include to the server')
 
+option('dynaimod',
+       type: 'boolean',
+       value: false,
+       description: 'Support dynamically loadable AI modules')
+
 option('appimage',
        type: 'boolean',
        value: false,
-- 
2.51.0

