418 lines
14 KiB
Python
418 lines
14 KiB
Python
import os.path as path
|
|
import os
|
|
import subprocess
|
|
|
|
# Build options
|
|
opts = Variables(None, ARGUMENTS)
|
|
|
|
opts.Add(EnumVariable('OS', "Target platform", str(Platform()), ('darwin', 'win32', 'posix')))
|
|
opts.Add('toolset', "Toolset to pass to the SCons builder", 'default')
|
|
opts.Add(BoolVariable('debug', "Build with debug symbols and no optimization", False))
|
|
opts.Add(EnumVariable('bits', "Build for 32-bit or 64-bit architectures", '32', ('32', '64')))
|
|
|
|
# Compiler configuration
|
|
opts.Add("CXX", "C++ compiler")
|
|
opts.Add("CC", "C compiler")
|
|
opts.Add("LINK", "Linker")
|
|
opts.Add("CCFLAGS", "Custom flags for both the C and C++ compilers")
|
|
opts.Add("CXXFLAGS", "Custom flags for the C++ compiler")
|
|
opts.Add("CFLAGS", "Custom flags for the C compiler")
|
|
opts.Add("LINKFLAGS", "Custom flags for the linker")
|
|
|
|
# Initialize environment with options and full user environment
|
|
env = Environment(variables=opts, ENV=os.environ)
|
|
Help(opts.GenerateHelpText(env))
|
|
|
|
platform = env['OS']
|
|
toolset = env['toolset']
|
|
arch = 'x86_64' if (env['bits'] == '64') else 'x86'
|
|
|
|
# Update env based on options
|
|
env.Replace(TARGET_ARCH=arch)
|
|
env.Replace(tools=[toolset])
|
|
|
|
# Check for platform support
|
|
if platform not in ("darwin", "win32", "posix"):
|
|
print("Sorry, your platform is not supported.")
|
|
print("Platform is:", platform)
|
|
print("Specify OS=<your-platform> if you believe this is incorrect.")
|
|
print("(Supported platforms are: darwin, win32, posix)")
|
|
Exit(1)
|
|
print('Building for:', platform)
|
|
print('Using toolchain:', toolset)
|
|
print('C++ compiler:', env['CXX'])
|
|
|
|
env.VariantDir('#build/obj', 'src')
|
|
env.VariantDir('#build/obj/test', 'test')
|
|
env.VariantDir('#build/obj/test/deps', 'deps')
|
|
|
|
if env['debug']:
|
|
env.Append(CCFLAGS=['-g','-o0'])
|
|
|
|
# This command generates the header with git revision information
|
|
def gen_gitrev(env, target, source):
|
|
revid = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True);
|
|
fulltag = subprocess.check_output(["git", "tag", "--sort=v:refname"], text=True).split('\n')[-1]
|
|
tagrev = subprocess.check_output(["git", "rev-parse", fulltag], text=True) if fulltag else ""
|
|
with open(target[0].path, 'w') as gitrev_hpp:
|
|
print(file=gitrev_hpp)
|
|
print('#define GIT_REVISION "' + revid[0:7] + '"', file=gitrev_hpp)
|
|
print('#define GIT_TAG "' + fulltag + '"', file=gitrev_hpp)
|
|
print('#define GIT_TAG_REVISION "' + tagrev[0:7] + '"', file=gitrev_hpp)
|
|
print(file=gitrev_hpp)
|
|
if path.exists(".git"):
|
|
git_refs = ['.git/HEAD']
|
|
with open('.git/HEAD') as git_head:
|
|
git_head = git_head.read().split()
|
|
if git_head[0] == 'ref:':
|
|
git_refs.append(path.join('.git', git_head[1]))
|
|
env.Command('src/tools/gitrev.hpp', git_refs, gen_gitrev)
|
|
else:
|
|
# Zipped source downloads from github do not include the repo (probably a good thing)
|
|
# TODO: This does not work on Windows
|
|
env.Command('src/tools/gitrev.hpp', '', r"""
|
|
echo -e "\n#define GIT_REVISION \"\"\n#define GIT_TAG \"\"\n#define GIT_TAG_REVISION \"\"\n" > #TARGET
|
|
""")
|
|
|
|
if platform == "darwin":
|
|
env.Append(CXXFLAGS=["-std=c++11","-stdlib=libc++","-include","global.hpp"], RPATH='../Frameworks')
|
|
env["CC"] = 'clang'
|
|
env["CXX"] = 'clang++'
|
|
env.Append(BUILDERS={
|
|
'Nib': Builder(
|
|
action = "ibtool --compile $TARGET $SOURCE",
|
|
suffix = ".nib",
|
|
src_suffix = ".xib"
|
|
)
|
|
}, LIBPATH=Split("""
|
|
/usr/lib
|
|
/usr/local/lib
|
|
"""), CPPPATH=Split("""
|
|
/usr/include
|
|
/usr/local/include
|
|
"""), FRAMEWORKPATH=Split("""
|
|
{SDKROOT}/System/Library/Frameworks
|
|
{SDKROOT}/Library/Frameworks
|
|
{HOME}/Library/Frameworks
|
|
""".format(HOME = os.environ['HOME'], SDKROOT = os.environ['SDKROOT'])))
|
|
def build_app_package(env, source, build_dir, info):
|
|
source_name = source[0].name
|
|
pkg_path = path.join(build_dir, "%s.app/Contents/" % source_name)
|
|
nib = env.Nib(info['nib'].replace('rsrc', 'build/obj'), info['nib'])
|
|
icons = [path.join("#rsrc/icons/mac/", x + ".icns") for x in Split(info['icons'])]
|
|
env.Install(path.join(pkg_path, "MacOS"), source)
|
|
env.Install(path.join(pkg_path, "Resources"), icons + nib)
|
|
env.InstallAs(path.join(pkg_path, "Info.plist"), info['plist'])
|
|
env.Command(path.join(pkg_path, 'PkgInfo'), '', action="echo 'APPL%s' > $TARGET" % info['creator'])
|
|
def change_install_name(lib, source, target):
|
|
subprocess.call(["install_name_tool", "-change", source, target, lib])
|
|
system_prefixes = ('/System', '/usr/lib')
|
|
def is_user_lib(lib):
|
|
return all(not lib.startswith(x) for x in system_prefixes)
|
|
def get_deps_for(source):
|
|
deps = subprocess.check_output(['otool', '-L', source]).splitlines()[1:]
|
|
deps = map(lambda s: s.decode('utf-8'), deps)
|
|
deps = list(map(str.strip, deps))
|
|
deps = list(filter(is_user_lib, deps))
|
|
deps = [x.split()[0] for x in deps]
|
|
return deps
|
|
def check_deps(source):
|
|
direct_deps = get_deps_for(source)
|
|
deps = set()
|
|
for i in range(len(direct_deps)):
|
|
dep = direct_deps[i]
|
|
if dep.startswith('@rpath/'):
|
|
direct_deps[i] = dep = dep.split('/', 1)[1]
|
|
if dep.startswith('../Frameworks/'):
|
|
direct_deps[i] = dep = dep.split('/', 2)[2]
|
|
else:
|
|
pass#change_install_name(dep, path.join('@rpath', dep), source)
|
|
if dep == source:
|
|
continue
|
|
deps.add(dep)
|
|
return deps
|
|
def split_path(lib):
|
|
while '.' not in path.basename(lib):
|
|
lib = path.dirname(lib)
|
|
return path.dirname(lib), path.basename(lib)
|
|
def bundle_libraries_for(target, source, env):
|
|
if not path.exists(target[0].path):
|
|
Execute(Mkdir(target))
|
|
deps = check_deps(source[0].path)
|
|
for dep in deps:
|
|
if 'framework' in dep:
|
|
paths = Split(env["FRAMEWORKPATH"])
|
|
else:
|
|
paths = env["LIBPATH"]
|
|
for search_path in paths:
|
|
check_path = path.join(search_path, dep)
|
|
if path.exists(check_path):
|
|
check_path = path.realpath(check_path)
|
|
src_dir, basefile = split_path(check_path)
|
|
src_path = path.join(src_dir, basefile)
|
|
dest_path = path.join(target[0].path, basefile)
|
|
if path.exists(dest_path):
|
|
break
|
|
Execute(Copy(dest_path, src_path))
|
|
bundle_libraries_for(target, [File(check_path)], env)
|
|
break
|
|
elif platform == "win32":
|
|
if 'msvc' in env['TOOLS']:
|
|
env.Append(
|
|
LINKFLAGS=['/SUBSYSTEM:WINDOWS','/ENTRY:mainCRTStartup','/MACHINE:X86'],
|
|
CXXFLAGS=['/EHsc','/MD','/FIglobal.hpp'],
|
|
LIBPATH=("C:\Program Files (x86)\Microsoft Visual Studio " + env['MSVC_VERSION'] + "\VC\lib"),
|
|
LIBS=Split("""
|
|
kernel32
|
|
user32
|
|
gdi32
|
|
winspool
|
|
comdlg32
|
|
advapi32
|
|
shell32
|
|
ole32
|
|
oleaut32
|
|
uuid
|
|
odbc32
|
|
odbccp32
|
|
""")
|
|
)
|
|
else:
|
|
env.Append(CXXFLAGS=["-include","global.hpp"])
|
|
def build_app_package(env, source, build_dir, info):
|
|
env.Install(build_dir, source)
|
|
elif platform == "posix":
|
|
env.Append(CXXFLAGS=["-std=c++14","-include","global.hpp"])
|
|
def build_app_package(env, source, build_dir, info):
|
|
env.Install(build_dir, source)
|
|
|
|
env.AddMethod(build_app_package, "Package")
|
|
|
|
# Allow user to specify additional library/include paths
|
|
env.Append(
|
|
LIBPATH=ARGUMENTS.get('LIBPATH', '').split(path.pathsep),
|
|
CPPPATH=ARGUMENTS.get('INCLUDEPATH', '').split(path.pathsep)
|
|
)
|
|
if platform == 'darwin':
|
|
env.Append(FRAMEWORKPATH=ARGUMENTS.get('FRAMEWORKPATH', '').split(path.pathsep))
|
|
# If any package managers are installed, add their dirs too.
|
|
if subprocess.call(['which', '-s', 'port']) == 0: # MacPorts
|
|
env.Append(
|
|
LIBPATH=['/opt/local/lib'],
|
|
CPPPATH=['/opt/local/include'],
|
|
FRAMEWORKPATH=['/opt/local/Library/Frameworks']
|
|
)
|
|
|
|
if subprocess.call(['which', '-s', 'fink']) == 0: # Fink
|
|
env.Append(
|
|
LIBPATH=['/sw/lib'],
|
|
CPPPATH=['/sw/include']
|
|
)
|
|
|
|
# pretty sketchy, but should point to your boost install
|
|
if subprocess.call(['which', '-s', 'brew']) == 0: # HomeBrew
|
|
brew_boost_version = '1.58.0'
|
|
env.Append(
|
|
LIBPATH=['/usr/local/Cellar/boost/'+brew_boost_version+'/lib'],
|
|
CPPPATH=['/usr/local/Cellar/boost/'+brew_boost_version+'/include'])
|
|
|
|
# Sometimes it's easier just to copy the dependencies into the repo dir
|
|
# We try to auto-detect this.
|
|
if path.exists('deps/lib'):
|
|
env.Append(LIBPATH=['deps/lib'])
|
|
if platform == 'darwin':
|
|
env.Append(FRAMEWORKPATH=['deps/lib'])
|
|
|
|
if path.exists('deps/include'):
|
|
env.Append(CPPPATH=['deps/include'])
|
|
|
|
# Include directories
|
|
|
|
|
|
env.Append(CPPPATH=Split("""
|
|
#src/
|
|
#src/fileio/gzstream/
|
|
#src/fileio/xml-parser/
|
|
"""))
|
|
|
|
env['CONFIGUREDIR'] = '#build/conf'
|
|
env['CONFIGURELOG'] = '#build/conf/config.log'
|
|
if not env.GetOption('clean'):
|
|
conf = Configure(env)
|
|
|
|
if not conf.CheckCC() or not conf.CheckCXX():
|
|
print("There's a problem with your compiler!")
|
|
Exit(1)
|
|
|
|
if not conf.CheckLib('zlib' if (platform == "win32" and 'mingw' not in env["TOOLS"]) else 'z'):
|
|
print('zlib must be installed!')
|
|
Exit(1)
|
|
|
|
def check_lib(lib, disp, suffixes=[], versions=[]):
|
|
if platform == "win32" and lib.startswith("boost"):
|
|
lib = "lib" + lib
|
|
if "mingw" in env["TOOLS"] and lib.startswith("sfml"):
|
|
lib = "lib" + lib
|
|
possible_names = [lib]
|
|
if platform == "win32":
|
|
if 'msvc' in env['TOOLS']:
|
|
vc_suffix = '-vc' + env['MSVC_VERSION'].replace('.','')
|
|
possible_names.append(lib + vc_suffix)
|
|
n = len(possible_names)
|
|
for i in range(n):
|
|
for suff in suffixes:
|
|
possible_names.append(possible_names[i] + suff)
|
|
for test in possible_names:
|
|
if conf.CheckLib(test, language='C++'):
|
|
bundled_libs.append(test)
|
|
return # Success!
|
|
for ver in versions:
|
|
if conf.CheckLib(test + ver, language='C++'):
|
|
bundled_libs.append(test + ver)
|
|
return # Success!
|
|
print(disp, 'must be installed!')
|
|
print(" If you're sure it's installed, try passing LIBPATH=...")
|
|
Exit(1)
|
|
|
|
def check_header(header, disp):
|
|
if not conf.CheckCXXHeader(header, '<>'):
|
|
print(disp, 'must be installed!')
|
|
print(" If you're sure it's installed, try passing INCLUDEPATH=...")
|
|
Exit(1)
|
|
|
|
boost_versions = ['-1_54', '-1_55', '-1_56', '-1_57', '-1_58'] # This is a bit of a hack. :(
|
|
bundled_libs = []
|
|
|
|
|
|
check_header('boost/lexical_cast.hpp', 'Boost.LexicalCast')
|
|
check_header('boost/optional.hpp', 'Boost.Optional')
|
|
check_header('boost/ptr_container/ptr_container.hpp', 'Boost.PointerContainer')
|
|
check_header('boost/any.hpp', 'Boost.Any')
|
|
check_header('boost/math_fwd.hpp', 'Boost.Math')
|
|
check_header('boost/spirit/include/classic.hpp', 'Boost.Spirit.Classic')
|
|
check_lib('boost_system', 'Boost.System', ['-mt'], boost_versions)
|
|
check_lib('boost_filesystem', 'Boost.Filesystem', ['-mt'], boost_versions)
|
|
check_lib('sfml-system', 'SFML-system')
|
|
check_lib('sfml-window', 'SFML-window')
|
|
check_lib('sfml-audio', 'SFML-audio')
|
|
check_lib('sfml-graphics', 'SFML-graphics')
|
|
|
|
env = conf.Finish()
|
|
|
|
env.Append(CPPDEFINES=["TIXML_USE_TICPP"])
|
|
|
|
if platform == "win32":
|
|
# For the *resource.h headers
|
|
env.Append(CPPPATH=["#rsrc/menus"])
|
|
|
|
if platform == "darwin":
|
|
env.Append(LIBS=Split("""
|
|
objc
|
|
c++
|
|
"""))
|
|
env.Append(FRAMEWORKS=Split("""
|
|
OpenGL
|
|
Cocoa
|
|
"""))
|
|
elif platform == "win32":
|
|
env.Append(LIBS=Split("""
|
|
opengl32
|
|
"""))
|
|
elif platform == "posix":
|
|
env.Append(LIBS=Split("""
|
|
GL
|
|
X11
|
|
tgui
|
|
"""))
|
|
|
|
Export("env platform")
|
|
|
|
# The VariantDir directives near the top mean that the SConscript files are
|
|
# copied from src/ and test/ into build/obj/ and build/obj/test respectively.
|
|
# Thus, any edits to them should be made to the originals in src/ or test/.
|
|
# However, when referencing them we have to reference the copies.
|
|
|
|
# Gather common sources
|
|
|
|
tools = SConscript("build/obj/tools/SConscript")
|
|
dlog_util = SConscript("build/obj/dialogxml/SConscript")
|
|
common_sources = dlog_util + tools
|
|
install_dir = "#build/Blades of Exile"
|
|
party_classes = Glob("#src/universe/*.cpp")
|
|
Export("install_dir party_classes common_sources")
|
|
|
|
# Programs
|
|
|
|
SConscript([
|
|
"build/obj/game/SConscript",
|
|
"build/obj/pcedit/SConscript",
|
|
"build/obj/scenedit/SConscript",
|
|
"build/obj/test/SConscript"
|
|
])
|
|
|
|
# Data files
|
|
|
|
data_dir = path.join(install_dir, "data")
|
|
Export("data_dir")
|
|
SConscript(["rsrc/SConscript", "doc/SConscript"])
|
|
|
|
# Bundle required frameworks and libraries
|
|
|
|
if platform == "darwin":
|
|
targets = [
|
|
"Blades of Exile",
|
|
"BoE Character Editor",
|
|
"BoE Scenario Editor",
|
|
]
|
|
for targ in targets:
|
|
target_dir = path.join(install_dir, targ + '.app', 'Contents/Frameworks')
|
|
binary = path.join(install_dir, targ + '.app', 'Contents/MacOS', targ)
|
|
env.Command(Dir(target_dir), binary, [Delete(target_dir), bundle_libraries_for])
|
|
elif platform == "win32":
|
|
bundled_libs = Split("""
|
|
libsndfile-1
|
|
openal32
|
|
sfml-audio-2
|
|
sfml-graphics-2
|
|
sfml-system-2
|
|
sfml-window-2
|
|
zlib1
|
|
""")
|
|
target_dirs = ["#build/Blades of Exile", "#build/test"]
|
|
for lib in bundled_libs:
|
|
for lpath in env['LIBPATH']:
|
|
src_file = path.join(lpath, lib + ".dll")
|
|
if path.exists(src_file):
|
|
for targ in target_dirs:
|
|
env.Install(targ, src_file)
|
|
break
|
|
elif 'lib' in lpath:
|
|
src_file = path.join(lpath.replace('lib', 'bin'), lib + ".dll")
|
|
if path.exists(src_file):
|
|
for targ in target_dirs:
|
|
env.Install(targ, src_file)
|
|
break
|
|
# Extra: Microsoft redistributable libraries installer
|
|
if 'msvc' in env["TOOLS"]:
|
|
if path.exists("dep/VCRedistInstall.exe"):
|
|
env.Install("build/Blades of Exile/", "dep/VCRedistInstall.exe")
|
|
else:
|
|
print("WARNING: Cannot find installer for the MSVC redistributable libraries for your version of Visual Studio.")
|
|
print("Please download it from Microsoft's website and place it at:")
|
|
print(" dep/VCRedistInstall.exe")
|
|
# Create it so its lack doesn't cause makensis to break
|
|
# (Because the installer is an optional component.)
|
|
open("build/Blades of Exile/VCRedistInstall.exe", 'w').close()
|
|
|
|
if platform == "darwin":
|
|
env.VariantDir("#build/pkg", "pkg/mac")
|
|
SConscript("build/pkg/SConscript")
|
|
elif platform == "win32" and subprocess.call(['where', '/Q', 'makensis']) == 0:
|
|
env.VariantDir("#build/pkg", "pkg/win")
|
|
SConscript("build/pkg/SConscript")
|
|
|
|
# Cleanup
|
|
|
|
env.Clean('.', 'build')
|
|
env.Clean('.', Glob('.sconsign.*'))
|