summaryrefslogtreecommitdiff
path: root/modules/mono/build_scripts
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/build_scripts')
-rw-r--r--modules/mono/build_scripts/__init__.py0
-rw-r--r--modules/mono/build_scripts/api_solution_build.py66
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py118
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py52
-rw-r--r--modules/mono/build_scripts/make_cs_compressed_header.py62
-rw-r--r--modules/mono/build_scripts/mono_configure.py407
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py118
-rw-r--r--modules/mono/build_scripts/patches/fix-mono-android-tkill.diff70
-rw-r--r--modules/mono/build_scripts/solution_builder.py214
-rw-r--r--modules/mono/build_scripts/tls_configure.py36
10 files changed, 1143 insertions, 0 deletions
diff --git a/modules/mono/build_scripts/__init__.py b/modules/mono/build_scripts/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/modules/mono/build_scripts/__init__.py
diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py
new file mode 100644
index 0000000000..1fe00a3028
--- /dev/null
+++ b/modules/mono/build_scripts/api_solution_build.py
@@ -0,0 +1,66 @@
+# Build the Godot API solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_api_solution(source, target, env):
+ # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+ module_dir = env['module_dir']
+
+ solution_path = os.path.join(module_dir, 'glue/Managed/Generated/GodotSharp.sln')
+
+ if not os.path.isfile(solution_path):
+ raise RuntimeError("Godot API solution not found. Did you forget to run '--generate-mono-glue'?")
+
+ build_config = env['solution_build_config']
+
+ extra_msbuild_args = ['/p:NoWarn=1591'] # Ignore missing documentation warnings
+
+ from .solution_builder import build_solution
+ build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
+
+ # Copy targets
+
+ core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharp', 'bin', build_config))
+ editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharpEditor', 'bin', build_config))
+
+ dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+ if not os.path.isdir(dst_dir):
+ assert not os.path.isfile(dst_dir)
+ os.makedirs(dst_dir)
+
+ def copy_target(target_path):
+ from shutil import copy
+ filename = os.path.basename(target_path)
+
+ src_path = os.path.join(core_src_dir, filename)
+ if not os.path.isfile(src_path):
+ src_path = os.path.join(editor_src_dir, filename)
+
+ copy(src_path, target_path)
+
+ for scons_target in target:
+ copy_target(str(scons_target))
+
+
+def build(env_mono):
+ assert env_mono['tools']
+
+ target_filenames = [
+ 'GodotSharp.dll', 'GodotSharp.pdb', 'GodotSharp.xml',
+ 'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml'
+ ]
+
+ for build_config in ['Debug', 'Release']:
+ output_dir = Dir('#bin').abspath
+ editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', build_config)
+
+ targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
+
+ cmd = env_mono.CommandNoCache(targets, [], build_api_solution,
+ module_dir=os.getcwd(), solution_build_config=build_config)
+ env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
new file mode 100644
index 0000000000..35daa6d307
--- /dev/null
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -0,0 +1,118 @@
+# Build GodotTools solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_godot_tools(source, target, env):
+ # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+ module_dir = env['module_dir']
+
+ solution_path = os.path.join(module_dir, 'editor/GodotTools/GodotTools.sln')
+ build_config = 'Debug' if env['target'] == 'debug' else 'Release'
+
+ from . solution_builder import build_solution, nuget_restore
+ nuget_restore(env, solution_path)
+ build_solution(env, solution_path, build_config)
+
+ # Copy targets
+
+ solution_dir = os.path.abspath(os.path.join(solution_path, os.pardir))
+
+ src_dir = os.path.join(solution_dir, 'GodotTools', 'bin', build_config)
+ dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+ if not os.path.isdir(dst_dir):
+ assert not os.path.isfile(dst_dir)
+ os.makedirs(dst_dir)
+
+ def copy_target(target_path):
+ from shutil import copy
+ filename = os.path.basename(target_path)
+ copy(os.path.join(src_dir, filename), target_path)
+
+ for scons_target in target:
+ copy_target(str(scons_target))
+
+
+def build_godot_tools_project_editor(source, target, env):
+ # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+ module_dir = env['module_dir']
+
+ project_name = 'GodotTools.ProjectEditor'
+
+ csproj_dir = os.path.join(module_dir, 'editor/GodotTools', project_name)
+ csproj_path = os.path.join(csproj_dir, project_name + '.csproj')
+ build_config = 'Debug' if env['target'] == 'debug' else 'Release'
+
+ from . solution_builder import build_solution, nuget_restore
+
+ # Make sure to restore NuGet packages in the project directory for the project to find it
+ nuget_restore(env, os.path.join(csproj_dir, 'packages.config'), '-PackagesDirectory',
+ os.path.join(csproj_dir, 'packages'))
+
+ build_solution(env, csproj_path, build_config)
+
+ # Copy targets
+
+ src_dir = os.path.join(csproj_dir, 'bin', build_config)
+ dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+ if not os.path.isdir(dst_dir):
+ assert not os.path.isfile(dst_dir)
+ os.makedirs(dst_dir)
+
+ def copy_target(target_path):
+ from shutil import copy
+ filename = os.path.basename(target_path)
+ copy(os.path.join(src_dir, filename), target_path)
+
+ for scons_target in target:
+ copy_target(str(scons_target))
+
+
+def build(env_mono):
+ assert env_mono['tools']
+
+ output_dir = Dir('#bin').abspath
+ editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+ editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', 'Debug')
+
+ source_filenames = ['GodotSharp.dll', 'GodotSharpEditor.dll']
+ sources = [os.path.join(editor_api_dir, filename) for filename in source_filenames]
+
+ target_filenames = [
+ 'GodotTools.dll', 'GodotTools.IdeConnection.dll', 'GodotTools.BuildLogger.dll',
+ 'GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll'
+ ]
+
+ if env_mono['target'] == 'debug':
+ target_filenames += [
+ 'GodotTools.pdb', 'GodotTools.IdeConnection.pdb', 'GodotTools.BuildLogger.pdb',
+ 'GodotTools.ProjectEditor.pdb', 'GodotTools.Core.pdb'
+ ]
+
+ targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
+
+ cmd = env_mono.CommandNoCache(targets, sources, build_godot_tools, module_dir=os.getcwd())
+ env_mono.AlwaysBuild(cmd)
+
+
+def build_project_editor_only(env_mono):
+ assert env_mono['tools']
+
+ output_dir = Dir('#bin').abspath
+ editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+
+ target_filenames = ['GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll']
+
+ if env_mono['target'] == 'debug':
+ target_filenames += ['GodotTools.ProjectEditor.pdb', 'GodotTools.Core.pdb']
+
+ targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
+
+ cmd = env_mono.CommandNoCache(targets, [], build_godot_tools_project_editor, module_dir=os.getcwd())
+ env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
new file mode 100644
index 0000000000..8cad204d7b
--- /dev/null
+++ b/modules/mono/build_scripts/make_android_mono_config.py
@@ -0,0 +1,52 @@
+
+def generate_compressed_config(config_src, output_dir):
+ import os.path
+ from compat import byte_to_str
+
+ # Source file
+ with open(os.path.join(output_dir, 'android_mono_config.gen.cpp'), 'w') as cpp:
+ with open(config_src, 'rb') as f:
+ buf = f.read()
+ decompr_size = len(buf)
+ import zlib
+ buf = zlib.compress(buf)
+ compr_size = len(buf)
+
+ bytes_seq_str = ''
+ for i, buf_idx in enumerate(range(compr_size)):
+ if i > 0:
+ bytes_seq_str += ', '
+ bytes_seq_str += byte_to_str(buf[buf_idx])
+
+ cpp.write('''/* THIS FILE IS GENERATED DO NOT EDIT */
+#include "android_mono_config.h"
+
+#ifdef ANDROID_ENABLED
+
+#include "core/io/compression.h"
+#include "core/pool_vector.h"
+
+namespace {
+
+// config
+static const int config_compressed_size = %d;
+static const int config_uncompressed_size = %d;
+static const unsigned char config_compressed_data[] = { %s };
+
+} // namespace
+
+String get_godot_android_mono_config() {
+ PoolVector<uint8_t> data;
+ data.resize(config_uncompressed_size);
+ PoolVector<uint8_t>::Write w = data.write();
+ Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data,
+ config_compressed_size, Compression::MODE_DEFLATE);
+ String s;
+ if (s.parse_utf8((const char *)w.ptr(), data.size())) {
+ ERR_FAIL_V(String());
+ }
+ return s;
+}
+
+#endif // ANDROID_ENABLED
+''' % (compr_size, decompr_size, bytes_seq_str))
diff --git a/modules/mono/build_scripts/make_cs_compressed_header.py b/modules/mono/build_scripts/make_cs_compressed_header.py
new file mode 100644
index 0000000000..ed49db5bb2
--- /dev/null
+++ b/modules/mono/build_scripts/make_cs_compressed_header.py
@@ -0,0 +1,62 @@
+
+def generate_header(src, dst, version_dst):
+ from compat import byte_to_str
+
+ with open(dst, 'w') as header:
+ header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
+ header.write('#ifndef CS_COMPRESSED_H\n')
+ header.write('#define CS_COMPRESSED_H\n\n')
+ header.write('#ifdef TOOLS_ENABLED\n\n')
+ header.write('#include "core/map.h"\n')
+ header.write('#include "core/ustring.h"\n')
+ inserted_files = ''
+ import os
+ latest_mtime = 0
+ cs_file_count = 0
+ for root, _, files in os.walk(src):
+ files = [f for f in files if f.endswith('.cs')]
+ for file in files:
+ cs_file_count += 1
+ filepath = os.path.join(root, file)
+ filepath_src_rel = os.path.relpath(filepath, src)
+ mtime = os.path.getmtime(filepath)
+ latest_mtime = mtime if mtime > latest_mtime else latest_mtime
+ with open(filepath, 'rb') as f:
+ buf = f.read()
+ decompr_size = len(buf)
+ import zlib
+ buf = zlib.compress(buf)
+ compr_size = len(buf)
+ name = str(cs_file_count)
+ header.write('\n')
+ header.write('// ' + filepath_src_rel + '\n')
+ header.write('static const int _cs_' + name + '_compressed_size = ' + str(compr_size) + ';\n')
+ header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decompr_size) + ';\n')
+ header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
+ for i, buf_idx in enumerate(range(compr_size)):
+ if i > 0:
+ header.write(', ')
+ header.write(byte_to_str(buf[buf_idx]))
+ header.write(' };\n')
+ inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
+ 'GodotCsCompressedFile(_cs_' + name + '_compressed_size, ' \
+ '_cs_' + name + '_uncompressed_size, ' \
+ '_cs_' + name + '_compressed));\n'
+ header.write('\nstruct GodotCsCompressedFile\n' '{\n'
+ '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
+ '\n\tGodotCsCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
+ '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
+ '\t\tdata = p_data;\n' '\t}\n' '\n\tGodotCsCompressedFile() {}\n' '};\n'
+ '\nvoid get_compressed_files(Map<String, GodotCsCompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
+ )
+ header.write('\n#endif // TOOLS_ENABLED\n')
+ header.write('\n#endif // CS_COMPRESSED_H\n')
+
+ glue_version = int(latest_mtime) # The latest modified time will do for now
+
+ with open(version_dst, 'w') as version_header:
+ version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
+ version_header.write('#ifndef CS_GLUE_VERSION_H\n')
+ version_header.write('#define CS_GLUE_VERSION_H\n\n')
+ version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
+ version_header.write('\n#endif // CS_GLUE_VERSION_H\n')
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
new file mode 100644
index 0000000000..4c1ebd8d74
--- /dev/null
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -0,0 +1,407 @@
+import os
+import os.path
+import sys
+import subprocess
+
+from SCons.Script import Dir, Environment
+
+if os.name == 'nt':
+ from . import mono_reg_utils as monoreg
+
+
+android_arch_dirs = {
+ 'armv7': 'armeabi-v7a',
+ 'arm64v8': 'arm64-v8a',
+ 'x86': 'x86',
+ 'x86_64': 'x86_64'
+}
+
+
+def get_android_out_dir(env):
+ return os.path.join(Dir('#platform/android/java/lib/libs').abspath,
+ 'release' if env['target'] == 'release' else 'debug',
+ android_arch_dirs[env['android_arch']])
+
+
+def find_file_in_dir(directory, files, prefix='', extension=''):
+ if not extension.startswith('.'):
+ extension = '.' + extension
+ for curfile in files:
+ if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
+ return curfile
+ return ''
+
+
+def copy_file(src_dir, dst_dir, name):
+ from shutil import copy
+
+ src_path = os.path.join(Dir(src_dir).abspath, name)
+ dst_dir = Dir(dst_dir).abspath
+
+ if not os.path.isdir(dst_dir):
+ os.makedirs(dst_dir)
+
+ copy(src_path, dst_dir)
+
+
+def configure(env, env_mono):
+ bits = env['bits']
+ is_android = env['platform'] == 'android'
+
+ tools_enabled = env['tools']
+ mono_static = env['mono_static']
+ copy_mono_root = env['copy_mono_root']
+
+ mono_prefix = env['mono_prefix']
+
+ mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
+
+ is_travis = os.environ.get('TRAVIS') == 'true'
+
+ if is_travis:
+ # Travis CI may have a Mono version lower than 5.12
+ env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS'])
+
+ if is_android and not env['android_arch'] in android_arch_dirs:
+ raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
+
+ if is_android and tools_enabled:
+ # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
+ raise RuntimeError('This module does not currently support building for android with tools enabled')
+
+ if is_android and mono_static:
+ # When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0
+ raise RuntimeError('Linking Mono statically is not currently supported on Android')
+
+ if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
+ print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
+
+ if env['platform'] == 'windows':
+ mono_root = mono_prefix
+
+ if not mono_root and os.name == 'nt':
+ mono_root = monoreg.find_mono_root_dir(bits)
+
+ if not mono_root:
+ raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
+
+ print('Found Mono root directory: ' + mono_root)
+
+ mono_lib_path = os.path.join(mono_root, 'lib')
+
+ env.Append(LIBPATH=mono_lib_path)
+ env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
+
+ if mono_static:
+ lib_suffix = Environment()['LIBSUFFIX']
+
+ if env.msvc:
+ mono_static_lib_name = 'libmono-static-sgen'
+ else:
+ mono_static_lib_name = 'libmonosgen-2.0'
+
+ if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)):
+ raise RuntimeError('Could not find static mono library in: ' + mono_lib_path)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix)
+
+ env.Append(LINKFLAGS='Mincore' + lib_suffix)
+ env.Append(LINKFLAGS='msvcrt' + lib_suffix)
+ env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
+ env.Append(LINKFLAGS='Psapi' + lib_suffix)
+ else:
+ env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
+
+ env.Append(LIBS=['psapi'])
+ env.Append(LIBS=['version'])
+ else:
+ mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+
+ if not mono_lib_name:
+ raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ else:
+ env.Append(LIBS=[mono_lib_name])
+
+ mono_bin_path = os.path.join(mono_root, 'bin')
+
+ mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
+
+ if not mono_dll_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
+
+ copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll')
+ else:
+ is_apple = (sys.platform == 'darwin' or "osxcross" in env)
+
+ sharedlib_ext = '.dylib' if is_apple else '.so'
+
+ mono_root = mono_prefix
+ mono_lib_path = ''
+ mono_so_name = ''
+
+ if not mono_root and is_android:
+ raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
+
+ if not mono_root and is_apple:
+ # Try with some known directories under OSX
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono']
+ for hint_dir in hint_dirs:
+ if os.path.isdir(hint_dir):
+ mono_root = hint_dir
+ break
+
+ # We can't use pkg-config to link mono statically,
+ # but we can still use it to find the mono root directory
+ if not mono_root and mono_static:
+ mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
+ if not mono_root:
+ raise RuntimeError("Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " + \
+ "specify one manually with the 'mono_prefix' SCons parameter")
+
+ if mono_root:
+ print('Found Mono root directory: ' + mono_root)
+
+ mono_lib_path = os.path.join(mono_root, 'lib')
+
+ env.Append(LIBPATH=mono_lib_path)
+ env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
+
+ mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a')
+
+ if not mono_lib:
+ raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+
+ env_mono.Append(CPPDEFINES=['_REENTRANT'])
+
+ if mono_static:
+ mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
+
+ if is_apple:
+ env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
+ else:
+ env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
+ else:
+ env.Append(LIBS=[mono_lib])
+
+ if is_apple:
+ env.Append(LIBS=['iconv', 'pthread'])
+ elif is_android:
+ pass # Nothing
+ else:
+ env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
+
+ if not mono_static:
+ mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+
+ if not mono_so_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
+
+ copy_file(mono_lib_path, '#bin', 'lib' + mono_so_name + sharedlib_ext)
+ else:
+ assert not mono_static
+
+ # TODO: Add option to force using pkg-config
+ print('Mono root directory not found. Using pkg-config instead')
+
+ env.ParseConfig('pkg-config monosgen-2 --libs')
+ env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
+
+ tmpenv = Environment()
+ tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
+ tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+
+ for hint_dir in tmpenv['LIBPATH']:
+ name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ if name_found:
+ mono_lib_path = hint_dir
+ mono_so_name = name_found
+ break
+
+ if not mono_so_name:
+ raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
+
+ if not mono_static:
+ libs_output_dir = get_android_out_dir(env) if is_android else '#bin'
+ copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext)
+
+ env.Append(LINKFLAGS='-rdynamic')
+
+ if not tools_enabled and not is_android:
+ if not mono_root:
+ mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+
+ make_template_dir(env, mono_root)
+ elif not tools_enabled and is_android:
+ # Compress Android Mono Config
+ from . import make_android_mono_config
+ config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config')
+ make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
+
+ # Copy the required shared libraries
+ copy_mono_shared_libs(env, mono_root, None)
+
+ if copy_mono_root:
+ if not mono_root:
+ mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+
+ if tools_enabled:
+ copy_mono_root_files(env, mono_root)
+ else:
+ print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.")
+
+
+def make_template_dir(env, mono_root):
+ from shutil import rmtree
+
+ platform = env['platform']
+ target = env['target']
+
+ template_dir_name = ''
+
+ if platform in ['windows', 'osx', 'x11', 'android']:
+ template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
+ else:
+ assert False
+
+ output_dir = Dir('#bin').abspath
+ template_dir = os.path.join(output_dir, template_dir_name)
+
+ template_mono_root_dir = os.path.join(template_dir, 'Mono')
+
+ if os.path.isdir(template_mono_root_dir):
+ rmtree(template_mono_root_dir) # Clean first
+
+ # Copy etc/mono/
+
+ template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
+ copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
+
+ # Copy the required shared libraries
+
+ copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
+
+
+def copy_mono_root_files(env, mono_root):
+ from glob import glob
+ from shutil import copy
+ from shutil import rmtree
+
+ if not mono_root:
+ raise RuntimeError('Mono installation directory not found')
+
+ output_dir = Dir('#bin').abspath
+ editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono')
+
+ if os.path.isdir(editor_mono_root_dir):
+ rmtree(editor_mono_root_dir) # Clean first
+
+ # Copy etc/mono/
+
+ editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono')
+ copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform'])
+
+ # Copy the required shared libraries
+
+ copy_mono_shared_libs(env, mono_root, editor_mono_root_dir)
+
+ # Copy framework assemblies
+
+ mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5')
+ mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades')
+
+ editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5')
+ editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades')
+
+ if not os.path.isdir(editor_mono_framework_dir):
+ os.makedirs(editor_mono_framework_dir)
+ if not os.path.isdir(editor_mono_framework_facades_dir):
+ os.makedirs(editor_mono_framework_facades_dir)
+
+ for assembly in glob(os.path.join(mono_framework_dir, '*.dll')):
+ copy(assembly, editor_mono_framework_dir)
+ for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')):
+ copy(assembly, editor_mono_framework_facades_dir)
+
+
+def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
+ from distutils.dir_util import copy_tree
+ from glob import glob
+ from shutil import copy
+
+ if not os.path.isdir(target_mono_config_dir):
+ os.makedirs(target_mono_config_dir)
+
+ mono_etc_dir = os.path.join(mono_root, 'etc', 'mono')
+ if not os.path.isdir(mono_etc_dir):
+ mono_etc_dir = ''
+ etc_hint_dirs = []
+ if platform != 'windows':
+ etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono']
+ if 'MONO_CFG_DIR' in os.environ:
+ etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')]
+ for etc_hint_dir in etc_hint_dirs:
+ if os.path.isdir(etc_hint_dir):
+ mono_etc_dir = etc_hint_dir
+ break
+ if not mono_etc_dir:
+ raise RuntimeError('Mono installation etc directory not found')
+
+ copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
+ copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
+ copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
+ if os.path.isdir(os.path.join(mono_etc_dir, 'mconfig')):
+ copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
+
+ for file in glob(os.path.join(mono_etc_dir, '*')):
+ if os.path.isfile(file):
+ copy(file, target_mono_config_dir)
+
+
+def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
+ from shutil import copy
+
+ def copy_if_exists(src, dst):
+ if os.path.isfile(src):
+ copy(src, dst)
+
+ platform = env['platform']
+
+ if platform == 'windows':
+ target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
+
+ if not os.path.isdir(target_mono_bin_dir):
+ os.makedirs(target_mono_bin_dir)
+
+ copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir)
+ else:
+ target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib')
+
+ if not os.path.isdir(target_mono_lib_dir):
+ os.makedirs(target_mono_lib_dir)
+
+ if platform == 'osx':
+ # TODO: Make sure nothing is missing
+ copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir)
+ elif platform == 'x11' or platform == 'android':
+ lib_file_names = [lib_name + '.so' for lib_name in [
+ 'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper',
+ 'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW'
+ ]]
+
+ for lib_file_name in lib_file_names:
+ copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
+
+
+def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
+ tmpenv = Environment()
+ tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
+ tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+ for hint_dir in tmpenv['LIBPATH']:
+ name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
+ return os.path.join(hint_dir, '..')
+ return ''
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
new file mode 100644
index 0000000000..b2c48f0a61
--- /dev/null
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -0,0 +1,118 @@
+import os
+import platform
+
+from compat import decode_utf8
+
+if os.name == 'nt':
+ import sys
+ if sys.version_info < (3,):
+ import _winreg as winreg
+ else:
+ import winreg
+
+
+def _reg_open_key(key, subkey):
+ try:
+ return winreg.OpenKey(key, subkey)
+ except (WindowsError, OSError):
+ if platform.architecture()[0] == '32bit':
+ bitness_sam = winreg.KEY_WOW64_64KEY
+ else:
+ bitness_sam = winreg.KEY_WOW64_32KEY
+ return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
+
+
+def _reg_open_key_bits(key, subkey, bits):
+ sam = winreg.KEY_READ
+
+ if platform.architecture()[0] == '32bit':
+ if bits == '64':
+ # Force 32bit process to search in 64bit registry
+ sam |= winreg.KEY_WOW64_64KEY
+ else:
+ if bits == '32':
+ # Force 64bit process to search in 32bit registry
+ sam |= winreg.KEY_WOW64_32KEY
+
+ return winreg.OpenKey(key, subkey, 0, sam)
+
+
+def _find_mono_in_reg(subkey, bits):
+ try:
+ with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
+ value = winreg.QueryValueEx(hKey, 'SdkInstallRoot')[0]
+ return value
+ except (WindowsError, OSError):
+ return None
+
+
+def _find_mono_in_reg_old(subkey, bits):
+ try:
+ with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
+ default_clr = winreg.QueryValueEx(hKey, 'DefaultCLR')[0]
+ if default_clr:
+ return _find_mono_in_reg(subkey + '\\' + default_clr, bits)
+ return None
+ except (WindowsError, EnvironmentError):
+ return None
+
+
+def find_mono_root_dir(bits):
+ root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits)
+ if root_dir is not None:
+ return str(root_dir)
+ root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits)
+ if root_dir is not None:
+ return str(root_dir)
+ return ''
+
+
+def find_msbuild_tools_path_reg():
+ import subprocess
+
+ vswhere = os.getenv('PROGRAMFILES(X86)')
+ if not vswhere:
+ vswhere = os.getenv('PROGRAMFILES')
+ vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe'
+
+ vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild']
+
+ try:
+ lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
+
+ for line in lines:
+ parts = decode_utf8(line).split(':', 1)
+
+ if len(parts) < 2 or parts[0] != 'installationPath':
+ continue
+
+ val = parts[1].strip()
+
+ if not val:
+ raise ValueError('Value of `installationPath` entry is empty')
+
+ # Since VS2019, the directory is simply named "Current"
+ msbuild_dir = os.path.join(val, 'MSBuild\\Current\\Bin')
+ if os.path.isdir(msbuild_dir):
+ return msbuild_dir
+
+ # Directory name "15.0" is used in VS 2017
+ return os.path.join(val, 'MSBuild\\15.0\\Bin')
+
+ raise ValueError('Cannot find `installationPath` entry')
+ except ValueError as e:
+ print('Error reading output from vswhere: ' + e.message)
+ except WindowsError:
+ pass # Fine, vswhere not found
+ except (subprocess.CalledProcessError, OSError):
+ pass
+
+ # Try to find 14.0 in the Registry
+
+ try:
+ subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0'
+ with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
+ value = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')[0]
+ return value
+ except (WindowsError, OSError):
+ return ''
diff --git a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff
new file mode 100644
index 0000000000..05f8dcadcc
--- /dev/null
+++ b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff
@@ -0,0 +1,70 @@
+diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h
+index e2bdf13ac3e..f962200ba4e 100644
+--- a/libgc/include/private/gcconfig.h
++++ b/libgc/include/private/gcconfig.h
+@@ -2255,6 +2255,14 @@
+ # define GETPAGESIZE() getpagesize()
+ # endif
+
++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
++ || defined(ARM32) || defined(I386) /* but not x32 */)
++ /* tkill() exists only on arm32/mips(32)/x86. */
++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
++# define USE_TKILL_ON_ANDROID
++#endif
++
+ # if defined(SUNOS5) || defined(DRSNX) || defined(UTS4)
+ /* OS has SVR4 generic features. Probably others also qualify. */
+ # define SVR4
+diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c
+index f93ce26b562..4a49a6d578c 100644
+--- a/libgc/pthread_stop_world.c
++++ b/libgc/pthread_stop_world.c
+@@ -336,7 +336,7 @@ void GC_push_all_stacks()
+ pthread_t GC_stopping_thread;
+ int GC_stopping_pid;
+
+-#ifdef HOST_ANDROID
++#ifdef USE_TKILL_ON_ANDROID
+ static
+ int android_thread_kill(pid_t tid, int sig)
+ {
+diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
+index ad9b8823f8f..3542b32b540 100644
+--- a/mono/metadata/threads.c
++++ b/mono/metadata/threads.c
+@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
+ #include <zircon/syscalls.h>
+ #endif
+
+-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
+-#define USE_TKILL_ON_ANDROID 1
++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
++ || defined(ARM32) || defined(I386) /* but not x32 */)
++ /* tkill() exists only on arm32/mips(32)/x86. */
++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
++# define USE_TKILL_ON_ANDROID
+ #endif
+
+ #ifdef HOST_ANDROID
+diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c
+index 3e4bf93de5f..79c9f731fe7 100644
+--- a/mono/utils/mono-threads-posix.c
++++ b/mono/utils/mono-threads-posix.c
+@@ -31,8 +31,12 @@
+
+ #include <errno.h>
+
+-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
+-#define USE_TKILL_ON_ANDROID 1
++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
++ || defined(ARM32) || defined(I386) /* but not x32 */)
++ /* tkill() exists only on arm32/mips(32)/x86. */
++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
++# define USE_TKILL_ON_ANDROID
+ #endif
+
+ #ifdef USE_TKILL_ON_ANDROID
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
new file mode 100644
index 0000000000..d1529a64d2
--- /dev/null
+++ b/modules/mono/build_scripts/solution_builder.py
@@ -0,0 +1,214 @@
+
+import os
+
+
+verbose = False
+
+
+def find_nuget_unix():
+ import os
+
+ if 'NUGET_PATH' in os.environ:
+ hint_path = os.environ['NUGET_PATH']
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ hint_path = os.path.join(hint_path, 'nuget')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+ import os.path
+ import sys
+
+ hint_dirs = ['/opt/novell/mono/bin']
+ if sys.platform == 'darwin':
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
+
+ for hint_dir in hint_dirs:
+ hint_path = os.path.join(hint_dir, 'nuget')
+ if os.path.isfile(hint_path):
+ return hint_path
+ elif os.path.isfile(hint_path + '.exe'):
+ return hint_path + '.exe'
+
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, 'nuget')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
+ return hint_path + '.exe'
+
+ return None
+
+
+def find_nuget_windows(env):
+ import os
+
+ if 'NUGET_PATH' in os.environ:
+ hint_path = os.environ['NUGET_PATH']
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ hint_path = os.path.join(hint_path, 'nuget.exe')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+ from . mono_reg_utils import find_mono_root_dir
+
+ mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
+
+ if mono_root:
+ mono_bin_dir = os.path.join(mono_root, 'bin')
+ nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat')
+
+ if os.path.isfile(nuget_mono):
+ return nuget_mono
+
+ # Standalone NuGet
+
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, 'nuget.exe')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+ return None
+
+
+def find_msbuild_unix(filename):
+ import os.path
+ import sys
+
+ hint_dirs = ['/opt/novell/mono/bin']
+ if sys.platform == 'darwin':
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
+
+ for hint_dir in hint_dirs:
+ hint_path = os.path.join(hint_dir, filename)
+ if os.path.isfile(hint_path):
+ return hint_path
+ elif os.path.isfile(hint_path + '.exe'):
+ return hint_path + '.exe'
+
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, filename)
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
+ return hint_path + '.exe'
+
+ return None
+
+
+def find_msbuild_windows(env):
+ from . mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
+
+ mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
+
+ if not mono_root:
+ raise RuntimeError('Cannot find mono root directory')
+
+ mono_bin_dir = os.path.join(mono_root, 'bin')
+ msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
+
+ msbuild_tools_path = find_msbuild_tools_path_reg()
+
+ if msbuild_tools_path:
+ return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), {})
+
+ if os.path.isfile(msbuild_mono):
+ # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
+ # building with Mono's MSBuild. They must point to the batch files
+ # in Mono's bin directory to make sure they are executed with Mono.
+ mono_msbuild_env = {
+ 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
+ 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
+ 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat')
+ }
+ return (msbuild_mono, mono_msbuild_env)
+
+ return None
+
+
+def run_command(command, args, env_override=None, name=None):
+ def cmd_args_to_str(cmd_args):
+ return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
+
+ args = [command] + args
+
+ if name is None:
+ name = os.path.basename(command)
+
+ if verbose:
+ print("Running '%s': %s" % (name, cmd_args_to_str(args)))
+
+ import subprocess
+ try:
+ if env_override is None:
+ subprocess.check_call(args)
+ else:
+ subprocess.check_call(args, env=env_override)
+ except subprocess.CalledProcessError as e:
+ raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
+
+
+def nuget_restore(env, *args):
+ global verbose
+ verbose = env['verbose']
+
+ # Find NuGet
+ nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix()
+ if nuget_path is None:
+ raise RuntimeError('Cannot find NuGet executable')
+
+ print('NuGet path: ' + nuget_path)
+
+ # Do NuGet restore
+ run_command(nuget_path, ['restore'] + list(args), name='nuget restore')
+
+
+def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
+ global verbose
+ verbose = env['verbose']
+
+ msbuild_env = os.environ.copy()
+
+ # Needed when running from Developer Command Prompt for VS
+ if 'PLATFORM' in msbuild_env:
+ del msbuild_env['PLATFORM']
+
+ # Find MSBuild
+ if os.name == 'nt':
+ msbuild_info = find_msbuild_windows(env)
+ if msbuild_info is None:
+ raise RuntimeError('Cannot find MSBuild executable')
+ msbuild_path = msbuild_info[0]
+ msbuild_env.update(msbuild_info[1])
+ else:
+ msbuild_path = find_msbuild_unix('msbuild')
+ if msbuild_path is None:
+ xbuild_fallback = env['xbuild_fallback']
+
+ if xbuild_fallback and os.name == 'nt':
+ print('Option \'xbuild_fallback\' not supported on Windows')
+ xbuild_fallback = False
+
+ if xbuild_fallback:
+ print('Cannot find MSBuild executable, trying with xbuild')
+ print('Warning: xbuild is deprecated')
+
+ msbuild_path = find_msbuild_unix('xbuild')
+
+ if msbuild_path is None:
+ raise RuntimeError('Cannot find xbuild executable')
+ else:
+ raise RuntimeError('Cannot find MSBuild executable')
+
+ print('MSBuild path: ' + msbuild_path)
+
+ # Build solution
+
+ msbuild_args = [solution_path, '/p:Configuration=' + build_config]
+ msbuild_args += extra_msbuild_args
+
+ run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name='msbuild')
diff --git a/modules/mono/build_scripts/tls_configure.py b/modules/mono/build_scripts/tls_configure.py
new file mode 100644
index 0000000000..622280b00b
--- /dev/null
+++ b/modules/mono/build_scripts/tls_configure.py
@@ -0,0 +1,36 @@
+from __future__ import print_function
+
+def supported(result):
+ return 'supported' if result else 'not supported'
+
+
+def check_cxx11_thread_local(conf):
+ print('Checking for `thread_local` support...', end=" ")
+ result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_declspec_thread(conf):
+ print('Checking for `__declspec(thread)` support...', end=" ")
+ result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_gcc___thread(conf):
+ print('Checking for `__thread` support...', end=" ")
+ result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def configure(conf):
+ if check_cxx11_thread_local(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
+ else:
+ if conf.env.msvc:
+ if check_declspec_thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
+ elif check_gcc___thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])