c5bd0c37ce
- Refactored all builder (make_*) functions into separate Python modules along to the build tree - Introduced utility function to wrap all invocations on Windows, but does not change it elsewhere - Introduced stub to use the builders module as a stand alone script and invoke a selected function There is a problem with file handles related to writing generated content (*.gen.h and *.gen.cpp) on Windows, which randomly causes a SHARING VIOLATION error to the compiler resulting in flaky builds. Running all such content generators in a new subprocess instead of directly inside the build script works around the issue. Yes, I tried the multiprocessing module. It did not work due to conflict with SCons on cPickle. Suggested workaround did not fully work either. Using the run_in_subprocess wrapper on osx and x11 platforms as well for consistency. In case of running a cross-compilation on Windows they would still be used, but likely it will not happen in practice. What counts is that the build itself is running on which platform, not the target platform. Some generated files are written directly in an SConstruct or SCsub file, before the parallel build starts. They don't need to be written in a subprocess, apparently, so I left them as is.
246 lines
9.0 KiB
Python
246 lines
9.0 KiB
Python
#!/usr/bin/env python
|
|
|
|
Import('env')
|
|
Import('env_modules')
|
|
|
|
env_mono = env_modules.Clone()
|
|
|
|
# TODO move functions to their own modules
|
|
|
|
def make_cs_files_header(src, dst):
|
|
from compat import byte_to_str
|
|
|
|
with open(dst, 'w') as header:
|
|
header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
|
|
header.write('#ifndef _CS_FILES_DATA_H\n')
|
|
header.write('#define _CS_FILES_DATA_H\n\n')
|
|
header.write('#include "map.h"\n')
|
|
header.write('#include "ustring.h"\n')
|
|
inserted_files = ''
|
|
import os
|
|
for file in os.listdir(src):
|
|
if file.endswith('.cs'):
|
|
with open(os.path.join(src, file), 'rb') as f:
|
|
buf = f.read()
|
|
decomp_size = len(buf)
|
|
import zlib
|
|
buf = zlib.compress(buf)
|
|
name = os.path.splitext(file)[0]
|
|
header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
|
|
header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
|
|
header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
|
|
for i, buf_idx in enumerate(range(len(buf))):
|
|
if i > 0:
|
|
header.write(', ')
|
|
header.write(byte_to_str(buf[buf_idx]))
|
|
inserted_files += '\tr_files.insert("' + file + '", ' \
|
|
'CompressedFile(_cs_' + name + '_compressed_size, ' \
|
|
'_cs_' + name + '_uncompressed_size, ' \
|
|
'_cs_' + name + '_compressed));\n'
|
|
header.write(' };\n')
|
|
version_file = os.path.join(src, 'VERSION.txt')
|
|
with open(version_file, 'r') as content_file:
|
|
try:
|
|
glue_version = int(content_file.read()) # make sure the format is valid
|
|
header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
|
|
except ValueError:
|
|
raise ValueError('Invalid C# glue version in: ' + version_file)
|
|
header.write('\nstruct CompressedFile\n' '{\n'
|
|
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
|
|
'\n\tCompressedFile(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\tCompressedFile() {}\n' '};\n'
|
|
'\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
|
|
)
|
|
header.write('#endif // _CS_FILES_DATA_H')
|
|
|
|
|
|
env_mono.add_source_files(env.modules_sources, '*.cpp')
|
|
env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp')
|
|
env_mono.add_source_files(env.modules_sources, 'utils/*.cpp')
|
|
|
|
if env['tools']:
|
|
env_mono.add_source_files(env.modules_sources, 'editor/*.cpp')
|
|
# NOTE: It is safe to generate this file here, since this is still execute serially
|
|
make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h')
|
|
|
|
vars = Variables()
|
|
vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
|
|
vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
|
|
vars.Update(env_mono)
|
|
|
|
# Glue sources
|
|
if env_mono['mono_glue']:
|
|
env_mono.add_source_files(env.modules_sources, 'glue/*.cpp')
|
|
else:
|
|
env_mono.Append(CPPDEFINES=['MONO_GLUE_DISABLED'])
|
|
|
|
if ARGUMENTS.get('yolo_copy', False):
|
|
env_mono.Append(CPPDEFINES=['YOLO_COPY'])
|
|
|
|
# Configure TLS checks
|
|
|
|
import tls_configure
|
|
conf = Configure(env_mono)
|
|
tls_configure.configure(conf)
|
|
env_mono = conf.Finish()
|
|
|
|
|
|
# Build GodotSharpTools solution
|
|
|
|
|
|
import os
|
|
|
|
|
|
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():
|
|
import mono_reg_utils as monoreg
|
|
|
|
bits = env['bits']
|
|
|
|
if bits == '32':
|
|
if os.getenv('MONO32_PREFIX'):
|
|
mono_root = os.getenv('MONO32_PREFIX')
|
|
else:
|
|
mono_root = monoreg.find_mono_root_dir(bits)
|
|
else:
|
|
if os.getenv('MONO64_PREFIX'):
|
|
mono_root = os.getenv('MONO64_PREFIX')
|
|
else:
|
|
mono_root = monoreg.find_mono_root_dir(bits)
|
|
|
|
if not mono_root:
|
|
raise RuntimeError('Cannot find mono root directory')
|
|
|
|
framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
|
|
mono_bin_dir = os.path.join(mono_root, 'bin')
|
|
msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
|
|
|
|
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, framework_path, mono_msbuild_env)
|
|
|
|
msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
|
|
|
|
if msbuild_tools_path:
|
|
return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
|
|
|
|
return None
|
|
|
|
|
|
def mono_build_solution(source, target, env):
|
|
import subprocess
|
|
import mono_reg_utils as monoreg
|
|
from shutil import copyfile
|
|
|
|
framework_path = ''
|
|
|
|
msbuild_env = os.environ.copy()
|
|
|
|
# Needed when running from Developer Command Prompt for VS
|
|
if 'PLATFORM' in msbuild_env:
|
|
del msbuild_env['PLATFORM']
|
|
|
|
if os.name == 'nt':
|
|
msbuild_info = find_msbuild_windows()
|
|
if msbuild_info is None:
|
|
raise RuntimeError('Cannot find MSBuild executable')
|
|
msbuild_path = msbuild_info[0]
|
|
framework_path = msbuild_info[1]
|
|
msbuild_env.update(msbuild_info[2])
|
|
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_config = 'Release'
|
|
|
|
msbuild_args = [
|
|
msbuild_path,
|
|
os.path.abspath(str(source[0])),
|
|
'/p:Configuration=' + build_config,
|
|
]
|
|
|
|
if framework_path:
|
|
msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
|
|
|
|
try:
|
|
subprocess.check_call(msbuild_args, env=msbuild_env)
|
|
except subprocess.CalledProcessError:
|
|
raise RuntimeError('GodotSharpTools build failed')
|
|
|
|
src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config))
|
|
dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
|
|
|
|
if not os.path.isdir(dst_dir):
|
|
if os.path.exists(dst_dir):
|
|
raise RuntimeError('Target directory is a file')
|
|
os.makedirs(dst_dir)
|
|
|
|
asm_file = 'GodotSharpTools.dll'
|
|
|
|
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
|
|
|
|
output_dir = Dir('#bin').abspath
|
|
assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
|
|
|
|
mono_sln_builder = Builder(action=mono_build_solution)
|
|
env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
|
|
env_mono.MonoBuildSolution(
|
|
os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'),
|
|
'editor/GodotSharpTools/GodotSharpTools.sln'
|
|
)
|
|
|
|
if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir):
|
|
rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir)
|
|
env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir})
|